use super::analysis::run_satd_analysis;
use super::types::QualityProfile;
fn write_crate(dir: &std::path::Path, body: &str) {
std::fs::create_dir_all(dir.join("src")).expect("mkdir");
std::fs::write(
dir.join("Cargo.toml"),
"[package]\nname = \"t\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
)
.expect("write manifest");
std::fs::write(dir.join("src/lib.rs"), body).expect("write source");
}
#[tokio::test]
async fn satd_markers_become_violations() {
let dir = tempfile::tempdir().expect("tempdir");
write_crate(
dir.path(),
"// FIXME: broken\n// HACK: workaround\npub fn f() -> i32 { 1 }\n",
);
let violations = run_satd_analysis(dir.path(), &QualityProfile::default(), None)
.await
.expect("analysis runs")
.violations;
assert!(
!violations.is_empty(),
"an enforcer that finds SATD must report it"
);
assert!(violations.iter().all(|v| v.violation_type == "satd"));
assert!(violations
.iter()
.all(|v| v.location.contains("lib.rs") && v.location.matches(':').count() >= 2));
}
#[tokio::test]
async fn clean_code_produces_no_violations() {
let dir = tempfile::tempdir().expect("tempdir");
write_crate(
dir.path(),
"//! Clean.\n\n/// Adds.\npub fn f() -> i32 { 1 }\n",
);
let violations = run_satd_analysis(dir.path(), &QualityProfile::default(), None)
.await
.expect("analysis runs")
.violations;
assert!(violations.is_empty(), "got {violations:?}");
}
#[tokio::test]
async fn an_unanalysable_path_errors_rather_than_reporting_zero_debt() {
let missing = std::path::Path::new("/nonexistent/pmat/satd/probe");
let outcome = run_satd_analysis(missing, &QualityProfile::default(), None)
.await
.expect("the phase reports rather than aborting the run");
assert!(outcome.violations.is_empty());
assert!(
!outcome.is_measured(),
"a path that cannot be analysed must report as unmeasured, not clean"
);
}
#[tokio::test]
async fn file_scope_ignores_a_dirty_sibling() {
let dir = tempfile::tempdir().expect("tempdir");
let src = dir.path().join("src");
std::fs::create_dir_all(&src).expect("mkdir");
std::fs::write(
dir.path().join("Cargo.toml"),
"[package]\nname = \"t\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
)
.expect("manifest");
let good = src.join("good.rs");
std::fs::write(&good, "//! clean\n/// g\npub fn g(a: i32) -> i32 { a }\n").expect("good");
std::fs::write(
src.join("bad.rs"),
"//! dirty\n// TODO: fix me later\n/// h\npub fn h(a: i32) -> i32 { a }\n",
)
.expect("bad");
let clean = run_satd_analysis(&src, &QualityProfile::default(), Some(&good))
.await
.expect("analysis runs");
assert!(
clean.violations.is_empty(),
"the named file is clean; a sibling's TODO is not its violation: {:?}",
clean.violations
);
assert!(clean.is_measured(), "the file was read, so this phase ran");
let dirty = run_satd_analysis(&src, &QualityProfile::default(), Some(&src.join("bad.rs")))
.await
.expect("analysis runs");
assert!(!dirty.violations.is_empty());
assert!(
dirty
.violations
.iter()
.all(|v| v.location.contains("bad.rs")),
"every violation must name the file it came from: {:?}",
dirty.violations
);
}
#[tokio::test]
async fn an_unreadable_file_target_is_unmeasured() {
let missing = std::path::Path::new("/nonexistent/pmat/satd/file.rs");
let outcome = run_satd_analysis(
missing.parent().expect("parent"),
&QualityProfile::default(),
Some(missing),
)
.await
.expect("the phase reports rather than aborting");
assert!(outcome.violations.is_empty());
assert!(!outcome.is_measured());
}