| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- // Copyright 2013-2014 The rust-url developers.
- //
- // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
- // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
- // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
- // option. This file may not be copied, modified, or distributed
- // except according to those terms.
- //! Data-driven tests imported from web-platform-tests
- use serde_json::Value;
- use std::collections::HashMap;
- use std::fmt::Write;
- #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
- use std::sync::Mutex;
- use url::Url;
- // https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/usage.html
- #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
- use wasm_bindgen_test::{console_log, wasm_bindgen_test, wasm_bindgen_test_configure};
- #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
- wasm_bindgen_test_configure!(run_in_browser);
- // wpt has its own test driver, but we shoe-horn this into wasm_bindgen_test
- // which will discard stdout and stderr. So, we make println! go to
- // console.log(), so we see failures that do not result in panics.
- #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
- static PRINT_BUF: Mutex<Option<String>> = Mutex::new(None);
- #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
- macro_rules! print {
- ($($arg:tt)*) => {
- let v = format!($($arg)*);
- {
- let mut buf = PRINT_BUF.lock().unwrap();
- if let Some(buf) = buf.as_mut() {
- buf.push_str(&v);
- } else {
- *buf = Some(v);
- }
- }
- };
- }
- #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
- macro_rules! println {
- () => {
- let buf = PRINT_BUF.lock().unwrap().take();
- match buf {
- Some(buf) => console_log!("{buf}"),
- None => console_log!(""),
- }
- };
- ($($arg:tt)*) => {
- let buf = PRINT_BUF.lock().unwrap().take();
- match buf {
- Some(buf) => {
- let v = format!($($arg)*);
- console_log!("{buf}{v}");
- },
- None => console_log!($($arg)*),
- }
- }
- }
- #[derive(Debug, serde_derive::Deserialize)]
- struct UrlTest {
- input: String,
- base: Option<String>,
- #[serde(flatten)]
- result: UrlTestResult,
- }
- #[derive(Debug, serde_derive::Deserialize)]
- #[serde(untagged)]
- #[allow(clippy::large_enum_variant)]
- enum UrlTestResult {
- Ok(UrlTestOk),
- Fail(UrlTestFail),
- }
- #[derive(Debug, serde_derive::Deserialize)]
- struct UrlTestOk {
- href: String,
- protocol: String,
- username: String,
- password: String,
- host: String,
- hostname: String,
- port: String,
- pathname: String,
- search: String,
- hash: String,
- }
- #[derive(Debug, serde_derive::Deserialize)]
- struct UrlTestFail {
- failure: bool,
- }
- #[derive(Debug, serde_derive::Deserialize)]
- struct SetterTest {
- href: String,
- new_value: String,
- expected: SetterTestExpected,
- }
- #[derive(Debug, serde_derive::Deserialize)]
- struct SetterTestExpected {
- href: Option<String>,
- protocol: Option<String>,
- username: Option<String>,
- password: Option<String>,
- host: Option<String>,
- hostname: Option<String>,
- port: Option<String>,
- pathname: Option<String>,
- search: Option<String>,
- hash: Option<String>,
- }
- #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), wasm_bindgen_test)]
- fn main() {
- let mut filter = None;
- let mut args = std::env::args().skip(1);
- while filter.is_none() {
- if let Some(arg) = args.next() {
- if arg == "--test-threads" {
- args.next();
- continue;
- }
- filter = Some(arg);
- } else {
- break;
- }
- }
- let mut expected_failures = include_str!("expected_failures.txt")
- .lines()
- .collect::<Vec<_>>();
- let mut errors = vec![];
- // Copied from https://github.com/web-platform-tests/wpt/blob/master/url/
- let url_json: Vec<Value> = serde_json::from_str(include_str!("urltestdata.json"))
- .expect("JSON parse error in urltestdata.json");
- let url_tests = url_json
- .into_iter()
- .filter(|val| val.is_object())
- .map(|val| serde_json::from_value::<UrlTest>(val).expect("parsing failed"))
- .collect::<Vec<_>>();
- let setter_json: HashMap<String, Value> =
- serde_json::from_str(include_str!("setters_tests.json"))
- .expect("JSON parse error in setters_tests.json");
- let setter_tests = setter_json
- .into_iter()
- .filter(|(k, _)| k != "comment")
- .map(|(k, v)| {
- let test = serde_json::from_value::<Vec<SetterTest>>(v).expect("parsing failed");
- (k, test)
- })
- .collect::<HashMap<_, _>>();
- for url_test in url_tests {
- let mut name = format!("<{}>", url_test.input.escape_default());
- if let Some(base) = &url_test.base {
- write!(&mut name, " against <{}>", base.escape_default()).unwrap();
- }
- if should_skip(&name, filter.as_deref()) {
- continue;
- }
- print!("{name} ... ");
- let res = run_url_test(url_test);
- report(name, res, &mut errors, &mut expected_failures);
- }
- for (kind, tests) in setter_tests {
- for test in tests {
- let name = format!(
- "<{}> set {} to <{}>",
- test.href.escape_default(),
- kind,
- test.new_value.escape_default()
- );
- if should_skip(&name, filter.as_deref()) {
- continue;
- }
- print!("{name} ... ");
- let res = run_setter_test(&kind, test);
- report(name, res, &mut errors, &mut expected_failures);
- }
- }
- println!();
- println!("====================");
- println!();
- if !errors.is_empty() {
- println!("errors:");
- println!();
- for (name, err) in errors {
- println!(" name: {name}");
- println!(" err: {err}");
- println!();
- }
- std::process::exit(1);
- } else {
- println!("all tests passed");
- }
- if !expected_failures.is_empty() && filter.is_none() {
- println!();
- println!("====================");
- println!();
- println!("tests were expected to fail but did not run:");
- println!();
- for name in expected_failures {
- println!(" {name}");
- }
- println!();
- println!("if these tests were removed, update expected_failures.txt");
- println!();
- std::process::exit(1);
- }
- }
- fn should_skip(name: &str, filter: Option<&str>) -> bool {
- match filter {
- Some(filter) => !name.contains(filter),
- None => false,
- }
- }
- fn report(
- name: String,
- res: Result<(), String>,
- errors: &mut Vec<(String, String)>,
- expected_failures: &mut Vec<&str>,
- ) {
- let expected_failure = expected_failures.contains(&&*name);
- expected_failures.retain(|&s| s != &*name);
- match res {
- Ok(()) => {
- if expected_failure {
- println!("🟠 (unexpected success)");
- errors.push((name, "unexpected success".to_string()));
- } else {
- println!("✅");
- }
- }
- Err(err) => {
- if expected_failure {
- println!("✅ (expected fail)");
- } else {
- println!("❌");
- errors.push((name, err));
- }
- }
- }
- }
- fn run_url_test(
- UrlTest {
- base,
- input,
- result,
- }: UrlTest,
- ) -> Result<(), String> {
- let base = match base {
- Some(base) => {
- let base = Url::parse(&base).map_err(|e| format!("errored while parsing base: {e}"))?;
- Some(base)
- }
- None => None,
- };
- let res = Url::options()
- .base_url(base.as_ref())
- .parse(&input)
- .map_err(|e| format!("errored while parsing input: {e}"));
- match result {
- UrlTestResult::Ok(ok) => check_url_ok(res, ok),
- UrlTestResult::Fail(fail) => {
- assert!(fail.failure);
- if res.is_ok() {
- return Err("expected failure, but parsed successfully".to_string());
- }
- Ok(())
- }
- }
- }
- fn check_url_ok(res: Result<Url, String>, ok: UrlTestOk) -> Result<(), String> {
- let url = match res {
- Ok(url) => url,
- Err(err) => {
- return Err(format!("expected success, but errored: {err:?}"));
- }
- };
- let href = url::quirks::href(&url);
- if href != ok.href {
- return Err(format!("expected href {:?}, but got {:?}", ok.href, href));
- }
- let protocol = url::quirks::protocol(&url);
- if protocol != ok.protocol {
- return Err(format!(
- "expected protocol {:?}, but got {:?}",
- ok.protocol, protocol
- ));
- }
- let username = url::quirks::username(&url);
- if username != ok.username {
- return Err(format!(
- "expected username {:?}, but got {:?}",
- ok.username, username
- ));
- }
- let password = url::quirks::password(&url);
- if password != ok.password {
- return Err(format!(
- "expected password {:?}, but got {:?}",
- ok.password, password
- ));
- }
- let host = url::quirks::host(&url);
- if host != ok.host {
- return Err(format!("expected host {:?}, but got {:?}", ok.host, host));
- }
- let hostname = url::quirks::hostname(&url);
- if hostname != ok.hostname {
- return Err(format!(
- "expected hostname {:?}, but got {:?}",
- ok.hostname, hostname
- ));
- }
- let port = url::quirks::port(&url);
- if port != ok.port {
- return Err(format!("expected port {:?}, but got {:?}", ok.port, port));
- }
- let pathname = url::quirks::pathname(&url);
- if pathname != ok.pathname {
- return Err(format!(
- "expected pathname {:?}, but got {:?}",
- ok.pathname, pathname
- ));
- }
- let search = url::quirks::search(&url);
- if search != ok.search {
- return Err(format!(
- "expected search {:?}, but got {:?}",
- ok.search, search
- ));
- }
- let hash = url::quirks::hash(&url);
- if hash != ok.hash {
- return Err(format!("expected hash {:?}, but got {:?}", ok.hash, hash));
- }
- Ok(())
- }
- fn run_setter_test(
- kind: &str,
- SetterTest {
- href,
- new_value,
- expected,
- }: SetterTest,
- ) -> Result<(), String> {
- let mut url = Url::parse(&href).map_err(|e| format!("errored while parsing href: {e}"))?;
- match kind {
- "protocol" => {
- url::quirks::set_protocol(&mut url, &new_value).ok();
- }
- "username" => {
- url::quirks::set_username(&mut url, &new_value).ok();
- }
- "password" => {
- url::quirks::set_password(&mut url, &new_value).ok();
- }
- "host" => {
- url::quirks::set_host(&mut url, &new_value).ok();
- }
- "hostname" => {
- url::quirks::set_hostname(&mut url, &new_value).ok();
- }
- "port" => {
- url::quirks::set_port(&mut url, &new_value).ok();
- }
- "pathname" => url::quirks::set_pathname(&mut url, &new_value),
- "search" => url::quirks::set_search(&mut url, &new_value),
- "hash" => url::quirks::set_hash(&mut url, &new_value),
- _ => {
- return Err(format!("unknown setter kind: {kind:?}"));
- }
- }
- if let Some(expected_href) = expected.href {
- let href = url::quirks::href(&url);
- if href != expected_href {
- return Err(format!("expected href {expected_href:?}, but got {href:?}"));
- }
- }
- if let Some(expected_protocol) = expected.protocol {
- let protocol = url::quirks::protocol(&url);
- if protocol != expected_protocol {
- return Err(format!(
- "expected protocol {expected_protocol:?}, but got {protocol:?}"
- ));
- }
- }
- if let Some(expected_username) = expected.username {
- let username = url::quirks::username(&url);
- if username != expected_username {
- return Err(format!(
- "expected username {expected_username:?}, but got {username:?}"
- ));
- }
- }
- if let Some(expected_password) = expected.password {
- let password = url::quirks::password(&url);
- if password != expected_password {
- return Err(format!(
- "expected password {expected_password:?}, but got {password:?}"
- ));
- }
- }
- if let Some(expected_host) = expected.host {
- let host = url::quirks::host(&url);
- if host != expected_host {
- return Err(format!("expected host {expected_host:?}, but got {host:?}"));
- }
- }
- if let Some(expected_hostname) = expected.hostname {
- let hostname = url::quirks::hostname(&url);
- if hostname != expected_hostname {
- return Err(format!(
- "expected hostname {expected_hostname:?}, but got {hostname:?}"
- ));
- }
- }
- if let Some(expected_port) = expected.port {
- let port = url::quirks::port(&url);
- if port != expected_port {
- return Err(format!("expected port {expected_port:?}, but got {port:?}"));
- }
- }
- if let Some(expected_pathname) = expected.pathname {
- let pathname = url::quirks::pathname(&url);
- if pathname != expected_pathname {
- return Err(format!(
- "expected pathname {expected_pathname:?}, but got {pathname:?}"
- ));
- }
- }
- if let Some(expected_search) = expected.search {
- let search = url::quirks::search(&url);
- if search != expected_search {
- return Err(format!(
- "expected search {expected_search:?}, but got {search:?}"
- ));
- }
- }
- if let Some(expected_hash) = expected.hash {
- let hash = url::quirks::hash(&url);
- if hash != expected_hash {
- return Err(format!("expected hash {expected_hash:?}, but got {hash:?}"));
- }
- }
- Ok(())
- }
|