crates/nornir-build-thing/tests/common/mod.rs
//! Shared fixture builder for the tests that need a **real** artifact.
//!
//! Both `receipt_end_to_end.rs` and `history_end_to_end.rs` need the same thing:
//! a small binary built by a real compiler and a real linker, carrying (or
//! deliberately not carrying) a receipt in `.nordisk.buildthing`. Two copies of
//! this would be two definitions of what "a real artifact" means here, and the
//! history tests exist precisely to check that the same bytes attest the same
//! way — so they must be the same bytes.
use std::path::{Path, PathBuf};
use std::process::Command;
/// Build a one-file binary whose source is `prelude` followed by a `main`.
///
/// The fixture has **no dependencies**, so it builds `--offline` with no
/// registry and these tests need no network.
///
/// `strip` picks whether the artifact keeps a symbol table, which is the axis the
/// receipt-proof assertions turn on.
///
/// # Panics
///
/// If cargo cannot be run, if the fixture does not build (the compiler's stderr
/// is included), or if no binary appears where cargo said it would.
// Used by `receipt_end_to_end.rs` and `history_end_to_end.rs`, not by
// `attestation_end_to_end.rs` — and a `mod common;` is compiled fresh into each
// test binary, so one of the three always reports it unused. The alternative is
// three copies of the fixture builder, which is the thing this file exists to
// prevent.
#[allow(dead_code)]
pub fn build_fixture(root: &Path, name: &str, prelude: &str, strip: bool) -> PathBuf {
build_fixture_with_main(root, name, prelude, "", strip)
}
/// [`build_fixture`] with a body spliced into `main` before the `println!`.
///
/// The extra parameter exists for one reason: a symbol only reaches the symbol
/// table if something keeps it, and `--release` + `lto = true` is entitled to
/// delete a function nothing reaches. A test that wants to measure a *family* of
/// symbols in a real binary therefore has to make `main` touch them, and a
/// fixture whose `main` is fixed cannot.
///
/// Kept as one function with a delegating wrapper rather than two builders:
/// "what a real artifact is here" has to have exactly one definition, or the
/// tests that compare artifacts against each other are comparing two things.
///
/// # Panics
///
/// As [`build_fixture`].
pub fn build_fixture_with_main(
root: &Path,
name: &str,
prelude: &str,
main_body: &str,
strip: bool,
) -> PathBuf {
let dir = root.join(name);
std::fs::create_dir_all(dir.join("src")).expect("fixture dirs");
std::fs::write(
dir.join("Cargo.toml"),
format!(
"[package]\nname = \"{name}\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\
[workspace]\n\
[profile.release]\nopt-level = 3\nlto = true\nstrip = {strip}\n"
),
)
.expect("fixture manifest");
std::fs::write(
dir.join("src/main.rs"),
format!("{prelude}\nfn main() {{ {main_body} println!(\"{name}\"); }}\n"),
)
.expect("fixture source");
let target_dir = root.join("t");
let out = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
.args(["build", "--release", "--offline"])
.arg("--target-dir")
.arg(&target_dir)
.current_dir(&dir)
.output()
.expect("run cargo");
assert!(
out.status.success(),
"fixture {name} did not build:\n{}",
String::from_utf8_lossy(&out.stderr)
);
let bin = target_dir.join("release").join(name);
assert!(bin.is_file(), "no fixture binary at {}", bin.display());
bin
}