From 09443f10ddc1e07d7536e5393f29344d1dfcf311 Mon Sep 17 00:00:00 2001 From: Ari Breitkreuz Date: Fri, 12 Jan 2024 17:08:26 +0100 Subject: [PATCH] Add coverage handling --- docs/src/commands/test.md | 45 +++++++++++++++++++++++++ src/command/test.rs | 69 ++++++++++++++++++++++++++++++++------- 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/docs/src/commands/test.md b/docs/src/commands/test.md index b851b0ab..3da3b49d 100644 --- a/docs/src/commands/test.md +++ b/docs/src/commands/test.md @@ -94,3 +94,48 @@ wasm-pack test --node # Run all tests which are intended to be executed in a browser wasm-pack test --firefox --headless ``` + +## Coverage (Experimental) + +
+ This feature is still highly experimental. You may experience some issues and it could break at any time. +
+ +### Enable the wasm-bindgen-test feature + +```text +cargo add --dev wasm-bindgen-test --features unstable-coverage +``` + +### Usage + +The easiest way to use the feature is to use it through [`cargo llvm-cov`](github.com/taiki-e/cargo-llvm-cov). + +```text +cargo llvm-cov wasm-pack --chrome --headless --all +``` + +#### Getting the coverage data manually + +You need to use `RUSTFLAGS="-Cinstrument-coverage -Zno-profiler-runtime` to build the project with profiling information. +Currently, `llvm-cov` is not able to get the debug information back out from a `.wasm` file, so until then, we can [get the debug info from LLVM-IR][wasmcov]. + +[wasmcov]: https://github.com/hknio/code-coverage-for-webassembly + +##### Options + +The following options are supported for coverage data: + +- `--coverage` to generate a single `.profraw` in your current working directory. +- `--profraw-out` to control the file name of the profraw or the directory in which it is placed +- `--profraw-prefix` to add a custom prefix to the profraw files. This can be useful if you're running the tests automatically in succession. + +##### Example workflow + +```text +RUSTFLAGS="-Cinstrument-coverage -Zno-profiler-runtime --emit=llvm-ir" wasm-pack test --coverage --profraw-out cov_data/ +# Generate the debug info on the host +clang target/wasm32-unknown-unknown/debug/deps/{your-dot-wasm-without-extension}.ll -Wno-override-module -c -o wasm.o +llvm-profdata merge --sparse cov_data/*.profraw -o cov_data/coverage.profdata +llvm-cov --instr-profile=cov_data/coverage.profdata wasm.o --format=html --output-dir=coverage/ --sources . +``` diff --git a/src/command/test.rs b/src/command/test.rs index 01ae1e2f..c411b08f 100644 --- a/src/command/test.rs +++ b/src/command/test.rs @@ -73,6 +73,18 @@ pub struct TestOptions { /// Build with the release profile. pub release: bool, + #[clap(long = "coverage")] + /// Experimental flag for generating coverage data. + pub coverage: bool, + + #[clap(long = "profraw-out")] + /// File to print to the profraw data of this run to. Requires --coverage + pub profraw_out: Option, + + #[clap(long = "profraw-prefix")] + /// Prefix to profraw files. This is helpful if you are running with tests with --all + pub profraw_prefix: Option, + /// Path to the Rust crate, and extra options to pass to `cargo test`. /// /// If the path is not provided, this command searches up the path from the current directory. @@ -97,6 +109,9 @@ pub struct Test { safaridriver: Option, headless: bool, release: bool, + coverage: bool, + profraw_out: Option, + profraw_prefix: Option, test_runner_path: Option, extra_options: Vec, } @@ -117,6 +132,9 @@ impl Test { geckodriver, safari, safaridriver, + coverage, + profraw_out, + profraw_prefix, mut path_and_extra_options, } = test_opts; @@ -149,6 +167,10 @@ impl Test { ) } + if profraw_out.is_some() && !coverage { + eprintln!("[WARN] profraw-out ignored due to missing --coverage flag!") + } + Ok(Test { cache: cache::get_wasm_pack_cache()?, crate_path, @@ -163,6 +185,9 @@ impl Test { safaridriver, headless, release, + coverage, + profraw_out, + profraw_prefix, test_runner_path: None, extra_options, }) @@ -311,18 +336,26 @@ impl Test { fn step_test_node(&mut self) -> Result<()> { assert!(self.node); info!("Running tests in node..."); - test::cargo_test_wasm( - &self.crate_path, - self.release, - vec![ - ( - "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", - &**self.test_runner_path.as_ref().unwrap(), - ), - ("WASM_BINDGEN_TEST_ONLY_NODE", "1".as_ref()), - ], - &self.extra_options, - )?; + let mut envs = vec![ + ( + "CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", + self.test_runner_path.as_ref().unwrap().to_str().unwrap(), + ), + ("WASM_BINDGEN_TEST_ONLY_NODE", "1".as_ref()), + ]; + if self.coverage { + envs.push(("WASM_BINDGEN_UNSTABLE_TEST_COVERAGE", "1".as_ref())) + } + if let Some(profraw_out) = &self.profraw_out { + envs.push(( + "WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_OUT", + profraw_out.to_str().unwrap(), + )) + } + if let Some(profraw_prefix) = &self.profraw_prefix { + envs.push(("WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_PREFIX", profraw_prefix)) + } + test::cargo_test_wasm(&self.crate_path, self.release, envs, &self.extra_options)?; info!("Finished running tests in node."); Ok(()) } @@ -409,6 +442,18 @@ impl Test { if !self.headless { envs.push(("NO_HEADLESS", "1")); } + if self.coverage { + envs.push(("WASM_BINDGEN_UNSTABLE_TEST_COVERAGE", "1")); + } + if let Some(profraw_out) = self.profraw_out.as_ref() { + envs.push(( + "WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_OUT", + profraw_out.to_str().unwrap(), + )) + } + if let Some(profraw_prefix) = &self.profraw_prefix { + envs.push(("WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_PREFIX", profraw_prefix)) + } envs } }