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

Use LLVM_PROFILE_FILE for test coverage #4367

Draft
wants to merge 1 commit into
base: main
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
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
Loading