Description
Description
Was really happy to find exit tests and had been adopting them over the last couple of days.
I have a project where adding just one more exit test causes it to reliably fail when SwiftPM(?) hoovers up the coverage data and calls out to llvm-profdata merge:
error: terminated(1): /usr/bin/llvm-profdata merge -sparse /pwd/.build/aarch64-unknown-linux-gnu/debug/codecov/XCTest13106936571747098128_0.profraw '/pwd/.build/aarch64-unknown-linux-gnu/debug/codecov/Swift Testing13106936571747098128_0.profraw' -o /pwd/.build/aarch64-unknown-linux-gnu/debug/codecov/default.profdata output:
warning: /pwd/.build/aarch64-unknown-linux-gnu/debug/codecov/Swift Testing13106936571747098128_0.profraw: excessively large counter value suggests corrupted profile data: 270326661043459020
error: no profile can be merged
This is occurring for me reliably in CI and locally on the 6.2 Linux snapshot toolchain.
My hypothesis here is that the path to the profdata file has been determined at the start of day, before Swift Testing forks for the exit test, and so they're all writing into the same file. Then, when SwiftPM comes along later to run llvm-profdata merge -sparse
it finds a corrupt file in some cases.
This is happening even when the suite is marked as .serialized
too.
I've included a reproducer below but seeing this in a real project too: https://github.com/swift-otel/swift-otel/actions/runs/16026382859/job/45215272013?pr=242#step:5:2523
Reproduction
Attaching as a zip file for convenience, but will include the entire repro contents here too.
repro-exit-tests-coverage-corruption.zip
Package.swift
// swift-tools-version: 6.2
import PackageDescription
let package = Package(
name: "repro-exit-tests-coverage-corruption",
dependencies: [
.package(url: "https://github.com/swift-server/async-http-client", from: "1.25.0"),
],
targets: [
.testTarget(
name: "ReproTests",
dependencies: [
.product(name: "AsyncHTTPClient", package: "async-http-client"),
.product(name: "Crypto", package: "swift-crypto"),
]),
]
)
Tests/ReproTests/ReproTests.swift
import AsyncHTTPClient
import Testing
@Suite(.serialized) struct Suite {
static func helper() async throws {
await #expect(processExitsWith: .success) {
let client = HTTPClient()
try await client.shutdown()
}
}
@Test func test0() async throws { try await Self.helper() }
@Test func test1() async throws { try await Self.helper() }
@Test func test2() async throws { try await Self.helper() }
@Test func test3() async throws { try await Self.helper() }
@Test func test4() async throws { try await Self.helper() }
@Test func test5() async throws { try await Self.helper() }
@Test func test6() async throws { try await Self.helper() }
@Test func test7() async throws { try await Self.helper() }
@Test func test8() async throws { try await Self.helper() }
@Test func test9() async throws { try await Self.helper() }
@Test func testA() async throws { try await Self.helper() }
@Test func testB() async throws { try await Self.helper() }
@Test func testC() async throws { try await Self.helper() }
@Test func testD() async throws { try await Self.helper() }
}
Repro
% swift test --enable-code-coverage
Build complete! (4.02s)
Test Suite 'All tests' started at 2025-07-03 16:09:37.012
Test Suite 'debug.xctest' started at 2025-07-03 16:09:37.012
Test Suite 'debug.xctest' passed at 2025-07-03 16:09:37.012
Executed 0 tests, with 0 failures (0 unexpected) in 0.0 (0.0) seconds
Test Suite 'All tests' passed at 2025-07-03 16:09:37.012
Executed 0 tests, with 0 failures (0 unexpected) in 0.0 (0.0) seconds
◇ Test run started.
↳ Testing Library Version: 6.2 (9ebfc4ebbb2840d)
↳ Target Platform: aarch64-unknown-linux-gnu
◇ Suite Suite started.
◇ Test test0() started.
✔ Test test0() passed after 0.023 seconds.
◇ Test test1() started.
✔ Test test1() passed after 0.022 seconds.
◇ Test test2() started.
✔ Test test2() passed after 0.022 seconds.
◇ Test test3() started.
✔ Test test3() passed after 0.022 seconds.
◇ Test test4() started.
✔ Test test4() passed after 0.022 seconds.
◇ Test test5() started.
✔ Test test5() passed after 0.021 seconds.
◇ Test test6() started.
✔ Test test6() passed after 0.021 seconds.
◇ Test test7() started.
✔ Test test7() passed after 0.023 seconds.
◇ Test test8() started.
✔ Test test8() passed after 0.022 seconds.
◇ Test test9() started.
✔ Test test9() passed after 0.023 seconds.
◇ Test testA() started.
✔ Test testA() passed after 0.023 seconds.
◇ Test testB() started.
✔ Test testB() passed after 0.022 seconds.
◇ Test testC() started.
✔ Test testC() passed after 0.022 seconds.
◇ Test testD() started.
✔ Test testD() passed after 0.022 seconds.
✔ Suite Suite passed after 0.319 seconds.
✔ Test run with 14 tests in 1 suite passed after 0.319 seconds.
error: terminated(1): /usr/bin/llvm-profdata merge -sparse /pwd/.build/aarch64-unknown-linux-gnu/debug/codecov/XCTest12847804116295998992_0.profraw '/pwd/.build/aarch64-unknown-linux-gnu/debug/codecov/Swift Testing12847804116295998992_0.profraw' -o /pwd/.build/aarch64-unknown-linux-gnu/debug/codecov/default.profdata output:
warning: /pwd/.build/aarch64-unknown-linux-gnu/debug/codecov/Swift Testing12847804116295998992_0.profraw: excessively large counter value suggests corrupted profile data: 344059485299246640
error: no profile can be merged
Expected behavior
- Coverage data is correctly handled with exit tests.
swift test
command exits zero since all tests pass.
Environment
↳ Testing Library Version: 6.2 (9ebfc4ebbb2840d)
% swift --version
Swift version 6.2-dev (LLVM ac66248b7c45109, Swift 92fd571d0786efe)
Target: aarch64-unknown-linux-gnu
Build config: +assertions
Additional information
No response