1#![feature(let_chains)]
18
19use std::collections::BTreeSet;
20use std::path::PathBuf;
21
22use difftest::TestInfo;
23
24use crate::analysis::AnalysisResult;
25use crate::difftest::Difftest;
26use crate::index_data::TestIndex;
27
28pub mod analysis;
29pub mod analysis_data;
30pub mod difftest;
31pub mod index_data;
32pub mod test_rerunner_core;
33pub mod bin_context;
34
35#[derive(thiserror::Error, Debug)]
37pub enum DifftestsError {
38 #[error("IO error: {0}")]
40 IO(#[from] std::io::Error),
41 #[error(
44 "JSON error: {0}{}",
45 match &.1 {
46 Some(it) => format!(" (in {it:?})"),
47 None => "".to_owned(),
48 }
49 )]
50 Json(#[source] serde_json::Error, Option<PathBuf>),
51 #[error("Self json does not exist: {0:?}")]
53 SelfJsonDoesNotExist(PathBuf),
54 #[error("cargo_difftests_version file does not exist: {0:?}")]
56 CargoDifftestsVersionDoesNotExist(PathBuf),
57 #[error("cargo difftests version mismatch: {0} (file) != {1} (cargo difftests)")]
61 CargoDifftestsVersionMismatch(String, String),
62 #[error("process failed: {name}")]
64 ProcessFailed { name: &'static str },
65 #[error("git error: {0}")]
67 Git(#[from] git2::Error),
68 #[error("difftest has been cleaned")]
70 DifftestCleaned,
71
72 #[error("multiple main group binaries: {1:?} (group at {0:?})")]
73 MultipleMainGroupBinaries(PathBuf, BTreeSet<PathBuf>),
74
75 #[error("no difftests found for group {0:?}")]
76 EmptyGroup(PathBuf),
77
78 #[error("invalid config: {0}")]
79 InvalidConfig(analysis::InvalidConfigError),
80}
81
82impl From<serde_json::Error> for DifftestsError {
83 fn from(e: serde_json::Error) -> Self {
84 DifftestsError::Json(e, None)
85 }
86}
87
88pub type DifftestsResult<T = ()> = Result<T, DifftestsError>;
89
90pub fn compare_indexes_touch_same_files(
96 index_a: &TestIndex,
97 index_b: &TestIndex,
98) -> Result<(), IndexCompareDifferences<TouchSameFilesDifference>> {
99 let mut diffs = IndexCompareDifferences {
100 differences: vec![],
101 };
102
103 let a_files = index_a.files.iter().collect::<BTreeSet<_>>();
104 let b_files = index_b.files.iter().collect::<BTreeSet<_>>();
105
106 diffs.differences.extend(
107 a_files
108 .difference(&b_files)
109 .map(|f| TouchSameFilesDifference::TouchedByFirstOnly((*f).clone())),
110 );
111
112 diffs.differences.extend(
113 b_files
114 .difference(&a_files)
115 .map(|f| TouchSameFilesDifference::TouchedBySecondOnly((*f).clone())),
116 );
117
118 if diffs.differences.is_empty() {
119 Ok(())
120 } else {
121 Err(diffs)
122 }
123}
124
125#[derive(Clone, Debug)]
127pub struct IndexCompareDifferences<D> {
128 differences: Vec<D>,
129}
130
131impl<D> IndexCompareDifferences<D> {
132 pub fn differences(&self) -> &[D] {
133 &self.differences
134 }
135}
136
137#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
140pub enum TouchSameFilesDifference {
141 #[serde(rename = "first_only")]
143 TouchedByFirstOnly(PathBuf),
144 #[serde(rename = "second_only")]
146 TouchedBySecondOnly(PathBuf),
147}
148
149#[derive(serde::Serialize, serde::Deserialize)]
155pub struct AnalyzeAllSingleTest {
156 pub difftest: Option<Difftest>,
160 pub test_info: TestInfo,
162 pub verdict: AnalysisVerdict,
164}
165
166#[derive(serde::Serialize, serde::Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
168pub enum AnalysisVerdict {
169 #[serde(rename = "clean")]
173 Clean,
174 #[serde(rename = "dirty")]
177 Dirty,
178}
179
180impl From<AnalysisResult> for AnalysisVerdict {
181 fn from(r: AnalysisResult) -> Self {
182 match r {
183 AnalysisResult::Clean => AnalysisVerdict::Clean,
184 AnalysisResult::Dirty => AnalysisVerdict::Dirty,
185 }
186 }
187}