use super::{snapshot::TestSnapshot, SgLang, TestSnapshots};
use ast_grep_config::RuleConfig;
use ast_grep_language::LanguageExt;
#[derive(PartialEq, Eq, Debug)]
pub enum CaseStatus<'a> {
Validated,
Reported,
Updated {
source: &'a str,
updated: TestSnapshot,
},
Wrong {
source: &'a str,
actual: TestSnapshot,
expected: Option<TestSnapshot>,
},
Missing(&'a str),
Noisy(&'a str),
Error,
}
impl<'a> CaseStatus<'a> {
pub fn verify_valid(rule_config: &RuleConfig<SgLang>, case: &'a str) -> Self {
let rule = &rule_config.matcher;
let sg = rule_config.language.ast_grep(case);
if sg.root().find(rule).is_some() {
CaseStatus::Noisy(case)
} else {
CaseStatus::Validated
}
}
pub fn verify_invalid(rule_config: &RuleConfig<SgLang>, case: &'a str) -> Self {
let sg = rule_config.language.ast_grep(case);
let rule = &rule_config.matcher;
if sg.root().find(rule).is_some() {
CaseStatus::Reported
} else {
CaseStatus::Missing(case)
}
}
pub fn verify_snapshot(
rule_config: &RuleConfig<SgLang>,
case: &'a str,
snapshot: Option<&TestSnapshot>,
) -> Self {
let actual = match TestSnapshot::generate(rule_config, case) {
Ok(Some(snap)) => snap,
Ok(None) => return CaseStatus::Missing(case),
Err(_) => return CaseStatus::Error,
};
match snapshot {
Some(e) if e == &actual => CaseStatus::Reported,
nullable => CaseStatus::Wrong {
source: case,
actual,
expected: nullable.cloned(),
},
}
}
pub fn accept(&mut self) -> bool {
let CaseStatus::Wrong { source, actual, .. } = self else {
return false;
};
let updated = std::mem::replace(
actual,
TestSnapshot {
fixed: None,
labels: vec![],
},
);
*self = CaseStatus::Updated { source, updated };
true
}
pub fn is_pass(&self) -> bool {
matches!(
self,
CaseStatus::Validated | CaseStatus::Reported | CaseStatus::Updated { .. }
)
}
}
#[derive(PartialEq, Eq, Default, Debug)]
pub struct CaseResult<'a> {
pub id: &'a str,
pub cases: Vec<CaseStatus<'a>>,
}
impl CaseResult<'_> {
pub fn passed(&self) -> bool {
self.cases.iter().all(CaseStatus::is_pass)
}
pub fn is_snapshot_mismatch_only_failure(&self) -> bool {
let mut has_failures = false;
for case in &self.cases {
if !case.is_pass() {
has_failures = true;
if !matches!(case, CaseStatus::Wrong { .. }) {
return false; }
}
}
has_failures }
pub fn changed_snapshots(&self) -> TestSnapshots {
let snapshots = self
.cases
.iter()
.filter_map(|c| match c {
CaseStatus::Updated { source, updated } => Some((source.to_string(), updated.clone())),
_ => None,
})
.collect();
TestSnapshots {
id: self.id.to_string(),
snapshots,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::verify::test::get_rule_config;
#[test]
fn test_snapshot() {
let rule = get_rule_config("pattern: let a = 1");
let ret = CaseStatus::verify_snapshot(&rule, "function () { let a = 1 }", None);
assert!(matches!(&ret, CaseStatus::Wrong { expected: None, .. }));
let CaseStatus::Wrong { actual, source, .. } = ret else {
panic!("wrong");
};
assert_eq!(source, "function () { let a = 1 }");
let primary = &actual.labels[0];
assert_eq!(primary.source, "let a = 1");
let ret = CaseStatus::verify_snapshot(&rule, "function () { let a = 1 }", Some(&actual));
assert!(matches!(ret, CaseStatus::Reported));
}
#[test]
fn test_is_snapshot_mismatch_only_failure() {
let snapshot_only_result = CaseResult {
id: "test",
cases: vec![
CaseStatus::Wrong {
source: "test",
actual: TestSnapshot {
fixed: None,
labels: vec![],
},
expected: None,
},
CaseStatus::Wrong {
source: "test2",
actual: TestSnapshot {
fixed: None,
labels: vec![],
},
expected: None,
},
],
};
assert!(snapshot_only_result.is_snapshot_mismatch_only_failure());
let mixed_result = CaseResult {
id: "test",
cases: vec![
CaseStatus::Wrong {
source: "test",
actual: TestSnapshot {
fixed: None,
labels: vec![],
},
expected: None,
},
CaseStatus::Missing("test2"),
],
};
assert!(!mixed_result.is_snapshot_mismatch_only_failure());
let passing_result = CaseResult {
id: "test",
cases: vec![CaseStatus::Validated, CaseStatus::Reported],
};
assert!(!passing_result.is_snapshot_mismatch_only_failure());
let non_snapshot_result = CaseResult {
id: "test",
cases: vec![CaseStatus::Missing("test"), CaseStatus::Noisy("test2")],
};
assert!(!non_snapshot_result.is_snapshot_mismatch_only_failure());
}
}