Skip to content

fix(cli): accept --swift-test-xunit-paths as the only report source - #1198

Merged
trunk-io[bot] merged 4 commits into
mainfrom
swift-test-xunit-paths-standalone
Sep 10, 2026
Merged

fix(cli): accept --swift-test-xunit-paths as the only report source#1198
trunk-io[bot] merged 4 commits into
mainfrom
swift-test-xunit-paths-standalone

Conversation

@dfrankland

@dfrankland dfrankland commented Sep 10, 2026

Copy link
Copy Markdown
Member

The bug

--swift-test-xunit-paths is absent from every required_unless_present_any list, so the usage its own help text documents does not parse:

$ trunk-analytics-cli upload --swift-test-xunit-paths=xunit-swift-testing.xml --org-url-slug=o --token=t
error: the following required arguments were not provided:
  --junit-paths <JUNIT_PATHS>
  --bazel-bep-path <BAZEL_BEP_PATH>
  --test-reports <TEST_REPORTS>

It is a real report source — coalesce_junit_path_wrappers handles it on its own branch and produces junit path wrappers from it — so it belongs in those lists alongside the other three. Adding it there is the whole fix.

Why the workaround is worse than the error

Callers have to supply a junit glob they do not want. The obvious value, the report they are already uploading, silently doubles every test: the two lists are appended rather than reconciled, so each test arrives once carrying the file a language server resolved and once carrying none.

Measured against 0.15.5-beta.1 on a 3-case suite, from a checkout with no Swift toolchain so nothing resolved:

Invocation Cases bundled
--junit-paths '<report glob>' 3
--swift-test-xunit-paths <report> + a glob matching nothing 3
both pointed at the real report 6

file feeds gen_info_id, so on a real checkout those six are two distinct tests rather than one test twice. Every consumer hitting this requirement has to pick between a glob they invented and a doubled upload.

The test could not have caught either problem

upload_bundle_using_swift_test_xunit inherits CommandBuilder's default --junit-paths ./*. That hid the missing requirement, and it also matches the two xunit files the test writes into the repo root — so the test has been exercising the double-upload path since it landed. Its assertions keyed a HashMap by test name, and a duplicate carries the same name, so the second copy overwrote the first and nothing looked wrong.

It now passes the reports through a new PathsState::SwiftTestXunitPaths variant and asserts the exact case count. PathsState is the report-source selector, so putting the flag there is the harness stating the same thing this change does — a fourth source alongside junit, BEP, and xcresult — rather than smuggling it past the selector through extra_args. Selecting it also suppresses the default glob, which is what makes this a genuine test of standalone usage, and the count gives the duplication somewhere to surface if it returns.

Not fixed here

If a caller passes an explicit --junit-paths glob that happens to match a file also named in --swift-test-xunit-paths, the double upload still happens. That is a live footgun for a monorepo using a broad glob like **/*.xml next to a Swift suite.

Fixing it properly means excluding the swift-handled files from the expanded junit set, which has to happen inside FileSetBuilder — the swift path uploads a rewritten temp copy, so the duplicate pair have different paths and a canonical-path dedup would not see them. That is a threaded exclusion parameter through build_file_sets, and a separate concern from this argument-parsing fix. Happy to follow up if you want it.

Verification

cargo check and the test suite have not been run locally — this environment has neither the pinned nightly-2026-06-11 toolchain nor the system OpenSSL openssl-sys needs, and I would rather say so than imply a green run. The change is four clap attribute lists, one enum variant with its match arm, and test assertions. CI is the gate; please don't merge on my word that it compiles.

The parsing behaviour above was verified by running the released 0.15.5-beta.1 binary directly.

🤖 Generated with Claude Code

`--swift-test-xunit-paths` is absent from every `required_unless_present_any`
list, so the usage its own help text documents

    trunk upload --swift-test-xunit-paths out-swift-testing.xml,out.xml

fails at argument parsing:

    error: the following required arguments were not provided:
      --junit-paths <JUNIT_PATHS>
      --bazel-bep-path <BAZEL_BEP_PATH>
      --test-reports <TEST_REPORTS>

It is a real report source -- `coalesce_junit_path_wrappers` handles it on its
own branch and produces junit path wrappers from it -- so it belongs in those
lists alongside the other three.

The workaround it forces is worse than the error. Callers have to supply a
junit glob they do not want, and the obvious value, the report they are already
uploading, silently doubles every test: the two lists are appended rather than
reconciled, so each test arrives once with the file a language server resolved
and once with none. Measured on a 3-case suite uploaded both ways, the bundle
holds 6 cases, and because `file` feeds `gen_info_id` those are two distinct
tests rather than one test twice.

`upload_bundle_using_swift_test_xunit` could not catch either problem. It
inherits `CommandBuilder`'s default `--junit-paths ./*`, which both hid the
missing requirement and matched the two xunit files it writes into the repo
root -- so the test has been exercising the double-upload path all along. Its
assertions keyed a HashMap by test name, and a duplicate carries the same name,
so the second copy overwrote the first and nothing looked wrong.

So the test now passes no paths argument at all, via a new `PathsState::NoPaths`
that is distinct from a `None` paths_state, and asserts the exact case count.
That makes it a real test of standalone usage, and gives the duplication
somewhere to show up if it comes back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@trunk-io

trunk-io Bot commented Sep 10, 2026

Copy link
Copy Markdown

😎 Merged successfully - details.

dfrankland and others added 2 commits September 10, 2026 18:47
`PathsState` is the report-source selector, and this change's whole claim is
that --swift-test-xunit-paths is a fourth source alongside junit, BEP, and
xcresult. A `NoPaths` variant said the opposite: that the source was something
smuggled past the selector through extra_args, with a second flag to suppress
the default. Nothing else wanted "no source at all", so the variant existed
only to work around its own framing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It contrasted PathsState against extra_args, which the code no longer does, so
it documented the shape of an earlier edit rather than the test. The assertion
keeps its own comment, which says why the count is exact and what six would
mean -- neither readable off the code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.85%. Comparing base (8396875) to head (63fcdc8).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1198      +/-   ##
==========================================
+ Coverage   83.63%   83.85%   +0.22%     
==========================================
  Files          74       74              
  Lines       17573    17573              
==========================================
+ Hits        14697    14736      +39     
+ Misses       2876     2837      -39     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@trunk-staging-io

trunk-staging-io Bot commented Sep 10, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

Failed Test Failure Summary Logs
pending_quarantine_test should be quarantined when run with variant A test marked as pending was expected to fail but unexpectedly passed. Logs ↗︎
variant_quarantine_test should be quarantined when run with variant A test expected the sum of 2 + 2 to be 5, but it was actually 4, indicating a failing assertion. Logs ↗︎

View Full Report ↗︎Docs

@trunk-io

trunk-io Bot commented Sep 10, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

`Limits::default()` sizes one resolution on a machine doing nothing else. A
test binary starts a `sourcekit-lsp` per test, all at once, under coverage
instrumentation and alongside the rest of the workspace's suite -- and a server
that has not answered `initialize` inside `request_timeout` is abandoned rather
than waited on, so every test in that process resolves to nothing.

That is what the x86_64 musl runner hit: three of these failed at exactly
30.016s, the default timeout, while the four that ran once the machine
quietened took 10s each. The same tests take 0.4s on aarch64 once warm.

Only the clock moves; every value the tests assert on is still resolved the way
the defaults resolve it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@trunk-io
trunk-io Bot merged commit ccaad7f into main Sep 10, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants