Skip to content

Commit 7582f0c

Browse files
committed
Experimental test coverage support
1 parent 2ea6ab1 commit 7582f0c

File tree

22 files changed

+348
-12
lines changed

22 files changed

+348
-12
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
* Added importing strings as `JsString` through `#[wasm_bindgen(thread_local, static_string)] static STRING: JsString = "a string literal";`.
7676
[#4055](https://github.com/rustwasm/wasm-bindgen/pull/4055)
7777

78+
* Added experimental test coverage support for `wasm-bindgen-test-runner`, see the guide for more information.
79+
[#4060](https://github.com/rustwasm/wasm-bindgen/pull/4060)
80+
7881
### Changed
7982

8083
* Stabilize Web Share API.

Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ serde_derive = "1.0"
5656
wasm-bindgen-test-crate-a = { path = 'tests/crates/a' }
5757
wasm-bindgen-test-crate-b = { path = 'tests/crates/b' }
5858

59+
[lints.rust]
60+
unexpected_cfgs = { level = "warn", check-cfg = [
61+
'cfg(wasm_bindgen_unstable_test_coverage)',
62+
] }
63+
5964
[workspace]
6065
members = [
6166
"benchmarks",

crates/backend/src/codegen.rs

+11
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ impl ToTokens for ast::Struct {
220220
(quote! {
221221
#[automatically_derived]
222222
impl #wasm_bindgen::describe::WasmDescribe for #name {
223+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
223224
fn describe() {
224225
use #wasm_bindgen::__wbindgen_if_not_std;
225226
use #wasm_bindgen::describe::*;
@@ -293,6 +294,7 @@ impl ToTokens for ast::Struct {
293294
#[doc(hidden)]
294295
// `allow_delayed` is whether it's ok to not actually free the `ptr` immediately
295296
// if it's still borrowed.
297+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
296298
pub unsafe extern "C" fn #free_fn(ptr: u32, allow_delayed: u32) {
297299
use #wasm_bindgen::__rt::alloc::rc::Rc;
298300

@@ -401,6 +403,7 @@ impl ToTokens for ast::Struct {
401403
}
402404

403405
impl #wasm_bindgen::describe::WasmDescribeVector for #name {
406+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
404407
fn describe_vector() {
405408
use #wasm_bindgen::describe::*;
406409
inform(VECTOR);
@@ -484,6 +487,7 @@ impl ToTokens for ast::StructField {
484487
const _: () = {
485488
#[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), no_mangle)]
486489
#[doc(hidden)]
490+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
487491
pub unsafe extern "C" fn #getter(js: u32)
488492
-> #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi>
489493
{
@@ -525,6 +529,7 @@ impl ToTokens for ast::StructField {
525529
const _: () = {
526530
#[no_mangle]
527531
#[doc(hidden)]
532+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
528533
pub unsafe extern "C" fn #setter(
529534
js: u32,
530535
#(#args,)*
@@ -781,6 +786,7 @@ impl TryToTokens for ast::Export {
781786
all(target_arch = "wasm32", target_os = "unknown"),
782787
export_name = #export_name,
783788
)]
789+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
784790
pub unsafe extern "C" fn #generated_name(#(#args),*) -> #wasm_bindgen::convert::WasmRet<#projection::Abi> {
785791
#start_check
786792

@@ -932,6 +938,7 @@ impl ToTokens for ast::ImportType {
932938
use #wasm_bindgen::__rt::core;
933939

934940
impl WasmDescribe for #rust_name {
941+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
935942
fn describe() {
936943
#description
937944
}
@@ -1222,6 +1229,7 @@ impl ToTokens for ast::StringEnum {
12221229

12231230
#[automatically_derived]
12241231
impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
1232+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
12251233
fn describe() {
12261234
use #wasm_bindgen::describe::*;
12271235
inform(STRING_ENUM);
@@ -1563,6 +1571,7 @@ impl ToTokens for ast::Enum {
15631571

15641572
#[automatically_derived]
15651573
impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
1574+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
15661575
fn describe() {
15671576
use #wasm_bindgen::describe::*;
15681577
inform(ENUM);
@@ -1599,6 +1608,7 @@ impl ToTokens for ast::Enum {
15991608
}
16001609

16011610
impl #wasm_bindgen::describe::WasmDescribeVector for #enum_name {
1611+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
16021612
fn describe_vector() {
16031613
use #wasm_bindgen::describe::*;
16041614
inform(VECTOR);
@@ -1795,6 +1805,7 @@ impl<'a, T: ToTokens> ToTokens for Descriptor<'a, T> {
17951805
#(#attrs)*
17961806
#[no_mangle]
17971807
#[doc(hidden)]
1808+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
17981809
pub extern "C" fn #name() {
17991810
use #wasm_bindgen::describe::*;
18001811
// See definition of `link_mem_intrinsics` for what this is doing

crates/cli/src/bin/wasm-bindgen-test-runner/main.rs

+29
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use anyhow::{anyhow, bail, Context};
1515
use log::error;
1616
use std::env;
1717
use std::fs;
18+
use std::path::Path;
1819
use std::path::PathBuf;
1920
use std::thread;
2021
use wasm_bindgen_cli_support::Bindgen;
@@ -222,6 +223,8 @@ fn main() -> anyhow::Result<()> {
222223
b.split_linked_modules(true);
223224
}
224225

226+
let coverage = coverage_args(&tmpdir);
227+
225228
b.debug(debug)
226229
.input_module(module, wasm)
227230
.keep_debug(false)
@@ -256,6 +259,7 @@ fn main() -> anyhow::Result<()> {
256259
&tests,
257260
test_mode,
258261
std::env::var("WASM_BINDGEN_TEST_NO_ORIGIN_ISOLATION").is_err(),
262+
coverage,
259263
)
260264
.context("failed to spawn server")?;
261265
let addr = srv.server_addr();
@@ -282,3 +286,28 @@ fn main() -> anyhow::Result<()> {
282286
}
283287
Ok(())
284288
}
289+
290+
fn coverage_args(tmpdir: &Path) -> PathBuf {
291+
fn generated(tmpdir: &Path, prefix: &str) -> String {
292+
let res = format!(
293+
"{prefix}{}.profraw",
294+
tmpdir.file_name().and_then(|s| s.to_str()).unwrap()
295+
);
296+
res
297+
}
298+
299+
let prefix = env::var_os("WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_PREFIX")
300+
.map(|s| s.to_str().unwrap().to_string())
301+
.unwrap_or_default();
302+
303+
match env::var_os("WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_OUT") {
304+
Some(s) => {
305+
let mut buf = PathBuf::from(s);
306+
if buf.is_dir() {
307+
buf.push(generated(tmpdir, &prefix));
308+
}
309+
buf
310+
}
311+
None => PathBuf::from(generated(tmpdir, &prefix)),
312+
}
313+
}

crates/cli/src/bin/wasm-bindgen-test-runner/server.rs

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::borrow::Cow;
22
use std::ffi::OsString;
33
use std::fs;
4+
use std::io::{Read, Write};
45
use std::net::SocketAddr;
5-
use std::path::Path;
6+
use std::path::{Path, PathBuf};
67

78
use anyhow::{anyhow, Context, Error};
89
use rouille::{Request, Response, Server};
@@ -18,9 +19,24 @@ pub(crate) fn spawn(
1819
tests: &[String],
1920
test_mode: TestMode,
2021
isolate_origin: bool,
22+
coverage: PathBuf,
2123
) -> Result<Server<impl Fn(&Request) -> Response + Send + Sync>, Error> {
2224
let mut js_to_execute = String::new();
2325

26+
let cov_import = if test_mode.no_modules() {
27+
"let __wbgtest_cov_dump = wasm_bindgen.__wbgtest_cov_dump;"
28+
} else {
29+
"__wbgtest_cov_dump,"
30+
};
31+
let cov_dump = r#"
32+
// Dump the coverage data collected during the tests
33+
const coverage = __wbgtest_cov_dump();
34+
await fetch("/__wasm_bindgen/coverage", {
35+
method: "POST",
36+
body: coverage
37+
});
38+
"#;
39+
2440
let wbg_import_script = if test_mode.no_modules() {
2541
String::from(
2642
r#"
@@ -30,6 +46,7 @@ pub(crate) fn spawn(
3046
let __wbgtest_console_info = wasm_bindgen.__wbgtest_console_info;
3147
let __wbgtest_console_warn = wasm_bindgen.__wbgtest_console_warn;
3248
let __wbgtest_console_error = wasm_bindgen.__wbgtest_console_error;
49+
{cov_import}
3350
let init = wasm_bindgen;
3451
"#,
3552
)
@@ -43,6 +60,7 @@ pub(crate) fn spawn(
4360
__wbgtest_console_info,
4461
__wbgtest_console_warn,
4562
__wbgtest_console_error,
63+
{cov_import}
4664
default as init,
4765
}} from './{}';
4866
"#,
@@ -116,6 +134,7 @@ pub(crate) fn spawn(
116134
117135
cx.args({1:?});
118136
await cx.run(tests.map(s => wasm[s]));
137+
{cov_dump}
119138
}}
120139
121140
port.onmessage = function(e) {{
@@ -250,6 +269,7 @@ pub(crate) fn spawn(
250269
cx.args({1:?});
251270
252271
await cx.run(test.map(s => wasm[s]));
272+
{cov_dump}
253273
}}
254274
255275
const tests = [];
@@ -300,6 +320,16 @@ pub(crate) fn spawn(
300320
}
301321

302322
return response;
323+
} else if request.url() == "/__wasm_bindgen/coverage" {
324+
return if let Err(e) = handle_coverage_dump(&coverage, request) {
325+
let s: &str = &format!("Failed to dump coverage: {e}");
326+
log::error!("{s}");
327+
let mut ret = Response::text(s);
328+
ret.status_code = 500;
329+
ret
330+
} else {
331+
Response::empty_204()
332+
};
303333
}
304334

305335
// Otherwise we need to find the asset here. It may either be in our
@@ -351,6 +381,21 @@ pub(crate) fn spawn(
351381
}
352382
}
353383

384+
fn handle_coverage_dump(profraw_path: &Path, request: &Request) -> anyhow::Result<()> {
385+
// This is run after all tests are done and dumps the data received in the request
386+
// into a single profraw file
387+
let mut profraw = std::fs::File::create(profraw_path)?;
388+
let mut data = Vec::new();
389+
if let Some(mut r_data) = request.data() {
390+
r_data.read_to_end(&mut data)?;
391+
}
392+
// Warnings about empty data should have already been handled by
393+
// the client
394+
395+
profraw.write_all(&data)?;
396+
Ok(())
397+
}
398+
354399
/*
355400
* Set the Cross-Origin-Opener-Policy and Cross-Origin_Embedder-Policy headers
356401
* on the Server response to enable worker context sharing, as described in:

crates/macro/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,8 @@ trybuild = "1.0"
3131
wasm-bindgen = { path = "../.." }
3232
wasm-bindgen-futures = { path = "../futures" }
3333
web-sys = { path = "../web-sys", features = ["Worker"] }
34+
35+
[lints.rust]
36+
unexpected_cfgs = { level = "warn", check-cfg = [
37+
'cfg(wasm_bindgen_unstable_test_coverage)',
38+
] }

crates/macro/src/lib.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-macro/0.2")]
2+
#![cfg_attr(
3+
wasm_bindgen_unstable_test_coverage,
4+
feature(allow_internal_unstable),
5+
allow(internal_features)
6+
)]
27

38
extern crate proc_macro;
49

510
use proc_macro::TokenStream;
611
use quote::quote;
712

813
#[proc_macro_attribute]
14+
#[cfg_attr(
15+
wasm_bindgen_unstable_test_coverage,
16+
allow_internal_unstable(coverage_attribute)
17+
)]
918
pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
1019
match wasm_bindgen_macro_support::expand(attr.into(), input.into()) {
1120
Ok(tokens) => {
@@ -32,6 +41,10 @@ pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
3241
/// let worker = Worker::new(&wasm_bindgen::link_to!(module = "/src/worker.js"));
3342
/// ```
3443
#[proc_macro]
44+
#[cfg_attr(
45+
wasm_bindgen_unstable_test_coverage,
46+
allow_internal_unstable(coverage_attribute)
47+
)]
3548
pub fn link_to(input: TokenStream) -> TokenStream {
3649
match wasm_bindgen_macro_support::expand_link_to(input.into()) {
3750
Ok(tokens) => {
@@ -48,6 +61,10 @@ pub fn link_to(input: TokenStream) -> TokenStream {
4861
}
4962

5063
#[proc_macro_attribute]
64+
#[cfg_attr(
65+
wasm_bindgen_unstable_test_coverage,
66+
allow_internal_unstable(coverage_attribute)
67+
)]
5168
pub fn __wasm_bindgen_class_marker(attr: TokenStream, input: TokenStream) -> TokenStream {
5269
match wasm_bindgen_macro_support::expand_class_marker(attr.into(), input.into()) {
5370
Ok(tokens) => {

crates/test-macro/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ syn = { version = "2.0", default-features = false, features = [ "parsing", "proc
2020
[dev-dependencies]
2121
wasm-bindgen-test = { path = "../test" }
2222
trybuild = "1.0"
23+
24+
[lints.rust]
25+
unexpected_cfgs = { level = "warn", check-cfg = [
26+
'cfg(wasm_bindgen_unstable_test_coverage)',
27+
] }

crates/test-macro/src/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
//! See the README for `wasm-bindgen-test` for a bit more info about what's
22
//! going on here.
33
4+
#![cfg_attr(
5+
wasm_bindgen_unstable_test_coverage,
6+
feature(allow_internal_unstable),
7+
allow(internal_features)
8+
)]
9+
410
extern crate proc_macro;
511

612
use proc_macro2::*;
@@ -12,6 +18,10 @@ use std::sync::atomic::*;
1218
static CNT: AtomicUsize = AtomicUsize::new(0);
1319

1420
#[proc_macro_attribute]
21+
#[cfg_attr(
22+
wasm_bindgen_unstable_test_coverage,
23+
allow_internal_unstable(coverage_attribute)
24+
)]
1525
pub fn wasm_bindgen_test(
1626
attr: proc_macro::TokenStream,
1727
body: proc_macro::TokenStream,
@@ -102,6 +112,7 @@ pub fn wasm_bindgen_test(
102112
tokens.extend(
103113
quote! {
104114
#[no_mangle]
115+
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
105116
pub extern "C" fn #name(cx: &#wasm_bindgen_path::__rt::Context) {
106117
let test_name = ::core::concat!(::core::module_path!(), "::", ::core::stringify!(#ident));
107118
#test_body

crates/test/Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,13 @@ wasm-bindgen-futures = { path = '../futures', version = '0.4.42' }
1818
wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.3.42' }
1919
gg-alloc = { version = "1.0", optional = true }
2020

21+
[target.'cfg(all(target_arch = "wasm32", wasm_bindgen_unstable_test_coverage))'.dependencies]
22+
minicov = "0.3"
23+
24+
[lints.rust]
25+
unexpected_cfgs = { level = "warn", check-cfg = [
26+
'cfg(wasm_bindgen_unstable_test_coverage)',
27+
] }
28+
2129
[lib]
2230
test = false

0 commit comments

Comments
 (0)