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 coverage handling #1362

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
45 changes: 45 additions & 0 deletions docs/src/commands/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

<div class="warning">
This feature is still highly experimental. You may experience some issues and it could break at any time.
</div>

### 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 .
```
69 changes: 57 additions & 12 deletions src/command/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf>,

#[clap(long = "profraw-prefix")]
/// Prefix to profraw files. This is helpful if you are running with tests with --all
pub profraw_prefix: Option<String>,

/// 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.
Expand All @@ -97,6 +109,9 @@ pub struct Test {
safaridriver: Option<PathBuf>,
headless: bool,
release: bool,
coverage: bool,
profraw_out: Option<PathBuf>,
profraw_prefix: Option<String>,
test_runner_path: Option<PathBuf>,
extra_options: Vec<String>,
}
Expand All @@ -117,6 +132,9 @@ impl Test {
geckodriver,
safari,
safaridriver,
coverage,
profraw_out,
profraw_prefix,
mut path_and_extra_options,
} = test_opts;

Expand Down Expand Up @@ -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,
Expand All @@ -163,6 +185,9 @@ impl Test {
safaridriver,
headless,
release,
coverage,
profraw_out,
profraw_prefix,
test_runner_path: None,
extra_options,
})
Expand Down Expand Up @@ -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(())
}
Expand Down Expand Up @@ -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
}
}
Loading