Skip to content

Commit

Permalink
fixes behavior of inverted expectations which can fulfill after timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
brennanMKE committed Aug 21, 2022
1 parent bfff294 commit b400036
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 9 deletions.
1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ let package = Package(
swiftSettings: nil,
linkerSettings: [.linkedFramework("XCTest")]
),

.testTarget(
name: "AsyncTestingTests",
dependencies: ["AsyncTesting"],
Expand Down
22 changes: 15 additions & 7 deletions Sources/AsyncTesting/AsyncExpectation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ public actor AsyncExpectation {
self.isInverted = isInverted
self.expectedFulfillmentCount = expectedFulfillmentCount
}


/// Marks the expectation as having been met.
///
/// It is an error to call this method on an expectation that has already been fulfilled,
/// or when the test case that vended the expectation has already completed.
public func fulfill(file: StaticString = #filePath, line: UInt = #line) {
guard state != .fulfilled else { return }

guard !isInverted else {
XCTFail("Inverted expectation fulfilled: \(expectationDescription)", file: file, line: line)
state = .fulfilled
finish()

if isInverted {
if state != .timedOut {
XCTFail("Inverted expectation fulfilled: \(expectationDescription)", file: file, line: line)
state = .fulfilled
finish()
}
return
}

Expand Down Expand Up @@ -70,7 +76,9 @@ public actor AsyncExpectation {

internal func timeOut(file: StaticString = #filePath,
line: UInt = #line) async {
if state != .fulfilled && !isInverted {
if isInverted {
state = .timedOut
} else if state != .fulfilled {
state = .timedOut
XCTFail("Expectation timed out: \(expectationDescription)", file: file, line: line)
}
Expand Down
47 changes: 46 additions & 1 deletion Tests/AsyncTestingTests/AsyncTestingTests.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
import XCTest
@testable import AsyncTesting

actor AsyncRunner {
typealias VoidNeverContinuation = CheckedContinuation<Void, Never>
private var continuations: [VoidNeverContinuation] = []

public func run(timeout: Double = 5.0) async {
await withTaskCancellationHandler {
Task {
await finish()
}
} operation: {
await withCheckedContinuation {
continuations.append($0)
}
Task {
try await Task.sleep(seconds: timeout)
finish()
}
}
}

public func finish() {
while !continuations.isEmpty {
let continuation = continuations.removeFirst()
continuation.resume(returning: ())
}
}
}

final class AsyncExpectationTests: XCTestCase {

func testDoneExpectation() async throws {
Expand Down Expand Up @@ -42,6 +70,23 @@ final class AsyncExpectationTests: XCTestCase {
task.cancel()
try await waitForExpectations([notDone], timeout: delay * 2)
}

func testNotYetDoneAndThenDoneExpectation() async throws {
let delay = 0.01
let notYetDone = asyncExpectation(description: "not yet done", isInverted: true)
let done = asyncExpectation(description: "done")

let task = Task {
await AsyncRunner().run()
XCTAssertTrue(Task.isCancelled)
await notYetDone.fulfill() // will timeout before being called
await done.fulfill() // will be called after cancellation
}

try await waitForExpectations([notYetDone], timeout: delay)
task.cancel()
try await waitForExpectations([done])
}

func testDoneAndNotDoneInvertedExpectation() async throws {
let delay = 0.01
Expand Down Expand Up @@ -91,5 +136,5 @@ final class AsyncExpectationTests: XCTestCase {

try await waitForExpectations([one, two, three])
}

}

0 comments on commit b400036

Please sign in to comment.