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="* Using always Mime types to represent `media-range` differs from the ABNF."] | ||||||
|     #[doc="* **FIXME**: `accept-ext` is not supported."] |     #[doc="* **FIXME**: `accept-ext` is not supported."] | ||||||
|     (Accept, "Accept") => (QualityItem<Mime>)+ |     (Accept, "Accept") => (QualityItem<Mime>)+ | ||||||
| } |  | ||||||
|  |  | ||||||
| #[cfg(test)] |     test_accept { | ||||||
| mod tests { |         // Tests from the RFC | ||||||
|     use mime::*; |         // FIXME: Test fails, first value containing a "*" fails to parse | ||||||
|  |         // test_header!( | ||||||
|     use header::{Header, Quality, QualityItem, qitem}; |         //    test1, | ||||||
|  |         //    vec![b"audio/*; q=0.2, audio/basic"], | ||||||
|     use super::Accept; |         //    Some(HeaderField(vec![ | ||||||
|  |         //        QualityItem::new(Mime(TopLevel::Audio, SubLevel::Star, vec![]), Quality(200)), | ||||||
|     #[test] |         //        qitem(Mime(TopLevel::Audio, SubLevel::Ext("basic".to_string()), vec![])), | ||||||
|     fn test_parse_header_no_quality() { |         //        ]))); | ||||||
|         let a: Accept = Header::parse_header([b"text/plain; charset=utf-8".to_vec()].as_ref()).unwrap(); |         test_header!( | ||||||
|         let b = Accept(vec![ |             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)])), |                 qitem(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])), | ||||||
|         ]); |                 ]))); | ||||||
|         assert_eq!(a, b); |         test_header!( | ||||||
|     } |             test4, | ||||||
|  |             vec![b"text/plain; charset=utf-8; q=0.5"], | ||||||
|     #[test] |             Some(Accept(vec![ | ||||||
|     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)), |                 QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]), Quality(500)), | ||||||
|         ]); |             ]))); | ||||||
|         assert_eq!(a, b); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,6 +16,10 @@ header! { | |||||||
|     #[doc="Accept-Charset = 1#( ( charset / \"*\" ) [ weight ] )"] |     #[doc="Accept-Charset = 1#( ( charset / \"*\" ) [ weight ] )"] | ||||||
|     #[doc="```"] |     #[doc="```"] | ||||||
|     (AcceptCharset, "Accept-Charset") => (QualityItem<Charset>)+ |     (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="language-range  = <language-range, see [RFC4647], Section 2.1>"] | ||||||
|     #[doc="```"] |     #[doc="```"] | ||||||
|     (AcceptLanguage, "Accept-Language") => (QualityItem<Language>)+ |     (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)] | #[cfg(test)] | ||||||
|   | |||||||
| @@ -17,6 +17,8 @@ header! { | |||||||
|     #[doc="Content-Encoding = 1#content-coding"] |     #[doc="Content-Encoding = 1#content-coding"] | ||||||
|     #[doc="```"] |     #[doc="```"] | ||||||
|     (ContentEncoding, "Content-Encoding") => (Encoding)+ |     (ContentEncoding, "Content-Encoding") => (Encoding)+ | ||||||
|  |  | ||||||
|  |     test_content_encoding {} | ||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(single, ContentEncoding, { vec![b"gzip".to_vec()] }); | 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_export] | ||||||
| macro_rules! header { | macro_rules! header { | ||||||
|     // $a:meta: Attributes associated with the header item (usually docs) |     // $a:meta: Attributes associated with the header item (usually docs) | ||||||
| @@ -129,7 +152,7 @@ macro_rules! header { | |||||||
|  |  | ||||||
|     }; |     }; | ||||||
|     // List header, one or more items |     // 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])* |         $(#[$a])* | ||||||
|         #[derive(Clone, Debug, PartialEq)] |         #[derive(Clone, Debug, PartialEq)] | ||||||
|         pub struct $id(pub Vec<$item>); |         pub struct $id(pub Vec<$item>); | ||||||
| @@ -153,6 +176,14 @@ macro_rules! header { | |||||||
|                 self.fmt_header(f) |                 self.fmt_header(f) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         #[allow(unused_imports)] | ||||||
|  |         mod $tm{ | ||||||
|  |             use $crate::header::*; | ||||||
|  |             use $crate::mime::*; | ||||||
|  |             use super::$id as HeaderField; | ||||||
|  |             $($tf)* | ||||||
|  |         } | ||||||
|     }; |     }; | ||||||
|     // Single value header |     // Single value header | ||||||
|     ($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty]) => { |     ($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty]) => { | ||||||
|   | |||||||
| @@ -14,6 +14,16 @@ header! { | |||||||
|     #[doc="Transfer-Encoding = 1#transfer-coding"] |     #[doc="Transfer-Encoding = 1#transfer-coding"] | ||||||
|     #[doc="```"] |     #[doc="```"] | ||||||
|     (TransferEncoding, "Transfer-Encoding") => (Encoding)+ |     (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()] }); | bench_header!(normal, TransferEncoding, { vec![b"chunked, gzip".to_vec()] }); | ||||||
|   | |||||||
| @@ -26,6 +26,18 @@ header! { | |||||||
|     #[doc="protocol-version = token"] |     #[doc="protocol-version = token"] | ||||||
|     #[doc="```"] |     #[doc="```"] | ||||||
|     (Upgrade, "Upgrade") => (Protocol)+ |     (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. | /// Protocol values that can appear in the Upgrade header. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user