Skip to content

Fix JUnit XML conformance and output structure#58

Merged
thetic merged 19 commits into
mainfrom
junity
Apr 17, 2026
Merged

Fix JUnit XML conformance and output structure#58
thetic merged 19 commits into
mainfrom
junity

Conversation

@thetic
Copy link
Copy Markdown
Owner

@thetic thetic commented Apr 16, 2026

Description

Reviews the JUnit XML spec at https://github.com/testmoapp/junitxml/ and fixes five conformance issues in the JUnitOutput implementation, then cleans up the resulting dead code.

Issue 1 — File path not XML-encoded in <failure> attribute
The message attribute of <failure> included a raw file path, which could contain <, >, &, \" characters that are illegal unescaped in XML attributes. The file name is now passed through encode_xml_text().

Issue 2 — Always-empty <system-err> element
Every test suite emitted a <system-err></system-err> block even though Mu::tiny has no mechanism to capture stderr. The element was removed rather than left misleadingly empty.

Issue 3 — No-op write_properties() stub
A write_properties() virtual method existed but its body was empty, silently discarding any properties. The stub was removed; TEST_PROPERTY support was already wired through print_test_property() / write_test_cases().

Issue 4 — No <testsuites> wrapper
Each test group was written to a separate file with no enclosing <testsuites> element. Groups are now accumulated into a single file written at the end of the run, wrapped in <testsuites> with aggregate tests, failures, errors, skipped, and time totals across all groups. Empty groups are suppressed.

Separate from this, the plugin now defaults the package name to the executable basename when -pjunit is given without =name, producing classnames like mutiny_unit.GroupName that identify the source executable in CI reports.

Issue 5 — <system-out> was suite-level only
Output printed during a test (via print()) was collected into a single suite-level <system-out> block. It is now routed per-testcase when inside a test, appearing inside the correct <testcase> element.

ctest collision fix
When MUTINY_JUNIT_REPORT is enabled, mutiny_discover_tests() previously appended -pjunit to the shared ARGS for every ctest invocation of a given executable. Because ctest runs each group as a separate process, all groups of the same executable wrote to the same file and clobbered each other. The fix moves JUnit flag generation into the discovery script, emitting a unique -pjunit=<target>.<group> per ctest test so each group gets its own collision-free output file.

Dead code removal
After the conformance fixes, the protected virtual methods on JUnitOutput (open_file_for_write, write_test_group_to_file, write_to_file, close_file, write_test_suite_summary, write_test_cases, encode_xml_text, encode_file_name, write_failure, write_error, write_file_ending) had no subclass overrides — they were vestigial hooks superseded by the injectable function pointers on Output. All are now private non-virtual.

Related Issues

Fixes # (issue number)

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I have written/updated documentation in docs/ for any user-facing changes.
  • My code follows the project's naming conventions (mu::tiny namespace, INCLUDED_MUTINY_ guards, mutiny_ C-prefix).
  • For new features, I have considered if a C-interface adapter (.h and .c.cpp) is required for parity.
  • I have reviewed the CONTRIBUTING.md file to ensure compliance with architectural guidelines.

thetic added 5 commits April 15, 2026 20:16
The file name was interpolated raw into the <failure message="...">
attribute while the failure message text was encoded. Encode the file
name via encode_xml_text() before use to prevent malformed XML if the
path contains characters like ", <, or &.
The element was always written empty — stderr was never captured.
Per the spec, <system-err> is optional; omitting it is cleaner
than emitting a misleading empty tag.
The suite-level <properties> virtual was never implemented or called
with any real data. Removing the dead stub and its call site.
Output printed during a test (between current_test_started and
current_test_ended) is now emitted as <system-out> inside the
<testcase> element. Output printed outside any test continues to
appear in the suite-level <system-out>.
-pjunit now defaults the package name to the basename of argv[0],
disambiguating output files in parallel ctest runs without any
user configuration. An explicit override is available via
-pjunit=<name> for users who need a different prefix.
@coveralls
Copy link
Copy Markdown

coveralls commented Apr 16, 2026

Coverage Status

coverage: 98.629% (-0.01%) from 98.642% — junity into main

thetic added 4 commits April 15, 2026 23:23
Instead of one file per test group, all groups are now accumulated into a
single file written at the end of the run. This eliminates file-name
collisions when ctest runs test executables in parallel, and produces a
conformant JUnit XML report with a top-level <testsuites> element whose
totals (tests, failures, errors, skipped, time) span the whole run.
Two related fixes:

1. create_file_name(): when a package is set, use it directly as the
   file stem rather than prepending "mutiny_".  This eliminates the
   double-prefix (mutiny_mutiny_unit.xml) that appeared when the plugin
   defaulted the package name to the executable basename.

2. Discovery script: instead of appending -pjunit to every test's shared
   ARGS (which caused all groups of the same executable to clobber the
   same file), pass JUNIT_REPORT:BOOL separately and emit a unique
   -pjunit=<target>.<group> per ctest test.  Each group now gets its own
   collision-free output file (mutiny_unit.JUnitOutput.xml, etc.).
- junit-output.rst: rewrite for current behavior — single file per run
  named after the executable/package, <testsuites> wrapper in the XML
  example, -pjunit=<name> override syntax, ctest per-group file naming,
  and corrected CI glob patterns (build/**/*.xml instead of cpputest_*.xml)
- Mutiny.cmake: correct the MUTINY_JUNIT_REPORT doc comment to describe
  the per-test -pjunit=<target>.<group> behavior
@thetic thetic marked this pull request as ready for review April 16, 2026 06:43
thetic added 7 commits April 15, 2026 23:54
Rename two test cases to describe what they actually test (between-test
output routing to suite-level system-out) and remove the UTPRINT mention
from the docs, replacing it with an accurate description of the output
routing rules.
stdout was never captured
XML declaration is now emitted inline in print_tests_ended.
The method now just clears current_group_xml; the parameter was
unused since the single-file refactor.
References said --junit; the actual flag is -pjunit[=<name>].
Verifies that the top-level wrapper sums tests, failures, and
skipped counts from multiple groups.
thetic added 2 commits April 16, 2026 20:05
Skip writing <testsuite> elements for groups with zero tests —
groups that are registered but filtered out produced empty
name="" / tests="0" noise in the output.

For SKIPPED_TEST tests, populate the <skipped> message attribute
with the macro name (e.g. "SKIPPED_TEST") so CI tools have
something useful to display. Also move Shell::get_macro_name()
from protected to public since it is pure read-only metadata.
No subclass overrides these methods — they were vestigial hooks from
the original CppUTest design, superseded by the Output-level injectable
function pointers (fopen_, fputs_, fclose_). Move all to private
and drop virtual. Rewrite the one test that called create_file_name()
directly to go through the public file-system interface instead.
@thetic thetic changed the title Junity Fix JUnit XML conformance and output structure Apr 17, 2026
14-year-old SKIPPED_TEST with no assertions, introduced in 2011 as a
profiling scaffold and never activated. Carried forward through every
refactor unchanged.
@thetic thetic merged commit e058398 into main Apr 17, 2026
40 checks passed
@thetic thetic deleted the junity branch April 17, 2026 04:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants