From 62cf711c9c72495c06c1d59fc5e3f501b956e971 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Tue, 28 Nov 2023 16:42:09 +1000 Subject: [PATCH] Fix multiple issues on wasm32: * remove `Url::socket_addrs` on wasm32-unknown-unknown (it won't work, those platform API calls are not supported) * disable unit tests which won't work on wasm32-unknown-unknown * run tests in `wasm_bindgen_test` on wasm32-unknown-unknown * remove `panic::catch_unwind` from wpt tests, as that conflicts with wasm-bindgen's panic handler * run the tests in CI --- .github/workflows/main.yml | 4 ++ url/Cargo.toml | 3 + url/src/lib.rs | 6 +- url/tests/unit.rs | 47 ++++++++------ url/tests/wpt.rs | 126 +++++++++++++++++++++++++------------ 5 files changed, 126 insertions(+), 60 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4265bccb2..3bcb61d54 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,7 +59,11 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: targets: wasm32-unknown-unknown + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - run: cargo build --target wasm32-unknown-unknown + - run: cd url && wasm-pack test --headless --chrome + - run: cd url && wasm-pack test --headless --firefox Lint: runs-on: ubuntu-latest diff --git a/url/Cargo.toml b/url/Cargo.toml index a50ab31f1..945e72b2e 100644 --- a/url/Cargo.toml +++ b/url/Cargo.toml @@ -21,6 +21,9 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" bencher = "0.1" +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] +wasm-bindgen-test = "0.3" + [dependencies] form_urlencoded = { version = "1.2.1", path = "../form_urlencoded" } idna = { version = "0.5.0", path = "../idna" } diff --git a/url/src/lib.rs b/url/src/lib.rs index d498ee365..c0947fe51 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -152,9 +152,12 @@ use std::borrow::Borrow; use std::cmp; use std::fmt::{self, Write}; use std::hash; +#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] use std::io; use std::mem; -use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; +use std::net::IpAddr; +#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] +use std::net::{SocketAddr, ToSocketAddrs}; use std::ops::{Range, RangeFrom, RangeTo}; use std::path::{Path, PathBuf}; use std::str; @@ -1250,6 +1253,7 @@ impl Url { /// }) /// } /// ``` + #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] pub fn socket_addrs( &self, default_port_number: impl Fn() -> Option, diff --git a/url/tests/unit.rs b/url/tests/unit.rs index c27f237ba..d710fdd0a 100644 --- a/url/tests/unit.rs +++ b/url/tests/unit.rs @@ -11,9 +11,16 @@ use std::borrow::Cow; use std::cell::{Cell, RefCell}; use std::net::{Ipv4Addr, Ipv6Addr}; +#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] use std::path::{Path, PathBuf}; use url::{form_urlencoded, Host, Origin, Url}; +// https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/usage.html +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +wasm_bindgen_test_configure!(run_in_browser); + #[test] fn size() { use std::mem::size_of; @@ -117,6 +124,7 @@ fn test_set_empty_query() { assert_eq!(base.as_str(), "moz://example.com/path"); } +#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] macro_rules! assert_from_file_path { ($path: expr) => { assert_from_file_path!($path, $path) @@ -130,6 +138,7 @@ macro_rules! assert_from_file_path { } #[test] +#[cfg(any(unix, windows))] fn new_file_paths() { if cfg!(unix) { assert_eq!(Url::from_file_path(Path::new("relative")), Err(())); @@ -161,28 +170,28 @@ fn new_path_bad_utf8() { } #[test] +#[cfg(windows)] fn new_path_windows_fun() { - if cfg!(windows) { - assert_from_file_path!(r"C:\foo\bar", "/C:/foo/bar"); - assert_from_file_path!("C:\\foo\\ba\0r", "/C:/foo/ba%00r"); + assert_from_file_path!(r"C:\foo\bar", "/C:/foo/bar"); + assert_from_file_path!("C:\\foo\\ba\0r", "/C:/foo/ba%00r"); - // Invalid UTF-8 - assert!(Url::parse("file:///C:/foo/ba%80r") - .unwrap() - .to_file_path() - .is_err()); + // Invalid UTF-8 + assert!(Url::parse("file:///C:/foo/ba%80r") + .unwrap() + .to_file_path() + .is_err()); - // test windows canonicalized path - let path = PathBuf::from(r"\\?\C:\foo\bar"); - assert!(Url::from_file_path(path).is_ok()); + // test windows canonicalized path + let path = PathBuf::from(r"\\?\C:\foo\bar"); + assert!(Url::from_file_path(path).is_ok()); - // Percent-encoded drive letter - let url = Url::parse("file:///C%3A/foo/bar").unwrap(); - assert_eq!(url.to_file_path(), Ok(PathBuf::from(r"C:\foo\bar"))); - } + // Percent-encoded drive letter + let url = Url::parse("file:///C%3A/foo/bar").unwrap(); + assert_eq!(url.to_file_path(), Ok(PathBuf::from(r"C:\foo\bar"))); } #[test] +#[cfg(any(unix, windows))] fn new_directory_paths() { if cfg!(unix) { assert_eq!(Url::from_directory_path(Path::new("relative")), Err(())); @@ -438,6 +447,7 @@ fn issue_61() { } #[test] +#[cfg(any(unix, target_os = "redox", target_os = "wasi"))] #[cfg(not(windows))] /// https://github.com/servo/rust-url/issues/197 fn issue_197() { @@ -622,6 +632,7 @@ fn test_origin_unicode_serialization() { } #[test] +#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] fn test_socket_addrs() { use std::net::ToSocketAddrs; @@ -803,11 +814,8 @@ fn test_expose_internals() { } #[test] +#[cfg(windows)] fn test_windows_unc_path() { - if !cfg!(windows) { - return; - } - let url = Url::from_file_path(Path::new(r"\\host\share\path\file.txt")).unwrap(); assert_eq!(url.as_str(), "file://host/share/path/file.txt"); @@ -927,6 +935,7 @@ fn test_url_from_file_path() { } /// https://github.com/servo/rust-url/issues/505 +#[cfg(any(unix, target_os = "redox", target_os = "wasi"))] #[cfg(not(windows))] #[test] fn test_url_from_file_path() { diff --git a/url/tests/wpt.rs b/url/tests/wpt.rs index 701044d67..e0199a8c2 100644 --- a/url/tests/wpt.rs +++ b/url/tests/wpt.rs @@ -8,13 +8,62 @@ //! Data-driven tests imported from web-platform-tests +use serde_json::Value; use std::collections::HashMap; use std::fmt::Write; -use std::panic; - -use serde_json::Value; +#[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> = 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::Deserialize)] struct UrlTest { input: String, @@ -71,6 +120,7 @@ struct SetterTestExpected { hash: Option, } +#[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); @@ -228,16 +278,16 @@ fn run_url_test( ) -> Result<(), String> { let base = match base { Some(base) => { - let base = panic::catch_unwind(|| Url::parse(&base)) - .map_err(|_| "panicked while parsing base".to_string())? - .map_err(|e| format!("errored while parsing base: {}", e))?; + let base = + Url::parse(&base).map_err(|e| format!("errored while parsing base: {}", e))?; Some(base) } None => None, }; - let res = panic::catch_unwind(move || Url::options().base_url(base.as_ref()).parse(&input)) - .map_err(|_| "panicked while parsing input".to_string())? + let res = Url::options() + .base_url(base.as_ref()) + .parse(&input) .map_err(|e| format!("errored while parsing input: {}", e)); match result { @@ -340,38 +390,34 @@ fn run_setter_test( expected, }: SetterTest, ) -> Result<(), String> { - let mut url = panic::catch_unwind(|| Url::parse(&href)) - .map_err(|_| "panicked while parsing href".to_string())? - .map_err(|e| format!("errored while parsing href: {}", e))?; - - let url = panic::catch_unwind(move || { - 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), - _ => panic!("unknown setter kind: {:?}", kind), - }; - url - }) - .map_err(|_| "panicked while setting value".to_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);