Skip to main content

testty/proof/
frame_text.rs

1//! Frame-text proof backend.
2//!
3//! [`FrameTextBackend`] renders a [`ProofReport`](super::report::ProofReport)
4//! as annotated plain text, producing the same output as
5//! [`ProofReport::to_annotated_text()`](super::report::ProofReport::to_annotated_text)
6//! but routed through the [`ProofBackend`](super::backend::ProofBackend) trait.
7
8use std::path::Path;
9
10use super::backend::ProofBackend;
11use super::report::{ProofError, ProofReport};
12
13/// Renders a proof report as annotated plain-text frame dumps.
14///
15/// This is the simplest backend, writing each captured frame as a
16/// bordered text block with step labels, descriptions, and assertion
17/// markers. The output is identical to [`ProofReport::to_annotated_text()`].
18pub struct FrameTextBackend;
19
20impl ProofBackend for FrameTextBackend {
21    /// Write the annotated text proof to the given output path.
22    ///
23    /// # Errors
24    ///
25    /// Returns a [`ProofError::Io`] if writing the file fails.
26    fn render(&self, report: &ProofReport, output: &Path) -> Result<(), ProofError> {
27        let text = report.to_annotated_text();
28        std::fs::write(output, text)?;
29
30        Ok(())
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use crate::frame::TerminalFrame;
38
39    #[test]
40    fn frame_text_backend_produces_same_output_as_to_annotated_text() {
41        // Arrange
42        let frame = TerminalFrame::new(40, 5, b"Hello proof");
43        let mut report = ProofReport::new("consistency_check");
44        report.add_capture("init", "Initial state", &frame);
45        report.add_capture("done", "Final state", &frame);
46
47        let expected = report.to_annotated_text();
48        let temp_dir = tempfile::tempdir().expect("failed to create temp dir");
49        let output_path = temp_dir.path().join("proof.txt");
50
51        // Act
52        let backend = FrameTextBackend;
53        backend
54            .render(&report, &output_path)
55            .expect("render should succeed");
56
57        // Assert
58        let written = std::fs::read_to_string(&output_path).expect("failed to read output");
59        assert_eq!(written, expected);
60    }
61
62    #[test]
63    fn frame_text_backend_writes_valid_file() {
64        // Arrange
65        let frame = TerminalFrame::new(80, 24, b"Test content");
66        let mut report = ProofReport::new("file_test");
67        report.add_capture("snap", "Snapshot", &frame);
68        report.add_assertion("snap", true, "content visible");
69
70        let temp_dir = tempfile::tempdir().expect("failed to create temp dir");
71        let output_path = temp_dir.path().join("proof_output.txt");
72
73        // Act
74        let backend = FrameTextBackend;
75        backend
76            .render(&report, &output_path)
77            .expect("render should succeed");
78
79        // Assert
80        let content = std::fs::read_to_string(&output_path).expect("failed to read");
81        assert!(content.contains("Proof Report: file_test"));
82        assert!(content.contains("[PASS] content visible"));
83        assert!(content.contains("Test content"));
84    }
85
86    #[test]
87    fn save_dispatches_through_backend_trait() {
88        // Arrange
89        let frame = TerminalFrame::new(40, 5, b"dispatch test");
90        let mut report = ProofReport::new("dispatch");
91        report.add_capture("step", "Dispatch step", &frame);
92
93        let temp_dir = tempfile::tempdir().expect("failed to create temp dir");
94        let output_path = temp_dir.path().join("dispatched.txt");
95
96        // Act
97        let backend = FrameTextBackend;
98        report
99            .save(&backend, &output_path)
100            .expect("save should succeed");
101
102        // Assert
103        let content = std::fs::read_to_string(&output_path).expect("failed to read");
104        assert!(content.contains("dispatch test"));
105    }
106}