diff --git a/Tests/AsyncAlgorithmsTests/TestChain.swift b/Tests/AsyncAlgorithmsTests/TestChain.swift index f7cd0fef..2ef9fc85 100644 --- a/Tests/AsyncAlgorithmsTests/TestChain.swift +++ b/Tests/AsyncAlgorithmsTests/TestChain.swift @@ -13,57 +13,68 @@ import AsyncAlgorithms final class TestChain2: XCTestCase { - func test_chain() async { - let chained = chain([1, 2, 3].async, [4, 5, 6].async) + func test_chain2_concatenates_elements_from_sequences_and_returns_nil_when_source_is_pastEnd() async { + let expected1 = [1, 2, 3] + let expected2 = [4, 5, 6] + let expected = expected1 + expected2 + let chained = chain(expected1.async, expected2.async) + var iterator = chained.makeAsyncIterator() - var actual = [Int]() + var collected = [Int]() while let item = await iterator.next() { - actual.append(item) + collected.append(item) } - XCTAssertEqual([1, 2, 3, 4, 5, 6], actual) + XCTAssertEqual(expected, collected) + let pastEnd = await iterator.next() XCTAssertNil(pastEnd) } - func test_throwing_first() async throws { + func test_chain2_outputs_elements_from_first_sequence_and_throws_when_first_throws() async throws { let chained = chain([1, 2, 3].async.map { try throwOn(3, $0) }, [4, 5, 6].async) var iterator = chained.makeAsyncIterator() - var actual = [Int]() + + var collected = [Int]() do { while let item = try await iterator.next() { - actual.append(item) + collected.append(item) } - XCTFail() + XCTFail("Chained sequence should throw when first sequence throws") } catch { XCTAssertEqual(Failure(), error as? Failure) } - XCTAssertEqual([1, 2], actual) + XCTAssertEqual([1, 2], collected) + let pastEnd = try await iterator.next() XCTAssertNil(pastEnd) } - func test_throwing_second() async throws { + func test_chain2_outputs_elements_from_sequences_and_throws_when_second_throws() async throws { let chained = chain([1, 2, 3].async, [4, 5, 6].async.map { try throwOn(5, $0) }) var iterator = chained.makeAsyncIterator() - var actual = [Int]() + + var collected = [Int]() do { while let item = try await iterator.next() { - actual.append(item) + collected.append(item) } - XCTFail() + XCTFail("Chained sequence should throw when second sequence throws") } catch { XCTAssertEqual(Failure(), error as? Failure) } - XCTAssertEqual([1, 2, 3, 4], actual) + XCTAssertEqual(collected, [1, 2, 3, 4]) + let pastEnd = try await iterator.next() XCTAssertNil(pastEnd) } - func test_cancellation() async { - let source = Indefinite(value: "test") - let sequence = chain(source.async, ["past indefinite"].async) + func test_chain2_finishes_when_task_is_cancelled() async { let finished = expectation(description: "finished") let iterated = expectation(description: "iterated") + + let source = Indefinite(value: "test") + let sequence = chain(source.async, ["past indefinite"].async) + let task = Task { var firstIteration = false for await _ in sequence { @@ -74,84 +85,101 @@ final class TestChain2: XCTestCase { } finished.fulfill() } + // ensure the other task actually starts wait(for: [iterated], timeout: 1.0) + // cancellation should ensure the loop finishes // without regards to the remaining underlying sequence task.cancel() + wait(for: [finished], timeout: 1.0) } } final class TestChain3: XCTestCase { - func test_chain() async { - let chained = chain([1, 2, 3].async, [4, 5, 6].async, [7, 8, 9].async) + func test_chain3_concatenates_elements_from_sequences_and_returns_nil_when_source_is_pastEnd() async { + let expected1 = [1, 2, 3] + let expected2 = [4, 5, 6] + let expected3 = [7, 8, 9] + let expected = expected1 + expected2 + expected3 + let chained = chain(expected1.async, expected2.async, expected3.async) var iterator = chained.makeAsyncIterator() - var actual = [Int]() + + var collected = [Int]() while let item = await iterator.next() { - actual.append(item) + collected.append(item) } - XCTAssertEqual([1, 2, 3, 4, 5, 6, 7, 8, 9], actual) + XCTAssertEqual(expected, collected) + let pastEnd = await iterator.next() XCTAssertNil(pastEnd) } - func test_throwing_first() async throws { + func test_chain3_outputs_elements_from_first_sequence_and_throws_when_first_throws() async throws { let chained = chain([1, 2, 3].async.map { try throwOn(3, $0) }, [4, 5, 6].async, [7, 8, 9].async) var iterator = chained.makeAsyncIterator() - var actual = [Int]() + + var collected = [Int]() do { while let item = try await iterator.next() { - actual.append(item) + collected.append(item) } - XCTFail() + XCTFail("Chained sequence should throw when first sequence throws") } catch { XCTAssertEqual(Failure(), error as? Failure) } - XCTAssertEqual([1, 2], actual) + XCTAssertEqual(collected, [1, 2]) + let pastEnd = try await iterator.next() XCTAssertNil(pastEnd) } - func test_throwing_second() async throws { + func test_chain3_outputs_elements_from_sequences_and_throws_when_second_throws() async throws { let chained = chain([1, 2, 3].async, [4, 5, 6].async.map { try throwOn(5, $0) }, [7, 8, 9].async) var iterator = chained.makeAsyncIterator() - var actual = [Int]() + + var collected = [Int]() do { while let item = try await iterator.next() { - actual.append(item) + collected.append(item) } - XCTFail() + XCTFail("Chained sequence should throw when second sequence throws") } catch { XCTAssertEqual(Failure(), error as? Failure) } - XCTAssertEqual([1, 2, 3, 4], actual) + XCTAssertEqual(collected, [1, 2, 3, 4]) + let pastEnd = try await iterator.next() XCTAssertNil(pastEnd) } - func test_throwing_third() async throws { + func test_chain3_outputs_elements_from_sequences_and_throws_when_third_throws() async throws { let chained = chain([1, 2, 3].async, [4, 5, 6].async, [7, 8, 9].async.map { try throwOn(8, $0) }) var iterator = chained.makeAsyncIterator() - var actual = [Int]() + + var collected = [Int]() do { while let item = try await iterator.next() { - actual.append(item) + collected.append(item) } - XCTFail() + XCTFail("Chained sequence should throw when third sequence throws") } catch { XCTAssertEqual(Failure(), error as? Failure) } - XCTAssertEqual([1, 2, 3, 4, 5, 6, 7], actual) + XCTAssertEqual(collected, [1, 2, 3, 4, 5, 6, 7]) + let pastEnd = try await iterator.next() XCTAssertNil(pastEnd) } - func test_cancellation() async { - let source = Indefinite(value: "test") - let sequence = chain(source.async, ["past indefinite"].async, ["and even further"].async) + func test_chain3_finishes_when_task_is_cancelled() async { let finished = expectation(description: "finished") let iterated = expectation(description: "iterated") + + let source = Indefinite(value: "test") + let sequence = chain(source.async, ["past indefinite"].async, ["and even further"].async) + let task = Task { var firstIteration = false for await _ in sequence { @@ -162,11 +190,14 @@ final class TestChain3: XCTestCase { } finished.fulfill() } + // ensure the other task actually starts wait(for: [iterated], timeout: 1.0) + // cancellation should ensure the loop finishes // without regards to the remaining underlying sequence task.cancel() + wait(for: [finished], timeout: 1.0) } }