From aced8ec066726d15e62c9503cafb74bb95f60e55 Mon Sep 17 00:00:00 2001 From: Robert Jakobsson Date: Wed, 22 Jul 2026 23:29:33 +0200 Subject: [PATCH] test: fix macOS concurrent validation harness --- .github/workflows/ci.yml | 37 +++++++++++++++++- .../ConfirmationValidationTests.swift | 39 ++++++++++++++----- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 788d3e7..164aef9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,16 +49,49 @@ jobs: macos-swift-6: name: Swift 6 / macOS 15 runs-on: macos-15 + env: + DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer steps: - name: Check out repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: Require Swift 6 - run: swift --version | grep -E "Swift version 6\\." + - name: Report and require pinned toolchain + run: | + swift --version + xcodebuild -version + uname -a + sw_vers + test "$(uname -m)" = "arm64" + swift --version | grep -F "Apple Swift version 6.1.2" + xcodebuild -version | grep -F "Xcode 16.4" - name: Validate package manifest run: swift package describe - name: Build run: swift build - name: Test run: swift test + - name: Collect test crash diagnostics + if: failure() + run: | + destination="$RUNNER_TEMP/securetoolkit-test-diagnostics" + mkdir -p "$destination" + { + swift --version + xcodebuild -version + uname -a + sw_vers + } > "$destination/environment.txt" + if [ -d "$HOME/Library/Logs/DiagnosticReports" ]; then + find "$HOME/Library/Logs/DiagnosticReports" -maxdepth 1 -type f \ + \( -name 'SecureToolKit*' -o -name 'swift-test*' \) \ + -exec cp '{}' "$destination" \; + fi + find .build -type d -name '*.xcresult' -exec cp -R '{}' "$destination" \; + - name: Upload test crash diagnostics + if: failure() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: macos-test-diagnostics + path: ${{ runner.temp }}/securetoolkit-test-diagnostics + if-no-files-found: error - name: Lint formatting run: swift format lint --recursive --strict Package.swift Sources Tests diff --git a/Tests/SecureToolKitTesting/ConfirmationValidationTests.swift b/Tests/SecureToolKitTesting/ConfirmationValidationTests.swift index 0461f82..fe6761a 100644 --- a/Tests/SecureToolKitTesting/ConfirmationValidationTests.swift +++ b/Tests/SecureToolKitTesting/ConfirmationValidationTests.swift @@ -1,6 +1,13 @@ import SecureToolKitCore import XCTest +private enum ConcurrentConfirmationValidationOutcome: Sendable, Equatable { + case matched + case mismatched + case rejected(Phase7Failure) + case unexpectedFailure +} + final class ConfirmationValidationTests: XCTestCase { func `testP7POS008ExactArtifactAndIndependentContextProduceEligibility`() throws { let fixture = try Phase7ValidationFixture() @@ -177,21 +184,35 @@ final class ConfirmationValidationTests: XCTestCase { func `testConcurrentValidationIsPureAndDeterministic`() async throws { let fixture = try Phase7ValidationFixture() let expected = try fixture.eligibility() - let values = try await withThrowingTaskGroup( - of: ConfirmedIssuanceEligibility.self, - returning: [ConfirmedIssuanceEligibility].self + let challenge = fixture.artifactFixture.authorityFixture.challenge + let artifact = fixture.artifact + let context = fixture.context + let outcomes = await withTaskGroup( + of: ConcurrentConfirmationValidationOutcome.self, + returning: [ConcurrentConfirmationValidationOutcome].self ) { group in for _ in 0..<100 { - group.addTask { try fixture.eligibility() } + group.addTask { + do { + return try fixture.eligibility() == expected ? .matched : .mismatched + } catch let failure as Phase7Failure { + return .rejected(failure) + } catch { + return .unexpectedFailure + } + } } - var results: [ConfirmedIssuanceEligibility] = [] - for try await value in group { - results.append(value) + var results: [ConcurrentConfirmationValidationOutcome] = [] + for await outcome in group { + results.append(outcome) } return results } - XCTAssertEqual(values.count, 100) - XCTAssertTrue(values.allSatisfy { $0 == expected }) + XCTAssertEqual(outcomes.count, 100) + XCTAssertTrue(outcomes.allSatisfy { $0 == .matched }) + XCTAssertEqual(challenge, fixture.artifactFixture.authorityFixture.challenge) + XCTAssertEqual(artifact, fixture.artifact) + XCTAssertEqual(context, fixture.context) } func `testValidationDoesNotMutateInputsOrReserveReplayState`() throws {