Skip to content

Commit

Permalink
Use LLVM_PROFILE_FILE for test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda committed Dec 19, 2024
1 parent e464ba3 commit 5b1ba32
Show file tree
Hide file tree
Showing 16 changed files with 176 additions and 50 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ jobs:
- run: cargo clippy --no-deps --all-features -p wasm-bindgen-macro-support -- -D warnings
- run: cargo clippy --no-deps --all-features -p wasm-bindgen-multi-value-xform -- -D warnings
- run: cargo clippy --no-deps --all-features -p wasm-bindgen-shared -- -D warnings
- run: cargo clippy --no-deps --all-features -p wasm-bindgen-test-shared -- -D warnings
- run: cargo clippy --no-deps --all-features --target wasm32-unknown-unknown -p wasm-bindgen-test -- -D warnings
- run: cargo clippy --no-deps --all-features -p wasm-bindgen-test-macro -- -D warnings
- run: cargo clippy --no-deps --all-features -p wasm-bindgen-threads-xform -- -D warnings
Expand Down Expand Up @@ -275,6 +276,7 @@ jobs:
- run: cargo test -p wasm-bindgen-wasm-interpreter
- run: cargo test -p wasm-bindgen-futures
- run: cargo test -p wasm-bindgen-shared
- run: cargo test -p wasm-bindgen-test-shared

test_with_geckodriver:
strategy:
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
* `console.*()` calls in tests are now always intercepted by default. To show them use `--nocapture`. When shown they are always printed in-place instead of after test results, analogous to `cargo test`.
[#4356](https://github.com/rustwasm/wasm-bindgen/pull/4356)

* Replace `WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_OUT` and `WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_PREFIX` with parsing `LLVM_PROFILE_FILE` analogous to Rust test coverage.
[#4367](https://github.com/rustwasm/wasm-bindgen/pull/4367)

### Fixed

- Fixed using [JavaScript keyword](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#keywords) as identifiers not being handled correctly.
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ resolver = "2"

[patch.crates-io]
js-sys = { path = 'crates/js-sys' }
minicov = { git = "https://github.com/daxpedda/minicov", branch = "module-signature" }
wasm-bindgen = { path = '.' }
wasm-bindgen-futures = { path = 'crates/futures' }
web-sys = { path = 'crates/web-sys' }
1 change: 1 addition & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ureq = { version = "2.7", default-features = false, features = ["brotli", "gzip"
walrus = "0.23"
wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.99" }
wasm-bindgen-shared = { path = "../shared", version = "=0.2.99" }
wasm-bindgen-test-shared = { path = "../test-shared", version = "=0.2.99" }

[dev-dependencies]
assert_cmd = "2"
Expand Down
34 changes: 1 addition & 33 deletions crates/cli/src/bin/wasm-bindgen-test-runner/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use clap::Parser;
use clap::ValueEnum;
use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::thread;
use wasm_bindgen_cli_support::Bindgen;
Expand Down Expand Up @@ -102,12 +101,6 @@ fn main() -> anyhow::Result<()> {

let shell = shell::Shell::new();

let file_name = cli
.file
.file_name()
.map(Path::new)
.context("file to test is not a valid file, can't extract file name")?;

// Collect all tests that the test harness is supposed to run. We assume
// that any exported function with the prefix `__wbg_test` is a test we need
// to execute.
Expand Down Expand Up @@ -276,8 +269,6 @@ fn main() -> anyhow::Result<()> {
b.split_linked_modules(true);
}

let coverage = coverage_args(file_name);

b.debug(debug)
.input_module(module, wasm)
.keep_debug(false)
Expand All @@ -288,7 +279,7 @@ fn main() -> anyhow::Result<()> {

match test_mode {
TestMode::Node { no_modules } => {
node::execute(module, tmpdir.path(), cli, &tests, !no_modules, coverage)?
node::execute(module, tmpdir.path(), cli, &tests, !no_modules)?
}
TestMode::Deno => deno::execute(module, tmpdir.path(), cli, &tests)?,
TestMode::Browser { .. }
Expand All @@ -310,7 +301,6 @@ fn main() -> anyhow::Result<()> {
&tests,
test_mode,
std::env::var("WASM_BINDGEN_TEST_NO_ORIGIN_ISOLATION").is_err(),
coverage,
)
.context("failed to spawn server")?;
let addr = srv.server_addr();
Expand Down Expand Up @@ -379,28 +369,6 @@ impl TestMode {
}
}

fn coverage_args(file_name: &Path) -> PathBuf {
fn generated(file_name: &Path, prefix: &str) -> String {
let res = format!("{prefix}{}.profraw", file_name.display());
res
}

let prefix = env::var_os("WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_PREFIX")
.map(|s| s.to_str().unwrap().to_string())
.unwrap_or_default();

match env::var_os("WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_OUT") {
Some(s) => {
let mut buf = PathBuf::from(s);
if buf.is_dir() {
buf.push(generated(file_name, &prefix));
}
buf
}
None => PathBuf::from(generated(file_name, &prefix)),
}
}

/// Possible values for the `--format` option.
#[derive(Debug, Clone, Copy, ValueEnum)]
enum FormatSetting {
Expand Down
22 changes: 17 additions & 5 deletions crates/cli/src/bin/wasm-bindgen-test-runner/node.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::process;
use std::process::Command;

use anyhow::{Context, Error};
Expand Down Expand Up @@ -49,8 +50,18 @@ pub fn execute(
cli: Cli,
tests: &[String],
module_format: bool,
coverage: PathBuf,
) -> Result<(), Error> {
let coverage_env = if let Ok(env) = env::var("LLVM_PROFILE_FILE") {
&format!("\"{env}\"")
} else {
"undefined"
};
let coverage_pid = process::id();
let coverage_temp_dir = env::temp_dir()
.to_str()
.map(String::from)
.context("failed to parse path to temporary directory")?;

let mut js_to_execute = format!(
r#"
{exit};
Expand All @@ -68,8 +79,10 @@ pub fn execute(
const ok = await cx.run(tests.map(n => wasm.__wasm[n]));
const coverage = wasm.__wbgtest_cov_dump();
if (coverage !== undefined)
await fs.writeFile('{coverage}', coverage);
if (coverage !== undefined) {{
const path = wasm.__wbgtest_coverage_path({coverage_env}, {coverage_pid}, {coverage_temp_dir:?}, wasm.__wbgtest_module_signature());
await fs.writeFile(path, coverage);
}}
if (!ok)
exit(1);
Expand All @@ -92,7 +105,6 @@ pub fn execute(
} else {
r"import fs from 'node:fs/promises'".to_string()
},
coverage = coverage.display(),
nocapture = cli.nocapture.clone(),
console_override = SHARED_SETUP,
args = cli.into_args(),
Expand Down
31 changes: 24 additions & 7 deletions crates/cli/src/bin/wasm-bindgen-test-runner/server.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::borrow::Cow;
use std::fs;
use std::io::{Read, Write};
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::path::Path;
use std::{env, fs, process};

use anyhow::{anyhow, Context, Error};
use rouille::{Request, Response, Server};
Expand All @@ -18,14 +18,14 @@ pub(crate) fn spawn(
tests: &[String],
test_mode: TestMode,
isolate_origin: bool,
coverage: PathBuf,
) -> Result<Server<impl Fn(&Request) -> Response + Send + Sync>, Error> {
let mut js_to_execute = String::new();

let cov_import = if test_mode.no_modules() {
"let __wbgtest_cov_dump = wasm_bindgen.__wbgtest_cov_dump;"
"let __wbgtest_cov_dump = wasm_bindgen.__wbgtest_cov_dump;\n\
let __wbgtest_module_signature = wasm_bindgen.__wbgtest_module_signature;"
} else {
"__wbgtest_cov_dump,"
"__wbgtest_cov_dump,__wbgtest_module_signature,"
};
let cov_dump = r#"
// Dump the coverage data collected during the tests
Expand All @@ -34,6 +34,9 @@ pub(crate) fn spawn(
if (coverage !== undefined) {
await fetch("/__wasm_bindgen/coverage", {
method: "POST",
headers: {
"Module-Signature": __wbgtest_module_signature(),
},
body: coverage
});
}
Expand Down Expand Up @@ -326,7 +329,13 @@ pub(crate) fn spawn(

return response;
} else if request.url() == "/__wasm_bindgen/coverage" {
return if let Err(e) = handle_coverage_dump(&coverage, request) {
let module_signature = request
.header("Module-Signature")
.expect("sent coverage data without module signature")
.parse()
.expect("sent invalid module signature");

return if let Err(e) = handle_coverage_dump(module_signature, request) {
let s: &str = &format!("Failed to dump coverage: {e}");
log::error!("{s}");
let mut ret = Response::text(s);
Expand Down Expand Up @@ -386,9 +395,17 @@ pub(crate) fn spawn(
}
}

fn handle_coverage_dump(profraw_path: &Path, request: &Request) -> anyhow::Result<()> {
fn handle_coverage_dump(module_signature: u64, request: &Request) -> anyhow::Result<()> {
// This is run after all tests are done and dumps the data received in the request
// into a single profraw file
let profraw_path = wasm_bindgen_test_shared::coverage_path(
env::var("LLVM_PROFILE_FILE").ok().as_deref(),
process::id(),
env::temp_dir()
.to_str()
.context("failed to parse path to temporary directory")?,
module_signature,
);
let mut profraw = std::fs::File::create(profraw_path)?;
let mut data = Vec::new();
if let Some(mut r_data) = request.data() {
Expand Down
18 changes: 18 additions & 0 deletions crates/test-shared/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
authors = ["The wasm-bindgen Developers"]
description = """
Shared support between wasm-bindgen-test and wasm-bindgen-test-runner, an internal
dependency.
"""
documentation = "https://docs.rs/wasm-bindgen-test-shared"
edition = "2021"
homepage = "https://rustwasm.github.io/wasm-bindgen/"
include = ["/LICENSE-*", "/src"]
license = "MIT OR Apache-2.0"
name = "wasm-bindgen-test-shared"
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/test-shared"
rust-version = "1.57"
version = "0.2.99"

[lints]
workspace = true
1 change: 1 addition & 0 deletions crates/test-shared/LICENSE-APACHE
1 change: 1 addition & 0 deletions crates/test-shared/LICENSE-MIT
80 changes: 80 additions & 0 deletions crates/test-shared/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-test-shared/0.2")]
#![no_std]

extern crate alloc;

use alloc::string::{String, ToString};

pub fn coverage_path(env: Option<&str>, pid: u32, tmpdir: &str, module_signature: u64) -> String {
let env = env.unwrap_or("default_%m_%p.profraw");

let mut path = String::new();
let mut chars = env.chars().enumerate().peekable();

while let Some((index, char)) = chars.next() {
if char != '%' {
path.push(char);
continue;
}

if chars.next_if(|(_, c)| *c == 'p').is_some() {
path.push_str(&pid.to_string())
} else if chars.next_if(|(_, c)| *c == 'h').is_some() {
path.push_str("wbgt")
} else if chars.next_if(|(_, c)| *c == 't').is_some() {
path.push_str(tmpdir)
} else {
let mut last_index = index;

loop {
if let Some((index, _)) = chars.next_if(|(_, c)| c.is_ascii_digit()) {
last_index = index;
} else if chars.next_if(|(_, c)| *c == 'm').is_some() {
path.push_str(&module_signature.to_string());
path.push_str("_0");
break;
} else {
path.push_str(&env[index..=last_index]);
break;
}
}
}
}

path
}

#[test]
fn test() {
fn asssert<'a>(env: impl Into<Option<&'a str>>, result: &str) {
assert_eq!(coverage_path(env.into(), 123, "tmp", 456), result);
}

asssert(None, "default_456_0_123.profraw");
asssert("", "");
asssert("%p", "123");
asssert("%h", "wbgt");
asssert("%t", "tmp");
asssert("%m", "456_0");
asssert("%0123456789m", "456_0");
asssert("%", "%");
asssert("%%", "%%");
asssert("%a", "%a");
asssert("%0123456789", "%0123456789");
asssert("%0123456789p", "%0123456789p");
asssert("%%p", "%123");
asssert("%%%p", "%%123");
asssert("%a%p", "%a123");
asssert("%0123456789%p", "%0123456789123");
asssert("%p%", "123%");
asssert("%p%%", "123%%");
asssert("%p%a", "123%a");
asssert("%p%0123456789", "123%0123456789");
asssert("%p%0123456789p", "123%0123456789p");
asssert("%m%a", "456_0%a");
asssert("%m%0123456789", "456_0%0123456789");
asssert("%m%0123456789p", "456_0%0123456789p");
asssert("%0123456789m%a", "456_0%a");
asssert("%0123456789m%0123456789", "456_0%0123456789");
asssert("%0123456789m%0123456789p", "456_0%0123456789p");
}
1 change: 1 addition & 0 deletions crates/test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ js-sys = { path = '../js-sys', version = '=0.3.76', default-features = false }
wasm-bindgen = { path = '../..', version = '=0.2.99', default-features = false }
wasm-bindgen-futures = { path = '../futures', version = '=0.4.49', default-features = false }
wasm-bindgen-test-macro = { path = '../test-macro', version = '=0.3.49' }
wasm-bindgen-test-shared = { path = "../test-shared", version = "=0.2.99" }

[target.'cfg(all(target_arch = "wasm32", wasm_bindgen_unstable_test_coverage))'.dependencies]
minicov = "0.3"
Expand Down
12 changes: 12 additions & 0 deletions crates/test/src/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,15 @@ pub fn __wbgtest_cov_dump() -> Option<Vec<u8>> {
pub fn __wbgtest_cov_dump() -> Option<Vec<u8>> {
None
}

#[cfg(wasm_bindgen_unstable_test_coverage)]
#[wasm_bindgen]
pub fn __wbgtest_module_signature() -> Option<u64> {
Some(minicov::module_signature())
}

#[cfg(not(wasm_bindgen_unstable_test_coverage))]
#[wasm_bindgen]
pub fn __wbgtest_module_signature() -> Option<u64> {
None
}
11 changes: 11 additions & 0 deletions crates/test/src/rt/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,14 @@ impl super::Formatter for Node {
NodeError::from(err.clone()).stack()
}
}

/// Path to use for coverage data.
#[wasm_bindgen]
pub fn __wbgtest_coverage_path(env: Option<String>, pid: u32, temp_dir: &str, module_signature: u64) -> String {
wasm_bindgen_test_shared::coverage_path(
env.as_deref(),
pid,
temp_dir,
module_signature,
)
}
Loading

0 comments on commit 5b1ba32

Please sign in to comment.