Skip to content

XCResultToolClient and new-format schema structs (#391) - #441

Merged
tylervick merged 2 commits into
mainfrom
tylervick/xcresulttool-client-391
Aug 12, 2026
Merged

tylervick merged 2 commits into
mainfrom
tylervick/xcresulttool-client-391

Conversation

@tylervick

Copy link
Copy Markdown
Member

Part of #391 (milestone 4.0). Implements migration plan Tasks 6 and 7: the modern-backend subprocess client and the Codable mirrors of the new xcresulttool format. Deliberately independent of the ParsedResult port PR — purely additive, no existing files under Classes/Models/ touched, no references to ParsedResult/ResultReader.

What's here

  • XCResultToolClient (ResultReading/Modern/XCResultToolClient.swift): runs xcrun xcresulttool, pinning --schema-version 0.1.0 on every invocation so an Apple schema revision fails loudly instead of silently mis-decoding. Includes the XCResultToolInvoking seam (Task 8 injects a failing client through it), json<T: Decodable>, run, and legacyCapability — a three-state probe (available/unavailable/unknown) parsing xcresulttool version for the legacy-commands parenthetical.
  • TestResultsSchema (ResultReading/Modern/TestResultsSchema.swift): Decodable structs for get test-results summary|tests|activities, the export attachments manifest, and get log. Fields optional throughout — xcresulttool omits keys rather than emitting nulls.
  • Tests: XCResultToolClientTests (3 tests, real SanityResults.xcresult fixture) and TestResultsSchemaTests (3 tests), including decoding Arguments/Test Value nodes — added from the published schema (get test-results tests --schema), since no fixture produces them yet (design doc answer 6).

Deviations from the plan, amended in the plan doc

  • LegacyCapability is defined in XCResultToolClient.swift, not first in Task 11's ResultBackend.swift: Task 6 cannot compile without it and the tasks land as separate PRs. Task 11's snippet now says to reuse it.
  • TestNode's nodeType doc lists the full published TestNodeType enum (verified against schema 0.1.0 on xcresulttool 24514), and Task 7 gained the third test above.

Also

  • Narrowed the three fixture .gitignore patterns (TestResults* → TestResults*.xcresult): the bare pattern was silently swallowing TestResultsSchema.swift and its test file. Generated fixture bundles remain ignored.

Verification

  • swift test: 39 tests, 0 failures (2 pre-existing skips), on Xcode 26.2 / xcresulttool 24514, schema 0.1.0, fixtures freshly generated via ./prepareTestResults.sh.
  • Verified --schema-version is accepted by get test-results * and export attachments, and that nodeIdentifierURL appears in observed output despite being absent from the published TestNode schema.

🤖 Generated with Claude Code

tylervick and others added 2 commits August 12, 2026 14:57
Narrows the fixture .gitignore patterns to *.xcresult: the bare
TestResults* pattern was silently swallowing TestResultsSchema.swift and
its tests.

Amends the plan doc where implementation corrected it: LegacyCapability
lives in XCResultToolClient.swift (Task 6 cannot compile without it and
lands before Task 11), and TestNode's nodeType doc now lists the full
published TestNodeType enum, with an added test decoding Arguments/Test
Value nodes from schema-derived JSON.

Co-Authored-By: Claude Fable 5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@tylervick, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 92a57221-90d4-4900-bdb0-105bfb758407

📥 Commits

Reviewing files that changed from the base of the PR and between 013ae6c and edf1aa4.

📒 Files selected for processing (6)
  • .gitignore
  • Sources/XCTestHTMLReportCore/Classes/ResultReading/Modern/TestResultsSchema.swift
  • Sources/XCTestHTMLReportCore/Classes/ResultReading/Modern/XCResultToolClient.swift
  • Tests/XCTestHTMLReportTests/TestResultsSchemaTests.swift
  • Tests/XCTestHTMLReportTests/XCResultToolClientTests.swift
  • docs/superpowers/plans/2026-08-10-xcresulttool-legacy-migration.md

Comment @coderabbitai help to get the list of available commands.

@tylervick
tylervick merged commit e2a3e9c into main Aug 12, 2026
8 checks passed
@tylervick
tylervick deleted the tylervick/xcresulttool-client-391 branch August 12, 2026 22:24
tylervick added a commit that referenced this pull request Aug 13, 2026
…t typing (#391, Tasks 8–10) (#447)

* test: pin extension-based attachment typing and map public.data on the legacy backend

Task 10 of the migration plan. The AttachmentType(filenameExtension:)
initializer itself landed with #443; what was missing is the cross-backend
agreement pin and one table entry: LegacyResultReader's UTI fallback omitted
public.data, so an extensionless public.data attachment degraded
.data -> .unknown on the legacy backend only.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix: surface XCResultToolError diagnoses through LocalizedError

Carried forward from the #441 review: consumers now exist (faults and
warnings format these errors), and localizedDescription printed the generic
NSError boilerplate rather than the command, exit status, and stderr.

Co-Authored-By: Claude Fable 5 <[email protected]>

* feat: export attachments through the modern xcresulttool path

Task 9 of the migration plan. One `export attachments` call per bundle plus
the manifest.json join (activity attachment uuid <-> basename of
exportedFileName) replaces the legacy per-payload export and its lock table;
only the one-shot export is guarded. Run logs come from `get log --type
action` and render from the structured messages, since the new format has
no emittedOutput.

Committed ahead of Task 8 so each commit builds: the reader's payloadStore
property references this type.

Co-Authored-By: Claude Fable 5 <[email protected]>

* feat: add ModernResultReader for the new xcresulttool format

Task 8 of the migration plan. Parity rules carried exactly: statuses map
into the neutral enum (Expected Failure still flattens to unknown
downstream); multi-repetition status derives from the Repetition children
and ignores the parent Test Case's own result (RetryResults reports Passed
there for a Failed-then-Passed test); the tree renders flat, without the
legacy wrapper groups.

Failure text joins the two documents rather than choosing one: measured on
Xcode 26.2, every assertion failure appears both as a failure-flagged
activity row (positioned and timestamped, but stripped of file:line) and as
a Failure Message node (file:line kept, no timestamp). Each message retitles
the first unclaimed failure activity whose title is its exact suffix, in
document order, nested rows included; unmatched messages (skip reasons,
expected-failure notes) append. The plan's original guard kept the activity
title instead, which shipped strictly less information than the spec's
failureTitlePrefix entry documents — coordinator-approved correction,
amended in the plan.

A failed activities query records the new .missingActivities fault; fields
the format structurally lacks never fault, pinned by a full modern-path
render of all three fixtures asserting zero faults.

The sample app gains a parameterized @test so Arguments nodes stop being
fixture-unexercised; its argument sets merge into one rendered row, moving
the TestResults header total from 17 to 18.

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs: amend plan and spec for Tasks 8-10 as executed

The load-bearing correction: failure text is a join of the two documents,
not a choice between them. Measured on Xcode 26.2, the activities document
carries every assertion failure as a positioned, timestamped, sometimes
nested failure row missing only the file:line prefix; the plan's original
anti-double-count guard would have dropped the prefixed Failure Message and
shipped less than the spec's failureTitlePrefix entry documents.
Coordinator-approved retitle-join recorded in both documents.

Mechanical notes: Task 9 commits before Task 8 (type dependency), TestRun is
nested in TestActivities, the AttachmentType initializer shipped failable in
443, and regenerating fixtures after the sample-app change collapses the
plan's 6-of-7 intermediate state.

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
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.

1 participant