mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
463 lines
15 KiB
Swift
463 lines
15 KiB
Swift
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-concurrency %import-libdispatch -parse-as-library)
|
|
|
|
// REQUIRES: concurrency
|
|
// REQUIRES: libdispatch
|
|
// REQUIRES: executable_test
|
|
// UNSUPPORTED: use_os_stdlib
|
|
|
|
// https://bugs.swift.org/browse/SR-14466
|
|
// UNSUPPORTED: OS=windows-msvc
|
|
|
|
import _Concurrency
|
|
import StdlibUnittest
|
|
import Dispatch
|
|
|
|
struct SomeError: Error, Equatable {
|
|
var value = Int.random(in: 0..<100)
|
|
}
|
|
|
|
var tests = TestSuite("AsyncStream")
|
|
|
|
@main struct Main {
|
|
static func main() async {
|
|
if #available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) {
|
|
final class Expectation: UnsafeSendable {
|
|
var fulfilled = false
|
|
}
|
|
|
|
tests.test("yield with no awaiting next") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with no awaiting next throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
expectEqual(await iterator.next(), "hello")
|
|
}
|
|
|
|
tests.test("yield with awaiting next throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
expectEqual(await iterator.next(), "hello")
|
|
expectEqual(await iterator.next(), "world")
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and finish") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish()
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
expectEqual(await iterator.next(), "hello")
|
|
expectEqual(await iterator.next(), "world")
|
|
expectEqual(await iterator.next(), nil)
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and finish throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish()
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
expectEqual(try await iterator.next(), nil)
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and throw") {
|
|
let thrownError = SomeError()
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish(throwing: thrownError)
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
try await iterator.next()
|
|
expectUnreachable("expected thrown error")
|
|
} catch {
|
|
if let failure = error as? SomeError {
|
|
expectEqual(failure, thrownError)
|
|
} else {
|
|
expectUnreachable("unexpected error type")
|
|
}
|
|
}
|
|
}
|
|
|
|
tests.test("yield with no awaiting next detached") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
}
|
|
}
|
|
}
|
|
|
|
tests.test("yield with no awaiting next detached throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
}
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next detached") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
expectEqual(await iterator.next(), "hello")
|
|
}
|
|
|
|
tests.test("yield with awaiting next detached throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 detached") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
expectEqual(await iterator.next(), "hello")
|
|
expectEqual(await iterator.next(), "world")
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 detached throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and finish detached") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish()
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
expectEqual(await iterator.next(), "hello")
|
|
expectEqual(await iterator.next(), "world")
|
|
expectEqual(await iterator.next(), nil)
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and finish detached throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish()
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
expectEqual(try await iterator.next(), nil)
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and throw detached") {
|
|
let thrownError = SomeError()
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish(throwing: thrownError)
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
try await iterator.next()
|
|
expectUnreachable("expected thrown error")
|
|
} catch {
|
|
if let failure = error as? SomeError {
|
|
expectEqual(failure, thrownError)
|
|
} else {
|
|
expectUnreachable("unexpected error type")
|
|
}
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and finish detached with value after finish") {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish()
|
|
continuation.yield("This should not be emitted")
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
expectEqual(await iterator.next(), "hello")
|
|
expectEqual(await iterator.next(), "world")
|
|
expectEqual(await iterator.next(), nil)
|
|
expectEqual(await iterator.next(), nil)
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and finish detached with value after finish throwing") {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish()
|
|
continuation.yield("This should not be emitted")
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
expectEqual(try await iterator.next(), nil)
|
|
expectEqual(try await iterator.next(), nil)
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and finish detached with throw after finish throwing") {
|
|
let thrownError = SomeError()
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
detach {
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish()
|
|
continuation.finish(throwing: thrownError)
|
|
}
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
expectEqual(try await iterator.next(), nil)
|
|
expectEqual(try await iterator.next(), nil)
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("yield with awaiting next 2 and finish with throw after finish throwing") {
|
|
let thrownError = SomeError()
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
continuation.yield("hello")
|
|
continuation.yield("world")
|
|
continuation.finish()
|
|
continuation.finish(throwing: thrownError)
|
|
}
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
expectEqual(try await iterator.next(), "hello")
|
|
expectEqual(try await iterator.next(), "world")
|
|
expectEqual(try await iterator.next(), nil)
|
|
expectEqual(try await iterator.next(), nil)
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
}
|
|
|
|
tests.test("cancellation behavior on deinit with no values being awaited") {
|
|
let expectation = Expectation()
|
|
|
|
func scopedLifetime(_ expectation: Expectation) {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
continuation.onTermination = { @Sendable _ in expectation.fulfilled = true }
|
|
}
|
|
}
|
|
|
|
scopedLifetime(expectation)
|
|
|
|
expectTrue(expectation.fulfilled)
|
|
}
|
|
|
|
tests.test("termination behavior on deinit with no values being awaited") {
|
|
let expectation = Expectation()
|
|
|
|
func scopedLifetime(_ expectation: Expectation) {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
continuation.onTermination = { @Sendable _ in expectation.fulfilled = true }
|
|
continuation.finish()
|
|
}
|
|
}
|
|
|
|
scopedLifetime(expectation)
|
|
|
|
expectTrue(expectation.fulfilled)
|
|
}
|
|
|
|
tests.test("cancellation behavior on deinit with no values being awaited") {
|
|
let expectation = Expectation()
|
|
|
|
func scopedLifetime(_ expectation: Expectation) {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
continuation.onTermination = { @Sendable terminal in
|
|
switch terminal {
|
|
case .cancelled:
|
|
expectation.fulfilled = true
|
|
default: break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
scopedLifetime(expectation)
|
|
|
|
expectTrue(expectation.fulfilled)
|
|
}
|
|
|
|
tests.test("cancellation behavior on deinit with no values being awaited throwing") {
|
|
let expectation = Expectation()
|
|
|
|
func scopedLifetime(_ expectation: Expectation) {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
continuation.onTermination = { @Sendable terminal in
|
|
switch terminal {
|
|
case .cancelled:
|
|
expectation.fulfilled = true
|
|
default: break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
scopedLifetime(expectation)
|
|
|
|
expectTrue(expectation.fulfilled)
|
|
}
|
|
|
|
tests.test("cancellation behavior of value emitted in handler") {
|
|
let ready = DispatchSemaphore(value: 0)
|
|
let done = DispatchSemaphore(value: 0)
|
|
let task = detach {
|
|
let series = AsyncStream(String.self) { continuation in
|
|
continuation.onTermination = { @Sendable _ in continuation.yield("Hit cancel") }
|
|
}
|
|
ready.signal()
|
|
var iterator = series.makeAsyncIterator()
|
|
let first = await iterator.next()
|
|
expectEqual(first, "Hit cancel")
|
|
let second = await iterator.next()
|
|
expectEqual(second, nil)
|
|
done.signal()
|
|
}
|
|
ready.wait()
|
|
task.cancel()
|
|
let result = done.wait(timeout: DispatchTime(uptimeNanoseconds: DispatchTime.now().uptimeNanoseconds + 1_000_000_000))
|
|
switch result {
|
|
case .timedOut:
|
|
expectFalse(true, "Timeout when awaiting finished state")
|
|
default: break
|
|
}
|
|
}
|
|
|
|
tests.test("cancellation behavior of value emitted in handler throwing") {
|
|
let ready = DispatchSemaphore(value: 0)
|
|
let done = DispatchSemaphore(value: 0)
|
|
let task = detach {
|
|
let series = AsyncThrowingStream(String.self) { continuation in
|
|
continuation.onTermination = { @Sendable _ in continuation.yield("Hit cancel") }
|
|
}
|
|
ready.signal()
|
|
var iterator = series.makeAsyncIterator()
|
|
do {
|
|
let first = try await iterator.next()
|
|
expectEqual(first, "Hit cancel")
|
|
let second = try await iterator.next()
|
|
expectEqual(second, nil)
|
|
} catch {
|
|
expectUnreachable("unexpected error thrown")
|
|
}
|
|
done.signal()
|
|
}
|
|
ready.wait()
|
|
task.cancel()
|
|
let result = done.wait(timeout: DispatchTime(uptimeNanoseconds: DispatchTime.now().uptimeNanoseconds + 1_000_000_000))
|
|
switch result {
|
|
case .timedOut:
|
|
expectFalse(true, "Timeout when awaiting finished state")
|
|
default: break
|
|
}
|
|
}
|
|
|
|
await runAllTestsAsync()
|
|
}
|
|
}
|
|
}
|
|
|
|
|