Skip to content

Commit a7a07a2

Browse files
committed
Add @mainactor to waitForExpectations(timeout:handler:) #428
1 parent 7f69208 commit a7a07a2

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

Sources/XCTest/Public/Asynchronous/XCTestCase+Asynchronous.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public extension XCTestCase {
4141
/// these environments. To ensure compatibility of tests between
4242
/// swift-corelibs-xctest and Apple XCTest, it is not recommended to pass
4343
/// explicit values for `file` and `line`.
44-
// 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`.
44+
@preconcurrency @MainActor
4545
func waitForExpectations(timeout: TimeInterval, file: StaticString = #file, line: Int = #line, handler: XCWaitCompletionHandler? = nil) {
4646
precondition(Thread.isMainThread, "\(#function) must be called on the main thread")
4747
if currentWaiter != nil {

Sources/XCTest/Public/XCTestCase.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ open class XCTestCase: XCTest {
4848
return 1
4949
}
5050

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

5454
/// The set of expectations made upon this test case.

Tests/Functional/Asynchronous/Expectations/main.swift

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,37 @@ class ExpectationsTestCase: XCTestCase {
551551
RunLoop.main.run(until: Date() + 1)
552552
}
553553

554-
static var allTests = {
554+
// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsFromMainActor' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
555+
// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsFromMainActor' failed \(\d+\.\d+ seconds\)
556+
func test_waitForExpectationsAsync() async {
557+
// Basic check that waitForExpectations() is functional when used with the
558+
// await keyword in an async function.
559+
let expectation = self.expectation(description: "foo")
560+
expectation.fulfill()
561+
await self.waitForExpectations(timeout: 0.0)
562+
}
563+
564+
// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsFromMainActor' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
565+
// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsFromMainActor' failed \(\d+\.\d+ seconds\)
566+
@MainActor func test_waitForExpectationsFromMainActor() {
567+
// Basic check that waitForExpectations() is functional and does not need
568+
// the await keyword when used from a main-actor-isolated test function.
569+
let expectation = self.expectation(description: "foo")
570+
expectation.fulfill()
571+
self.waitForExpectations(timeout: 0.0)
572+
}
573+
574+
// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsFromMainActor_async' started at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
575+
// CHECK: Test Case 'ExpectationsTestCase.test_waitForExpectationsFromMainActor_async' failed \(\d+\.\d+ seconds\)
576+
@MainActor func test_waitForExpectationsFromMainActor_async() async {
577+
// Basic check that waitForExpectations() is functional and does not need
578+
// the await keyword when used from a main-actor-isolated test function.
579+
let expectation = self.expectation(description: "foo")
580+
expectation.fulfill()
581+
self.waitForExpectations(timeout: 0.0)
582+
}
583+
584+
static var allTests: [(String, (ExpectationsTestCase) -> () throws -> Void)] = {
555585
return [
556586
("test_waitingForAnUnfulfilledExpectation_fails", test_waitingForAnUnfulfilledExpectation_fails),
557587
("test_waitingForUnfulfilledExpectations_outputsAllExpectations_andFails", test_waitingForUnfulfilledExpectations_outputsAllExpectations_andFails),
@@ -603,15 +633,20 @@ class ExpectationsTestCase: XCTestCase {
603633
("test_expectationCreationOnSecondaryThread", test_expectationCreationOnSecondaryThread),
604634
("test_expectationCreationWhileWaiting", test_expectationCreationWhileWaiting),
605635
("test_runLoopInsideDispatch", test_runLoopInsideDispatch),
636+
637+
// waitForExpectations() + @MainActor
638+
("test_waitForExpectationsAsync", asyncTest(test_waitForExpectationsAsync)),
639+
("test_waitForExpectationsFromMainActor", asyncTest { test_waitForExpectationsFromMainActor($0) }),
640+
("test_waitForExpectationsFromMainActor_async", asyncTest { test_waitForExpectationsFromMainActor_async($0) }),
606641
]
607642
}()
608643
}
609644
// CHECK: Test Suite 'ExpectationsTestCase' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
610-
// CHECK: \t Executed 35 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
645+
// CHECK: \t Executed 38 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
611646

612647
XCTMain([testCase(ExpectationsTestCase.allTests)])
613648

614649
// CHECK: Test Suite '.*\.xctest' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
615-
// CHECK: \t Executed 35 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
650+
// CHECK: \t Executed 38 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
616651
// CHECK: Test Suite 'All tests' failed at \d+-\d+-\d+ \d+:\d+:\d+\.\d+
617-
// CHECK: \t Executed 35 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds
652+
// CHECK: \t Executed 38 tests, with 16 failures \(2 unexpected\) in \d+\.\d+ \(\d+\.\d+\) seconds

0 commit comments

Comments
 (0)