pub mod gates;
pub mod git;
pub mod metadata;
pub mod parse;
pub mod runner;
pub mod snapshot;
pub use gates::{
evaluate_floor, gate_checks_pass, gate_enumeration_superset, gate_file_scope,
gate_no_new_clippy, gate_no_regression, gate_no_test_gaming, FloorInputs, FloorVerdict,
GateKind, GateOutcome, Violation,
};
pub use snapshot::{
hash_sorted, BaselineMismatch, BaselineSnapshot, CheckRun, ClippySnapshot, ClippyWarning,
Coverage, RunSnapshot, TestId, TestSnapshot, BASELINE_SCHEMA_VERSION,
};
#[derive(Debug, thiserror::Error)]
pub enum FloorError {
#[error("floor git error: {message}")]
Git {
message: String,
},
#[error("floor capture error ({what}): {message}")]
Capture {
what: &'static str,
message: String,
},
}
#[cfg(test)]
mod tests {
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use tempfile::TempDir;
use std::collections::BTreeSet;
use super::runner::{assertion_counts_at_ref, assertion_counts_on_disk};
use super::{
evaluate_floor, git::changed_files, CheckRun, FloorInputs, RunSnapshot, TestId,
TestSnapshot,
};
fn passed_t() -> BTreeSet<TestId> {
[TestId::new("pkg", "lib", "pkg", "t")]
.into_iter()
.collect()
}
fn git_in(dir: &Path, args: &[&str]) {
let ok = Command::new("git")
.arg("-C")
.arg(dir)
.args([
"-c",
"user.email=t@t",
"-c",
"user.name=t",
"-c",
"commit.gpgsign=false",
])
.args(args)
.output()
.expect("git runs")
.status
.success();
assert!(ok, "git {args:?} failed");
}
fn head(dir: &Path) -> String {
String::from_utf8_lossy(
&Command::new("git")
.arg("-C")
.arg(dir)
.args(["rev-parse", "HEAD"])
.output()
.unwrap()
.stdout,
)
.trim()
.to_string()
}
fn clean_check(passed: bool) -> CheckRun {
CheckRun {
desc: "feature check".into(),
run: "cargo test".into(),
cwd: None,
passed,
exit_code: Some(i32::from(!passed)),
stdout: String::new(),
stderr: String::new(),
}
}
fn repo_with_baseline() -> (TempDir, String) {
let dir = TempDir::new().unwrap();
let p = dir.path();
git_in(p, &["init", "-q", "-b", "main"]);
fs::create_dir_all(p.join("src")).unwrap();
fs::write(
p.join("src/a.rs"),
"#[test] fn t() { assert!(x); assert_eq!(a, b); }\n",
)
.unwrap();
git_in(p, &["add", "."]);
git_in(p, &["commit", "-qm", "baseline fork"]);
let base = head(p);
(dir, base)
}
#[test]
fn end_to_end_clean_change_passes_the_floor() {
let (dir, base) = repo_with_baseline();
let p = dir.path();
let declared = vec![PathBuf::from("src/a.rs")];
let base_assert = assertion_counts_at_ref(p, &base, &declared).unwrap();
assert_eq!(base_assert[&PathBuf::from("src/a.rs")], 2);
fs::write(
p.join("src/a.rs"),
"#[test] fn t() { assert!(x); assert_eq!(a, b); assert_ne!(c, d); }\n",
)
.unwrap();
git_in(p, &["add", "."]);
git_in(p, &["commit", "-qm", "in-scope edit"]);
let changed = changed_files(p, &base, "HEAD").unwrap();
assert_eq!(changed, vec![PathBuf::from("src/a.rs")]);
let cur_assert = assertion_counts_on_disk(p, &declared);
let baseline = RunSnapshot {
tests: TestSnapshot {
passed: passed_t(),
..Default::default()
},
..Default::default()
};
let current = baseline.clone();
let inputs = FloorInputs {
baseline: &baseline,
current: ¤t,
check_results: &[clean_check(true)],
declared_files: &declared,
changed_files: &changed,
baseline_assertions: &base_assert,
current_assertions: &cur_assert,
file_scope_slack: 0,
};
let verdict = evaluate_floor(&inputs);
assert!(verdict.passed(), "{verdict:#?}");
}
#[test]
fn end_to_end_out_of_scope_and_gutted_assertions_fail() {
let (dir, base) = repo_with_baseline();
let p = dir.path();
let declared = vec![PathBuf::from("src/a.rs")];
let base_assert = assertion_counts_at_ref(p, &base, &declared).unwrap();
fs::write(p.join("src/a.rs"), "#[test] fn t() {}\n").unwrap();
fs::write(p.join("src/sneaky.rs"), "fn s() {}\n").unwrap();
git_in(p, &["add", "."]);
git_in(p, &["commit", "-qm", "gamed"]);
let changed = changed_files(p, &base, "HEAD").unwrap();
let cur_assert = assertion_counts_on_disk(p, &declared);
assert_eq!(cur_assert[&PathBuf::from("src/a.rs")], 0);
let snap = RunSnapshot {
tests: TestSnapshot {
passed: passed_t(),
..Default::default()
},
..Default::default()
};
let inputs = FloorInputs {
baseline: &snap,
current: &snap,
check_results: &[clean_check(true)],
declared_files: &declared,
changed_files: &changed,
baseline_assertions: &base_assert,
current_assertions: &cur_assert,
file_scope_slack: 0,
};
let verdict = evaluate_floor(&inputs);
assert!(!verdict.passed());
use super::{GateKind, Violation};
assert!(verdict
.failed_gates()
.any(|g| g.gate == GateKind::FileScope));
assert!(verdict.violations().any(|v| matches!(
v,
Violation::AssertionDensityRegressed { file, .. } if file == &PathBuf::from("src/a.rs")
)));
}
#[test]
fn floor_error_is_returned_when_capture_cannot_run() {
let (dir, _base) = repo_with_baseline();
let err =
assertion_counts_at_ref(dir.path(), "deadbeefdeadbeef", &[PathBuf::from("src/a.rs")]);
assert!(err.is_err());
}
#[test]
fn end_to_end_real_cargo_captures_metadata_expected_targets_and_doctests() {
use super::metadata;
use super::runner::{capture_doctests, capture_test_snapshot};
let dir = TempDir::new().unwrap();
let p = dir.path();
fs::write(
p.join("Cargo.toml"),
"[package]\nname = \"floortest\"\nversion = \"0.0.0\"\nedition = \"2021\"\n",
)
.unwrap();
fs::create_dir_all(p.join("src")).unwrap();
fs::write(
p.join("src/lib.rs"),
"//! ```\n//! assert_eq!(floortest::add(2, 2), 4);\n//! ```\n\
pub fn add(a: i32, b: i32) -> i32 { a + b }\n\
#[cfg(test)]\nmod tests { #[test] fn works() { assert_eq!(super::add(1, 1), 2); } }\n",
)
.unwrap();
let meta = metadata::load(p).unwrap();
metadata::reject_forged_harness(&meta).unwrap();
assert!(metadata::expected_test_targets(&meta).contains("floortest/lib/floortest"));
let td = TempDir::new().unwrap();
let mut snap = capture_test_snapshot("cargo test", p, td.path()).unwrap();
metadata::verify_enumeration(&meta, &snap.targets).unwrap();
assert!(
snap.targets.contains("floortest/lib/floortest"),
"{:?}",
snap.targets
);
assert!(
snap.passed.iter().any(|t| t.target_kind == "lib"),
"unit test captured: {:?}",
snap.passed
);
capture_doctests(p, td.path(), &meta, &mut snap).unwrap();
assert!(
snap.targets.contains("floortest/doctest/floortest"),
"{:?}",
snap.targets
);
assert!(
snap.passed.iter().any(|t| t.target_kind == "doctest"),
"doctest captured: {:?}",
snap.passed
);
}
#[test]
fn real_cargo_rejects_forged_test_harness_but_allows_bench() {
use super::metadata;
let dir = TempDir::new().unwrap();
let p = dir.path();
fs::write(
p.join("Cargo.toml"),
"[package]\nname = \"forge\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\n\
[[bench]]\nname = \"crit\"\npath = \"benches/crit.rs\"\nharness = false\n\n\
[[test]]\nname = \"e2e\"\npath = \"tests/e2e.rs\"\nharness = false\n",
)
.unwrap();
fs::create_dir_all(p.join("src")).unwrap();
fs::write(p.join("src/lib.rs"), "pub fn f() {}\n").unwrap();
fs::create_dir_all(p.join("benches")).unwrap();
fs::write(p.join("benches/crit.rs"), "fn main() {}\n").unwrap();
fs::create_dir_all(p.join("tests")).unwrap();
fs::write(p.join("tests/e2e.rs"), "fn main() {}\n").unwrap();
let meta = metadata::load(p).unwrap();
let err = metadata::reject_forged_harness(&meta).unwrap_err();
let msg = format!("{err}");
assert!(msg.contains("forged custom test harness"), "{msg}");
assert!(msg.contains("[test]") && msg.contains("e2e"), "{msg}");
assert!(!msg.contains("crit"), "bench wrongly flagged: {msg}");
}
}