Skip to main content

dev_mutate/
producer.rs

1//! [`Producer`] integration: wrap a [`MutateRun`] + [`MutateThreshold`]
2//! and emit a [`Report`] every time the producer runs.
3//!
4//! [`Producer`]: dev_report::Producer
5//! [`Report`]: dev_report::Report
6
7use dev_report::{CheckResult, Producer, Report, Severity};
8
9use crate::{MutateRun, MutateThreshold};
10
11/// `Producer` adapter that drives a [`MutateRun`] and converts the
12/// result into a `Report` against the configured [`MutateThreshold`].
13///
14/// Subprocess failures map to a single failing `CheckResult` named
15/// `mutate::<subject>` with `Severity::Critical`. No panics.
16///
17/// # Example
18///
19/// ```no_run
20/// use dev_mutate::{MutateProducer, MutateRun, MutateThreshold};
21/// use dev_report::Producer;
22///
23/// let producer = MutateProducer::new(
24///     MutateRun::new("my-crate", "0.1.0"),
25///     MutateThreshold::min_kill_pct(70.0),
26/// );
27/// let report = producer.produce();
28/// println!("{}", report.to_json().unwrap());
29/// ```
30pub struct MutateProducer {
31    run: MutateRun,
32    threshold: MutateThreshold,
33}
34
35impl MutateProducer {
36    /// Build a producer with the given run + threshold.
37    pub fn new(run: MutateRun, threshold: MutateThreshold) -> Self {
38        Self { run, threshold }
39    }
40}
41
42impl Producer for MutateProducer {
43    fn produce(&self) -> Report {
44        let subject = self.run.subject().to_string();
45        let version = self.run.subject_version().to_string();
46        let mut report = Report::new(&subject, &version).with_producer("dev-mutate");
47        match self.run.execute() {
48            Ok(result) => {
49                report.push(result.into_check_result(self.threshold));
50            }
51            Err(e) => {
52                let check = CheckResult::fail(format!("mutate::{subject}"), Severity::Critical)
53                    .with_detail(e.to_string())
54                    .with_tag("mutate")
55                    .with_tag("subprocess");
56                report.push(check);
57            }
58        }
59        report.finish();
60        report
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    #[ignore = "spawns inner `cargo mutants` which deadlocks on the workspace target-dir lock when run from `cargo test`; run with CARGO_TARGET_DIR outside the workspace via `cargo test -- --ignored`"]
70    fn produce_returns_report_regardless_of_subprocess() {
71        // Contract: returns a Report, never panics.
72        let producer = MutateProducer::new(
73            MutateRun::new("self", "0.0.0"),
74            MutateThreshold::min_kill_pct(70.0),
75        );
76        let report = producer.produce();
77        assert_eq!(report.subject, "self");
78        assert!(!report.checks.is_empty());
79    }
80}