Skip to main content

speccoder_runtime/bug/
confirm.rs

1//! The one confirmation: bound to the digest of what the person was shown,
2//! recorded with who, through what and on what basis, and turned into the
3//! fix's pre-frozen acceptance set. A changed triad lapses it.
4
5use std::path::Path;
6
7use speccoder_core::acceptance::{Decided, PreFrozenAcceptance, Producer, VisibleTest};
8use speccoder_core::actions::Via;
9use speccoder_core::bug::{
10    AcceptanceRef, Basis, BugReport, Confirmation, Outcome, PreFrozenRef, Reproduction, Stage,
11};
12
13use crate::integrity::sha256_hex;
14
15/// Unix seconds now.
16#[must_use]
17pub fn now() -> u64 {
18    std::time::SystemTime::now()
19        .duration_since(std::time::UNIX_EPOCH)
20        .map_or(0, |d| d.as_secs())
21}
22
23/// `sha256:<hex>` over the reproductions shown: each statement, file,
24/// content digest, test and the command that runs it, sorted.
25#[must_use]
26pub fn digest(reproductions: &[Reproduction]) -> String {
27    let mut lines: Vec<String> = reproductions
28        .iter()
29        .map(|r| {
30            format!(
31                "{}\t{}\t{}\t{}\t{}",
32                r.statement,
33                r.file,
34                r.digest,
35                r.test,
36                serde_json::to_string(&r.argv).unwrap_or_default()
37            )
38        })
39        .collect();
40    lines.sort();
41    format!("sha256:{}", sha256_hex(lines.join("\n").as_bytes()))
42}
43
44/// Records the confirmation of `bug`'s reproductions and stores them as the
45/// fix's pre-frozen set; returns the record and the files.
46pub fn freeze(
47    state: &Path,
48    bug: &mut BugReport,
49    by: &str,
50    via: Via,
51    basis: Basis,
52) -> Result<(PreFrozenAcceptance, Vec<(String, String)>), String> {
53    let digest = bug
54        .confirm_digest
55        .clone()
56        .ok_or_else(|| "there are no reproductions to confirm".to_owned())?;
57    let paths: Vec<String> = bug.reproductions.iter().map(|r| r.file.clone()).collect();
58    let files = super::state::repro(state, &bug.unit, &bug.task, &paths);
59    if files.len() != paths.len() {
60        return Err("the reproductions' contents are missing from the state directory".to_owned());
61    }
62    let at = now();
63    let record = PreFrozenAcceptance {
64        unit: bug.unit.clone(),
65        requirement: bug.requirement.clone(),
66        wording_digest: bug.triad.digest.clone(),
67        approval_digest: digest.clone(),
68        visible: bug
69            .reproductions
70            .iter()
71            .map(|r| VisibleTest {
72                path: r.file.clone(),
73                sha256: r.digest.clone(),
74                cites: vec![r.statement.clone()],
75            })
76            .collect(),
77        held_out_sealed: bug.held_out_sealed.clone(),
78        producer: if basis == Basis::NamedTest {
79            Producer::PersonTest
80        } else {
81            Producer::BugConfirmation
82        },
83        decided: Decided {
84            by: by.to_owned(),
85            via,
86            at,
87        },
88    };
89    crate::pre_frozen::store(state, &record, &files)?;
90    bug.confirmation = Some(Confirmation {
91        digest: digest.clone(),
92        triad_digest: bug.triad.digest.clone(),
93        by: by.to_owned(),
94        via,
95        basis,
96        at,
97    });
98    bug.acceptance = Some(AcceptanceRef {
99        source: record.producer.source(),
100        pre_frozen: PreFrozenRef {
101            manifest: crate::pre_frozen::manifest_digest(&record),
102            approval: digest,
103            wording: record.wording_digest.clone(),
104        },
105    });
106    bug.stage = Stage::Fix;
107    bug.outcome = Outcome::Recorded;
108    bug.code = None;
109    bug.handoff = None;
110    super::state::save(state, bug)?;
111    Ok((record, files))
112}
113
114/// Forgets a confirmation the triad no longer stands for: the bug goes
115/// back to recorded, its reproductions to be written again.
116pub fn lapse(
117    state: &Path,
118    bug: &mut BugReport,
119    triad: speccoder_core::bug::Triad,
120) -> Result<(), String> {
121    crate::pre_frozen::remove(state, &bug.unit, &bug.requirement);
122    bug.triad = triad;
123    bug.confirmation = None;
124    bug.confirm_digest = None;
125    bug.acceptance = None;
126    bug.reproductions
127        .retain(|r| r.source == speccoder_core::bug::Source::Person);
128    bug.held_out.clear();
129    bug.held_out_sealed = None;
130    bug.tries.clear();
131    bug.stage = Stage::Reproduce;
132    bug.outcome = Outcome::Recorded;
133    super::state::save(state, bug)
134}