Hive Hive
Sign in

fix(cli): make suite-granularity sharding select Swift Testing suites

GitHub issue · Closed

Metadata
Source
tuist/tuist #11329
Updated
Jun 24, 2026
Domains
CLI
Details

What changed

Suite-granularity test sharding wrote each shard’s per-suite selection into the test products bundle’s .xctestrun as OnlyTestIdentifiers, then ran xcodebuild test-without-building -testProductsPath <bundle>. This now derives -only-testing identifiers (Target/Suite for suite granularity, bare Target for module granularity) and passes them to xcodebuild instead. ShardService no longer rewrites the bundle’s .xctestrun at all; both shard consumers (TestService.runShard and XcodeBuildTestCommandService) forward the identifiers.

  • ShardService: Shard now carries testIdentifiers + testProductsAreTemporary; filterXCTestRun and the xctestrun mutation are removed. Downloaded/extracted products are left untouched, and only Tuist-owned temp products are cleaned up.
  • TestService.runShard / XcodeBuildTestCommandService: emit -only-testing for shard.testIdentifiers; drop the -xctestrun switching.

Net -479 / +237 — mostly deleting the dead filtering path and its unit tests.

Why

OnlyTestIdentifiers in an .xctestrun filters XCTest only; it does not filter Swift Testing tests. The acceptance suites are entirely Swift Testing (import Testing + struct … { @Test … }), so when a shard’s products bundle downloaded successfully, test-without-building launched the test host, matched zero Swift Testing tests, and exited 0 — reporting ✔ The project tests ran successfully while running nothing.

This was systematic: every successful-download shard ran 0 tests; the only variation across CI runs was which shard’s Tigris bundle.zip download timed out (a separate, untouched bug). The result is that no sharded acceptance run had actually been executing the suite.

-only-testing is the documented mechanism that filters both XCTest and Swift Testing at the suite (Target/Suite, 2-component) level; the known Swift Testing -only-testing parenthesis gotcha is function-level only (3rd component), which sharding does not use.

Why this approach over the obvious alternative

Keeping the .xctestrun rewrite and trying to make OnlyTestIdentifiers select Swift Testing suites isn’t viable: OnlyTestIdentifiers can only ever exclude Swift Testing (any value you set excludes it, because no Swift Testing test matches an XCTest-style identifier) — it can never select one. Swift Testing selection isn’t a static xctestrun artifact at all; it’s a runtime test-session concept that -only-testing drives.

This was verified empirically against a minimal Swift Testing bundle:

  • Six OnlyTestIdentifiers formats (Suite, Suite/, Suite/test, Suite/test(), Suite/test()(), Target/Suite) all selected 0 Swift Testing tests; the same key with an XCTest class name runs it fine.
  • -only-testing writes no modified xctestrun — so it isn’t “OnlyTestIdentifiers with a better format.” Inspecting the xctest host under -only-testing shows an empty XCTestConfigurationFilePath and no --filter/SWT_*/selected-IDs arg or env var; the chosen tests are negotiated over the runtime test session (XPC). No xctestrun key (OnlyTestIdentifiers, CommandLineArguments, EnvironmentVariables, …) can carry it.

So -only-testing arguments are the only supported selection path that works for both frameworks. They compose with -testProductsPath, and dropping the rewrite also removes the per-shard .xctestrun files (multiple shards sharing one local products dir now just read it).

Tests

  • Unit (ShardServiceTests): rewritten to spec the new derivation — suite granularity → ["AppTests/LoginTests", …], module granularity → ["AppTests", …] — and to assert the bundle’s .xctestrun is left unmodified.
  • Wiring (TestServiceTests): asserts -only-testing AppTests reaches xcodebuild.
  • E2E (TestAcceptanceTests): new suite-granularity shard test over a fixture that now mixes one XCTest class with two Swift Testing suites (MacFrameworkSwiftTestingTests). It runs both shards, parses each result bundle via XCResultService, and asserts the Swift Testing tests actually executed across the shards. This verifies the fixed behavior end-to-end (green path). Caveat: because it is itself a Swift Testing test, it cannot reliably go red on a regression of the outer suite-granularity sharding — such a regression would silently skip it along with every other Swift Testing test. Robustly catching that regression class is the zero-executed guard’s job (see follow-ups).

Validation

The exact mechanism the fix turns on was reproduced directly with xcodebuild test-without-building against a minimal Swift Testing bundle (one XCTest class + two Swift Testing suites, mirroring the fixture):

Selecting Swift Testing suite GreetingTests via… Result
Control (no filter) all 3 suites run
OLDOnlyTestIdentifiers=["GreetingTests"] in the xctestrun (what filterXCTestRun wrote) Executed 0 tests … TEST EXECUTE SUCCEEDED — the false-green bug
NEW-only-testing LibTests/GreetingTests (what this PR emits) greeting_isStable() runs and passes
XCTest contrast — OnlyTestIdentifiers=["LegacyTests"] (an XCTest class) testHello runs — proving the failure is Swift-Testing-specific

That is the before/after in isolation: OnlyTestIdentifiers on a Swift Testing suite selects nothing (0 tests, silent success); -only-testing on the same suite runs it; and OnlyTestIdentifiers still works for XCTest, so the breakage is Swift-Testing-specific.

CI (green): the full compile chain (Build, Build Acceptance Tests, SwiftPM Build, Linux Build/Unit Tests, Lint), Unit Tests, and both Acceptance Tests (0/1) shards pass — i.e. the new unit + e2e tests and the whole suite-granularity acceptance run are green with the fix.

Local Tuist build/run is blocked in some sandboxes (the package graph hits the FileSystem product-name conflict under swift build, and tuist generate can fail on a BasicContainers dependency error), so the table above and CI are the validators.

Out of scope / follow-ups

  • Zero-executed guard (recommended). A production check that fails when a shard executes 0 tests against a non-empty selection would have caught this at the source, and is the robust guard for this regression class — since the e2e test, being Swift Testing, is itself skipped by the bug rather than failing. Not included here.
  • The Tigris bundle.zip download timeouts that intermittently hard-fail a shard are a separate, pre-existing issue.

How to test locally

On a machine where the Tuist project builds:

  1. tuist test MacFrameworkTests --build-only --shard-total 2 --shard-granularity suite --shard-skip-upload -- -testProductsPath /tmp/p.xctestproducts -destination platform=macOS (in examples/xcode/generated_ios_app_with_tests).
  2. For each shard: tuist test MacFrameworkTests --without-building --shard-index N --result-bundle-path /tmp/shard-N.xcresult -- -testProductsPath /tmp/p.xctestproducts -destination platform=macOS.
  3. Confirm the MacFrameworkGreetingTests / MacFrameworkValueTests Swift Testing suites actually run (result bundle shows greeting_isStable / greeting_isNotEmpty), rather than the run completing instantly with zero tests.

🤖 Generated with Claude Code

Comments
T
tuist[bot] Jun 17, 2026

🛠️ Tuist Run Report 🛠️

Previews 📦
App Commit Open on device
Tuist 9c59f020d
Tests 🧪
Scheme Status Cache hit rate Tests Skipped Ran Commit
TuistAcceptanceTests 0 % 0 0 0 c4316d796
TuistUnitTests 97 % 661 38 623 70ac11a09
Builds 🔨
Scheme Status Duration Commit
TuistAcceptanceTests 52.0s c4316d796
TuistApp 35.5s 9c59f020d
TuistUnitTests 58.0s 70ac11a09
Bundles 🧰
Bundle Commit Install size Download size
Tuist 9c59f020d 27.6 MBΔ +2.9 MB (+11.64%) 18.7 MBΔ +1.5 MB (+8.67%)
TA
tuist-atlas[bot] Jun 18, 2026

The fix for suite-granularity sharding to properly select Swift Testing suites is now available in 4.201.0-canary.8. Update to this version to use -only-testing identifiers that work with both XCTest and Swift Testing.

TA
tuist-atlas[bot] Jun 19, 2026

The fix for suite-granularity sharding to select Swift Testing suites is now available in 4.201.0-canary.9. Sharding now uses -only-testing identifiers that work for both XCTest and Swift Testing, instead of OnlyTestIdentifiers in xctestrun files which only filtered XCTest. This ensures Swift Testing suites actually execute when running sharded tests. Update to 4.201.0-canary.9 to get this fix.

TA
tuist-atlas[bot] Jun 19, 2026

The fix for suite-granularity sharding to select Swift Testing suites is now available in version 4.201.0-canary.13. Update to that version to get proper Swift Testing suite selection when using test sharding.

TA
tuist-atlas[bot] Jun 19, 2026

The fix for suite-granularity sharding to select Swift Testing suites is now available in 4.201.0-canary.12. Suite-granularity sharding now uses -only-testing identifiers instead of OnlyTestIdentifiers in the xctestrun, which correctly selects both XCTest and Swift Testing suites. Update to this version to get the fix.

TA
tuist-atlas[bot] Jun 19, 2026

The fix for suite-granularity sharding to select Swift Testing suites is now available in 4.201.0-canary.11. Sharding now derives -only-testing identifiers and passes them to xcodebuild, rather than using OnlyTestIdentifiers in the xctestrun which did not filter Swift Testing tests.

Update to 4.201.0-canary.11 to use this fix.

TA
tuist-atlas[bot] Jun 19, 2026

This is now available in 4.201.0-canary.10. Update to pick up the fix that makes suite-granularity sharding properly select Swift Testing suites.

TA
tuist-atlas[bot] Jun 20, 2026

The fix to make suite-granularity sharding select Swift Testing suites is now available in 4.201.0-canary.18. Update to this version to get it.

TA
tuist-atlas[bot] Jun 20, 2026

The fix for suite-granularity sharding with Swift Testing suites is now available in 4.201.0-canary.17. Update to this version for proper Swift Testing execution in sharded runs.

TA
tuist-atlas[bot] Jun 20, 2026

The fix for suite-granularity sharding to correctly select Swift Testing suites is now available in 4.201.0-canary.16. Shards now emit -only-testing identifiers instead of relying on xctestrun filtering, ensuring Swift Testing tests actually execute. Update to this version.

TA
tuist-atlas[bot] Jun 20, 2026

The fix for making suite-granularity sharding select Swift Testing suites is now available in 4.201.0-canary.15. Suite-granularity test sharding now uses -only-testing identifiers to properly filter Swift Testing tests. Update to this version to use the fix.

TA
tuist-atlas[bot] Jun 20, 2026

The fix for suite-granularity sharding to select Swift Testing suites is now available in version 4.201.0-canary.14. Update to this version to ensure Swift Testing tests are properly included in sharded test runs.

TA
tuist-atlas[bot] Jun 21, 2026

Suite-granularity sharding for Swift Testing suites is now available in 4.201.0-canary.19. This makes suite-level test sharding correctly select Swift Testing tests using -only-testing instead of OnlyTestIdentifiers in the xctestrun file. Update to 4.201.0-canary.19 to get this fix.