Generating Coverage Data
You can ask the runner to generate coverage data from functions marked as #[wasm_bindgen_test]
in the .profraw
format.
Enabling the feature
To enable this feature, you need to enable cfg(wasm_bindgen_unstable_test_coverage)
.
Generating the data
RUSTFLAGS
that need to be present
Make sure you are using RUSTFLAGS=-Cinstrument-coverage -Zno-profiler-runtime
.
Due to the current limitation of llvm-cov
, we can't collect profiling symbols from the generated .wasm
files. Instead, we can grab them from the LLVM IR with --emit=llvm-ir
by using Clang. Usage of Clang or any LLVM tools must match the LLVM version used by Rust.
Arguments to the test runner
The following environment variables can be used to control the coverage output when executing the test runner:
WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_OUT
to control the file name of the profraw or the directory in which it is placed. It might be necessary to provide the full path if e.g. running tests in a workspace.WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_PREFIX
to add a custom prefix to the profraw files. This can be useful if you're running the tests automatically in succession.
Target features
This feature relies on the minicov crate, which provides a profiling runtime for WebAssembly. It in turn uses cc to compile the runtime to Wasm, which currently doesn't support accounting for target feature. Use e.g. CFLAGS_wasm32_unknown_unknown="-matomics -mbulk-memory"
to account for that.
Example
This adapts code taken from the Rustc book, see that for more examples and general information on test coverage as well.
# Run the tests:
# `--tests` to not run documentation tests, which is currently not supported.
RUSTFLAGS="-Cinstrument-coverage -Zno-profiler-runtime --emit=llvm-ir --cfg=wasm_bindgen_unstable_test_coverage" \
CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner \
cargo +nightly test --tests
# Compile to object files:
# - Extract a list of compiled artifacts from Cargo and filter them with `jq`.
# - Figure out the path to the LLVM IR file corresponding to an artifact.
# - Compile to object file with Clang and store for later usage with `llvm-cov`.
crate_name=name_of_the_tested_crate_in_snake_case
objects=()
IFS=$'\n'
for file in $(
RUSTFLAGS="-Cinstrument-coverage -Zno-profiler-runtime --emit=llvm-ir --cfg=wasm_bindgen_unstable_test_coverage" \
cargo +nightly test --tests --no-run --message-format=json | \
jq -r "select(.reason == \"compiler-artifact\") | (select(.target.kind == [\"test\"]) // select(.target.name == \"$crate_name\")) | .filenames[0]"
)
do
if [[ ${file##*.} == "rlib" ]]; then
base=$(basename $file .rlib)
file=$(dirname $file)/${base#"lib"}.ll
else
file=$(dirname $file)/$(basename $file .wasm).ll
fi
output = $(basename $file .ll).o
clang-19 $file -Wno-override-module -c -o $output
objects+=(-object $output)
done
# Merge all generated raw profiling data.
llvm-profdata-19 merge -sparse *.profraw -o coverage.profdata
# Produce test coverage data in the HTML format and pass the object files we generated earlier.
llvm-cov-19 show -show-instantiations=false -Xdemangler=rustfilt -output-dir coverage -format=html -instr-profile=coverage.profdata ${objects[@]} -sources src
Attribution
These methods have originally been pioneered by Hacken OÜ, see their guide as well.