clnrm_core/reporting/
junit.rs1use crate::error::{CleanroomError, Result};
6use crate::validation::ValidationReport;
7use std::path::Path;
8
9pub struct JunitReporter;
11
12impl JunitReporter {
13 pub fn write(path: &Path, report: &ValidationReport) -> Result<()> {
25 let xml = Self::generate_xml(report);
26 Self::write_file(path, &xml)
27 }
28
29 fn generate_xml(report: &ValidationReport) -> String {
31 let mut xml = String::new();
32
33 Self::append_xml_header(&mut xml);
34 Self::append_testsuite_open(&mut xml, report);
35 Self::append_passed_tests(&mut xml, report);
36 Self::append_failed_tests(&mut xml, report);
37 Self::append_testsuite_close(&mut xml);
38
39 xml
40 }
41
42 fn append_xml_header(xml: &mut String) {
44 xml.push_str(r#"<?xml version="1.0" encoding="UTF-8"?>"#);
45 xml.push('\n');
46 }
47
48 fn append_testsuite_open(xml: &mut String, report: &ValidationReport) {
50 let total = report.passes().len() + report.failures().len();
51 xml.push_str(&format!(
52 r#"<testsuite name="clnrm" tests="{}" failures="{}" errors="0">"#,
53 total,
54 report.failures().len()
55 ));
56 xml.push('\n');
57 }
58
59 fn append_passed_tests(xml: &mut String, report: &ValidationReport) {
61 for pass_name in report.passes() {
62 xml.push_str(&format!(
63 r#" <testcase name="{}" />"#,
64 Self::escape_xml(pass_name)
65 ));
66 xml.push('\n');
67 }
68 }
69
70 fn append_failed_tests(xml: &mut String, report: &ValidationReport) {
72 for (fail_name, error) in report.failures() {
73 xml.push_str(&format!(
74 r#" <testcase name="{}">"#,
75 Self::escape_xml(fail_name)
76 ));
77 xml.push('\n');
78 xml.push_str(&format!(
79 r#" <failure message="{}" />"#,
80 Self::escape_xml(error)
81 ));
82 xml.push('\n');
83 xml.push_str(r#" </testcase>"#);
84 xml.push('\n');
85 }
86 }
87
88 fn append_testsuite_close(xml: &mut String) {
90 xml.push_str("</testsuite>\n");
91 }
92
93 fn escape_xml(s: &str) -> String {
95 s.replace('&', "&")
96 .replace('<', "<")
97 .replace('>', ">")
98 .replace('"', """)
99 .replace('\'', "'")
100 }
101
102 fn write_file(path: &Path, content: &str) -> Result<()> {
104 std::fs::write(path, content)
105 .map_err(|e| CleanroomError::report_error(format!("Failed to write JUnit XML: {}", e)))
106 }
107}