From c2649295c34f9c60ffa39818dcefea2dd06925b0 Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Mon, 5 Oct 2026 19:31:43 -0400 Subject: [PATCH] fix(test): count concurrent fetches through a Sendable box coalescesConcurrentFetches captured a local `Mutex` in `group.addTask` closures. `Mutex` is ~Copyable, so the closures capture it by reference and Swift 6 region-based isolation rejects the `sending` closure, which broke the test build in the "App vectors (build)" job. Hold the counter in a Sendable class instead. --- .../FlipcashCoreTests/UploadPolicyTests.swift | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/FlipcashCore/Tests/FlipcashCoreTests/UploadPolicyTests.swift b/FlipcashCore/Tests/FlipcashCoreTests/UploadPolicyTests.swift index 849f69b06..84c803cb6 100644 --- a/FlipcashCore/Tests/FlipcashCoreTests/UploadPolicyTests.swift +++ b/FlipcashCore/Tests/FlipcashCoreTests/UploadPolicyTests.swift @@ -9,6 +9,15 @@ import Testing import FlipcashAPI @testable import FlipcashCore +/// `Mutex` is `~Copyable`, so a local one captured by `addTask` closures is a +/// by-reference capture that region-based isolation rejects. A `Sendable` box +/// gives the closures a value they can share. +private final class FetchCounter: Sendable { + private let count = Mutex(0) + var value: Int { count.withLock { $0 } } + func increment() { count.withLock { $0 += 1 } } +} + @Suite("Upload policy") struct UploadPolicyTests { @@ -97,14 +106,14 @@ struct UploadPolicyTests { @Test("Concurrent requests share one fetch") func coalescesConcurrentFetches() async throws { let cache = UploadPolicyCache() - let fetches = Mutex(0) + let fetches = FetchCounter() let owner = try Self.owner() let versions = try await withThrowingTaskGroup(of: String.self) { group in for _ in 0..<5 { group.addTask { try await cache.policy(for: owner) { - fetches.withLock { $0 += 1 } + fetches.increment() try await Task.sleep(for: .milliseconds(20)) return Self.policy("v1") }.version @@ -114,7 +123,7 @@ struct UploadPolicyTests { } #expect(versions == Array(repeating: "v1", count: 5)) - #expect(fetches.withLock { $0 } == 1) + #expect(fetches.value == 1) } @Test("A different version echoed by the server forces a re-fetch")