Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for testing in more worker types #3804

Merged
merged 5 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ yarn.lock
/publish
/publish.exe
.vscode
webdriver.json
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@

## Unreleased

### Added

* Added support for running tests in shared and service workers with ``wasm_bindgen_test_configure!` `run_in_shared_worker` and `run_in_service_worker`.
[#3804](https://github.com/rustwasm/wasm-bindgen/pull/3804)

### Changed

* Stabilize `ClipboardEvent`.
Expand All @@ -11,6 +16,9 @@
* Use immutable buffers in `SubtleCrypto` methods.
[#3797](https://github.com/rustwasm/wasm-bindgen/pull/3797)

* Deprecate `wasm_bindgen_test_configure!`s `run_in_worker` in favor of `run_in_dedicated_worker`.
[#3804](https://github.com/rustwasm/wasm-bindgen/pull/3804)

## [0.2.90](https://github.com/rustwasm/wasm-bindgen/compare/0.2.89...0.2.90)

Released 2024-01-06
Expand Down
54 changes: 44 additions & 10 deletions crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,29 @@ enum TestMode {
Node,
Deno,
Browser { no_modules: bool },
Worker { no_modules: bool },
DedicatedWorker { no_modules: bool },
SharedWorker { no_modules: bool },
ServiceWorker { no_modules: bool },
}

impl TestMode {
fn is_worker(self) -> bool {
matches!(
self,
Self::DedicatedWorker { .. } | Self::SharedWorker { .. } | Self::ServiceWorker { .. }
)
}

fn no_modules(self) -> bool {
match self {
Self::Node => true,
Self::Deno => true,
Self::Browser { no_modules }
| Self::DedicatedWorker { no_modules }
| Self::SharedWorker { no_modules }
| Self::ServiceWorker { no_modules } => no_modules,
}
}
}

struct TmpDirDeleteGuard(PathBuf);
Expand Down Expand Up @@ -120,7 +142,13 @@ fn main() -> anyhow::Result<()> {
Some(section) if section.data.contains(&0x01) => TestMode::Browser {
no_modules: std::env::var("WASM_BINDGEN_USE_NO_MODULE").is_ok(),
},
Some(section) if section.data.contains(&0x10) => TestMode::Worker {
Some(section) if section.data.contains(&0x02) => TestMode::DedicatedWorker {
no_modules: std::env::var("WASM_BINDGEN_USE_NO_MODULE").is_ok(),
},
Some(section) if section.data.contains(&0x03) => TestMode::SharedWorker {
no_modules: std::env::var("WASM_BINDGEN_USE_NO_MODULE").is_ok(),
},
Some(section) if section.data.contains(&0x04) => TestMode::ServiceWorker {
no_modules: std::env::var("WASM_BINDGEN_USE_NO_MODULE").is_ok(),
},
Some(_) => bail!("invalid __wasm_bingen_test_unstable value"),
Expand Down Expand Up @@ -175,11 +203,15 @@ fn main() -> anyhow::Result<()> {
match test_mode {
TestMode::Node => b.nodejs(true)?,
TestMode::Deno => b.deno(true)?,
TestMode::Browser { no_modules: false } | TestMode::Worker { no_modules: false } => {
b.web(true)?
}
TestMode::Browser { no_modules: true } | TestMode::Worker { no_modules: true } => {
b.no_modules(true)?
TestMode::Browser { .. }
| TestMode::DedicatedWorker { .. }
| TestMode::SharedWorker { .. }
| TestMode::ServiceWorker { .. } => {
if test_mode.no_modules() {
b.no_modules(true)?
} else {
b.web(true)?
}
}
};

Expand All @@ -200,7 +232,10 @@ fn main() -> anyhow::Result<()> {
match test_mode {
TestMode::Node => node::execute(module, &tmpdir, &args, &tests)?,
TestMode::Deno => deno::execute(module, &tmpdir, &args, &tests)?,
TestMode::Browser { no_modules } | TestMode::Worker { no_modules } => {
TestMode::Browser { .. }
| TestMode::DedicatedWorker { .. }
| TestMode::SharedWorker { .. }
| TestMode::ServiceWorker { .. } => {
let srv = server::spawn(
&if headless {
"127.0.0.1:0".parse().unwrap()
Expand All @@ -214,8 +249,7 @@ fn main() -> anyhow::Result<()> {
&tmpdir,
&args,
&tests,
no_modules,
matches!(test_mode, TestMode::Worker { no_modules: _ }),
test_mode,
)
.context("failed to spawn server")?;
let addr = srv.server_addr();
Expand Down
104 changes: 89 additions & 15 deletions crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ use std::path::Path;
use anyhow::{anyhow, Context, Error};
use rouille::{Request, Response, Server};

pub fn spawn(
use crate::TestMode;

pub(crate) fn spawn(
addr: &SocketAddr,
headless: bool,
module: &'static str,
tmpdir: &Path,
args: &[OsString],
tests: &[String],
no_module: bool,
worker: bool,
test_mode: TestMode,
) -> Result<Server<impl Fn(&Request) -> Response + Send + Sync>, Error> {
let mut js_to_execute = String::new();

let wbg_import_script = if no_module {
let wbg_import_script = if test_mode.no_modules() {
String::from(
r#"
let Context = wasm_bindgen.WasmBindgenTestContext;
Expand Down Expand Up @@ -48,15 +49,34 @@ pub fn spawn(
)
};

if worker {
let mut worker_script = if no_module {
if test_mode.is_worker() {
let mut worker_script = if test_mode.no_modules() {
format!(r#"importScripts("{0}.js");"#, module)
} else {
String::new()
};

worker_script.push_str(&wbg_import_script);

match test_mode {
TestMode::DedicatedWorker { .. } => worker_script.push_str("const port = self\n"),
TestMode::SharedWorker { .. } => worker_script.push_str(
r#"
addEventListener('connect', (e) => {
const port = e.ports[0]
"#,
),
TestMode::ServiceWorker { .. } => worker_script.push_str(
r#"
addEventListener('install', (e) => skipWaiting());
addEventListener('activate', (e) => e.waitUntil(clients.claim()));
addEventListener('message', (e) => {
const port = e.ports[0]
"#,
),
_ => unreachable!(),
}

worker_script.push_str(&format!(
r#"
const wrap = method => {{
Expand All @@ -65,15 +85,15 @@ pub fn spawn(
if (self[on_method]) {{
self[on_method](args);
}}
postMessage(["__wbgtest_" + method, args]);
port.postMessage(["__wbgtest_" + method, args]);
}};
}};

self.__wbg_test_invoke = f => f();
self.__wbg_test_output = "";
self.__wbg_test_output_writeln = function (line) {{
self.__wbg_test_output += line + "\n";
postMessage(["__wbgtest_output", self.__wbg_test_output]);
port.postMessage(["__wbgtest_output", self.__wbg_test_output]);
}}

wrap("debug");
Expand All @@ -97,15 +117,27 @@ pub fn spawn(
await cx.run(tests.map(s => wasm[s]));
}}

onmessage = function(e) {{
port.onmessage = function(e) {{
let tests = e.data;
run_in_worker(tests);
}}
"#,
module, args,
));

let worker_js_path = tmpdir.join("worker.js");
if matches!(
test_mode,
TestMode::SharedWorker { .. } | TestMode::ServiceWorker { .. }
) {
worker_script.push_str("})");
}

let name = if matches!(test_mode, TestMode::ServiceWorker { .. }) {
"service.js"
} else {
"worker.js"
};
let worker_js_path = tmpdir.join(name);
fs::write(worker_js_path, worker_script).context("failed to write JS file")?;

js_to_execute.push_str(&format!(
Expand All @@ -114,9 +146,9 @@ pub fn spawn(
// status text as at this point we should be asynchronously fetching the
// wasm module.
document.getElementById('output').textContent = "Loading wasm module...";
const worker = new Worker("worker.js", {{type: "{}"}});
{}

worker.addEventListener("message", function(e) {{
port.addEventListener("message", function(e) {{
// Checking the whether the message is from wasm_bindgen_test
if(
e.data &&
Expand All @@ -141,12 +173,54 @@ pub fn spawn(
}});

async function main(test) {{
worker.postMessage(test)
port.postMessage(test)
}}

const tests = [];
"#,
if no_module { "classic" } else { "module" }
{
let module = if test_mode.no_modules() {
"classic"
} else {
"module"
};

match test_mode {
TestMode::DedicatedWorker { .. } => {
format!("const port = new Worker('worker.js', {{type: '{module}'}});\n")
}
TestMode::SharedWorker { .. } => {
format!(
r#"
const worker = new SharedWorker("worker.js?random=" + crypto.randomUUID(), {{type: "{module}"}});
const port = worker.port;
port.start();
"#
)
}
TestMode::ServiceWorker { .. } => {
format!(
r#"
const url = "service.js?random=" + crypto.randomUUID();
await navigator.serviceWorker.register(url, {{type: "{module}"}});
await new Promise((resolve) => {{
navigator.serviceWorker.addEventListener('controllerchange', () => {{
if (navigator.serviceWorker.controller.scriptURL != location.href + url) {{
throw "`wasm-bindgen-test-runner` does not support running multiple service worker tests at the same time"
}}
resolve();
}});
}});
const channel = new MessageChannel();
navigator.serviceWorker.controller.postMessage(undefined, [channel.port2]);
const port = channel.port1;
port.start();
"#
)
}
_ => unreachable!(),
}
}
));
} else {
js_to_execute.push_str(&wbg_import_script);
Expand Down Expand Up @@ -203,7 +277,7 @@ pub fn spawn(
} else {
include_str!("index.html")
};
let s = if no_module {
let s = if !test_mode.is_worker() && test_mode.no_modules() {
s.replace(
"<!-- {IMPORT_SCRIPTS} -->",
&format!(
Expand Down
26 changes: 24 additions & 2 deletions crates/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ macro_rules! console_log {
///
/// * `run_in_browser` - requires that this test is run in a browser rather than
/// node.js, which is the default for executing tests.
/// * `run_in_worker` - requires that this test is run in a web worker rather than
/// * `run_in_dedicated_worker` - requires that this test is run in a web worker rather than
/// node.js, which is the default for executing tests.
/// * `run_in_shared_worker` - requires that this test is run in a shared worker rather than
/// node.js, which is the default for executing tests.
/// * `run_in_service_worker` - requires that this test is run in a service worker rather than
/// node.js, which is the default for executing tests.
///
/// This macro may be invoked at most one time per test suite (an entire binary
Expand All @@ -51,7 +55,25 @@ macro_rules! wasm_bindgen_test_configure {
(run_in_worker $($others:tt)*) => (
#[link_section = "__wasm_bindgen_test_unstable"]
#[cfg(target_arch = "wasm32")]
pub static __WBG_TEST_RUN_IN_WORKER: [u8; 1] = [0x10];
pub static __WBG_TEST_RUN_IN_DEDICATED_WORKER: [u8; 1] = [0x02];
$crate::wasm_bindgen_test_configure!($($others)*);
);
(run_in_dedicated_worker $($others:tt)*) => (
#[link_section = "__wasm_bindgen_test_unstable"]
#[cfg(target_arch = "wasm32")]
pub static __WBG_TEST_RUN_IN_DEDICATED_WORKER: [u8; 1] = [0x02];
$crate::wasm_bindgen_test_configure!($($others)*);
);
(run_in_shared_worker $($others:tt)*) => (
#[link_section = "__wasm_bindgen_test_unstable"]
#[cfg(target_arch = "wasm32")]
pub static __WBG_TEST_RUN_IN_SHARED_WORKER: [u8; 1] = [0x03];
$crate::wasm_bindgen_test_configure!($($others)*);
);
(run_in_service_worker $($others:tt)*) => (
#[link_section = "__wasm_bindgen_test_unstable"]
#[cfg(target_arch = "wasm32")]
pub static __WBG_TEST_RUN_IN_SERVICE_WORKER: [u8; 1] = [0x04];
$crate::wasm_bindgen_test_configure!($($others)*);
);
() => ()
Expand Down
4 changes: 3 additions & 1 deletion crates/test/src/rt/detect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ pub fn detect() -> Runtime {
// only be true in browsers.
match js_sys::global().unchecked_into::<This>().self_() {
Some(scope) => match scope.constructor().name().as_str() {
"DedicatedWorkerGlobalScope" | "SharedWorkerGlobalScope" => Runtime::Worker,
"DedicatedWorkerGlobalScope"
| "SharedWorkerGlobalScope"
| "ServiceWorkerGlobalScope" => Runtime::Worker,
_ => Runtime::Browser,
},
None => Runtime::Node,
Expand Down
21 changes: 11 additions & 10 deletions guide/src/wasm-bindgen-test/browsers.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ snippet.
```rust
use wasm_bindgen_test::wasm_bindgen_test_configure;

wasm_bindgen_test_configure!(run_in_worker);
// Run in dedicated worker.
wasm_bindgen_test_configure!(run_in_dedicated_worker);
// Or run in shared worker.
wasm_bindgen_test_configure!(run_in_shared_worker);
// Or run in service worker.
wasm_bindgen_test_configure!(run_in_service_worker);
```

Note that although a particular test crate must target either headless browsers
Expand All @@ -27,9 +32,11 @@ project by using multiple test crates. For example:
```
$MY_CRATE/
`-- tests
|-- node.rs # The tests in this suite use the default Node.js.
|-- worker.rs # The tests in this suite are configured for workers.
`-- web.rs # The tests in this suite are configured for browsers.
|-- node.rs # The tests in this suite use the default Node.js.
|-- dedicated_worker.rs # The tests in this suite are configured for dedicated workers.
|-- shared_worker.rs # The tests in this suite are configured for shared workers.
|-- service_worker.rs # The tests in this suite are configured for service workers.
`-- web.rs # The tests in this suite are configured for browsers.
```

## Configuring Which Browser is Used
Expand Down Expand Up @@ -86,12 +93,6 @@ Full list supported capabilities can be found:

Note that the `headless` argument is always enabled for both browsers.

You have to enable the special preference `dom.workers.modules.enabled` for
firefox when running the tests in Web Workers without using
`WASM_BINDGEN_USE_NO_MODULE` variable. Because firefox supported
ECMAScript modules in last release (2023-03-14) behind a special
preference.

### Debugging Headless Browser Tests

Omitting the `--headless` flag will disable headless mode, and allow you to
Expand Down
Loading