diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 2e07ce3c09923..65883dd36a169 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -11,7 +11,7 @@ use crate::{ pub struct JunitFormatter { out: OutputLocation, - results: Vec<(TestDesc, TestResult, Duration)>, + results: Vec<(TestDesc, TestResult, Duration, Vec)>, } impl JunitFormatter { @@ -26,6 +26,18 @@ impl JunitFormatter { } } +fn str_to_cdata(s: &str) -> String { + // Drop the stdout in a cdata. Unfortunately, you can't put either of `]]>` or + // `", "]]]]>"); + let escaped_output = escaped_output.replace(" ", ""); + format!("", escaped_output) +} + impl OutputFormatter for JunitFormatter { fn write_discovery_start(&mut self) -> io::Result<()> { Err(io::Error::new(io::ErrorKind::NotFound, "Not yet implemented!")) @@ -63,14 +75,14 @@ impl OutputFormatter for JunitFormatter { desc: &TestDesc, result: &TestResult, exec_time: Option<&time::TestExecTime>, - _stdout: &[u8], + stdout: &[u8], _state: &ConsoleTestState, ) -> io::Result<()> { // Because the testsuite node holds some of the information as attributes, we can't write it // until all of the tests have finished. Instead of writing every result as they come in, we add // them to a Vec and write them all at once when run is complete. let duration = exec_time.map(|t| t.0).unwrap_or_default(); - self.results.push((desc.clone(), result.clone(), duration)); + self.results.push((desc.clone(), result.clone(), duration, stdout.to_vec())); Ok(()) } fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result { @@ -85,7 +97,7 @@ impl OutputFormatter for JunitFormatter { >", state.failed, state.total, state.ignored ))?; - for (desc, result, duration) in std::mem::take(&mut self.results) { + for (desc, result, duration, stdout) in std::mem::take(&mut self.results) { let (class_name, test_name) = parse_class_name(&desc); match result { TestResult::TrIgnored => { /* no-op */ } @@ -98,6 +110,11 @@ impl OutputFormatter for JunitFormatter { duration.as_secs_f64() ))?; self.write_message("")?; + if !stdout.is_empty() { + self.write_message("")?; + self.write_message(&str_to_cdata(&String::from_utf8_lossy(&stdout)))?; + self.write_message("")?; + } self.write_message("")?; } @@ -110,6 +127,11 @@ impl OutputFormatter for JunitFormatter { duration.as_secs_f64() ))?; self.write_message(&format!(""))?; + if !stdout.is_empty() { + self.write_message("")?; + self.write_message(&str_to_cdata(&String::from_utf8_lossy(&stdout)))?; + self.write_message("")?; + } self.write_message("")?; } @@ -136,11 +158,19 @@ impl OutputFormatter for JunitFormatter { TestResult::TrOk => { self.write_message(&format!( "", + name=\"{}\" time=\"{}\"", class_name, test_name, duration.as_secs_f64() ))?; + if stdout.is_empty() { + self.write_message("/>")?; + } else { + self.write_message(">")?; + self.write_message(&str_to_cdata(&String::from_utf8_lossy(&stdout)))?; + self.write_message("")?; + self.write_message("")?; + } } } } diff --git a/tests/run-make/libtest-junit/output-default.xml b/tests/run-make/libtest-junit/output-default.xml index ea61562a33ccc..0c300611e1f76 100644 --- a/tests/run-make/libtest-junit/output-default.xml +++ b/tests/run-make/libtest-junit/output-default.xml @@ -1 +1 @@ - + diff --git a/tests/run-make/libtest-junit/output-stdout-success.xml b/tests/run-make/libtest-junit/output-stdout-success.xml index ea61562a33ccc..0c300611e1f76 100644 --- a/tests/run-make/libtest-junit/output-stdout-success.xml +++ b/tests/run-make/libtest-junit/output-stdout-success.xml @@ -1 +1 @@ - +