Add support for testing in more worker types (#3804)

Co-authored-by: Liam Murphy <43807659+Liamolucko@users.noreply.github.com>
This commit is contained in:
daxpedda 2024-01-23 10:35:26 +01:00 committed by GitHub
parent 1942791d88
commit 0979fea92b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 217 additions and 47 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ yarn.lock
/publish
/publish.exe
.vscode
webdriver.json

View File

@ -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`.
@ -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

View File

@ -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);
@ -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"),
@ -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)?
}
}
};
@ -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()
@ -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();

View File

@ -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;
@ -48,8 +49,8 @@ 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()
@ -57,6 +58,25 @@ pub fn spawn(
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 => {{
@ -65,7 +85,7 @@ pub fn spawn(
if (self[on_method]) {{
self[on_method](args);
}}
postMessage(["__wbgtest_" + method, args]);
port.postMessage(["__wbgtest_" + method, args]);
}};
}};
@ -73,7 +93,7 @@ pub fn spawn(
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");
@ -97,7 +117,7 @@ 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);
}}
@ -105,7 +125,19 @@ pub fn spawn(
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!(
@ -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 &&
@ -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);
@ -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!(

View File

@ -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
@ -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)*);
);
() => ()

View File

@ -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,

View File

@ -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
@ -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
@ -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

18
tests/worker/dedicated.rs Normal file
View File

@ -0,0 +1,18 @@
#![cfg(target_arch = "wasm32")]
extern crate js_sys;
extern crate wasm_bindgen;
extern crate wasm_bindgen_test;
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::wasm_bindgen_test_configure;
wasm_bindgen_test_configure!(run_in_dedicated_worker);
pub mod modules;
// should not be executed
#[wasm_bindgen(start)]
fn start() {
panic!();
}

18
tests/worker/service.rs Normal file
View File

@ -0,0 +1,18 @@
#![cfg(target_arch = "wasm32")]
extern crate js_sys;
extern crate wasm_bindgen;
extern crate wasm_bindgen_test;
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::wasm_bindgen_test_configure;
wasm_bindgen_test_configure!(run_in_service_worker);
pub mod modules;
// should not be executed
#[wasm_bindgen(start)]
fn start() {
panic!();
}

View File

@ -7,7 +7,7 @@ extern crate wasm_bindgen_test;
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::wasm_bindgen_test_configure;
wasm_bindgen_test_configure!(run_in_worker);
wasm_bindgen_test_configure!(run_in_shared_worker);
pub mod modules;

View File

@ -1,8 +0,0 @@
{
"moz:firefoxOptions": {
"prefs": {
"dom.workers.modules.enabled": true
},
"args": []
}
}