xcresult
A Python library and CLI tool for reading Xcode's .xcresult bundles. It uses
xcresulttool's modern JSON reports and generated Python dataclasses to inspect
build issues, extract test results, export attachments, and generate JUnit XML.
Neither generation nor runtime access uses the deprecated legacy object API.
Installation
pip install xcresult
Requires Python 3.10+ and Xcode 27's xcresulttool (schema version 0.4.0).
Select the Xcode installation using xcode-select or DEVELOPER_DIR. Older
Xcode schema versions are not currently a supported compatibility target.
Library Usage
import xcresult
bundle = xcresult.Xcresults("/path/to/MyApp.xcresult")
# Check build issues
for error in bundle.build_results.errors:
print(error.message, error.sourceURL)
# Check availability before requesting test reports from build-only bundles
if bundle.content_availability.hasTestResults:
print(bundle.test_summary.failedTests)
for node in bundle.tests.testNodes:
print(node.name, node.result)
# Read a specific test's individual runs or activity trees
details = bundle.test_details("MyTests/testExample()")
activities = bundle.test_activities("MyTests/testExample()")
# Export test attachments (screenshots, etc.)
manifest = bundle.export_test_attachments("/path/to/output/")
# Write JUnit XML
bundle.write_junit("/path/to/results.junit")
build_results, content_availability, test_summary, and tests are loaded
lazily and cached for the lifetime of the Xcresults instance. Test identifiers
can be identifier strings or identifier URLs; prefer URLs when targets contain
identically named tests. Apple field names retain their camelCase spelling.
Attachments use xcresulttool's export layout: files and manifest.json in the
output directory. The returned manifest identifies each test's files through
attachment.exportedFileName. To export only one test or suite, pass
test_id="MyTests/testExample()".
Use a fresh output directory for each export: xcresulttool refuses to overwrite
an existing manifest.json. The JUnit export_attachments_path option performs
its own export, so it also needs a directory without an existing manifest.
JUnit options
bundle.write_junit(
"/path/to/results.junit",
export_attachments_path="/path/to/attachments",
test_class_prefix="MyApp",
test_class_suffix="UI",
collapse_retries=True,
test_filter=lambda test: not test.name.startswith("disabled"),
)
By default, each individual run becomes a testcase, rather than reporting only
the aggregate result. Retry collapsing prefers a passing attempt and keeps
different devices, configurations, and arguments separate. Expected failures
are treated as successful outcomes; skipped tests become <skipped> elements.
Unknown outcomes are not treated as passes. The filter receives a modern
TestNode; excluded tests do not contribute to XML counts or leave empty suites.
Suite names follow the modern bundle/suite hierarchy, with configuration and
device recorded as properties. Attachment links come from the export manifest.
Build-only bundles produce an empty JUnit report.
CLI Usage
The package installs an xcresult command with three subcommands:
Export test attachments
xcresult -b /path/to/MyApp.xcresult export -o /path/to/output/
Generate JUnit XML
xcresult -b /path/to/MyApp.xcresult junit -o /path/to/results.junit
Optionally export attachments alongside the JUnit file:
xcresult -b /path/to/MyApp.xcresult junit -o /path/to/results.junit --export-attachments-path /path/to/attachments/
Collapse retry attempts:
xcresult -b /path/to/MyApp.xcresult junit -o /path/to/results.junit --collapse-retries
Check for issues
xcresult -b /path/to/MyApp.xcresult check-issues
Filter by issue type with --issue-types:
xcresult -b /path/to/MyApp.xcresult check-issues --issue-types error warning
Available issue types: error, warning, analyzer-warning, test-failure, test-warning.
Returns exit code 1 if any matching issues are found.
Migrating from the legacy API
This is a breaking library API change. The command-line subcommands remain available, but the underlying models, attachment layout, and JUnit suite hierarchy now follow the modern reports.
| Legacy API | Modern API |
|---|---|
actions_invocation_record.issues |
build_results.errors, .warnings, .analyzerWarnings, plus test_summary.testFailures and .runtimeWarnings |
actions_invocation_record.actions and testsRef traversal |
tests.testNodes and test_details(test_id).testRuns |
get(object_id) and summary references |
test_details(test_id) or test_activities(test_id) |
export_attachment(payload_id, type, path) |
export_test_attachments(directory, test_id=...) and its returned manifest |
ActionTestMetadata in test filters |
TestNode (name, nodeIdentifier, nodeIdentifierURL, result) |
deserialize(legacy_typed_json) |
Tests.from_dict(json_data) or deserialize(json_data, Tests) |
Legacy Action*, Reference, and other internal object-graph models are no
longer exported. Modern models are dataclasses with required constructor
arguments and optional fields defaulting to None. Missing required data and
invalid field types raise errors; unrecognized properties are logged and
ignored to accommodate additive tool changes. Decoding does not mutate input.
Test results use modern values such as Passed, Failed, Skipped, and
Expected Failure, rather than legacy testStatus values. Timestamp fields
are UNIX seconds, not datetime objects. Models use normal mutable dataclass
equality and are not hashable.
Development
Dependencies are managed with Poetry. Regenerate the models from the selected Xcode installation's endpoint schemas, then format them:
python -m generator
python -m black --line-length 100 xcresult/model.py
The generator reads the schemas for test summaries, test trees, test details,
activities, build results, content availability, and attachment manifests. It
reconciles shared types and keeps distinct activity/export attachment models.
Two known schema cardinality errors are corrected to match report output:
Summary.devicesAndConfigurations and Activities.testRuns are arrays.
Generated models are checked in; using the package does not run the generator.
python -m pytest tests
XCRESULT_RUN_INTEGRATION=1 python -m pytest tests/test_modern_integration.py
Tests cover ordinary JSON decoding, command routing, issue reporting, retries, filtering, report counts, and manifest-based attachment links. Xcode integration checks use disposable fixture copies because xcresulttool may update a bundle's database while reading it. The live test-report, activity, and export checks are opt-in and require a working local Xcode test-report cache; command failures are surfaced rather than silently falling back to the legacy API.
License
MIT
Metadata
Release files for xcresult 27.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xcresult-27.0.0.tar.gz | 18.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xcresult-27.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.4 kB
Release files / xcresult-27.0.0.tar.gz
| Download URL | xcresult-27.0.0.tar.gz |
|---|---|
| Size | 18.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8cfc429cc5b8bc97cbca4647ac9096610053e856d0810801ae32242adfc4bbff
|
|
BLAKE2b-256 checksum How to use checksums |
2d2e59c1b3b22560bd5f18be8ba0db0651bd80089ff877ae88ca70eb081b7408
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.3 CPython/3.12.14 Darwin/25.6.0
|
Release files / xcresult-27.0.0-py3-none-any.whl
| Download URL | xcresult-27.0.0-py3-none-any.whl |
|---|---|
| Size | 20.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ececaa4372dedfe3a71f90d88475341e8f28e42ec8cd039ed2053002825a6f03
|
|
BLAKE2b-256 checksum How to use checksums |
767821a5a6e0ef94bf385403b2b1d15c30e20934134930a55fb57f03ae65ed6f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.3 CPython/3.12.14 Darwin/25.6.0
|