@@ -41,6 +41,34 @@ private struct TimedAsyncSequence<Element>: AsyncSequence, AsyncIteratorProtocol
4141 }
4242}
4343
44+ private struct CancellationAwareSequence < Element> : AsyncSequence , AsyncIteratorProtocol {
45+ typealias Element = Element
46+ typealias AsyncIterator = CancellationAwareSequence
47+
48+ let onStart : @Sendable ( ) -> Void
49+ let onCancel : @Sendable ( ) -> Void
50+ var hasStarted = false
51+
52+ mutating func next( ) async throws -> Element ? {
53+ if !hasStarted {
54+ hasStarted = true
55+ onStart ( )
56+ }
57+
58+ do {
59+ try await Task . sleep ( nanoseconds: 5_000_000_000 )
60+ return nil
61+ } catch {
62+ onCancel ( )
63+ return nil
64+ }
65+ }
66+
67+ func makeAsyncIterator( ) -> AsyncIterator {
68+ self
69+ }
70+ }
71+
4472final class AsyncMergeSequenceTests : XCTestCase {
4573 func testMerge_merges_sequences_according_to_the_timeline_using_asyncSequences( ) async throws {
4674 // -- 0 ------------------------------- 1000 ----------------------------- 2000 -
@@ -306,4 +334,34 @@ final class AsyncMergeSequenceTests: XCTestCase {
306334
307335 task. cancel ( )
308336 }
337+
338+ func testMerge_cancels_other_bases_on_error( ) async {
339+ let baseStartedExpectation = expectation ( description: " The blocking base has started " )
340+ let baseCancelledExpectation = expectation ( description: " The blocking base has been cancelled " )
341+
342+ let blockingBase = CancellationAwareSequence < Int > (
343+ onStart: { baseStartedExpectation. fulfill ( ) } ,
344+ onCancel: { baseCancelledExpectation. fulfill ( ) }
345+ )
346+ let failingBase = TimedAsyncSequence ( intervalInMills: [ 0 , 0 ] , sequence: [ 1 , 2 ] , indexOfError: 1 )
347+
348+ let sut = merge ( failingBase, blockingBase)
349+ var iterator = sut. makeAsyncIterator ( )
350+
351+ do {
352+ _ = try await iterator. next ( )
353+ } catch {
354+ XCTFail ( " The first element should not fail " )
355+ }
356+ await fulfillment ( of: [ baseStartedExpectation] , timeout: 1 )
357+
358+ do {
359+ _ = try await iterator. next ( )
360+ XCTFail ( " The iteration should fail " )
361+ } catch {
362+ XCTAssertEqual ( error as? MockError , MockError ( code: 1 ) )
363+ }
364+
365+ await fulfillment ( of: [ baseCancelledExpectation] , timeout: 1 )
366+ }
309367}
0 commit comments