From e2aca2074679c284064a0df447f4eff4cd4f1fbf Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 4 Feb 2016 17:05:33 +0100 Subject: [PATCH] Make each parsing test pass/fail separately. --- Cargo.toml | 12 +++ tests/tests.rs | 193 +----------------------------------------- tests/wpt.rs | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 192 deletions(-) create mode 100644 tests/wpt.rs diff --git a/Cargo.toml b/Cargo.toml index b0475198..428b6273 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,18 @@ readme = "README.md" keywords = ["url", "parser"] license = "MIT/Apache-2.0" +[[test]] name = "format" +[[test]] name = "form_urlencoded" +[[test]] name = "idna" +[[test]] name = "punycode" +[[test]] name = "tests" +[[test]] +name = "wpt" +harness = false + +[dev-dependencies] +rustc-test = "0.1" + [features] query_encoding = ["encoding"] serde_serialization = ["serde"] diff --git a/tests/tests.rs b/tests/tests.rs index 0c90e790..45c403db 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -8,199 +8,8 @@ extern crate url; -use std::char; use std::net::{Ipv4Addr, Ipv6Addr}; -use url::{Host, RelativeSchemeData, SchemeData, Url}; - - -#[test] -fn url_parsing() { - for test in parse_test_data(include_str!("urltestdata.txt")) { - let Test { - input, - base, - scheme: expected_scheme, - username: expected_username, - password: expected_password, - host: expected_host, - port: expected_port, - path: expected_path, - query: expected_query, - fragment: expected_fragment, - expected_failure, - } = test; - let base = match Url::parse(&base) { - Ok(base) => base, - Err(message) => panic!("Error parsing base {}: {}", base, message) - }; - let url = base.join(&input); - if expected_scheme.is_none() { - if url.is_ok() && !expected_failure { - panic!("Expected a parse error for URL {}", input); - } - continue - } - let Url { scheme, scheme_data, query, fragment, .. } = match url { - Ok(url) => url, - Err(message) => { - if expected_failure { - continue - } else { - panic!("Error parsing URL {}: {}", input, message) - } - } - }; - - macro_rules! assert_eq { - ($a: expr, $b: expr) => { - { - let a = $a; - let b = $b; - if a != b { - if expected_failure { - continue - } else { - panic!("{:?} != {:?}", a, b) - } - } - } - } - } - - assert_eq!(Some(scheme), expected_scheme); - match scheme_data { - SchemeData::Relative(RelativeSchemeData { - username, password, host, port, default_port: _, path, - }) => { - assert_eq!(username, expected_username); - assert_eq!(password, expected_password); - let host = host.serialize(); - assert_eq!(host, expected_host); - assert_eq!(port, expected_port); - assert_eq!(Some(format!("/{}", str_join(&path, "/"))), expected_path); - }, - SchemeData::NonRelative(scheme_data) => { - assert_eq!(Some(scheme_data), expected_path); - assert_eq!(String::new(), expected_username); - assert_eq!(None, expected_password); - assert_eq!(String::new(), expected_host); - assert_eq!(None, expected_port); - }, - } - fn opt_prepend(prefix: &str, opt_s: Option) -> Option { - opt_s.map(|s| format!("{}{}", prefix, s)) - } - assert_eq!(opt_prepend("?", query), expected_query); - assert_eq!(opt_prepend("#", fragment), expected_fragment); - - assert!(!expected_failure, "Unexpected success for {}", input); - } -} - -// FIMXE: Remove this when &[&str]::join (the new name) lands in the stable channel. -#[allow(deprecated)] -fn str_join>(pieces: &[T], separator: &str) -> String { - pieces.connect(separator) -} - -struct Test { - input: String, - base: String, - scheme: Option, - username: String, - password: Option, - host: String, - port: Option, - path: Option, - query: Option, - fragment: Option, - expected_failure: bool, -} - -fn parse_test_data(input: &str) -> Vec { - let mut tests: Vec = Vec::new(); - for line in input.lines() { - if line == "" || line.starts_with("#") { - continue - } - let mut pieces = line.split(' ').collect::>(); - let expected_failure = pieces[0] == "XFAIL"; - if expected_failure { - pieces.remove(0); - } - let input = unescape(pieces.remove(0)); - let mut test = Test { - input: input, - base: if pieces.is_empty() || pieces[0] == "" { - tests.last().unwrap().base.clone() - } else { - unescape(pieces.remove(0)) - }, - scheme: None, - username: String::new(), - password: None, - host: String::new(), - port: None, - path: None, - query: None, - fragment: None, - expected_failure: expected_failure, - }; - for piece in pieces { - if piece == "" || piece.starts_with("#") { - continue - } - let colon = piece.find(':').unwrap(); - let value = unescape(&piece[colon + 1..]); - match &piece[..colon] { - "s" => test.scheme = Some(value), - "u" => test.username = value, - "pass" => test.password = Some(value), - "h" => test.host = value, - "port" => test.port = Some(value.parse().unwrap()), - "p" => test.path = Some(value), - "q" => test.query = Some(value), - "f" => test.fragment = Some(value), - _ => panic!("Invalid token") - } - } - tests.push(test) - } - tests -} - -fn unescape(input: &str) -> String { - let mut output = String::new(); - let mut chars = input.chars(); - loop { - match chars.next() { - None => return output, - Some(c) => output.push( - if c == '\\' { - match chars.next().unwrap() { - '\\' => '\\', - 'n' => '\n', - 'r' => '\r', - 's' => ' ', - 't' => '\t', - 'f' => '\x0C', - 'u' => { - char::from_u32(((( - chars.next().unwrap().to_digit(16).unwrap()) * 16 + - chars.next().unwrap().to_digit(16).unwrap()) * 16 + - chars.next().unwrap().to_digit(16).unwrap()) * 16 + - chars.next().unwrap().to_digit(16).unwrap()).unwrap() - } - _ => panic!("Invalid test data input"), - } - } else { - c - } - ) - } - } -} - +use url::{Host, Url}; #[test] fn new_file_paths() { diff --git a/tests/wpt.rs b/tests/wpt.rs new file mode 100644 index 00000000..6a32287b --- /dev/null +++ b/tests/wpt.rs @@ -0,0 +1,223 @@ +// Copyright 2013-2014 Simon Sapin. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Tests copied form https://github.com/w3c/web-platform-tests/blob/master/url/ + +extern crate test; +extern crate url; + +use std::char; +use url::{RelativeSchemeData, SchemeData, Url}; + + +fn run_one(entry: Entry) { + // FIXME: Don’t re-indent to make merging the 1.0 branch easier. + { + let Entry { + input, + base, + scheme: expected_scheme, + username: expected_username, + password: expected_password, + host: expected_host, + port: expected_port, + path: expected_path, + query: expected_query, + fragment: expected_fragment, + expected_failure, + } = entry; + let base = match Url::parse(&base) { + Ok(base) => base, + Err(message) => panic!("Error parsing base {}: {}", base, message) + }; + let url = base.join(&input); + if expected_scheme.is_none() { + if url.is_ok() && !expected_failure { + panic!("Expected a parse error for URL {}", input); + } + return + } + let Url { scheme, scheme_data, query, fragment, .. } = match url { + Ok(url) => url, + Err(message) => { + if expected_failure { + return + } else { + panic!("Error parsing URL {}: {}", input, message) + } + } + }; + + macro_rules! assert_eq { + ($a: expr, $b: expr) => { + { + let a = $a; + let b = $b; + if a != b { + if expected_failure { + return + } else { + panic!("{:?} != {:?}", a, b) + } + } + } + } + } + + assert_eq!(Some(scheme), expected_scheme); + match scheme_data { + SchemeData::Relative(RelativeSchemeData { + username, password, host, port, default_port: _, path, + }) => { + assert_eq!(username, expected_username); + assert_eq!(password, expected_password); + let host = host.serialize(); + assert_eq!(host, expected_host); + assert_eq!(port, expected_port); + assert_eq!(Some(format!("/{}", str_join(&path, "/"))), expected_path); + }, + SchemeData::NonRelative(scheme_data) => { + assert_eq!(Some(scheme_data), expected_path); + assert_eq!(String::new(), expected_username); + assert_eq!(None, expected_password); + assert_eq!(String::new(), expected_host); + assert_eq!(None, expected_port); + }, + } + fn opt_prepend(prefix: &str, opt_s: Option) -> Option { + opt_s.map(|s| format!("{}{}", prefix, s)) + } + assert_eq!(opt_prepend("?", query), expected_query); + assert_eq!(opt_prepend("#", fragment), expected_fragment); + + assert!(!expected_failure, "Unexpected success for {}", input); + } +} + +// FIMXE: Remove this when &[&str]::join (the new name) lands in the stable channel. +#[allow(deprecated)] +fn str_join>(pieces: &[T], separator: &str) -> String { + pieces.connect(separator) +} + +struct Entry { + input: String, + base: String, + scheme: Option, + username: String, + password: Option, + host: String, + port: Option, + path: Option, + query: Option, + fragment: Option, + expected_failure: bool, +} + +fn parse_test_data(input: &str) -> Vec { + let mut tests: Vec = Vec::new(); + for line in input.lines() { + if line == "" || line.starts_with("#") { + continue + } + let mut pieces = line.split(' ').collect::>(); + let expected_failure = pieces[0] == "XFAIL"; + if expected_failure { + pieces.remove(0); + } + let input = unescape(pieces.remove(0)); + let mut test = Entry { + input: input, + base: if pieces.is_empty() || pieces[0] == "" { + tests.last().unwrap().base.clone() + } else { + unescape(pieces.remove(0)) + }, + scheme: None, + username: String::new(), + password: None, + host: String::new(), + port: None, + path: None, + query: None, + fragment: None, + expected_failure: expected_failure, + }; + for piece in pieces { + if piece == "" || piece.starts_with("#") { + continue + } + let colon = piece.find(':').unwrap(); + let value = unescape(&piece[colon + 1..]); + match &piece[..colon] { + "s" => test.scheme = Some(value), + "u" => test.username = value, + "pass" => test.password = Some(value), + "h" => test.host = value, + "port" => test.port = Some(value.parse().unwrap()), + "p" => test.path = Some(value), + "q" => test.query = Some(value), + "f" => test.fragment = Some(value), + _ => panic!("Invalid token") + } + } + tests.push(test) + } + tests +} + +fn unescape(input: &str) -> String { + let mut output = String::new(); + let mut chars = input.chars(); + loop { + match chars.next() { + None => return output, + Some(c) => output.push( + if c == '\\' { + match chars.next().unwrap() { + '\\' => '\\', + 'n' => '\n', + 'r' => '\r', + 's' => ' ', + 't' => '\t', + 'f' => '\x0C', + 'u' => { + char::from_u32(((( + chars.next().unwrap().to_digit(16).unwrap()) * 16 + + chars.next().unwrap().to_digit(16).unwrap()) * 16 + + chars.next().unwrap().to_digit(16).unwrap()) * 16 + + chars.next().unwrap().to_digit(16).unwrap()).unwrap() + } + _ => panic!("Invalid test data input"), + } + } else { + c + } + ) + } + } +} + +fn make_test(entry: Entry) -> test::TestDescAndFn { + test::TestDescAndFn { + desc: test::TestDesc { + name: test::DynTestName(format!("{:?} base {:?}", entry.input, entry.base)), + ignore: false, + should_panic: test::ShouldPanic::No, + }, + testfn: test::TestFn::dyn_test_fn(move || run_one(entry)), + } + +} + +fn main() { + test::test_main( + &std::env::args().collect::>(), + parse_test_data(include_str!("urltestdata.txt")).into_iter().map(make_test).collect(), + ) +}