diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs
index 1566f1cb1dac..74d99e0f1270 100644
--- a/library/test/src/formatters/junit.rs
+++ b/library/test/src/formatters/junit.rs
@@ -189,8 +189,10 @@ fn write_merged_doctests_times(
compilation_time: f64,
) -> io::Result<()> {
self.write_message(&format!(
- "\n",
- ))
+ "",
+ ))?;
+ self.out.write_all(b"\n")?;
+ Ok(())
}
}
diff --git a/tests/run-make/doctests-junit/doctest-2021.xml b/tests/run-make/doctests-junit/doctest-2021.xml
new file mode 100644
index 000000000000..5facfb80ce62
--- /dev/null
+++ b/tests/run-make/doctests-junit/doctest-2021.xml
@@ -0,0 +1 @@
+
diff --git a/tests/run-make/doctests-junit/doctest-2024.xml b/tests/run-make/doctests-junit/doctest-2024.xml
new file mode 100644
index 000000000000..4f94f01c3e32
--- /dev/null
+++ b/tests/run-make/doctests-junit/doctest-2024.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/tests/run-make/doctests-junit/doctest.rs b/tests/run-make/doctests-junit/doctest.rs
new file mode 100644
index 000000000000..1873d63a49c6
--- /dev/null
+++ b/tests/run-make/doctests-junit/doctest.rs
@@ -0,0 +1,14 @@
+/// ```
+/// assert_eq!(doctest::add(2, 2), 4);
+/// ```
+///
+/// ```should_panic
+/// assert_eq!(doctest::add(2, 2), 5);
+/// ```
+///
+/// ```compile_fail
+/// assert_eq!(doctest::add(2, 2), "banana");
+/// ```
+pub fn add(a: i32, b: i32) -> i32 {
+ a + b
+}
diff --git a/tests/run-make/doctests-junit/rmake.rs b/tests/run-make/doctests-junit/rmake.rs
new file mode 100644
index 000000000000..e3198502bb17
--- /dev/null
+++ b/tests/run-make/doctests-junit/rmake.rs
@@ -0,0 +1,45 @@
+// Check rustdoc's test JUnit (XML) output against snapshots.
+
+//@ ignore-cross-compile (running doctests)
+//@ ignore-stage1 (rustdoc depends on a fix in libtest)
+//@ needs-unwind (test file contains `should_panic` test)
+
+use std::path::Path;
+
+use run_make_support::{cwd, diff, python_command, rustc, rustdoc};
+
+fn main() {
+ let rlib = cwd().join("libdoctest.rlib");
+ rustc().input("doctest.rs").crate_type("rlib").output(&rlib).run();
+
+ run_doctests(&rlib, "2021", "doctest-2021.xml");
+ run_doctests(&rlib, "2024", "doctest-2024.xml");
+}
+
+#[track_caller]
+fn run_doctests(rlib: &Path, edition: &str, expected_xml: &str) {
+ let rustdoc_out = rustdoc()
+ .input("doctest.rs")
+ .args(&[
+ "--test",
+ "--test-args=-Zunstable-options",
+ "--test-args=--test-threads=1",
+ "--test-args=--format=junit",
+ ])
+ .edition(edition)
+ .env("RUST_BACKTRACE", "0")
+ .extern_("doctest", rlib.display().to_string())
+ .run();
+ let rustdoc_stdout = &rustdoc_out.stdout_utf8();
+
+ // FIXME: merged output of compile_fail tests is broken
+ if edition != "2024" {
+ python_command().arg("validate_junit.py").stdin_buf(rustdoc_stdout).run();
+ }
+
+ diff()
+ .expected_file(expected_xml)
+ .actual_text("output", rustdoc_stdout)
+ .normalize(r#"\b(time|total_time|compilation_time)="[0-9.]+""#, r#"$1="$$TIME""#)
+ .run();
+}
diff --git a/tests/run-make/doctests-junit/validate_junit.py b/tests/run-make/doctests-junit/validate_junit.py
new file mode 100755
index 000000000000..a9cb0a059563
--- /dev/null
+++ b/tests/run-make/doctests-junit/validate_junit.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+# Trivial Python script that reads lines from stdin, and checks that each line
+# is a well-formed XML document.
+#
+# This takes advantage of the fact that Python has a built-in XML parser,
+# whereas doing the same check in Rust would require us to pull in an XML
+# crate just for this relatively-minor test.
+#
+# If you're trying to remove Python scripts from the test suite, think twice
+# before removing this one. You could do so, but it's probably not worth it.
+
+import sys
+import xml.etree.ElementTree as ET
+
+# Read the entire output and try to decode it as XML.
+junit = sys.stdin.read()
+try:
+ ET.fromstring(junit)
+except ET.ParseError:
+ print("Invalid xml: %r" % junit)
+ raise
diff --git a/tests/run-make/libtest-junit/validate_junit.py b/tests/run-make/libtest-junit/validate_junit.py
index f92473751b03..a9cb0a059563 100755
--- a/tests/run-make/libtest-junit/validate_junit.py
+++ b/tests/run-make/libtest-junit/validate_junit.py
@@ -13,10 +13,10 @@
import sys
import xml.etree.ElementTree as ET
-# Try to decode line in order to ensure it is a valid XML document
-for line in sys.stdin:
- try:
- ET.fromstring(line)
- except ET.ParseError:
- print("Invalid xml: %r" % line)
- raise
+# Read the entire output and try to decode it as XML.
+junit = sys.stdin.read()
+try:
+ ET.fromstring(junit)
+except ET.ParseError:
+ print("Invalid xml: %r" % junit)
+ raise