<?xml version="1.0"?>
<!DOCTYPE riscos-prm PUBLIC "-//Gerph//DTD PRM documentation 1.03//EN"
                            "http://gerph.org/dtd/103/prm.dtd">
<riscos-prm>
<chapter title="JUnitXML">

<section title="Introduction">

<p>
The JUnitXML module provides an interface for generating JUnit-compatible XML test result files
on RISC OS systems. It allows test frameworks and test runners to record test results in a
standardised format that can be consumed by continuous integration systems, test reporting tools,
and other software development utilities.
</p>

<p>
The module implements a SWI-based interface for creating test suites, recording test cases with
their outcomes (success, failure, error, or skipped), generating XML output conforming to the
JUnit XML schema, and retrieving aggregate result counts.
</p>

<p>
Key features include:
</p>

<p>
<list type='unordered'>
 <item><p>Support for multiple test suites within a single output file</p></item>
 <item><p>Flexible timestamp handling (current time, RISC OS time, Unix time, or ISO 8601 strings)</p></item>
 <item><p>Package and class name support for organised test hierarchies</p></item>
 <item><p>Custom properties for test suites</p></item>
 <item><p>Detailed failure and error reporting with type and message</p></item>
 <item><p>Duration tracking for test suites and individual test cases</p></item>
 <item><p>Result retrieval for obtaining aggregate pass, failure, error, and skip counts</p></item>
</list>
</p>

</section>

<section title="Technical Details">

<p>
The JUnitXML module maintains internal state for each open handle, tracking test suites and test
cases as they are created. The module uses a handle-based API where each handle represents an
independent XML output stream.
</p>

<subsection title="Memory Management">

<p>
The module allocates memory dynamically for:
</p>

<p>
<list type='unordered'>
 <item><p>Handle structures</p></item>
 <item><p>Test suite structures and their properties</p></item>
 <item><p>Test case structures</p></item>
 <item><p>String data (names, IDs, messages, timestamps)</p></item>
</list>
</p>

<p>
All memory is freed when a handle is closed or when the module is terminated. The module maintains
a linked list of handles to allow multiple concurrent XML output streams.
</p>

</subsection>

<subsection title="Timestamp Handling">

<p>
The module supports multiple timestamp formats for test suite creation:
</p>

<p>
<list type='unordered'>
 <item><p><strong>Current time:</strong> Automatically uses the current system time</p></item>
 <item><p><strong>RISC OS time:</strong> Accepts a RISC OS 5-byte quin (centiseconds since 1900-01-01)</p></item>
 <item><p><strong>Unix time:</strong> Accepts a Unix epoch timestamp (seconds since 1970-01-01)</p></item>
 <item><p><strong>ISO 8601:</strong> Accepts a pre-formatted ISO 8601 timestamp string</p></item>
</list>
</p>

<p>
RISC OS time values are converted to ISO 8601 format using the Territory_ConvertDateAndTime SWI
for local time conversion, with manual adjustment for UTC epoch differences.
</p>

</subsection>

<subsection title="XML Output">

<p>
The module generates JUnit-compatible XML output with the following structure:
</p>

<p>
<list type='unordered'>
 <item><p>XML declaration with UTF-8 encoding</p></item>
 <item><p>Root testsuites element with aggregate statistics</p></item>
 <item><p>Individual testsuite elements with properties and test cases</p></item>
 <item><p>Test case elements with optional failure or error child elements</p></item>
</list>
</p>

<p>
The XML is written incrementally as test cases are closed, reducing memory usage for large test
suites.
</p>

</subsection>

</section>

<section title="SWI Calls">

<swi-definition name='JUnitXML_Create' number='5AC00'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0' name='JUnitXML_Create_FilenameGiven'>
      If set, R1 points to a filename string
     </bit>
     <bit number='1-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Filename pointer (if bit 0 of flags set)
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Handle ID (positive) or error code (negative)
   </register-use>
  </exit>
  <use>
   <p>
   JUnitXML_Create creates a new JUnit XML output handle. The handle can be used to create test
   suites and record test results.
   </p>

   <p>
   Flags:
   </p>

   <p>
   <list type='unordered'>
    <item><p>Bit 0 (JUnitXML_Create_FilenameGiven): If set, R1 points to a filename string where
    the XML output will be written. If clear, the module will generate a default filename.</p></item>
   </list>
   </p>

   <p>
   The function returns a positive handle ID on success, or a negative error code on failure.
   </p>
  </use>
  <example>
   <p><userinput>SWI "JUnitXML_Create"</userinput></p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_Close'/>
   <reference type='swi' name='JUnitXML_TestSuite'/>
   <reference type='swi' name='JUnitXML_Result'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestSuite' number='5AC01'
                description='Performs operations on test suites'>
  <entry>
   <register-use number='0'>
    <p>Reason code:</p>
    <p>
    <value-table head-number="Reason" head-value="Action">
     <value number="0"><reference type='swi' name='JUnitXML_TestSuite' reason="0" use-description='yes'/></value>
     <value number="1"><reference type='swi' name='JUnitXML_TestSuite' reason="1" use-description='yes'/></value>
     <value number="2"><reference type='swi' name='JUnitXML_TestSuite' reason="2" use-description='yes'/></value>
     <value number="3"><reference type='swi' name='JUnitXML_TestSuite' reason="3" use-description='yes'/></value>
    </value-table>
    </p>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
   <register-use number='2-5'>
    Dependent on reason code
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>

  </exit>
  <use>
   <p>
   JUnitXML_TestSuite performs operations on test suites within a given handle. The operation is
   determined by bits 0-3 of the flags word in R0. R1 always contains the handle ID.
   </p>
  </use>
  <related>
   <reference type='swi' name='JUnitXML_Create'/>
   <reference type='swi' name='JUnitXML_TestCase'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestSuite' number='5AC01'
                reason='0' reasonname='Create'
                description='Creates a new test suite within the handle'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0-3' name='Operation'>
      0 (Create)
     </bit>
     <bit number='4' name='PackageSupplied'>
      If set, R4 points to a package name
     </bit>
     <bit number='5-6' name='TSFormat'>
      Timestamp format: 0=Current, 1=RISC OS, 2=Unix, 3=ISO 8601
     </bit>
     <bit number='7-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
   <register-use number='2'>
    ID string pointer (or NULL to assign a numeric ID automatically)
   </register-use>
   <register-use number='3'>
    Name string pointer
   </register-use>
   <register-use number='4'>
    Package string pointer (if bit 4 of R0 set, else ignored)
   </register-use>
   <register-use number='5'>
    Timestamp value (interpretation depends on bits 5-6 of R0)
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>

  </exit>
  <use>
   <p>
   Creates a new test suite within the handle. The suite is identified by the ID string in R2; if
   R2 is NULL a numeric ID is assigned automatically, starting from 1 and incrementing for each suite or test case within the handle or suite respectively.
   </p>

   <p>
   If bit 4 (PackageSupplied) is set, R4 must point to a null-terminated package name string which
   will be recorded in the XML output.
   </p>

   <p>
   The timestamp format for the suite start time is selected by bits 5-6 (TSFormat) of R0:
   </p>

   <p>
   <value-table head-number="Value" head-value="Meaning">
    <value number="0">JUNIT_TS_CURRENT — R5 ignored, uses current system time</value>
    <value number="1">JUNIT_TS_RISCOS — R5 points to a 5-byte RISC OS time quin (centiseconds since 1900-01-01)</value>
    <value number="2">JUNIT_TS_UNIX — R5 contains Unix epoch time (seconds since 1970-01-01)</value>
    <value number="3">JUNIT_TS_ISO8601 — R5 points to a pre-formatted ISO 8601 string</value>
   </value-table>
   </p>
  </use>
  <example>
   <p>Creating a test suite with current timestamp:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_TestSuite", 0, jx%, "suite1", "My Test Suite"
   </extended-example>
   </p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_TestSuite' reason='1'/>
   <reference type='swi' name='JUnitXML_TestSuite' reason='3'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestSuite' number='5AC01'
                reason='1' reasonname='Close'
                description='Closes the current test suite, writing final statistics'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0-3' name='Operation'>
      1 (Close)
     </bit>
     <bit number='4-6' state='reserved'>
      Reserved
     </bit>
     <bit number='7' name='DurationPresent'>
      If set, R2 contains the suite duration in centiseconds
     </bit>
     <bit number='8-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
   <register-use number='2'>
    Duration in centiseconds (if bit 7 of R0 set, else ignored)
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>

  </exit>
  <use>
   <p>
   Closes the current open test suite, calculating and writing final aggregate statistics to the
   output.
   </p>

   <p>
   If bit 7 (DurationPresent) is set, R2 gives the elapsed duration of the suite in centiseconds,
   which is recorded in the XML output. If clear, the duration is omitted.
   </p>
  </use>
  <example>
   <p>Closing a test suite with duration of one second:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_TestSuite", 1 OR (1&lt;&lt;7), jx%, 100
   </extended-example>
   </p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_TestSuite' reason='0'/>
   <reference type='swi' name='JUnitXML_TestSuite' reason='2'/>
   <reference type='swi' name='JUnitXML_TestSuite' reason='3'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestSuite' number='5AC01'
                reason='2' reasonname='Update'
                description='Updates metadata on the current open test suite'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0-3' name='Operation'>
      2 (Update)
     </bit>
     <bit number='4' name='JUnitXML_TestSuite_HostnameSupplied'>
      If set, R2 points to a hostname string
     </bit>
     <bit number='5' name='JUnitXML_TestSuite_UpdateName'>
      If set, R3 points to a new suite name string
     </bit>
     <bit number='6' name='JUnitXML_TestSuite_UpdatePackage'>
      If set, R4 points to a new package name string
     </bit>
     <bit number='7-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
   <register-use number='2'>
    Hostname string pointer (if bit 4 of R0 set), otherwise ignored
   </register-use>
   <register-use number='3'>
    New suite name string pointer (if bit 5 of R0 set), otherwise ignored
   </register-use>
   <register-use number='4'>
    New package name string pointer (if bit 6 of R0 set), otherwise ignored
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>
  </exit>

  <use>
   <p>
   Updates metadata on the currently open test suite for the given handle. This call
   allows the hostname, suite name, and package name to be set or changed after the suite
   has been created with reason 0.
   </p>

   <p>
   Only the fields for which the corresponding flag bit is set are updated; fields for
   which the bit is clear are left unchanged.
   </p>

   <p>
   This call must be made before the first property or test case is added to the suite,
   since the suite header is written to the XML output lazily just before those items.
   Calling Update after a property or test case has been added returns
   <reference type='error' name='JUnitXML_BadSuiteOp'/>.
   </p>
  </use>
  <example>
   <p>Setting the hostname on the current suite:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_TestSuite", 2 + (1&lt;&lt;4), jx%, "my-machine"
   </extended-example>
   </p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_TestSuite' reason='0'/>
   <reference type='swi' name='JUnitXML_TestSuite' reason='1'/>
   <reference type='swi' name='JUnitXML_TestSuite' reason='3'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestSuite' number='5AC01'
                reason='3' reasonname='Property'
                description='Sets a property on the current test suite'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0-3' name='Operation'>
      3 (Property)
     </bit>
     <bit number='4-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
   <register-use number='2'>
    Property name string pointer
   </register-use>
   <register-use number='3'>
    Property value string pointer
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>

  </exit>
  <use>
   <p>
   Sets a named property on the currently open test suite. Properties are written to the XML output
   as child elements of the testsuite element.
   </p>

   <p>
   Both the property name (R2) and property value (R3) must be non-NULL null-terminated strings.
   </p>
  </use>
  <example>
   <p>Setting a property on a test suite:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_TestSuite", 3, jx%, "host", "RISC OS"
   </extended-example>
   </p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_TestSuite' reason='0'/>
   <reference type='swi' name='JUnitXML_TestSuite' reason='1'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestCase' number='5AC02'
                description='Performs operations on test cases'>
  <entry>
   <register-use number='0'>
    <p>Reason code:</p>
    <p>
    <value-table head-number="Reason" head-value="Action">
     <value number="0"><reference type='swi' name='JUnitXML_TestCase' reason="0" use-description='yes'/></value>
     <value number="1"><reference type='swi' name='JUnitXML_TestCase' reason="1" use-description='yes'/></value>
     <value number="2"><reference type='swi' name='JUnitXML_TestCase' reason="2" use-description='yes'/></value>
    </value-table>
    </p>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
   <register-use number='2-6'>
    Dependent on reason code
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>

  </exit>
  <use>
   <p>
   JUnitXML_TestCase performs operations on test cases within the current suite. The operation is
   determined by bits 0-3 of the flags word in R0. R1 always contains the handle ID.
   </p>
  </use>
  <related>
   <reference type='swi' name='JUnitXML_TestSuite'/>
   <reference type='swi' name='JUnitXML_Close'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestCase' number='5AC02'
                reason='0' reasonname='Create'
                description='Creates a new test case within the current suite'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0-3' name='Operation'>
      0 (Create)
     </bit>
     <bit number='4-7' name='Status'>
      Test status code (see below)
     </bit>
     <bit number='8' name='ErrorBlock'>
      If set, R5 points to a RISC OS error block rather than a string
     </bit>
     <bit number='9-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
   <register-use number='2'>
    ID string pointer (or NULL to assign a numeric ID automatically)
   </register-use>
   <register-use number='3'>
    Classname string pointer
   </register-use>
   <register-use number='4'>
    Test name string pointer
   </register-use>
   <register-use number='5'>
    Failure type string pointer, or RISC OS error block pointer if bit 8 set (or NULL)
   </register-use>
   <register-use number='6'>
    Failure message string pointer (or NULL)
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>

  </exit>
  <use>
   <p>
   Creates a new test case within the currently open suite. The test case is identified by the ID
   string in R2; if R2 is NULL a numeric ID is assigned automatically, starting from 1 and incrementing for each suite or test case within the handle or suite respectively.
   </p>

   <p>
   The test status is given by bits 4-7 (Status) of R0:
   </p>

   <p>
   <value-table head-number="Value" head-value="Meaning">
    <value number="0">JUNIT_STATUS_NONE — no status specified</value>
    <value number="1">JUNIT_STATUS_SUCCESS — test passed</value>
    <value number="2">JUNIT_STATUS_FAILURE — test failed (assertion failure)</value>
    <value number="3">JUNIT_STATUS_ERROR — test errored (unexpected error)</value>
    <value number="4">JUNIT_STATUS_SKIPPED — test was skipped</value>
   </value-table>
   </p>

   <p>
   For failure and error statuses, R5 and R6 provide optional failure details. If bit 8
   (ErrorBlock) is set in R0, R5 points to a RISC OS error block from which the error number and
   message are extracted; otherwise R5 points to a plain null-terminated string giving the failure
   type. R6 points to a null-terminated string giving an optional failure message.
   </p>
  </use>
  <example>
   <p>Creating a successful test case:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_TestCase", 1&lt;&lt;4, jx%, "test1", "Calculator", "testAdd", 0, 0
   </extended-example>
   </p>

   <p>Creating a failed test case:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_TestCase", 2&lt;&lt;4, jx%, "test2", "Calculator", "testDivide", "AssertionError", "Expected 5 but got 3"
   </extended-example>
   </p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_TestCase' reason='1'/>
   <reference type='swi' name='JUnitXML_TestSuite'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestCase' number='5AC02'
                reason='1' reasonname='Close'
                description='Closes the current test case, writing it to the output'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0-3' name='Operation'>
      1 (Close)
     </bit>
     <bit number='4-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
   <register-use number='2'>
    Duration in centiseconds
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>

  </exit>
  <use>
   <p>
   Closes the current test case and writes it to the XML output. R2 gives the elapsed duration of
   the test case in centiseconds.
   </p>
  </use>
  <example>
   <p>Closing a test case with duration of half a second:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_TestCase", 1, jx%, 50
   </extended-example>
   </p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_TestCase' reason='0'/>
   <reference type='swi' name='JUnitXML_TestCase' reason='2'/>
   <reference type='swi' name='JUnitXML_TestSuite'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_TestCase' number='5AC02'
                reason='2' reasonname='Update'
                description='Reserved for future use; not yet implemented'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0-3' name='Operation'>
      2 (Update)
     </bit>
     <bit number='4-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
  </entry>

  <use>
   <p>
   This reason code is reserved for future use. It is not currently implemented; calling it
   always returns <reference type='error' name='JUnitXML_BadCaseOp'/>.
   </p>
  </use>
  <related>
   <reference type='swi' name='JUnitXML_TestCase' reason='0'/>
   <reference type='swi' name='JUnitXML_TestCase' reason='1'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_Close' number='5AC03'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0' name='JUnitXML_Close_FilenameGiven'>
      If set, R1 points to an output filename
     </bit>
     <bit number='1-31' state='reserved'>
      Reserved
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Filename pointer (if bit 0 set)
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Preserved
   </register-use>

  </exit>
  <use>
   <p>
   JUnitXML_Close closes a JUnit XML handle, writing the final XML structure and freeing resources.
   The handle is removed from the internal handle list.
   </p>

   <p>
   Flags:
   </p>

   <p>
   <list type='unordered'>
    <item><p>Bit 0 (JUnitXML_Close_FilenameGiven): If set, R1 points to an output filename.
    This overrides the filename specified during handle creation.</p></item>
   </list>
   </p>

   <p>
   The function closes the most recently created handle if multiple handles exist. For precise
   control, ensure handles are closed in reverse order of creation.
   </p>
  </use>
  <example>
   <p>Closing a handle with the original filename:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_Close", 0
   </extended-example>
   </p>

   <p>Closing a handle with a different filename:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_Close", 1, "$.results.junit/xml"
   </extended-example>
   </p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_Create'/>
   <reference type='swi' name='JUnitXML_Result'/>
  </related>
</swi-definition>

<swi-definition name='JUnitXML_Result' number='5AC04'>
  <entry>
   <register-use number='0'>
    Flags
    <bitfield-table>
     <bit number='0-31' state='reserved'>
      Reserved, must be zero
     </bit>
    </bitfield-table>
   </register-use>
   <register-use number='1'>
    Handle ID
   </register-use>
  </entry>
  <exit>
   <register-use number='0'>
    Number of tests present
   </register-use>
   <register-use number='1'>
    Number of passes
   </register-use>
   <register-use number='2'>
    Number of failures
   </register-use>
   <register-use number='3'>
    Number of errors
   </register-use>
   <register-use number='4'>
    Number of skips
   </register-use>

  </exit>
  <use>
   <p>
   JUnitXML_Result returns aggregate result counts for all test suites recorded within the given
   handle. The counts are summed across all suites, including suites that are still open.
   </p>

   <p>
   R0 (flags) must be zero on entry; all flag bits are reserved for future use.
   </p>

   <p>
   This SWI may be called at any point while the handle is valid — before or after closing
   individual suites. It must not be called after JUnitXML_Close has been used to close the
   handle, as the handle will no longer be valid.
   </p>

   <p>
   The returned counts are:
   </p>

   <p>
   <list type='unordered'>
    <item><p>R0: Total number of test cases recorded across all suites</p></item>
    <item><p>R1: Number of passing tests (total minus failures, errors, and skips)</p></item>
    <item><p>R2: Number of tests that reported a failure status</p></item>
    <item><p>R3: Number of tests that reported an error status</p></item>
    <item><p>R4: Number of tests that were skipped</p></item>
   </list>
   </p>
  </use>
  <example>
   <p>Reading result counts after running tests:</p>
   <p>
   <extended-example type='basic'>
    SYS "JUnitXML_Result", 0, jx% TO tests%, passes%, failures%, errors%, skips%<br/>
    PRINT "Tests: "; tests%<br/>
    PRINT "Passes: "; passes%<br/>
    PRINT "Failures: "; failures%<br/>
    PRINT "Errors: "; errors%<br/>
    PRINT "Skipped: "; skips%<br/>
    IF failures% + errors% &gt; 0 THEN PRINT "FAILED" ELSE PRINT "PASSED"
   </extended-example>
   </p>
  </example>
  <related>
   <reference type='swi' name='JUnitXML_Create'/>
   <reference type='swi' name='JUnitXML_TestCase'/>
   <reference type='swi' name='JUnitXML_Close'/>
  </related>
</swi-definition>

</section>

<section title="Error Messages">

<p>
The JUnitXML module returns error blocks in the standard RISC OS format. The error numbers are
module-specific with base &amp;822A00, and are returned as negative values from SWI calls or via
the carry flag.
</p>

<error-definition number='822A00' name='JUnitXML_CreateFailed' description='Failed to create JUnitXML handle'>
  <use>
   <p>Failed to create a new JUnit XML handle. This may be due to memory exhaustion or invalid
   parameters.</p>
  </use>
</error-definition>

<error-definition number='822A01' name='JUnitXML_CreateSuiteFailed' description='Failed to create test suite'>
  <use>
   <p>Failed to create a test suite within the handle. This may occur if the handle is invalid or
   if memory allocation fails.</p>
  </use>
</error-definition>

<error-definition number='822A02' name='JUnitXML_CloseSuiteFailed' description='Failed to close test suite'>
  <use>
   <p>Failed to close a test suite. This may occur if the suite is not properly formed or if
   writing to the output file fails.</p>
  </use>
</error-definition>

<error-definition number='822A03' name='JUnitXML_SetPropertyFailed' description='Failed to set property'>
  <use>
   <p>Failed to set a property on a test suite. This may occur if the property name or value is
   invalid, or if memory allocation fails.</p>
  </use>
</error-definition>

<error-definition number='822A04' name='JUnitXML_BadSuiteOp' description='Unknown TestSuite operation'>
  <use>
   <p>An unknown operation code was specified for JUnitXML_TestSuite. The supported operations
   are 0 (Create), 1 (Close), and 3 (Property).</p>
  </use>
</error-definition>

<error-definition number='822A05' name='JUnitXML_CreateCaseFailed' description='Failed to create test case'>
  <use>
   <p>Failed to create a test case. This may occur if the handle or current suite is invalid, or
   if memory allocation fails.</p>
  </use>
</error-definition>

<error-definition number='822A06' name='JUnitXML_CloseCaseFailed' description='Failed to close test case'>
  <use>
   <p>Failed to close a test case. This may occur if writing to the output file fails.</p>
  </use>
</error-definition>

<error-definition number='822A07' name='JUnitXML_BadCaseOp' description='Unknown TestCase operation'>
  <use>
   <p>An unknown operation code was specified for JUnitXML_TestCase. The supported operations
   are 0 (Create) and 1 (Close).</p>
  </use>
</error-definition>

<error-definition number='822A08' name='JUnitXML_CloseFailed' description='Failed to close JUnitXML handle'>
  <use>
   <p>Failed to close a JUnit XML handle. This may occur if writing the final XML structure fails
   or if the handle is invalid.</p>
  </use>
</error-definition>

<error-definition number='822A09' name='JUnitXML_NoHandle' description='No JUnitXML handle to close'>
  <use>
   <p>No handle is available to close. All handles may have already been closed, or the module
   may not have been initialised.</p>
  </use>
</error-definition>

<error-definition number='822A0A' name='JUnitXML_InitFailed' description='Failed to initialise JUnitXML state'>
  <use>
   <p>Module initialisation failed. The internal state manager could not be initialised.</p>
  </use>
</error-definition>

</section>

<section title="Usage">

<p>
The JUnitXML module is loaded automatically when a program links against it, or it can be loaded
manually using the RMEnsure command.
</p>

<subsection title="Loading the Module">

<p>
To load the module manually:
</p>

<p>
<list type='unordered'>
 <item><p><userinput>*RMEnsure JUnitXML 0.01 RMLoad JUnitXML</userinput></p></item>
</list>
</p>

<p>
The module will be loaded from the usual module search path. Once loaded, the SWI calls described
above can be used to create and manage JUnit XML output.
</p>

</subsection>

<subsection title="Typical Usage Pattern">

<p>
A typical usage pattern for generating JUnit XML output:
</p>

<p>
<list type='ordered'>
 <item><p>Call JUnitXML_Create to obtain a handle</p></item>
 <item><p>Call JUnitXML_TestSuite with operation Create to start a test suite</p></item>
 <item><p>For each test case:</p>
  <list type='unordered'>
   <item><p>Call JUnitXML_TestCase with operation Create</p></item>
   <item><p>Call JUnitXML_TestCase with operation Close</p></item>
  </list>
 </item>
 <item><p>Call JUnitXML_TestSuite with operation Close to finish the test suite</p></item>
 <item><p>Call JUnitXML_Close to close the handle and write the final XML</p></item>
</list>
</p>

</subsection>

<subsection title="Killing the Module">

<p>
To kill the module:
</p>

<p>
<list type='unordered'>
 <item><p><userinput>*RMKill JUnitXML</userinput></p></item>
</list>
</p>

<p>
All open handles will be closed and their resources freed when the module is killed.
</p>

</subsection>

</section>

<section title="Examples">

<subsection title="Complete Example in C">

<p>
This example shows how to create a simple JUnit XML file with one test suite containing three
test cases:
</p>

<p>
<extended-example type='c'>
#include &lt;stdio.h&gt;<br/>
#include "swis.h"<br/>
#include "kernel.h"<br/>
<br/>
#define JUnitXML_Create_FilenameGiven  (1 &lt;&lt; 0)<br/>
#define JUnitXML_TestSuite_OpCreate    0<br/>
#define JUnitXML_TestSuite_OpClose     1<br/>
#define JUnitXML_TestCase_OpCreate     0<br/>
#define JUnitXML_TestCase_OpClose      1<br/>
#define JUNIT_STATUS_SUCCESS           1<br/>
#define JUNIT_STATUS_FAILURE           2<br/>
<br/>
int main(void)<br/>
{<br/>
&nbsp;&nbsp;&nbsp;&nbsp;_kernel_swi_regs r;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;_kernel_oserror *err;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;int handle;<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;/* Create a handle with output filename */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[0] = JUnitXML_Create_FilenameGiven;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[1] = (int)"TestResults.xml";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;err = _kernel_swi(0x5AC00, &amp;r, &amp;r); /* JUnitXML_Create */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;if (err) {<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Failed to create handle: %s\\n", err->errmess);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 1;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;handle = r.r[0];<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;/* Create a test suite */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[0] = JUnitXML_TestSuite_OpCreate; /* Create, current time */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[1] = handle;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[2] = (int)"suite1";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[3] = (int)"MyTestSuite";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;err = _kernel_swi(0x5AC01, &amp;r, &amp;r); /* JUnitXML_TestSuite */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;if (err) {<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("Failed to create suite: %s\\n", err->errmess);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 1;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;}<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;/* Create test case 1: success */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[0] = JUnitXML_TestCase_OpCreate | (JUNIT_STATUS_SUCCESS &lt;&lt; 4);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[1] = handle;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[2] = (int)"test1";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[3] = (int)"MyTestClass";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[4] = (int)"test_addition";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[5] = 0;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[6] = 0;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;err = _kernel_swi(0x5AC02, &amp;r, &amp;r); /* JUnitXML_TestCase */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;if (err) goto error;<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;/* Close test case 1 */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[0] = JUnitXML_TestCase_OpClose;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[1] = handle;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[2] = 25; /* 25 centiseconds */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;err = _kernel_swi(0x5AC02, &amp;r, &amp;r);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;if (err) goto error;<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;/* Create test case 2: failure */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[0] = JUnitXML_TestCase_OpCreate | (JUNIT_STATUS_FAILURE &lt;&lt; 4);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[1] = handle;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[2] = (int)"test2";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[3] = (int)"MyTestClass";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[4] = (int)"test_subtraction";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[5] = (int)"AssertionError";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[6] = (int)"Expected 3 but got 4";<br/>
&nbsp;&nbsp;&nbsp;&nbsp;err = _kernel_swi(0x5AC02, &amp;r, &amp;r);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;if (err) goto error;<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;/* Close test case 2 */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[0] = JUnitXML_TestCase_OpClose;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[1] = handle;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[2] = 30;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;err = _kernel_swi(0x5AC02, &amp;r, &amp;r);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;if (err) goto error;<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;/* Close the test suite */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[0] = JUnitXML_TestSuite_OpClose | (1 &lt;&lt; 7); /* Duration present */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[1] = handle;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[2] = 100; /* 100 centiseconds total */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;err = _kernel_swi(0x5AC01, &amp;r, &amp;r);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;if (err) goto error;<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;/* Close the handle */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;r.r[0] = 0;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;err = _kernel_swi(0x5AC03, &amp;r, &amp;r); /* JUnitXML_Close */<br/>
&nbsp;&nbsp;&nbsp;&nbsp;if (err) goto error;<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;printf("JUnit XML file created successfully\\n");<br/>
&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br/>
<br/>
error:<br/>
&nbsp;&nbsp;&nbsp;&nbsp;printf("Error: %s\\n", err ? err-&gt;errmess : "Unknown");<br/>
&nbsp;&nbsp;&nbsp;&nbsp;return 1;<br/>
}<br/>
</extended-example>
</p>

</subsection>

<subsection title="Example Output">

<p>
The above C code would generate XML similar to:
</p>

<p>
<extended-example type='format'>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br/>
&lt;testsuites&gt;<br/>
&nbsp;&nbsp;&lt;testsuite name="MyTestSuite" package=""<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id="suite1" tests="2" failures="1" errors="0" skipped="0"<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time="1.00" timestamp="2026-03-27T10:30:00Z"&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&lt;testcase id="test1" classname="MyTestClass" name="test_addition"<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time="0.25"/&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&lt;testcase id="test2" classname="MyTestClass" name="test_subtraction"<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time="0.30"&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;failure type="AssertionError"&gt;Expected 3 but got 4&lt;/failure&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/testcase&gt;<br/>
&nbsp;&nbsp;&lt;/testsuite&gt;<br/>
&lt;/testsuites&gt;<br/>
</extended-example>
</p>

</subsection>

</section>

</chapter>

<meta>
  <maintainer>
   <email name="Charles Ferguson" address="gerph@gerph.org"/>
  </maintainer>
  <disclaimer>
   <p>Copyright © 2026 Charles Ferguson. All rights reserved.</p>
   <p>This documentation is provided "as is" without warranty of any kind.</p>
  </disclaimer>
  <history>
   <revision number="1" date="27 March 2026" author="CF" title="Initial version">
    <change>Created initial PRM-in-XML documentation for JUnitXML module</change>
   </revision>
   <revision number="2" date="09 April 2026" author="CF" title="Add JUnitXML_Result SWI">
    <change>Added documentation for JUnitXML_Result SWI which returns aggregate test result counts</change>
   </revision>
  </history>
  <related>
   <reference type='document' href='?' name='JUnit XML Schema Documentation'/>
   <reference type='link' href='https://github.com/junit-team/junit5' name='JUnit 5 Project'/>
  </related>
</meta>

</riscos-prm>
