Skip to content

feat(cli): add --inspect-mode flag for remote xcresult processing#10145

Merged
fortmarek merged 22 commits intomainfrom
feat/test-mode-flag
Apr 1, 2026
Merged

feat(cli): add --inspect-mode flag for remote xcresult processing#10145
fortmarek merged 22 commits intomainfrom
feat/test-mode-flag

Conversation

@fortmarek
Copy link
Copy Markdown
Member

@fortmarek fortmarek commented Apr 1, 2026

Summary

  • Adds --inspect-mode {local|remote} flag to tuist test, tuist xcodebuild test, and tuist xcodebuild test-without-building (env: TUIST_INSPECT_TEST_MODE, shared with tuist inspect test --mode)
  • Local mode (default): parses the xcresult locally and uploads the summary (existing behavior)
  • Remote mode: uploads the raw xcresult for server-side processing via xcode_processor, skipping expensive local parsing (action logs, attachments, crash reports, durations)
  • Embeds quarantined_tests.json into the xcresult directory before remote upload so the xcode_processor applies the same quarantine list the CLI had at test time
  • Adds lightweight parseTestStatuses method that only extracts test name/module/status for quarantine and selective testing checks, skipping full xcresult parsing
  • Extracts shared remote upload logic into UploadResultBundleService.uploadResultBundle (renamed from uploadResultBundleRemotely), old local upload renamed to uploadTestSummary
  • Moves onlyQuarantinedTestsFailed(testStatuses:quarantinedTests:) into TestQuarantineService for reuse across commands

CLI changes

  • TestRunCommand, XcodeBuildTestCommand, XcodeBuildTestWithoutBuildingCommand: new --inspect-mode option
  • TestService, XcodeBuildTestCommandService: mode-aware upload and quarantine handling
  • UploadResultBundleService: writes quarantine JSON into xcresult, handles remote upload
  • InspectTestCommandService: simplified to use shared uploadResultBundle
  • TestResultStatuses: new lightweight struct for quarantine/selective testing checks

xcode_processor changes

  • Reads quarantined_tests.json from extracted xcresult
  • Applies is_quarantined marking to parsed test cases matching the quarantine list

Feedback requested

Flag naming: The flag is currently --inspect-mode (shared env var TUIST_INSPECT_TEST_MODE with tuist inspect test --mode). However, on tuist test the name "inspect" may be confusing since "inspect" is a separate command. Alternatives to consider:

  • --processing-mode -- describes what it controls (where parsing happens)
  • --upload-mode -- describes the upload behavior
  • Keep --inspect-mode -- consistent with the tuist inspect test command

Would appreciate feedback on which name feels most intuitive for users.

Test plan

  • tuist test --inspect-mode local behaves identically to current behavior
  • tuist test --inspect-mode remote uploads raw xcresult, server processes it
  • TUIST_INSPECT_TEST_MODE=remote tuist test works via env var
  • tuist xcodebuild test --inspect-mode remote works
  • Quarantine suppression works in remote mode (only-quarantined failures don't fail the run)
  • quarantined_tests.json is written into xcresult and read by xcode_processor
  • Unit tests pass for TestResultStatuses, TestQuarantineService, UploadResultBundleService, XCResultProcessor

🤖 Generated with Claude Code

fortmarek and others added 9 commits April 1, 2026 12:32
…ocessing

Allow users to choose between local and remote xcresult processing when
running tuist test, matching the existing --mode flag on tuist inspect test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…vice

Move the remote xcresult upload logic from both InspectTestCommandService
and TestService into a shared uploadResultBundleRemotely method on
UploadResultBundleService, eliminating duplication.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ect test

Both tuist test --mode and tuist inspect test --mode now use the same
TUIST_INSPECT_TEST_MODE environment variable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use --inspect-mode instead of --mode to be more specific about what the
flag controls and avoid potential future naming conflicts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ResultBundleRemotely to uploadResultBundle

The old uploadResultBundle(testSummary:...) is now uploadTestSummary
and uploadResultBundleRemotely is now simply uploadResultBundle,
which better reflects what each method does.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In remote mode, the server handles parsing, so there's no need to parse
the xcresult locally before uploading.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tive testing

Adds TestResultStatuses, a minimal struct with just name/suite/module/status
per test case, and a parseTestStatuses method that skips action log parsing,
attachment extraction, failure details, and duration computation.

Used in the testSchemes error path where only pass/fail status per module
is needed for selective testing and quarantine checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When uploading in remote mode, the CLI writes quarantined_tests.json
into the xcresult directory before zipping and uploading. The
xcode_processor reads this file after extraction and applies
is_quarantined marking to the parsed test cases, ensuring remote
processing uses the same quarantine list the CLI had at test time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds the --inspect-mode flag to tuist xcodebuild test and
tuist xcodebuild test-without-building, with the same local/remote
behavior as tuist test. Moves ExpressibleByArgument conformance for
TestProcessingMode into TuistKit so all commands can use it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
method: test.method
)
}
let filePath = resultBundlePath.appending(component: "quarantined_tests.json")
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the xcode processor needs to know about which tests were quarantined when the client was running the tests. Instead of passing that through the API, decided to include that in the .xcresult, so the xcode processor has all information it needs in the .xcresult artifact itself.

fortmarek and others added 9 commits April 1, 2026 14:43
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… to TestQuarantineService

Share the quarantine check logic between tuist test and tuist xcodebuild
test by placing it in TestQuarantineService instead of a private method
on TestService.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…est remote mode

In remote mode testSummary is nil, so the quarantine suppression check
was never triggered. Now falls back to parseTestStatuses for the
quarantine decision, matching the tuist test behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e list

Let other file read errors and JSON parse errors propagate instead of
silently returning an empty list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TestResultStatuses: hasFailures, passingModuleNames, testCasesByModule
- TestQuarantineService: onlyQuarantinedTestsFailed with TestResultStatuses
- UploadResultBundleService: remote upload, quarantined_tests.json writing
- XCResultProcessor: quarantine marking from embedded JSON

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…gModuleNames a method

Move TestProcessingMode enum out of TestService.swift into its own file
to fix mid-file import and improve discoverability. Change
passingModuleNames from a computed property to a method to signal
non-trivial computation cost.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covered by XCResultServiceTests instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts:
#	xcode_processor/lib/xcode_processor/xcresult_processor.ex
#	xcode_processor/test/xcode_processor/xcresult_processor_test.exs
@fortmarek fortmarek changed the title feat(cli): add --mode flag to tuist test feat(cli): add --inspect-mode flag for remote xcresult processing Apr 1, 2026
@fortmarek fortmarek marked this pull request as ready for review April 1, 2026 14:05
@dosubot dosubot Bot added size:XL For issues that need several days to implement type:enhancement New feature or request labels Apr 1, 2026
@tuist
Copy link
Copy Markdown

tuist Bot commented Apr 1, 2026

🛠️ Tuist Run Report 🛠️

Previews 📦

App Commit Open on device
Tuist 1d24507a9

Tests 🧪

Scheme Status Cache hit rate Tests Skipped Ran Commit
TuistAcceptanceTests 0 % 174 0 174 1d24507a9
TuistApp 98 % 25 1 24 1d24507a9
TuistCacheEEUnitTests 96 % 132 1 131 1d24507a9
TuistUnitTests 98 % 2451 32 2419 1d24507a9

Flaky Tests ⚠️

  • TuistUnitTests: 2 flaky tests (View all)
Test case Module Suite
parseTestStatuses_returnsCorrectStatuses() TuistXCResultServiceTests XCResultServiceTests
parseTestStatuses_extractsModuleAndSuiteNames() TuistXCResultServiceTests XCResultServiceTests

Builds 🔨

Scheme Status Duration Commit
TuistAcceptanceTests 40.3s 1d24507a9
TuistApp 51.8s 1d24507a9
TuistCacheEEAcceptanceTests 2m 35s 1d24507a9
TuistCacheEEUnitTests 59.2s 774c6390d
TuistUnitTests 1m 20s 1d24507a9

Bundles 🧰

Bundle Commit Install size Download size
Tuist 1d24507a9
28.4 MB
19.5 MB
Δ +10 B (+0.00%)

@Option(name: .long, help: "The zero-based shard index to execute.")
var shardIndex: Int?

@Option(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about surfacing this somewhere in the docs. As a user, it's not obvious when I'd opt into server-side processing and what the implications of that decision are.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about surfacing this somewhere in the docs. As a user, it's not obvious when I'd opt into server-side processing and what the implications of that decision are.

Agree, this is coming along with switching the default as this will be a requirement for teams using self-hosting (unless they want to run the xcode processor node)

"xcresult_quarantine_test_#{:erlang.unique_integer([:positive])}"
)

File.mkdir_p!(temp_dir)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe for a Credo rule, but we should lean on using ExUnit's tmp_dir tag so they take care of the lifecycle for us.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Apr 1, 2026
fortmarek and others added 2 commits April 1, 2026 16:10
The test imports XCResultParser for TestResultStatuses but the module
wasn't declared as a test dependency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ceTests

The testSchemes error path now calls parseTestStatuses instead of parse,
so tests need the mock stubbed to avoid crashes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fortmarek and others added 2 commits April 1, 2026 16:57
# Conflicts:
#	cli/Sources/TuistKit/Services/TestService.swift
#	cli/Sources/TuistTestCommand/TestRunCommand.swift
Reset xcResultService mocks and use .any matcher to ensure
parseTestStatuses stub is matched regardless of path resolution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@fortmarek fortmarek merged commit f0a9a65 into main Apr 1, 2026
26 of 27 checks passed
@fortmarek fortmarek deleted the feat/test-mode-flag branch April 1, 2026 15:36
asmitbm pushed a commit that referenced this pull request Apr 2, 2026
…0145)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:XL For issues that need several days to implement type:enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants