Skip to content

Commit

Permalink
Add support for testing in more worker types (#3804)
Browse files Browse the repository at this point in the history
Co-authored-by: Liam Murphy <43807659+Liamolucko@users.noreply.github.com>
  • Loading branch information
daxpedda and Liamolucko committed Jan 23, 2024
1 parent 1942791 commit 0979fea
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 47 deletions.
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

0 comments on commit 0979fea

Please sign in to comment.