diff --git a/crates/webidl/Cargo.toml b/crates/webidl/Cargo.toml index c37b3989a..fb23661c4 100644 --- a/crates/webidl/Cargo.toml +++ b/crates/webidl/Cargo.toml @@ -9,6 +9,7 @@ path = "tests/all/lib.rs" [dev-dependencies] wasm-bindgen-backend = { version = "=0.2.11", path = "../backend", features = ["extra-traits"] } +diff = "0.1.11" [dependencies] failure = "0.1" diff --git a/crates/webidl/tests/all/event.rs b/crates/webidl/tests/all/event.rs deleted file mode 100644 index 54facf3d5..000000000 --- a/crates/webidl/tests/all/event.rs +++ /dev/null @@ -1,24 +0,0 @@ -use super::backend; -use proc_macro2; -use syn; - -assert_parse!( - event, - include_str!("./Event.webidl"), - backend::ast::Program { - exports: vec![], - imports: vec![backend::ast::Import { - module: None, - version: None, - js_namespace: None, - kind: backend::ast::ImportKind::Type(backend::ast::ImportType { - vis: syn::Visibility::Public(syn::VisPublic { - pub_token: Default::default(), - }), - name: syn::Ident::new("Event", proc_macro2::Span::call_site()), - }), - }], - enums: vec![], - structs: vec![], - } -); diff --git a/crates/webidl/tests/all/lib.rs b/crates/webidl/tests/all/lib.rs index bcb50c280..522af500d 100644 --- a/crates/webidl/tests/all/lib.rs +++ b/crates/webidl/tests/all/lib.rs @@ -1,21 +1,41 @@ +extern crate diff; extern crate proc_macro2; extern crate syn; extern crate wasm_bindgen_backend as backend; extern crate wasm_bindgen_webidl as wb_webidl; -pub fn assert_parse(webidl: &str, expected: backend::ast::Program) { - let actual = wb_webidl::parse(webidl).expect("should parse the webidl source OK"); - assert_eq!(expected, actual); -} +#[macro_use] +mod util; +use util::*; -macro_rules! assert_parse { - ($test_name:ident, $webidl_source:expr, $expected_ast:expr) => { - #[test] - fn $test_name() { - $crate::assert_parse($webidl_source, $expected_ast); +/// Tests for parsing WebIDL into an expected wasm-bindgen AST. +mod parse { + use super::*; + + assert_parse!(empty, backend::ast::Program::default()); + + assert_parse!( + Event, + backend::ast::Program { + exports: vec![], + imports: vec![backend::ast::Import { + module: None, + version: None, + js_namespace: None, + kind: backend::ast::ImportKind::Type(backend::ast::ImportType { + vis: syn::Visibility::Public(syn::VisPublic { + pub_token: Default::default(), + }), + name: syn::Ident::new("Event", proc_macro2::Span::call_site()), + }), + }], + enums: vec![], + structs: vec![], } - }; + ); } -mod event; -mod simple; +/// Tests for compiling WebIDL into Rust bindings. +mod compile { + assert_compile!(Event); +} diff --git a/crates/webidl/tests/all/simple.rs b/crates/webidl/tests/all/simple.rs deleted file mode 100644 index dd2118ef6..000000000 --- a/crates/webidl/tests/all/simple.rs +++ /dev/null @@ -1,3 +0,0 @@ -use super::backend; - -assert_parse!(empty, "", backend::ast::Program::default()); diff --git a/crates/webidl/tests/all/util.rs b/crates/webidl/tests/all/util.rs new file mode 100644 index 000000000..06f6be1c6 --- /dev/null +++ b/crates/webidl/tests/all/util.rs @@ -0,0 +1,171 @@ +use backend; +use diff; +use std::io::{self, Write}; +use std::process; +use std::sync::{Once, ONCE_INIT}; +use wb_webidl; + +pub fn assert_parse(webidl: &str, expected: backend::ast::Program) { + let actual = wb_webidl::parse(webidl).expect("should parse the webidl source OK"); + assert_eq!(expected, actual); +} + +macro_rules! assert_parse { + ($test_name:ident, $expected_ast:expr) => { + #[test] + #[allow(non_snake_case)] + fn $test_name() { + let webidl_source = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/fixtures/", + stringify!($test_name), + ".webidl" + )); + $crate::assert_parse(webidl_source, $expected_ast); + } + }; +} + +fn rustfmt>(source: S) -> (String, String) { + let source = source.into(); + + static CHECK_RUSTFMT: Once = ONCE_INIT; + + CHECK_RUSTFMT.call_once(|| { + let have_working_rustfmt = process::Command::new("rustup") + .args(&["run", "nightly", "rustfmt", "--version"]) + .stdout(process::Stdio::null()) + .stderr(process::Stdio::null()) + .status() + .ok() + .map_or(false, |status| status.success()); + + if !have_working_rustfmt { + panic!( + " +The latest `rustfmt` is required to run the `wasm-bindgen` test suite. Install +`rustfmt` with: + + $ rustup component add rustfmt-preview --toolchain nightly +" + ); + } + }); + + let mut child = process::Command::new("rustup") + .args(&[ + "run", + "nightly", + "rustfmt", + "--config-path", + concat!(env!("CARGO_MANIFEST_DIR"), "/tests/rustfmt.toml"), + ]) + .stdin(process::Stdio::piped()) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::piped()) + .spawn() + .expect("should spawn `rustup run nightly rustfmt`"); + + let mut stdin = child.stdin.take().unwrap(); + let mut stdout = child.stdout.take().unwrap(); + let mut stderr = child.stderr.take().unwrap(); + + // Write to stdin in a new thread, so that we can read from stdout on this + // thread. This keeps the child from blocking on writing to its stdout which + // might block us from writing to its stdin. + let stdin_handle = ::std::thread::spawn(move || stdin.write_all(source.as_bytes())); + + // Read stderr on a new thread for similar reasons. + let stderr_handle = ::std::thread::spawn(move || { + let mut output = vec![]; + io::copy(&mut stderr, &mut output).map(|_| String::from_utf8_lossy(&output).to_string()) + }); + + let mut output = vec![]; + io::copy(&mut stdout, &mut output).expect("Should copy stdout into vec OK"); + + // Ignore actual rustfmt status because it is often non-zero for trivial + // things. + let _ = child.wait().expect("should wait on rustfmt child OK"); + + stdin_handle + .join() + .expect("writer thread should not have panicked") + .expect("should have written to child rustfmt's stdin OK"); + + let formatted = String::from_utf8(output).expect("rustfmt should only emit valid utf-8"); + + let stderr = stderr_handle + .join() + .expect("stderr reader thread should not have panicked") + .expect("should have read child rustfmt's stderr OK"); + + (formatted, stderr) +} + +fn strip_wasm_bindgen_generated(source: String) -> String { + let lines: Vec<_> = source + .lines() + .filter(|l| !l.contains("__WASM_BINDGEN_GENERATED")) + .collect(); + lines.join("\n") +} + +pub fn assert_compile(webidl: &str, expected: &str) { + let actual = wb_webidl::compile(webidl).expect("should compile the webidl source OK"); + + let (actual, actual_stderr) = rustfmt(actual); + let (expected, expected_stderr) = rustfmt(expected); + + let actual = strip_wasm_bindgen_generated(actual); + let expected = strip_wasm_bindgen_generated(expected); + + if expected == actual { + return; + } + + eprintln!("rustfmt(expected) stderr:"); + eprintln!("{}", expected_stderr); + eprintln!(); + + eprintln!("rustfmt(actual) stderr:"); + eprintln!("{}", actual_stderr); + eprintln!(); + + eprintln!( + "assert_compile failed: actual compiled output and expected compiled output do not match:" + ); + eprintln!("--- expected"); + eprintln!("+++ actual"); + for d in diff::lines(&expected, &actual) { + match d { + diff::Result::Left(l) => eprintln!("-{}", l), + diff::Result::Right(r) => eprintln!("+{}", r), + diff::Result::Both(b, _) => eprintln!(" {}", b), + } + } + + panic!() +} + +macro_rules! assert_compile { + ($test_name:ident) => { + #[test] + #[allow(non_snake_case)] + fn $test_name() { + let webidl_source = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/fixtures/", + stringify!($test_name), + ".webidl" + )); + let expected_output = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/expected/", + stringify!($test_name), + ".rs" + )); + $crate::assert_compile(webidl_source, expected_output); + } + }; +} diff --git a/crates/webidl/tests/expected/Event.rs b/crates/webidl/tests/expected/Event.rs new file mode 100644 index 000000000..747f964ba --- /dev/null +++ b/crates/webidl/tests/expected/Event.rs @@ -0,0 +1,58 @@ +#[allow(bad_style)] +pub struct Event { + obj: ::wasm_bindgen::JsValue, +} +impl ::wasm_bindgen::describe::WasmDescribe for Event { + fn describe() { + ::wasm_bindgen::JsValue::describe(); + } +} +impl ::wasm_bindgen::convert::IntoWasmAbi for Event { + type Abi = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::IntoWasmAbi>::Abi; + fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi { + self.obj.into_abi(extra) + } +} +impl ::wasm_bindgen::convert::FromWasmAbi for Event { + type Abi = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi>::Abi; + unsafe fn from_abi(js: Self::Abi, extra: &mut ::wasm_bindgen::convert::Stack) -> Self { + Event { + obj: ::wasm_bindgen::JsValue::from_abi(js, extra), + } + } +} +impl<'a> ::wasm_bindgen::convert::IntoWasmAbi for &'a Event { + type Abi = <&'a ::wasm_bindgen::JsValue as ::wasm_bindgen::convert::IntoWasmAbi>::Abi; + fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi { + (&self.obj).into_abi(extra) + } +} +impl ::wasm_bindgen::convert::RefFromWasmAbi for Event { + type Abi = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::RefFromWasmAbi>::Abi; + type Anchor = ::wasm_bindgen::__rt::core::mem::ManuallyDrop; + unsafe fn ref_from_abi( + js: Self::Abi, + extra: &mut ::wasm_bindgen::convert::Stack, + ) -> Self::Anchor { + let tmp = + <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::RefFromWasmAbi>::ref_from_abi( + js, extra, + ); + ::wasm_bindgen::__rt::core::mem::ManuallyDrop::new(Event { + obj: ::wasm_bindgen::__rt::core::mem::ManuallyDrop::into_inner(tmp), + }) + } +} +impl From<::wasm_bindgen::JsValue> for Event { + fn from(obj: ::wasm_bindgen::JsValue) -> Event { + Event { obj } + } +} +impl From for ::wasm_bindgen::JsValue { + fn from(obj: Event) -> ::wasm_bindgen::JsValue { + obj.obj + } +} +#[allow(non_upper_case_globals)] +#[wasm_custom_section = "__wasm_bindgen_unstable"] +const __WASM_BINDGEN_GENERATED_wasm_bindgen_webidl_0_1_0_0 : [ u8 ; 180usize ] = * b"\xB0\0\0\0{\"exports\":[],\"enums\":[],\"imports\":[{\"module\":null,\"version\":null,\"js_namespace\":null,\"kind\":{\"kind\":\"type\"}}],\"structs\":[],\"version\":\"0.2.11 (3879f6f42)\",\"schema_version\":\"4\"}" ; diff --git a/crates/webidl/tests/all/Event.webidl b/crates/webidl/tests/fixtures/Event.webidl similarity index 100% rename from crates/webidl/tests/all/Event.webidl rename to crates/webidl/tests/fixtures/Event.webidl diff --git a/crates/webidl/tests/fixtures/empty.webidl b/crates/webidl/tests/fixtures/empty.webidl new file mode 100644 index 000000000..e69de29bb diff --git a/crates/webidl/tests/rustfmt.toml b/crates/webidl/tests/rustfmt.toml new file mode 100644 index 000000000..e69de29bb