Chapter 14Lesson 02220–300 min

Logs, Reports, output.xml, Rebot, and Result Post-Processing: Guided Hands-On Workflow

Create two disposable executions, inspect their XML, regenerate presentations, combine independent results, rerun a controlled failure, merge it correctly, and produce xUnit evidence.

Hands-onRebot workflowRerun mergexUnitEvidence

Learning objectives

  • Create separate, deterministic output directories without overwriting earlier evidence.
  • Inspect output.xml root/suite/test/status metadata with a small standard-library Python tool.
  • Regenerate log/report from a raw output using Rebot.
  • Combine independent roots and merge a failed-test rerun using the correct identity semantics.
  • Generate xUnit and record artifact sizes/checksums/privacy observations.

Current compatibility baseline. Verified 2026-08-31: Robot Framework 7.4.2 is the current stable release and requires Python 3.8+; 7.5b1 is a pre-release and is not required here. The 7.4.2 result.xsd is schema version 5; default XML outputs carry a schemaversion attribute and Robot Framework 7.x can also read legacy output. --legacyoutput exists for Robot Framework 6.x-compatible consumers. Rebot combines independent outputs by creating a new parent suite; --merge is for the same logical top-level suite, including reruns, where later matching test results replace earlier results and later SKIP results do not replace originals. The built-in Testdoc tool is deprecated in 7.4.2 in favor of external Testdoc and is not a result-reporting substitute.

1. Disposable scenario and safety boundary

Create a new local directory named rf14-evidence. Everything in this lesson is synthetic: no browser, API, database, SSH host, cloud account, production service, credential, or external network target is required. Results live under results/; source files live under suites/; a tiny read-only XML inspection helper lives under tools/.

Do not reuse a production results directory. The commands use separate subdirectories so the first run, rerun, combined outputs, and derivatives remain distinguishable. Delete only the disposable rf14-evidence directory after verification.

2. Preflight and exact assumptions

python --version
python -m robot --version
python -m robot.rebot --version

Expected baseline for this chapter: Python 3.8+ and Robot Framework 7.4.2. If your stable version differs, re-check Rebot/output-format behavior before copying version-specific expectations such as schema version 5. The mandatory path uses only Robot Framework core/BuiltIn and Python standard library.

3. Create three tiny suites

*** Test Cases ***
Independent A Is Healthy
    [Tags]    evidence    independent-a
    Should Be Equal    alpha    alpha
    Log    run-a synthetic evidence

Save as suites/independent_a.robot.

*** Test Cases ***
Independent B Is Healthy
    [Tags]    evidence    independent-b
    Should Be Equal As Integers    2    2
    Log    run-b synthetic evidence

Save as suites/independent_b.robot.

*** Variables ***
${OBSERVED}    broken

*** Test Cases ***
Release Gate
    [Tags]    evidence    rerun
    Should Be Equal    ${OBSERVED}    repaired    msg=Release gate expected repaired state

Stable Check
    [Tags]    evidence    rerun
    Should Be Equal    stable    stable

Save as suites/rerun.robot. The variable is intentionally overridable from the command line so the initial failure and repaired rerun use the same source identity.

4. Build a read-only output inspector

from pathlib import Path
import sys
import xml.etree.ElementTree as ET

path = Path(sys.argv[1])
root = ET.parse(path).getroot()
suite = root.find("suite")
tests = root.findall(".//test")
print("file:", path)
print("root:", root.tag)
print("generator:", root.attrib.get("generator"))
print("generated:", root.attrib.get("generated"))
print("schemaversion:", root.attrib.get("schemaversion"))
print("rpa:", root.attrib.get("rpa"))
print("top suite:", suite.attrib.get("name") if suite is not None else None)
for test in tests:
    status = test.find("status")
    print("test:", test.attrib.get("name"), "status:", status.attrib.get("status") if status is not None else None)

Save as tools/inspect_output.py. It reads XML only; it does not mutate Robot results. That makes it suitable for before/after evidence comparisons.

5. Run two independent suites into separate directories

python -m robot --outputdir results/run-a suites/independent_a.robot
python -m robot --outputdir results/run-b suites/independent_b.robot
python tools/inspect_output.py results/run-a/output.xml
python tools/inspect_output.py results/run-b/output.xml

Expected: both runs PASS and each directory contains output.xml, log.html, and report.html. The inspector should show schema metadata and different top-level suite names derived from the two files. Nothing in this step changes external state beyond the result files themselves.

6. Regenerate a presentation without rerunning tests

python -m robot.rebot   --outputdir results/rebuilt-a   --log log.html   --report report.html   results/run-a/output.xml

PowerShell can use the same arguments on one line, or backticks for continuation. Rebot reads results/run-a/output.xml and creates new HTML under results/rebuilt-a. By default Rebot does not create a new XML output unless --output is explicitly supplied. The test was not re-executed.

7. Combine independent roots

python -m robot.rebot   --name "Independent Evidence Bundle"   --output combined.xml   --outputdir results/combined   results/run-a/output.xml   results/run-b/output.xml
python tools/inspect_output.py results/combined/combined.xml

Expected: Rebot creates a new top-level suite named Independent Evidence Bundle, with the original A and B roots as children. This is correct because the inputs are independent. The resulting report should show two passing tests. The command also creates a new machine-readable combined.xml because --output was requested.

8. Create a controlled failure, rerun only the failure, then merge

python -m robot   --variable OBSERVED:broken   --outputdir results/rerun/original   suites/rerun.robot
# Expected non-zero status because Release Gate fails.

python -m robot   --rerunfailed results/rerun/original/output.xml   --variable OBSERVED:repaired   --outputdir results/rerun/retry   suites/rerun.robot

python -m robot.rebot   --merge   --output merged.xml   --outputdir results/rerun/merged   results/rerun/original/output.xml   results/rerun/retry/output.xml
python tools/inspect_output.py results/rerun/merged/merged.xml

The first run should contain one FAIL and one PASS. The rerun should execute only the previously failed test and PASS when OBSERVED is overridden to repaired. --merge is valid because both files describe the same top-level suite. The merged result should contain two tests with final PASS status, and the replaced test message in the merged result records that an earlier result was replaced.

Do not delete the original failure. The merged result is a corrected operational view, not a reason to destroy the first-failure artifact.

9. Generate a CI-friendly xUnit derivative

python -m robot.rebot   --xunit xunit.xml   --log NONE   --report NONE   --outputdir results/rerun/xunit   results/rerun/merged/merged.xml

Expected: results/rerun/xunit/xunit.xml. xUnit is useful for CI test dashboards, but it is a derivative summary. Keep the Robot result needed for Robot-aware diagnosis and post-processing.

10. Record artifact sizes and checksums

from pathlib import Path
import hashlib

for p in sorted(Path("results").rglob("*")):
    if p.is_file():
        digest = hashlib.sha256(p.read_bytes()).hexdigest()[:16]
        print(f"{p}  bytes={p.stat().st_size}  sha256={digest}")

Save as tools/manifest.py and run python tools/manifest.py. The checksum does not prove the run was correct, but it lets a later reviewer detect whether an artifact changed after capture.

11. Privacy/redaction check before publishing artifacts

from pathlib import Path

FORBIDDEN = ["password-FAKE_DO_NOT_USE", "token-FAKE_DO_NOT_USE", "PRIVATE_KEY"]
for p in Path("results").rglob("*"):
    if not p.is_file() or p.suffix.lower() not in {".xml", ".html", ".txt", ".json"}:
        continue
    text = p.read_text(encoding="utf-8", errors="ignore")
    hits = [value for value in FORBIDDEN if value in text]
    if hits:
        print("REVIEW", p, hits)

This is only a synthetic guardrail, not a complete DLP system. In production, define organization-specific patterns and access rules. If a real secret has already reached raw output, do not claim that generating a cleaner report makes the leak disappear.

12. Challenge: choose the transformation, do not copy a command

You receive three artifacts: two independent platform runs and one rerun of failed tests from only the Linux platform. Design the smallest evidence pipeline that (1) merges Linux original+rereun first, (2) leaves Windows independent, and (3) combines the corrected Linux root with Windows under one aggregate report. Explain why reversing those two operations would distort identity.

13. Verification and cleanup

  • Both independent runs have separate output/log/report files.
  • The rebuilt HTML is derived from the original XML without new execution.
  • Combined evidence contains two independent child roots.
  • The rerun result contains only the selected failed test.
  • The merged rerun result contains the full logical suite with the replacement result.
  • xUnit exists as a derivative, not the sole retained source.
  • Artifact sizes/checksums and privacy scan were recorded.

After review, remove only the disposable rf14-evidence directory. Preserve it if it is your evidence packet.

14. Knowledge check

Why does rebuilding log.html with Rebot not change the system under test?

Why must the original failed output remain after a successful rerun merge?

What structural change should you expect when combining run-a and run-b?

What should be different in the rerun output compared with the original release suite?

Why is a forbidden-marker scan not a complete redaction solution?

15. Summary and next step

You have executed the complete result pipeline: isolated runs, raw XML inspection, presentation regeneration, correct combining, correct rerun merging, xUnit generation, checksums, and privacy checks. Lesson 3 turns these mechanics into design policy for large CI estates.

Next lesson

Logs, Reports, output.xml, Rebot, and Result Post-Processing: Configuration, Design Patterns, and Trade-Offs

Continue with Logs, Reports, output.xml, Rebot, and Result Post-Processing: Configuration, Design Patterns, and Trade-Offs. It builds directly on the state, evidence, and operating assumptions established here, so carry those constraints forward rather than treating the next page as an isolated topic.

Further reading

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.