Skip to main content

fallow_output/
health_mutation.rs

1//! Mutation evidence in a health report, read from a saved `fallow mutation`
2//! report with `fallow health --mutation-report <file>`.
3//!
4//! The mutation score is a separate value. It never changes the CRAP score of
5//! a finding or the composite health score.
6
7use serde::{Deserialize, Serialize};
8
9use crate::{MutationCompletionStatus, MutationStatusCounts};
10
11/// Whether the mutation evidence still describes the current source file.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
14#[serde(rename_all = "snake_case")]
15pub enum HealthMutationState {
16    /// The file has the SHA-256 digest of the report.
17    Fresh,
18    /// The file changed, is missing, or has no digest in the report. The
19    /// evidence has no score and no counts.
20    Stale,
21}
22
23/// The mutation evidence of one complexity finding.
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
26pub struct HealthFindingMutation {
27    /// Whether the evidence describes the current file.
28    pub state: HealthMutationState,
29    /// Why the evidence is stale. Absent when the state is `fresh`.
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub stale_reason: Option<HealthMutationStaleReason>,
32    /// The mutation score of the function, a fraction from 0 to 1:
33    /// `(killed + timeout) / (killed + timeout + survived)`. Absent when the
34    /// state is `stale`, and when no mutant of the function ran to a tested
35    /// result.
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub score: Option<f64>,
38    /// The number of mutants of the function for each status. Absent when
39    /// the state is `stale`.
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub counts: Option<MutationStatusCounts>,
42}
43
44/// Why mutation evidence is stale.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
46#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
47#[serde(rename_all = "kebab-case")]
48pub enum HealthMutationStaleReason {
49    /// The file does not exist or cannot be read.
50    FileMissing,
51    /// The report has no digest for the file.
52    NoDigest,
53    /// The SHA-256 digest of the file differs from the report.
54    SourceChanged,
55}
56
57/// One file of the mutation report that does not match the current source.
58#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
60pub struct HealthMutationStaleFile {
61    /// The root-relative path, with forward slashes.
62    pub path: String,
63    /// Why the file is stale.
64    pub reason: HealthMutationStaleReason,
65}
66
67/// The join of a saved mutation report with the complexity findings.
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
70pub struct HealthMutationSummary {
71    /// The completion status of the run that wrote the report. Only
72    /// `complete` makes an empty survivor list conclusive. Absent when the
73    /// report has no completion.
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub completion: Option<MutationCompletionStatus>,
76    /// The number of function results in the report.
77    pub functions: usize,
78    /// The number of complexity findings with fresh mutation evidence.
79    pub findings_fresh: usize,
80    /// The number of complexity findings with stale mutation evidence.
81    pub findings_stale: usize,
82    /// The number of complexity findings that no function result of the
83    /// report matches. A finding matches when the path, the first line and
84    /// the name are equal.
85    pub findings_unmatched: usize,
86    /// The report files that do not match the current source, sorted by
87    /// path. Their functions have no score.
88    #[serde(default, skip_serializing_if = "Vec::is_empty")]
89    pub stale_files: Vec<HealthMutationStaleFile>,
90}