Skip to content

Add @MainActor to waitForExpectations(timeout:handler:) #428 #452

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public extension XCTestCase {
/// these environments. To ensure compatibility of tests between
/// swift-corelibs-xctest and Apple XCTest, it is not recommended to pass
/// explicit values for `file` and `line`.
// FIXME: This should have `@MainActor` to match Xcode XCTest, but adding it causes errors in tests authored pre-Swift Concurrency which don't typically have `@MainActor`.
@preconcurrency @MainActor
func waitForExpectations(timeout: TimeInterval, file: StaticString = #file, line: Int = #line, handler: XCWaitCompletionHandler? = nil) {
precondition(Thread.isMainThread, "\(#function) must be called on the main thread")
if currentWaiter != nil {
Expand Down
2 changes: 1 addition & 1 deletion Sources/XCTest/Public/XCTestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ open class XCTestCase: XCTest {
return 1
}

// FIXME: Once `waitForExpectations(timeout:...handler:)` gains `@MainActor`, this may be able to add it as well.
@MainActor
internal var currentWaiter: XCTWaiter?

/// The set of expectations made upon this test case.
Expand Down
32 changes: 29 additions & 3 deletions Tests/Functional/Asynchronous/Expectations/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,28 @@ class ExpectationsTestCase: XCTestCase {
RunLoop.main.run(until: Date() + 1)
}

// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsAsync' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsAsync' passed \(\d+\.\d+ seconds\)
func test_waitForExpectationsAsync() async {
// Basic check that waitForExpectations() is functional when used with the
// await keyword in an async function.
let expectation = self.expectation(description: "foo")
expectation.fulfill()
await self.waitForExpectations(timeout: 0.0)
}

// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsFromMainActor' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsFromMainActor' passed \(\d+\.\d+ seconds\)
func test_waitForExpectationsFromMainActor() async {
await MainActor.run {
// Basic check that waitForExpectations() is functional and does not need
// the await keyword when used from a main-actor-isolated test function.
let expectation = self.expectation(description: "foo")
expectation.fulfill()
self.waitForExpectations(timeout: 0.0)
}
}

static var allTests = {
return [
("test_waitingForAnUnfulfilledExpectation_fails", test_waitingForAnUnfulfilledExpectation_fails),
Expand Down Expand Up @@ -603,15 +625,19 @@ class ExpectationsTestCase: XCTestCase {
("test_expectationCreationOnSecondaryThread", test_expectationCreationOnSecondaryThread),
("test_expectationCreationWhileWaiting", test_expectationCreationWhileWaiting),
("test_runLoopInsideDispatch", test_runLoopInsideDispatch),

// waitForExpectations() + @MainActor
("test_waitForExpectationsAsync", asyncTest(test_waitForExpectationsAsync)),
("test_waitForExpectationsFromMainActor", asyncTest(test_waitForExpectationsFromMainActor)),
]
}()
}
// CHECK: Test Suite 'ExpectationsTestCase' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
// CHECK: \t Executed 35 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
// CHECK: \t Executed 37 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds

XCTMain([testCase(ExpectationsTestCase.allTests)])

// CHECK: Test Suite '.*\.xctest' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
// CHECK: \t Executed 35 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
// CHECK: \t Executed 37 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
// CHECK: Test Suite 'All tests' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
// CHECK: \t Executed 35 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
// CHECK: \t Executed 37 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds