test(headers): Allow tests inside list header macros, add tests.
Adds test cases from the relevant RFCs for a few headers. See also: #468, do we want the test cases rendered as examples in the docs?
This commit is contained in:
		| @@ -27,32 +27,39 @@ header! { | ||||
|     #[doc="* Using always Mime types to represent `media-range` differs from the ABNF."] | ||||
|     #[doc="* **FIXME**: `accept-ext` is not supported."] | ||||
|     (Accept, "Accept") => (QualityItem<Mime>)+ | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use mime::*; | ||||
|  | ||||
|     use header::{Header, Quality, QualityItem, qitem}; | ||||
|  | ||||
|     use super::Accept; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_header_no_quality() { | ||||
|         let a: Accept = Header::parse_header([b"text/plain; charset=utf-8".to_vec()].as_ref()).unwrap(); | ||||
|         let b = Accept(vec![ | ||||
|             qitem(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])), | ||||
|         ]); | ||||
|         assert_eq!(a, b); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_header_with_quality() { | ||||
|         let a: Accept = Header::parse_header([b"text/plain; charset=utf-8; q=0.5".to_vec()].as_ref()).unwrap(); | ||||
|         let b = Accept(vec![ | ||||
|             QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]), Quality(500)), | ||||
|         ]); | ||||
|         assert_eq!(a, b); | ||||
|     test_accept { | ||||
|         // Tests from the RFC | ||||
|         // FIXME: Test fails, first value containing a "*" fails to parse | ||||
|         // test_header!( | ||||
|         //    test1, | ||||
|         //    vec![b"audio/*; q=0.2, audio/basic"], | ||||
|         //    Some(HeaderField(vec![ | ||||
|         //        QualityItem::new(Mime(TopLevel::Audio, SubLevel::Star, vec![]), Quality(200)), | ||||
|         //        qitem(Mime(TopLevel::Audio, SubLevel::Ext("basic".to_string()), vec![])), | ||||
|         //        ]))); | ||||
|         test_header!( | ||||
|             test2, | ||||
|             vec![b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"], | ||||
|             Some(HeaderField(vec![ | ||||
|                 QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![]), Quality(500)), | ||||
|                 qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])), | ||||
|                 QualityItem::new(Mime(TopLevel::Text, SubLevel::Ext("x-dvi".to_string()), vec![]), Quality(800)), | ||||
|                 qitem(Mime(TopLevel::Text, SubLevel::Ext("x-c".to_string()), vec![])), | ||||
|                 ]))); | ||||
|         // Custom tests | ||||
|         test_header!( | ||||
|             test3, | ||||
|             vec![b"text/plain; charset=utf-8"], | ||||
|             Some(Accept(vec![ | ||||
|                 qitem(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])), | ||||
|                 ]))); | ||||
|         test_header!( | ||||
|             test4, | ||||
|             vec![b"text/plain; charset=utf-8; q=0.5"], | ||||
|             Some(Accept(vec![ | ||||
|                 QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]), Quality(500)), | ||||
|             ]))); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,10 @@ header! { | ||||
|     #[doc="Accept-Charset = 1#( ( charset / \"*\" ) [ weight ] )"] | ||||
|     #[doc="```"] | ||||
|     (AcceptCharset, "Accept-Charset") => (QualityItem<Charset>)+ | ||||
|  | ||||
|     test_accept_charset { | ||||
|         test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -49,6 +49,10 @@ header! { | ||||
|     #[doc="language-range  = <language-range, see [RFC4647], Section 2.1>"] | ||||
|     #[doc="```"] | ||||
|     (AcceptLanguage, "Accept-Language") => (QualityItem<Language>)+ | ||||
|  | ||||
|     test_accept_language { | ||||
|         test_header!(test1, vec![b"da, en-gb;q=0.8, en;q=0.7"]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
|   | ||||
| @@ -17,6 +17,8 @@ header! { | ||||
|     #[doc="Content-Encoding = 1#content-coding"] | ||||
|     #[doc="```"] | ||||
|     (ContentEncoding, "Content-Encoding") => (Encoding)+ | ||||
|  | ||||
|     test_content_encoding {} | ||||
| } | ||||
|  | ||||
| bench_header!(single, ContentEncoding, { vec![b"gzip".to_vec()] }); | ||||
|   | ||||
| @@ -94,6 +94,29 @@ macro_rules! deref( | ||||
|     } | ||||
| ); | ||||
|  | ||||
| macro_rules! test_header { | ||||
|     ($id:ident, $test:expr) => { | ||||
|         #[test] | ||||
|         fn $id() { | ||||
|             let a: Vec<Vec<u8>> = $test.iter().map(|x| x.to_vec()).collect(); | ||||
|             HeaderField::parse_header(&a[..]).unwrap(); | ||||
|         } | ||||
|     }; | ||||
|     ($id:ident, $raw:expr, $typed:expr) => { | ||||
|         #[test] | ||||
|         fn $id() { | ||||
|             use std::str; | ||||
|             let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect(); | ||||
|             let val = HeaderField::parse_header(&a[..]); | ||||
|             // Test parsing | ||||
|             assert_eq!(val, $typed); | ||||
|             // Test formatting | ||||
|             let res: &str = str::from_utf8($raw[0]).unwrap(); | ||||
|             assert_eq!(format!("{}", $typed.unwrap()), res); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! header { | ||||
|     // $a:meta: Attributes associated with the header item (usually docs) | ||||
| @@ -129,7 +152,7 @@ macro_rules! header { | ||||
|  | ||||
|     }; | ||||
|     // List header, one or more items | ||||
|     ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)+) => { | ||||
|     ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)+ $tm:ident{$($tf:item)*}) => { | ||||
|         $(#[$a])* | ||||
|         #[derive(Clone, Debug, PartialEq)] | ||||
|         pub struct $id(pub Vec<$item>); | ||||
| @@ -153,6 +176,14 @@ macro_rules! header { | ||||
|                 self.fmt_header(f) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         #[allow(unused_imports)] | ||||
|         mod $tm{ | ||||
|             use $crate::header::*; | ||||
|             use $crate::mime::*; | ||||
|             use super::$id as HeaderField; | ||||
|             $($tf)* | ||||
|         } | ||||
|     }; | ||||
|     // Single value header | ||||
|     ($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty]) => { | ||||
|   | ||||
| @@ -14,6 +14,16 @@ header! { | ||||
|     #[doc="Transfer-Encoding = 1#transfer-coding"] | ||||
|     #[doc="```"] | ||||
|     (TransferEncoding, "Transfer-Encoding") => (Encoding)+ | ||||
|  | ||||
|     transfer_encoding { | ||||
|         test_header!( | ||||
|             test1, | ||||
|             vec![b"gzip, chunked"], | ||||
|             Some(HeaderField( | ||||
|                 vec![Encoding::Gzip, Encoding::Chunked] | ||||
|                 ))); | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| bench_header!(normal, TransferEncoding, { vec![b"chunked, gzip".to_vec()] }); | ||||
|   | ||||
| @@ -26,6 +26,18 @@ header! { | ||||
|     #[doc="protocol-version = token"] | ||||
|     #[doc="```"] | ||||
|     (Upgrade, "Upgrade") => (Protocol)+ | ||||
|  | ||||
|     test_upgrade { | ||||
|         test_header!( | ||||
|             test1, | ||||
|             vec![b"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11"], | ||||
|             Some(HeaderField(vec![ | ||||
|                 Protocol::ProtocolExt("HTTP/2.0".to_string()), | ||||
|                 Protocol::ProtocolExt("SHTTP/1.3".to_string()), | ||||
|                 Protocol::ProtocolExt("IRC/6.9".to_string()), | ||||
|                 Protocol::ProtocolExt("RTA/x11".to_string()), | ||||
|                 ]))); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Protocol values that can appear in the Upgrade header. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user