From a3f4b66f07bcfd8d7fe224e3830c0d90b728fd88 Mon Sep 17 00:00:00 2001 From: Alessio Nossa Date: Tue, 9 Dec 2025 19:07:17 +0100 Subject: [PATCH 1/2] Introduce test_share_with_no_buffering --- Tests/AsyncAlgorithmsTests/TestShare.swift | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Tests/AsyncAlgorithmsTests/TestShare.swift b/Tests/AsyncAlgorithmsTests/TestShare.swift index ac817536..14aa273c 100644 --- a/Tests/AsyncAlgorithmsTests/TestShare.swift +++ b/Tests/AsyncAlgorithmsTests/TestShare.swift @@ -129,6 +129,42 @@ final class TestShare: XCTestCase { XCTAssertEqual(results1.withLock { $0 }.sorted(), [1, 2, 3, 4, 5]) XCTAssertEqual(results2.withLock { $0 }.sorted(), [1, 2, 3, 4, 5]) } + + func test_share_with_no_buffering() async { + let shared = [1, 2, 3, 4, 5].async.share(bufferingPolicy: .bounded(0)) + + let results1 = Mutex([Int]()) + let results2 = Mutex([Int]()) + + let consumer1 = Task { + // Consumer 1 reads first element + for await value in shared { + results1.withLock { $0.append(value) } + // Delay to allow consumer 2 to get ahead + try? await Task.sleep(for: .milliseconds(10)) + } + } + + let consumer2 = Task { + // Consumer 2 reads all elements quickly + for await value in shared { + results2.withLock { $0.append(value) } + } + } + + await consumer1.value + await consumer2.value + + // Both consumers should receive all elements + XCTAssertEqual(results1.withLock { $0 }.sorted(), [1, 2, 3, 4, 5]) + XCTAssertEqual(results2.withLock { $0 }.sorted(), [1, 2, 3, 4, 5]) + } + + func test_share_with_no_buffering_multiple() async { + for _ in 0..<10 { + await test_share_with_no_buffering() + } + } func test_share_with_unbounded_buffering() async { let source = [1, 2, 3, 4, 5] From a524bda0e23bd9b60e367e24ce76f9162d95e690 Mon Sep 17 00:00:00 2001 From: Alessio Nossa Date: Tue, 9 Dec 2025 19:34:38 +0100 Subject: [PATCH 2/2] Update test_share_with_no_buffering implementation --- Tests/AsyncAlgorithmsTests/TestShare.swift | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Tests/AsyncAlgorithmsTests/TestShare.swift b/Tests/AsyncAlgorithmsTests/TestShare.swift index 14aa273c..d4c21456 100644 --- a/Tests/AsyncAlgorithmsTests/TestShare.swift +++ b/Tests/AsyncAlgorithmsTests/TestShare.swift @@ -132,26 +132,39 @@ final class TestShare: XCTestCase { func test_share_with_no_buffering() async { let shared = [1, 2, 3, 4, 5].async.share(bufferingPolicy: .bounded(0)) + + let expectation1 = XCTestExpectation(description: "Consumer 1 finished looping") + let expectation2 = XCTestExpectation(description: "Consumer 2 finished looping") let results1 = Mutex([Int]()) let results2 = Mutex([Int]()) + let gate1 = Gate() + let gate2 = Gate() let consumer1 = Task { - // Consumer 1 reads first element - for await value in shared { + var iterator = shared.makeAsyncIterator() + gate2.open() + await gate1.enter() + while let value = await iterator.next(isolation: nil) { results1.withLock { $0.append(value) } - // Delay to allow consumer 2 to get ahead - try? await Task.sleep(for: .milliseconds(10)) + // Add some delay to consumer 1 + try? await Task.sleep(for: .milliseconds(1)) } + expectation1.fulfill() } let consumer2 = Task { - // Consumer 2 reads all elements quickly - for await value in shared { + var iterator = shared.makeAsyncIterator() + gate1.open() + await gate2.enter() + while let value = await iterator.next(isolation: nil) { results2.withLock { $0.append(value) } } + expectation2.fulfill() } - + + await fulfillment(of: [expectation1, expectation2], timeout: 5) + await consumer1.value await consumer2.value