#![cfg_attr(coverage_nightly, coverage(off))]
use super::types::*;
use std::fs;
use std::path::Path;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BashrsLintResult {
pub file: String,
pub issues: Vec<BashrsIssue>,
pub passed: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BashrsIssue {
pub code: String,
pub message: String,
pub line: usize,
pub severity: String,
}
#[derive(Default)]
pub(super) struct CoverageTargetState {
pub(super) active: bool,
pub(super) line: usize,
pub(super) has_nextest: bool,
pub(super) has_llvm_cov: bool,
pub(super) has_proptest_cases: bool,
pub(super) has_lib_flag: bool,
pub(super) runs_cargo_tests: bool,
}
impl CoverageTargetState {
pub(super) fn reset(&mut self, line: usize) {
self.active = true;
self.line = line;
self.has_nextest = false;
self.has_llvm_cov = false;
self.has_proptest_cases = false;
self.has_lib_flag = false;
self.runs_cargo_tests = false;
}
pub(super) fn update_from_line(&mut self, line: &str) {
let trimmed = line.trim();
if trimmed.starts_with('#') || trimmed.starts_with("@#") {
return;
}
let is_echo = trimmed.starts_with("@echo") || trimmed.starts_with("echo");
if !is_echo && line.contains("nextest") {
self.has_nextest = true;
self.runs_cargo_tests = true;
}
if line.contains("llvm-cov") || line.contains("cargo-llvm-cov") {
self.has_llvm_cov = true;
}
if !is_echo && (line.contains("cargo test") || line.contains("cargo llvm-cov test")) {
self.runs_cargo_tests = true;
}
if line.contains("PROPTEST_CASES") || line.contains("QUICKCHECK_TESTS") {
self.has_proptest_cases = true;
}
if line.contains("--lib") {
self.has_lib_flag = true;
}
}
pub(super) fn collect_violations(&self, file_path: &str) -> Vec<CbPatternViolation> {
let mut violations = Vec::new();
if !self.runs_cargo_tests {
return violations;
}
if self.has_nextest && self.has_llvm_cov {
violations.push(CbPatternViolation {
pattern_id: "CB-127-A".to_string(),
file: file_path.to_string(),
line: self.line,
description: "CRITICAL: nextest + llvm-cov causes profraw explosion. \
Use 'cargo llvm-cov test' instead"
.to_string(),
severity: Severity::Error,
});
}
if !self.has_proptest_cases {
violations.push(CbPatternViolation {
pattern_id: "CB-127-B".to_string(),
file: file_path.to_string(),
line: self.line,
description: "Coverage target missing PROPTEST_CASES/QUICKCHECK_TESTS".to_string(),
severity: Severity::Warning,
});
}
if !self.has_lib_flag && self.has_llvm_cov {
violations.push(CbPatternViolation {
pattern_id: "CB-127-C".to_string(),
file: file_path.to_string(),
line: self.line,
description: "Coverage target missing --lib flag".to_string(),
severity: Severity::Warning,
});
}
violations
}
}
include!("quality_checks_coverage_gaming.rs");
include!("quality_checks_slow_tests.rs");
include!("quality_checks_bashrs.rs");
#[cfg(test)]
#[allow(clippy::field_reassign_with_default)]
mod coverage_target_state_tests {
use super::*;
#[test]
fn test_coverage_target_state_reset_clears_all_flags_and_sets_line() {
let mut s = CoverageTargetState::default();
s.has_nextest = true;
s.has_llvm_cov = true;
s.has_proptest_cases = true;
s.has_lib_flag = true;
s.runs_cargo_tests = true;
s.line = 999;
s.active = false;
s.reset(42);
assert!(s.active, "reset must activate the target");
assert_eq!(s.line, 42);
assert!(!s.has_nextest);
assert!(!s.has_llvm_cov);
assert!(!s.has_proptest_cases);
assert!(!s.has_lib_flag);
assert!(!s.runs_cargo_tests);
}
#[test]
fn test_update_from_line_detects_nextest_and_marks_runs_cargo_tests() {
let mut s = CoverageTargetState::default();
s.update_from_line("\tcargo nextest run --lib");
assert!(s.has_nextest);
assert!(s.runs_cargo_tests);
}
#[test]
fn test_update_from_line_detects_llvm_cov_does_not_mark_runs_cargo_tests() {
let mut s = CoverageTargetState::default();
s.update_from_line("\tcargo llvm-cov report --html");
assert!(s.has_llvm_cov);
assert!(
!s.runs_cargo_tests,
"plain llvm-cov report must NOT mark runs_cargo_tests"
);
}
#[test]
fn test_update_from_line_detects_cargo_llvm_cov_test_marks_runs_cargo_tests() {
let mut s = CoverageTargetState::default();
s.update_from_line("\tcargo llvm-cov test --lib");
assert!(s.has_llvm_cov);
assert!(s.runs_cargo_tests, "llvm-cov TEST marks runs_cargo_tests");
}
#[test]
fn test_update_from_line_detects_cargo_test_alone() {
let mut s = CoverageTargetState::default();
s.update_from_line("\tcargo test --lib --quiet");
assert!(s.runs_cargo_tests);
assert!(s.has_lib_flag);
}
#[test]
fn test_update_from_line_detects_proptest_cases_env_var() {
let mut s = CoverageTargetState::default();
s.update_from_line("\tPROPTEST_CASES=1000 cargo test");
assert!(s.has_proptest_cases);
let mut s = CoverageTargetState::default();
s.update_from_line("\tQUICKCHECK_TESTS=1000 cargo test");
assert!(s.has_proptest_cases);
}
#[test]
fn test_update_from_line_comment_lines_are_skipped() {
let mut s = CoverageTargetState::default();
s.update_from_line("# cargo test --lib with nextest");
assert!(!s.runs_cargo_tests);
assert!(!s.has_nextest);
}
#[test]
fn test_update_from_line_echo_lines_do_not_mark_runs_cargo_tests() {
let mut s = CoverageTargetState::default();
s.update_from_line("\t@echo \"Running nextest...\"");
assert!(!s.has_nextest);
assert!(!s.runs_cargo_tests);
}
#[test]
fn test_collect_violations_empty_when_not_running_cargo_tests() {
let mut s = CoverageTargetState::default();
s.reset(10);
let v = s.collect_violations("Makefile");
assert!(v.is_empty());
}
#[test]
fn test_collect_violations_cb127a_fires_for_nextest_plus_llvm_cov() {
let mut s = CoverageTargetState::default();
s.reset(20);
s.runs_cargo_tests = true;
s.has_nextest = true;
s.has_llvm_cov = true;
s.has_proptest_cases = true;
s.has_lib_flag = true;
let v = s.collect_violations("Makefile");
assert!(v.iter().any(|x| x.pattern_id == "CB-127-A"));
}
#[test]
fn test_collect_violations_cb127b_fires_when_proptest_cases_missing() {
let mut s = CoverageTargetState::default();
s.reset(20);
s.runs_cargo_tests = true;
let v = s.collect_violations("Makefile");
assert!(v.iter().any(|x| x.pattern_id == "CB-127-B"));
}
#[test]
fn test_collect_violations_cb127c_fires_for_llvm_cov_without_lib_flag() {
let mut s = CoverageTargetState::default();
s.reset(20);
s.runs_cargo_tests = true;
s.has_llvm_cov = true;
s.has_proptest_cases = true; let v = s.collect_violations("Makefile");
assert!(v.iter().any(|x| x.pattern_id == "CB-127-C"));
}
#[test]
fn test_collect_violations_no_violations_when_all_good() {
let mut s = CoverageTargetState::default();
s.reset(20);
s.runs_cargo_tests = true;
s.has_llvm_cov = true;
s.has_proptest_cases = true;
s.has_lib_flag = true;
let v = s.collect_violations("Makefile");
assert!(v.is_empty());
}
#[test]
fn test_collect_violations_carries_file_path_and_line() {
let mut s = CoverageTargetState::default();
s.reset(77);
s.runs_cargo_tests = true;
let v = s.collect_violations("sub/Makefile");
let cb_b = v.iter().find(|x| x.pattern_id == "CB-127-B").unwrap();
assert_eq!(cb_b.file, "sub/Makefile");
assert_eq!(cb_b.line, 77);
}
}