Skip to content
Merged
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
1 change: 0 additions & 1 deletion quest/m0/checks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Each child reports its CPU saved, cold and warm, against `origin/main`.

## Required

- [A cheaper differential](/quest/m0/checks/differential.md) - fixtures' wasm builds at opt-level 1 under single-threaded Node, and `stable_names` reuses `lang`'s translation
- [Checks by the diff](/quest/m0/checks/dispatch.md) - `just ci` runs only the projects a change touches, as moq's `sh/dispatch.sh` does, with a nightly `just gate`
- [A warm gate](/quest/m0/checks/cache.md) - the land gate builds through mbx, as moq's does
- [Stable names on a small fixture](/quest/m0/checks/stable-names.md) - `stable_names` runs on a small fixture with generic instances and trait impls
Expand Down
18 changes: 0 additions & 18 deletions quest/m0/checks/differential.md

This file was deleted.

2 changes: 1 addition & 1 deletion quest/m0/checks/stable-names.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ has generic instances and trait impls, instead of translating all of `lang`.

Decided with the maintainer on 2026-11-01: it caught 3 failures nothing else did, so it
stays in the gate, but on a fixture that costs a fraction of `lang`'s (about 300
CPU-seconds saved after [the reuse](/quest/m0/checks/differential.md)). Cover each naming
CPU-seconds saved now that it reuses `lang`'s translation). Cover each naming
rule it relies on in `lang` today.

Public API: none.
7 changes: 7 additions & 0 deletions ts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ toml = "1.1.8"
[lints]
workspace = true

# The differential tests' native side: optimized, since V8 runs a debug build's wasm several
# times slower, with dev's overflow checks and debug assertions, so its panics match the
# translation's.
[profile.differential]
inherits = "dev"
opt-level = 1

# The size-optimized profile moq's WASM shipped with: the benchmark's WASM route.
[profile.wasm-release]
inherits = "release"
Expand Down
81 changes: 45 additions & 36 deletions ts/tests/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@

use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::sync::OnceLock;

const TARGET: &str = "wasm32-unknown-unknown";

/// The native side's profile, from `Cargo.toml`.
const PROFILE: &str = "differential";

fn manifest_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
Expand Down Expand Up @@ -161,6 +165,8 @@ fn differential(package: &str, build: &[&str], out: &Path, entry: &str, cases: u
package,
"--target",
TARGET,
"--profile",
PROFILE,
"--target-dir",
])
.arg(&target_dir)
Expand All @@ -173,10 +179,13 @@ fn differential(package: &str, build: &[&str], out: &Path, entry: &str, cases: u
assert!(status.success(), "building {package} for {TARGET} failed");
let wasm = target_dir
.join(TARGET)
.join("debug")
.join(PROFILE)
.join(format!("{}.wasm", package.replace('-', "_")));
std::fs::write(out.join("package.json"), "{ \"type\": \"module\" }\n").unwrap();
// One thread: the cases run one at a time, and V8's background compilers and
// collectors would spend more CPU than they save.
let output = Command::new("node")
.arg("--single-threaded")
.arg(manifest_dir().join("tests").join("differential.ts"))
.arg(&wasm)
.arg(out.join(entry))
Expand Down Expand Up @@ -284,50 +293,44 @@ fn shake(out: &Path) {
/// text and order, though translation reaches it in another order.
#[test]
fn source_order() {
added_root(
"codec",
r#"roots = ["harness::run", "bench::run"]"#,
r#"roots = ["harness::run"]"#,
// First, so that translation reaches what both share in another order.
r#"roots = ["bench::run", "harness::run"]"#,
);
let roots = r#"roots = ["harness::run", "bench::run"]"#;
let base = rooted("codec", roots, "base", r#"roots = ["harness::run"]"#);
// First, so that translation reaches what both share in another order.
let added = r#"roots = ["bench::run", "harness::run"]"#;
kept(&base, &rooted("codec", roots, "added", added), added);
}

/// Adding a root that reaches another instance of a generic function, or another trait's
/// impl of a method, renames nothing the old roots reach.
#[test]
fn stable_names() {
// `lang`'s own roots, so its translation is the base.
let roots = r#"roots = ["harness::run", "harness::op", "fmt::Fault"]"#;
added_root(
"lang",
roots,
roots,
r#"roots = ["fmt::added", "harness::run", "harness::op", "fmt::Fault"]"#,
);
let added = r#"roots = ["fmt::added", "harness::run", "harness::op", "fmt::Fault"]"#;
kept(&sources(lang_out()), &rooted("lang", roots, "added", added), added);
}

/// Translate fixture `name` with its config's `roots` line replaced by `base`, then by
/// `added`, and check that every line of the first output stays, in order, in the second.
fn added_root(name: &str, roots: &str, base: &str, added: &str) {
/// Translate fixture `name` into `file` with its config's `roots` line replaced by `with`.
fn rooted(name: &str, roots: &str, file: &str, with: &str) -> Vec<(PathBuf, String)> {
let config = std::fs::read_to_string(fixture(name).join("ts.toml")).unwrap();
assert!(config.contains(roots), "{name}'s roots changed: update this test");
let dir = scratch().join(name).join("order");
std::fs::create_dir_all(&dir).unwrap();
let run = |file: &str, with: &str| {
let path = dir.join(format!("{file}.toml"));
std::fs::write(&path, config.replace(roots, with)).unwrap();
let out = dir.join(file);
let output = driver(name, &path, &["--out".as_ref(), out.as_ref()], "order");
assert!(
output.status.success(),
"translating {name} with {with} failed:\n{}",
String::from_utf8_lossy(&output.stderr)
);
sources(&out)
};
let base = run("base", base);
let more = run("added", added);
for (rel, text) in &base {
let path = dir.join(format!("{file}.toml"));
std::fs::write(&path, config.replace(roots, with)).unwrap();
let out = dir.join(file);
let output = driver(name, &path, &["--out".as_ref(), out.as_ref()], "order");
assert!(
output.status.success(),
"translating {name} with {with} failed:\n{}",
String::from_utf8_lossy(&output.stderr)
);
sources(&out)
}

/// Check that every line of `base` stays, in order, in `more`, its translation once `added`.
fn kept(base: &[(PathBuf, String)], more: &[(PathBuf, String)], added: &str) {
for (rel, text) in base {
let more = more.iter().find(|(r, _)| r == rel).map(|(_, text)| text);
let more = more.unwrap_or_else(|| panic!("{} is gone once {added}", rel.display()));
let mut rest = lines(more).into_iter();
Expand Down Expand Up @@ -358,9 +361,15 @@ fn lines(text: &str) -> Vec<String> {

#[test]
fn lang() {
let out = translated("lang");
snapshot("lang", &out);
lang_differential(&out);
let out = lang_out();
snapshot("lang", out);
lang_differential(out);
}

/// `lang`'s translation, which `stable_names` shares rather than translating it again.
fn lang_out() -> &'static Path {
static OUT: OnceLock<PathBuf> = OnceLock::new();
OUT.get_or_init(|| translated("lang"))
}

fn lang_differential(out: &Path) {
Expand Down Expand Up @@ -630,7 +639,7 @@ fn sysroot() {

/// Selects the fixture's own getrandom backend, which `rand::rng()` seeds from natively:
/// wasm32 has no OS to read. Only getrandom's flags change, so nothing else rebuilds.
const GETRANDOM: &str = r#"profile.dev.package.getrandom.rustflags=["--cfg", 'getrandom_backend="custom"']"#;
const GETRANDOM: &str = r#"profile.differential.package.getrandom.rustflags=["--cfg", 'getrandom_backend="custom"']"#;

/// `bytes`, `rand`, and `tracing`, which translation maps without a binding or config:
/// it picks getrandom's backend itself, as a browser's binary would.
Expand Down