mirror of
https://github.com/bdbrown4/peltier.io.git
synced 2026-11-07 10:58:28 +00:00
Merge pull request #4 from bdbrown4/claude/test-wsk42i
This commit is contained in:
@@ -53,6 +53,7 @@ Built so far (command-level, pre-target-integration):
|
|||||||
just build / test / lint # trust-layer workspace: cargo build/test/clippy+fmt
|
just build / test / lint # trust-layer workspace: cargo build/test/clippy+fmt
|
||||||
just aa [cmd] # A/A self-test of bench-runner on a shell command
|
just aa [cmd] # A/A self-test of bench-runner on a shell command
|
||||||
just compare <a> <b> # interleaved A/B of two shell commands, bootstrap CI
|
just compare <a> <b> # interleaved A/B of two shell commands, bootstrap CI
|
||||||
|
just gates <target> # corpus pin + upstream tests + golden replay
|
||||||
just pin-check <target> # verify corpus MANIFEST.sha256
|
just pin-check <target> # verify corpus MANIFEST.sha256
|
||||||
just calibrate <cmd> <out> # automated A/A + regression-injection calibration
|
just calibrate <cmd> <out> # automated A/A + regression-injection calibration
|
||||||
```
|
```
|
||||||
|
|||||||
Generated
+1
@@ -183,6 +183,7 @@ name = "diff-test"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"clap",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ repository.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
clap.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
//! declares an explicit FP tolerance).
|
//! declares an explicit FP tolerance).
|
||||||
|
|
||||||
pub mod pin;
|
pub mod pin;
|
||||||
|
pub mod target;
|
||||||
pub mod policy;
|
pub mod policy;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
//! Gate orchestration: corpus pin → upstream tests → golden replay.
|
||||||
|
//! Refuses to run anything if the corpus manifest mismatches (SPEC §3.2).
|
||||||
|
//! Differential fuzz and sanitizers remain per-attempt manual steps in
|
||||||
|
//! Phase 1 and are reported as skipped with a reason.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::Parser;
|
||||||
|
use diff_test::target::{expected_golden_hash, TargetSpec};
|
||||||
|
use diff_test::{all_passed, pin, GateLayer, GateOutcome};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(name = "diff-test", about = "hotpath equivalence-gate orchestrator")]
|
||||||
|
struct Cli {
|
||||||
|
/// Target name under targets/.
|
||||||
|
target: String,
|
||||||
|
/// Repository root (defaults to current directory).
|
||||||
|
#[arg(long, default_value = ".")]
|
||||||
|
root: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sh(root: &PathBuf, cmd: &str) -> Result<std::process::Output> {
|
||||||
|
Ok(Command::new("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(cmd)
|
||||||
|
.current_dir(root)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
let root = cli.root.canonicalize()?;
|
||||||
|
let spec = TargetSpec::load(&root, &cli.target)?;
|
||||||
|
let mut results: Vec<(GateLayer, GateOutcome)> = Vec::new();
|
||||||
|
|
||||||
|
// Corpus pin is a precondition, not a gate: mismatch = refuse to run.
|
||||||
|
let verified = pin::verify_manifest(&root.join(&spec.corpus.manifest), &root.join(&spec.corpus.root))?;
|
||||||
|
println!("corpus pin: {} files verified against {}", verified, spec.corpus.manifest.display());
|
||||||
|
|
||||||
|
print!("upstream tests ... ");
|
||||||
|
let t = sh(&root, &spec.gates.tests)?;
|
||||||
|
let tests_outcome = if t.status.success() {
|
||||||
|
println!("PASS");
|
||||||
|
GateOutcome::Passed
|
||||||
|
} else {
|
||||||
|
println!("FAIL");
|
||||||
|
GateOutcome::Failed {
|
||||||
|
detail: String::from_utf8_lossy(&t.stderr).chars().take(2000).collect(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
results.push((GateLayer::UpstreamTests, tests_outcome));
|
||||||
|
|
||||||
|
print!("golden replay ... ");
|
||||||
|
let golden_cmd = spec.gates.golden.replace("{binary}", &spec.build.binary);
|
||||||
|
let g = sh(&root, &golden_cmd)?;
|
||||||
|
let expected = expected_golden_hash(&root.join(&spec.corpus.golden_sha256))?;
|
||||||
|
let actual = format!("{:x}", Sha256::digest(&g.stdout));
|
||||||
|
let golden_outcome = if g.status.success() && actual == expected {
|
||||||
|
println!("PASS (stdout sha256 = pinned golden)");
|
||||||
|
GateOutcome::Passed
|
||||||
|
} else {
|
||||||
|
println!("FAIL");
|
||||||
|
GateOutcome::Failed {
|
||||||
|
detail: format!("exit={:?} expected={expected} actual={actual}", g.status.code()),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
results.push((GateLayer::GoldenReplay, golden_outcome));
|
||||||
|
|
||||||
|
results.push((
|
||||||
|
GateLayer::DifferentialFuzz,
|
||||||
|
GateOutcome::Skipped { reason: "per-attempt manual step in Phase 1 (needs old/new pair)".into() },
|
||||||
|
));
|
||||||
|
results.push((
|
||||||
|
GateLayer::Sanitizers,
|
||||||
|
GateOutcome::Skipped { reason: "per-attempt manual step in Phase 1 (nightly toolchain)".into() },
|
||||||
|
));
|
||||||
|
|
||||||
|
for (layer, outcome) in &results {
|
||||||
|
if let GateOutcome::Skipped { reason } = outcome {
|
||||||
|
println!("{layer:?}: skipped — {reason}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if all_passed(&results) {
|
||||||
|
println!("gates: PASS ({})", cli.target);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("gates: FAIL ({})", cli.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
//! Per-target spec (`targets/<name>/target.toml`). All commands and
|
||||||
|
//! paths are relative to the repository root; `{binary}` in commands is
|
||||||
|
//! substituted with the built binary path.
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct TargetSpec {
|
||||||
|
pub source: Source,
|
||||||
|
pub build: Build,
|
||||||
|
pub gates: Gates,
|
||||||
|
pub corpus: Corpus,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Source {
|
||||||
|
pub repo: String,
|
||||||
|
pub commit: String,
|
||||||
|
pub license: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub submodules: toml::value::Table,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Build {
|
||||||
|
/// Baseline build command, run from repo root.
|
||||||
|
pub baseline: String,
|
||||||
|
/// Built binary path, relative to repo root.
|
||||||
|
pub binary: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Gates {
|
||||||
|
/// Upstream test suite command, run from repo root.
|
||||||
|
pub tests: String,
|
||||||
|
/// Golden replay command; stdout is hashed. `{binary}` substituted.
|
||||||
|
pub golden: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Corpus {
|
||||||
|
/// sha256sum-format manifest of the corpus inputs.
|
||||||
|
pub manifest: PathBuf,
|
||||||
|
/// Directory the manifest's relative paths resolve against.
|
||||||
|
pub root: PathBuf,
|
||||||
|
/// File whose last whitespace-delimited-first-field line is the
|
||||||
|
/// expected sha256 of the golden command's stdout.
|
||||||
|
pub golden_sha256: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TargetSpec {
|
||||||
|
pub fn load(repo_root: &Path, name: &str) -> anyhow::Result<Self> {
|
||||||
|
let path = repo_root.join("targets").join(name).join("target.toml");
|
||||||
|
let raw = std::fs::read_to_string(&path)
|
||||||
|
.map_err(|e| anyhow::anyhow!("cannot read {}: {e}", path.display()))?;
|
||||||
|
Ok(toml::from_str(&raw)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the expected stdout hash out of a GOLDEN.sha256 file
|
||||||
|
/// (comment lines starting with '#' ignored).
|
||||||
|
pub fn expected_golden_hash(path: &Path) -> anyhow::Result<String> {
|
||||||
|
let raw = std::fs::read_to_string(path)?;
|
||||||
|
raw.lines()
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|l| !l.is_empty() && !l.starts_with('#'))
|
||||||
|
.filter_map(|l| l.split_whitespace().next())
|
||||||
|
.next_back()
|
||||||
|
.map(str::to_string)
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("no hash entry in {}", path.display()))
|
||||||
|
}
|
||||||
@@ -33,3 +33,8 @@ pin-check target:
|
|||||||
calibrate cmd out sessions="20":
|
calibrate cmd out sessions="20":
|
||||||
cargo run -p bench-runner -- --config config/accept.toml calibrate \
|
cargo run -p bench-runner -- --config config/accept.toml calibrate \
|
||||||
--cmd "{{cmd}}" --sessions {{sessions}} --out "{{out}}"
|
--cmd "{{cmd}}" --sessions {{sessions}} --out "{{out}}"
|
||||||
|
|
||||||
|
# Equivalence gates for a target: corpus pin (refuses on mismatch) ->
|
||||||
|
# upstream tests -> golden replay. Fuzz/sanitizers stay per-attempt.
|
||||||
|
gates target:
|
||||||
|
cargo run -p diff-test -- {{target}}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ license = "BSD-2-Clause"
|
|||||||
# git clone <submodule repo> workspace/vendor/progit && git -C ... checkout <commit>
|
# git clone <submodule repo> workspace/vendor/progit && git -C ... checkout <commit>
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
baseline = "cargo build --release --manifest-path workspace/Cargo.toml"
|
baseline = "cargo build --release --manifest-path targets/comrak/workspace/Cargo.toml"
|
||||||
binary = "workspace/target/release/comrak"
|
binary = "targets/comrak/workspace/target/release/comrak"
|
||||||
|
|
||||||
[bench]
|
[bench]
|
||||||
# Upstream workload (benches/bench.sh): full Pro Git book through the
|
# Upstream workload (benches/bench.sh): full Pro Git book through the
|
||||||
@@ -28,6 +28,11 @@ command = "cat corpora/comrak/progit.md | {binary} > /dev/null"
|
|||||||
workload = "Pro Git book, all translations, 11.0 MB concatenated markdown, single stream"
|
workload = "Pro Git book, all translations, 11.0 MB concatenated markdown, single stream"
|
||||||
|
|
||||||
[gates]
|
[gates]
|
||||||
tests = "cargo test --release --manifest-path workspace/Cargo.toml"
|
tests = "cargo test --release --manifest-path targets/comrak/workspace/Cargo.toml"
|
||||||
# Golden replay: corpus through the binary, stdout must be byte-identical.
|
# Golden replay: corpus through the binary, stdout must be byte-identical.
|
||||||
golden = "cat corpora/comrak/progit.md | {binary}"
|
golden = "cat corpora/comrak/progit.md | {binary}"
|
||||||
|
|
||||||
|
[corpus]
|
||||||
|
manifest = "corpora/comrak/MANIFEST.sha256"
|
||||||
|
root = "corpora/comrak"
|
||||||
|
golden_sha256 = "corpora/comrak/GOLDEN.sha256"
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ commit = "fa44e5194060305576514d59b850353643afbfc8" # v14.0.0
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
baseline = "cargo build --release --manifest-path workspace/Cargo.toml"
|
baseline = "cargo build --release --manifest-path targets/tokei/workspace/Cargo.toml"
|
||||||
binary = "workspace/target/release/tokei"
|
binary = "targets/tokei/workspace/target/release/tokei"
|
||||||
|
|
||||||
[bench]
|
[bench]
|
||||||
# Single rayon thread: measurement first (SPEC conventions); the
|
# Single rayon thread: measurement first (SPEC conventions); the
|
||||||
@@ -19,5 +19,10 @@ command = "RAYON_NUM_THREADS=1 {binary} --sort code corpora/tokei/input > /dev/n
|
|||||||
workload = "8 replicas of pinned progit + comrak/src trees, 221 MB, 4360 files, single rayon thread"
|
workload = "8 replicas of pinned progit + comrak/src trees, 221 MB, 4360 files, single rayon thread"
|
||||||
|
|
||||||
[gates]
|
[gates]
|
||||||
tests = "cargo test --release --manifest-path workspace/Cargo.toml"
|
tests = "cargo test --release --manifest-path targets/tokei/workspace/Cargo.toml"
|
||||||
golden = "RAYON_NUM_THREADS=1 {binary} --sort code corpora/tokei/input"
|
golden = "RAYON_NUM_THREADS=1 {binary} --sort code corpora/tokei/input"
|
||||||
|
|
||||||
|
[corpus]
|
||||||
|
manifest = "corpora/tokei/MANIFEST.sha256"
|
||||||
|
root = "corpora/tokei/input/copy1"
|
||||||
|
golden_sha256 = "corpora/tokei/GOLDEN.sha256"
|
||||||
|
|||||||
Reference in New Issue
Block a user