Merge pull request #481 from pyfisch/headertests

Add tests for headers
This commit is contained in:
Sean McArthur
2015-04-27 11:12:57 -07:00
29 changed files with 272 additions and 78 deletions

View File

@@ -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)),
])));
}
}

View File

@@ -16,18 +16,8 @@ header! {
#[doc="Accept-Charset = 1#( ( charset / \"*\" ) [ weight ] )"]
#[doc="```"]
(AcceptCharset, "Accept-Charset") => (QualityItem<Charset>)+
}
#[test]
fn test_parse_header() {
use header::{self, q};
let a: AcceptCharset = header::Header::parse_header(
[b"iso-8859-5, iso-8859-6;q=0.8".to_vec()].as_ref()).unwrap();
let b = AcceptCharset(vec![
QualityItem { item: Charset::Iso_8859_5, quality: q(1.0) },
QualityItem { item: Charset::Iso_8859_6, quality: q(0.8) },
]);
assert_eq!(format!("{}", a), format!("{}", b));
assert_eq!(a, b);
test_accept_charset {
test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]);
}
}

View File

@@ -16,6 +16,17 @@ header! {
#[doc="codings = content-coding / \"identity\" / \"*\""]
#[doc="```"]
(AcceptEncoding, "Accept-Encoding") => (QualityItem<Encoding>)*
test_accept_encoding {
// From the RFC
test_header!(test1, vec![b"compress, gzip"]);
test_header!(test2, vec![b""]);
test_header!(test3, vec![b"*"]);
// Note: Removed quality 1 from gzip
test_header!(test4, vec![b"compress;q=0.5, gzip"]);
// FIXME: Formatting of 0 as quality value
// test_header!(test5, vec![b"gzip;q=1.0, identity; q=0.5, *;q=0"]);
}
}
#[cfg(test)]

View File

@@ -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)]

View File

@@ -8,4 +8,6 @@ header! {
#[doc="response to a preflight request, which header field names can be used"]
#[doc="during the actual request."]
(AccessControlAllowHeaders, "Access-Control-Allow-Headers") => (UniCase<String>)*
}
test_access_control_allow_headers {}
}

View File

@@ -8,4 +8,6 @@ header! {
#[doc="response to a preflight request, which methods can be used during the"]
#[doc="actual request."]
(AccessControlAllowMethods, "Access-Control-Allow-Methods") => (Method)*
test_access_control_allow_methods {}
}

View File

@@ -5,4 +5,6 @@ header! {
#[doc="The `Access-Control-Max-Age` header indicates how long the results of a"]
#[doc="preflight request can be cached in a preflight result cache."]
(AccessControlMaxAge, "Access-Control-Max-Age") => [u32]
}
test_access_control_max_age {}
}

View File

@@ -8,4 +8,6 @@ header! {
#[doc="be used in the actual request as part of the preflight request."]
#[doc="during the actual request."]
(AccessControlRequestHeaders, "Access-Control-Request-Headers") => (UniCase<String>)*
test_access_control_request_headers {}
}

View File

@@ -7,4 +7,6 @@ header! {
#[doc="The `Access-Control-Request-Method` header indicates which method will be"]
#[doc="used in the actual request as part of the preflight request."]
(AccessControlRequestMethod, "Access-Control-Request-Method") => [Method]
}
test_access_control_request_method {}
}

View File

@@ -13,23 +13,33 @@ header! {
#[doc="Allow = #method"]
#[doc="```"]
(Allow, "Allow") => (Method)*
}
#[cfg(test)]
mod tests {
use super::Allow;
use header::Header;
use method::Method::{self, Options, Get, Put, Post, Delete, Head, Trace, Connect, Patch, Extension};
#[test]
fn test_allow() {
let mut allow: Option<Allow>;
allow = Header::parse_header([b"OPTIONS,GET,PUT,POST,DELETE,HEAD,TRACE,CONNECT,PATCH,fOObAr".to_vec()].as_ref());
assert_eq!(allow, Some(Allow(vec![Options, Get, Put, Post, Delete, Head, Trace, Connect, Patch, Extension("fOObAr".to_string())])));
allow = Header::parse_header([b"".to_vec()].as_ref());
assert_eq!(allow, Some(Allow(Vec::<Method>::new())));
test_allow {
// From the RFC
test_header!(
test1,
vec![b"GET, HEAD, PUT"],
Some(HeaderField(vec![Method::Get, Method::Head, Method::Put])));
// Own tests
test_header!(
test2,
vec![b"OPTIONS, GET, PUT, POST, DELETE, HEAD, TRACE, CONNECT, PATCH, fOObAr"],
Some(HeaderField(vec![
Method::Options,
Method::Get,
Method::Put,
Method::Post,
Method::Delete,
Method::Head,
Method::Trace,
Method::Connect,
Method::Patch,
Method::Extension("fOObAr".to_string())])));
// FIXME: Formatting fails
// test_header!(
// test3,
// vec![b""],
// Some(HeaderField(Vec::<Method>::new())));
}
}

View File

@@ -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()] });

View File

@@ -16,6 +16,11 @@ header! {
#[doc="Content-Length = 1*DIGIT"]
#[doc="```"]
(ContentLength, "Content-Length") => [u64]
test_content_length {
// Testcase from RFC
test_header!(test1, vec![b"3495"], Some(HeaderField(3495)));
}
}
bench_header!(bench, ContentLength, { vec![b"42349984".to_vec()] });

View File

@@ -17,6 +17,15 @@ header! {
#[doc="Content-Type = media-type"]
#[doc="```"]
(ContentType, "Content-Type") => [Mime]
test_content_type {
test_header!(
test1,
// FIXME: Should be b"text/html; charset=ISO-8859-4" but mime crate lowercases
// the whole value so parsing and formatting the value gives a different result
vec![b"text/html; charset=iso-8859-4"],
Some(HeaderField(Mime(TopLevel::Text, SubLevel::Html, vec![(Attr::Charset, Value::Ext("iso-8859-4".to_string()))]))));
}
}
bench_header!(bench, ContentType, { vec![b"application/json; charset=utf-8".to_vec()] });

View File

@@ -11,6 +11,10 @@ header! {
#[doc="Date = HTTP-date"]
#[doc="```"]
(Date, "Date") => [HttpDate]
test_date {
test_header!(test1, vec![b"Tue, 15 Nov 1994 08:12:31 GMT"]);
}
}
bench_header!(imf_fixdate, Date, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] });

View File

@@ -18,6 +18,12 @@ header! {
#[doc="ETag = entity-tag"]
#[doc="```"]
(ETag, "ETag") => [EntityTag]
test_etag {
test_header!(test1, vec![b"\"xyzzy\""], Some(HeaderField(EntityTag::new(false, "xyzzy".to_string()))));
test_header!(test2, vec![b"W/\"xyzzy\""], Some(HeaderField(EntityTag::new(true, "xyzzy".to_string()))));
test_header!(test3, vec![b"\"\""], Some(HeaderField(EntityTag::new(false, "".to_string()))));
}
}
#[cfg(test)]

View File

@@ -15,6 +15,11 @@ header! {
#[doc="Expires = HTTP-date"]
#[doc="```"]
(Expires, "Expires") => [HttpDate]
test_expires {
// Testcase from RFC
test_header!(test1, vec![b"Thu, 01 Dec 1994 16:00:00 GMT"]);
}
}
bench_header!(imf_fixdate, Expires, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] });

View File

@@ -21,24 +21,21 @@ header! {
#[doc="If-Match = \"*\" / 1#entity-tag"]
#[doc="```"]
(IfMatch, "If-Match") => {Any / (EntityTag)+}
}
#[test]
fn test_parse_header() {
use header::Header;
{
let a: IfMatch = Header::parse_header(
[b"*".to_vec()].as_ref()).unwrap();
assert_eq!(a, IfMatch::Any);
}
{
let a: IfMatch = Header::parse_header(
[b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"".to_vec()].as_ref()).unwrap();
let b = IfMatch::Items(
vec![EntityTag::new(false, "xyzzy".to_string()),
EntityTag::new(false, "r2d2xxxx".to_string()),
EntityTag::new(false, "c3piozzzz".to_string())]);
assert_eq!(a, b);
test_if_match {
test_header!(
test1,
vec![b"\"xyzzy\""],
Some(HeaderField::Items(
vec![EntityTag::new(false, "xyzzy".to_string())])));
test_header!(
test2,
vec![b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\""],
Some(HeaderField::Items(
vec![EntityTag::new(false, "xyzzy".to_string()),
EntityTag::new(false, "r2d2xxxx".to_string()),
EntityTag::new(false, "c3piozzzz".to_string())])));
test_header!(test3, vec![b"*"], Some(IfMatch::Any));
}
}

View File

@@ -15,6 +15,11 @@ header! {
#[doc="If-Unmodified-Since = HTTP-date"]
#[doc="```"]
(IfModifiedSince, "If-Modified-Since") => [HttpDate]
test_if_modified_since {
// Testcase from RFC
test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]);
}
}
bench_header!(imf_fixdate, IfModifiedSince, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] });

View File

@@ -20,6 +20,14 @@ header! {
#[doc="If-None-Match = \"*\" / 1#entity-tag"]
#[doc="```"]
(IfNoneMatch, "If-None-Match") => {Any / (EntityTag)+}
test_if_none_match {
test_header!(test1, vec![b"\"xyzzy\""]);
test_header!(test2, vec![b"W/\"xyzzy\""]);
test_header!(test3, vec![b"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\""]);
test_header!(test4, vec![b"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\""]);
test_header!(test5, vec![b"*"]);
}
}
#[cfg(test)]

View File

@@ -15,6 +15,11 @@ header! {
#[doc="If-Unmodified-Since = HTTP-date"]
#[doc="```"]
(IfUnmodifiedSince, "If-Unmodified-Since") => [HttpDate]
test_if_unmodified_since {
// Testcase from RFC
test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]);
}
}
bench_header!(imf_fixdate, IfUnmodifiedSince, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] });

View File

@@ -13,6 +13,10 @@ header! {
#[doc="Expires = HTTP-date"]
#[doc="```"]
(LastModified, "Last-Modified") => [HttpDate]
test_last_modified {
// Testcase from RFC
test_header!(test1, vec![b"Sat, 29 Oct 1994 19:43:31 GMT"]);}
}
bench_header!(imf_fixdate, LastModified, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] });

View File

@@ -14,6 +14,12 @@ header! {
// TODO: Use URL
(Location, "Location") => [String]
test_location {
// Testcase from RFC
test_header!(test1, vec![b"/People.html#tim"]);
test_header!(test2, vec![b"http://www.example.net/index.html"]);
}
}
bench_header!(bench, Location, { vec![b"http://foo.com/hello:3000".to_vec()] });

View File

@@ -94,6 +94,35 @@ macro_rules! deref(
}
);
macro_rules! test_header {
($id:ident, $raw:expr) => {
#[test]
fn $id() {
use std::ascii::AsciiExt;
let raw = $raw;
let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect();
let value = HeaderField::parse_header(&a[..]);
let result = format!("{}", value.unwrap());
let expected = String::from_utf8(raw[0].to_vec()).unwrap();
let result_cmp: Vec<String> = result.to_ascii_lowercase().split(' ').map(|x| x.to_string()).collect();
let expected_cmp: Vec<String> = expected.to_ascii_lowercase().split(' ').map(|x| x.to_string()).collect();
assert_eq!(result_cmp.concat(), expected_cmp.concat());
}
};
($id:ident, $raw:expr, $typed:expr) => {
#[test]
fn $id() {
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)
@@ -102,7 +131,7 @@ macro_rules! header {
// $nn:expr: Nice name of the header
// List header, zero 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>);
@@ -126,10 +155,19 @@ macro_rules! header {
self.fmt_header(f)
}
}
#[allow(unused_imports)]
mod $tm{
use std::str;
use $crate::header::*;
use $crate::mime::*;
use $crate::method::Method;
use super::$id as HeaderField;
$($tf)*
}
};
// 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,9 +191,18 @@ macro_rules! header {
self.fmt_header(f)
}
}
#[allow(unused_imports)]
mod $tm{
use std::str;
use $crate::header::*;
use $crate::mime::*;
use $crate::method::Method;
use super::$id as HeaderField;
$($tf)*
}
};
// Single value header
($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty]) => {
($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty] $tm:ident{$($tf:item)*}) => {
$(#[$a])*
#[derive(Clone, Debug, PartialEq)]
pub struct $id(pub $value);
@@ -178,9 +225,18 @@ macro_rules! header {
::std::fmt::Display::fmt(&**self, f)
}
}
#[allow(unused_imports)]
mod $tm{
use std::str;
use $crate::header::*;
use $crate::mime::*;
use $crate::method::Method;
use super::$id as HeaderField;
$($tf)*
}
};
// List header, one or more items with "*" option
($(#[$a:meta])*($id:ident, $n:expr) => {Any / ($item:ty)+}) => {
($(#[$a:meta])*($id:ident, $n:expr) => {Any / ($item:ty)+} $tm:ident{$($tf:item)*}) => {
$(#[$a])*
#[derive(Clone, Debug, PartialEq)]
pub enum $id {
@@ -219,6 +275,15 @@ macro_rules! header {
self.fmt_header(f)
}
}
#[allow(unused_imports)]
mod $tm{
use std::str;
use $crate::header::*;
use $crate::mime::*;
use $crate::method::Method;
use super::$id as HeaderField;
$($tf)*
}
};
}

View File

@@ -14,6 +14,11 @@ header! {
#[doc="```"]
// TODO: Use URL
(Referer, "Referer") => [String]
test_referer {
// Testcase from the RFC
test_header!(test1, vec![b"http://www.example.org/hypertext/Overview.html"]);
}
}
bench_header!(bench, Referer, { vec![b"http://foo.com/hello:3000".to_vec()] });

View File

@@ -15,6 +15,11 @@ header! {
#[doc="```"]
// TODO: Maybe parse as defined in the spec?
(Server, "Server") => [String]
test_server {
// Testcase from RFC
test_header!(test1, vec![b"CERN/3.0 libwww/2.17"]);
}
}
bench_header!(bench, Server, { vec![b"Some String".to_vec()] });

View File

@@ -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()] });

View File

@@ -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.

View File

@@ -18,6 +18,11 @@ header! {
#[doc="```"]
// TODO: Maybe write parsing according to the spec? (Split the String)
(UserAgent, "User-Agent") => [String]
test_user_agent {
// Testcase from RFC
test_header!(test1, vec![b"CERN-LineMode/2.15 libwww/2.17b3"]);
}
}
#[test] fn test_format() {

View File

@@ -15,6 +15,10 @@ header! {
#[doc="Vary = \"*\" / 1#field-name"]
#[doc="```"]
(Vary, "Vary") => {Any / (UniCase<String>)+}
test_vary {
test_header!(test1, vec![b"accept-encoding, accept-language"]);
}
}
#[cfg(test)]