Skip to main content

mantra_schema/report/
tests.rs

1use relative_path::RelativePathBuf;
2
3use crate::{
4    ConversionError, FmtHash,
5    report::{
6        Aggregated, annotations::TraceReference, requirement::RequirementReference,
7        test_case::TestCaseReference, test_run::TestRunReference,
8    },
9};
10
11#[derive(
12    Debug,
13    Clone,
14    Copy,
15    Default,
16    PartialEq,
17    serde::Serialize,
18    serde::Deserialize,
19    schemars::JsonSchema,
20)]
21#[serde(rename_all = "snake_case")]
22pub struct TestsSummary {
23    pub total: i64,
24    pub passed: Aggregated,
25    pub failed: Aggregated,
26    pub skipped: Aggregated,
27    pub unknown: Aggregated,
28    pub obsolete: Aggregated,
29}
30
31impl TestsSummary {
32    pub fn add(&mut self, other: &Self) {
33        self.total += other.total;
34
35        self.passed.cnt += other.passed.cnt;
36        self.failed.cnt += other.failed.cnt;
37        self.skipped.cnt += other.skipped.cnt;
38        self.unknown.cnt += other.unknown.cnt;
39        self.obsolete.cnt += other.obsolete.cnt;
40
41        self.update_percentages();
42    }
43
44    pub fn update_percentages(&mut self) {
45        self.passed.update_percentage(self.total);
46        self.failed.update_percentage(self.total);
47        self.skipped.update_percentage(self.total);
48        self.unknown.update_percentage(self.total);
49        self.obsolete.update_percentage(self.total);
50    }
51}
52
53/// Possible states a test run or test case may be in.
54#[derive(
55    Debug,
56    Clone,
57    Copy,
58    PartialEq,
59    Eq,
60    Hash,
61    serde::Serialize,
62    serde::Deserialize,
63    schemars::JsonSchema,
64)]
65#[serde(rename_all = "snake_case")]
66pub enum TestState {
67    /// Test failed.
68    Failed = 0,
69    /// Test passed successfully.
70    Passed = 1,
71    /// Test was skipped in the related test run.
72    Skipped = 2,
73    /// Test is in an unknown state.
74    ///
75    /// This likely indicates that a test case did not finish execution,
76    /// and is treated as *failed* state.
77    /// [req("testcov.test_case.state.unknown")]
78    Unknown = 3,
79    /// Test is obsolete.
80    ///
81    /// Obsolete tests must not be considered for coverage analysis.
82    Obsolete = 4,
83}
84
85impl TestState {
86    pub fn as_nr(&self) -> i32 {
87        *self as i32
88    }
89}
90
91impl TryFrom<i64> for TestState {
92    type Error = ConversionError;
93
94    fn try_from(value: i64) -> Result<Self, Self::Error> {
95        if value == TestState::Failed.as_nr() as i64 {
96            Ok(TestState::Failed)
97        } else if value == TestState::Skipped.as_nr() as i64 {
98            Ok(TestState::Skipped)
99        } else if value == TestState::Passed.as_nr() as i64 {
100            Ok(TestState::Passed)
101        } else if value == TestState::Unknown.as_nr() as i64 {
102            Ok(TestState::Unknown)
103        } else if value == TestState::Obsolete.as_nr() as i64 {
104            Ok(TestState::Obsolete)
105        } else {
106            Err(ConversionError::UnknownState)
107        }
108    }
109}
110
111#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
112#[serde(rename_all = "snake_case")]
113pub struct TestCoverage {
114    pub summary: TestCoverageSummary,
115    pub covered_files: Vec<TestCoveredFile>,
116    pub covered_traces: Option<Vec<TraceReference>>,
117}
118
119#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
120#[serde(rename_all = "snake_case")]
121pub struct TestCoveredFile {
122    #[schemars(with = "Vec<String>")]
123    pub filepath: RelativePathBuf,
124    pub file_hash: Option<FmtHash>,
125}
126
127#[derive(
128    Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
129)]
130#[serde(rename_all = "snake_case")]
131pub struct TestCoverageSummary {
132    pub lines: CoverageSummary,
133}
134
135#[derive(
136    Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
137)]
138#[serde(rename_all = "snake_case")]
139pub struct CoverageSummary {
140    pub total: i64,
141    pub covered: Aggregated,
142    pub excluded: Aggregated,
143    pub overridden_covered: Aggregated,
144    pub overridden_uncovered: Aggregated,
145    pub uncovered: Aggregated,
146}
147
148impl CoverageSummary {
149    pub fn add(&mut self, other: &Self) {
150        self.total += other.total;
151
152        self.covered.cnt += other.covered.cnt;
153        self.excluded.cnt += other.excluded.cnt;
154        self.overridden_covered.cnt += other.overridden_covered.cnt;
155        self.overridden_uncovered.cnt += other.overridden_uncovered.cnt;
156        self.uncovered.cnt += other.uncovered.cnt;
157
158        self.update_percentages();
159    }
160
161    pub fn update_percentages(&mut self) {
162        self.covered.update_percentage(self.total);
163        self.excluded.update_percentage(self.total);
164        self.overridden_covered.update_percentage(self.total);
165        self.overridden_uncovered.update_percentage(self.total);
166        self.uncovered.update_percentage(self.total);
167    }
168}
169
170#[derive(
171    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
172)]
173pub struct TestRelatedRequirement {
174    pub req: RequirementReference,
175    pub kind: TestRelatedRequirementKind,
176}
177
178#[derive(
179    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
180)]
181#[serde(rename_all = "snake_case")]
182pub enum TestRelatedRequirementKind {
183    Direct,
184    Traced(Vec<TraceReference>),
185}
186
187#[derive(
188    Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema,
189)]
190#[serde(rename_all = "snake_case")]
191pub enum TestReference {
192    TestRun(TestRunReference),
193    TestCase(TestCaseReference),
194}
195
196#[derive(
197    Debug,
198    Clone,
199    Copy,
200    PartialEq,
201    Eq,
202    Hash,
203    serde::Serialize,
204    serde::Deserialize,
205    schemars::JsonSchema,
206)]
207#[serde(rename_all = "snake_case")]
208pub enum ResolvedLineCoverageState {
209    Covered = 0,
210    Excluded = 1,
211    OverriddenCovered = 2,
212    OverriddenUncovered = 3,
213    Uncovered = 4,
214}
215
216impl ResolvedLineCoverageState {
217    pub fn as_nr(&self) -> i32 {
218        *self as i32
219    }
220}
221
222impl TryFrom<i64> for ResolvedLineCoverageState {
223    type Error = ConversionError;
224
225    fn try_from(value: i64) -> Result<Self, Self::Error> {
226        if value == ResolvedLineCoverageState::Covered.as_nr() as i64 {
227            Ok(ResolvedLineCoverageState::Covered)
228        } else if value == ResolvedLineCoverageState::Excluded.as_nr() as i64 {
229            Ok(ResolvedLineCoverageState::Excluded)
230        } else if value == ResolvedLineCoverageState::OverriddenCovered.as_nr() as i64 {
231            Ok(ResolvedLineCoverageState::OverriddenCovered)
232        } else if value == ResolvedLineCoverageState::OverriddenUncovered.as_nr() as i64 {
233            Ok(ResolvedLineCoverageState::OverriddenUncovered)
234        } else if value == ResolvedLineCoverageState::Uncovered.as_nr() as i64 {
235            Ok(ResolvedLineCoverageState::Uncovered)
236        } else {
237            Err(ConversionError::UnknownState)
238        }
239    }
240}