Skip to content

Add support for WASI platform #478

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 8 commits into from
Mar 13, 2024
16 changes: 15 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ project(XCTest LANGUAGES Swift)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(USE_FOUNDATION_FRAMEWORK "Use Foundation.framework on Darwin" NO)

if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin)
set(DISABLE_XCTWAITER_default NO)

if(CMAKE_SYSTEM_PROCESSOR STREQUAL wasm32)
set(DISABLE_XCTWAITER_default ON)
endif()

option(DISABLE_XCTWAITER "Disable XCTWaiter" "${DISABLE_XCTWAITER_default}")

if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin AND NOT DISABLE_XCTWAITER)
find_package(dispatch CONFIG REQUIRED)
find_package(Foundation CONFIG REQUIRED)
endif()
Expand Down Expand Up @@ -49,6 +57,12 @@ add_library(XCTest
Sources/XCTest/Public/Asynchronous/XCTWaiter.swift
Sources/XCTest/Public/Asynchronous/XCTestCase+Asynchronous.swift
Sources/XCTest/Public/Asynchronous/XCTestExpectation.swift)

if(DISABLE_XCTWAITER)
target_compile_definitions(XCTest PRIVATE
DISABLE_XCTWAITER)
endif()

if(USE_FOUNDATION_FRAMEWORK)
target_compile_definitions(XCTest PRIVATE
USE_FOUNDATION_FRAMEWORK)
Expand Down
3 changes: 3 additions & 0 deletions Sources/XCTest/Private/WaiterManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//
// WaiterManager.swift
//
#if !DISABLE_XCTWAITER

internal protocol ManageableWaiter: AnyObject, Equatable {
var isFinished: Bool { get }
Expand Down Expand Up @@ -143,3 +144,5 @@ internal final class WaiterManager<WaiterType: ManageableWaiter> : NSObject {
}

}

#endif
21 changes: 17 additions & 4 deletions Sources/XCTest/Private/XCTestCase.TearDownBlocksState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,42 @@ extension XCTestCase {
/// Supports async and sync throwing methods.
final class TeardownBlocksState {

#if DISABLE_XCTWAITER
typealias TeardownBlock = @Sendable () async throws -> Void
#else
typealias TeardownBlock = () throws -> Void
#endif

private var wasFinalized = false
private var blocks: [() throws -> Void] = []
private var blocks: [TeardownBlock] = []

// We don't want to overload append(_:) below because of how Swift will implicitly promote sync closures to async closures,
// which can unexpectedly change their semantics in difficult to track down ways.
//
// Because of this, we chose the unusual decision to forgo overloading (which is a super sweet language feature <3) to prevent this issue from surprising any contributors to corelibs-xctest
@available(macOS 12.0, *)
func appendAsync(_ block: @Sendable @escaping () async throws -> Void) {
#if DISABLE_XCTWAITER
XCTestCase.subsystemQueue.sync {
precondition(wasFinalized == false, "API violation -- attempting to add a teardown block after teardown blocks have been dequeued")
blocks.append(block)
}
#else
self.append {
try awaitUsingExpectation { try await block() }
}
#endif
}

func append(_ block: @escaping () throws -> Void) {
XCTWaiter.subsystemQueue.sync {
XCTestCase.subsystemQueue.sync {
precondition(wasFinalized == false, "API violation -- attempting to add a teardown block after teardown blocks have been dequeued")
blocks.append(block)
}
}

func finalize() -> [() throws -> Void] {
XCTWaiter.subsystemQueue.sync {
func finalize() -> [TeardownBlock] {
XCTestCase.subsystemQueue.sync {
precondition(wasFinalized == false, "API violation -- attempting to run teardown blocks after they've already run")
wasFinalized = true
return blocks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
// XCTNSNotificationExpectation.swift
//

#if !DISABLE_XCTWAITER

/// Expectation subclass for waiting on a condition defined by a Foundation Notification instance.
open class XCTNSNotificationExpectation: XCTestExpectation {

Expand Down Expand Up @@ -114,3 +116,5 @@ open class XCTNSNotificationExpectation: XCTestExpectation {
/// - SeeAlso: `XCTNSNotificationExpectation.handler`
@available(*, deprecated, renamed: "XCTNSNotificationExpectation.Handler")
public typealias XCNotificationExpectationHandler = XCTNSNotificationExpectation.Handler

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
// XCTNSPredicateExpectation.swift
//

#if !DISABLE_XCTWAITER

/// Expectation subclass for waiting on a condition defined by an NSPredicate and an optional object.
open class XCTNSPredicateExpectation: XCTestExpectation {

Expand Down Expand Up @@ -133,3 +135,4 @@ open class XCTNSPredicateExpectation: XCTestExpectation {
/// - SeeAlso: `XCTNSPredicateExpectation.handler`
@available(*, deprecated, renamed: "XCTNSPredicateExpectation.Handler")
public typealias XCPredicateExpectationHandler = XCTNSPredicateExpectation.Handler
#endif
3 changes: 3 additions & 0 deletions Sources/XCTest/Public/Asynchronous/XCTWaiter+Validation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//
// XCTWaiter+Validation.swift
//
#if !DISABLE_XCTWAITER

protocol XCTWaiterValidatableExpectation: Equatable {
var isFulfilled: Bool { get }
Expand Down Expand Up @@ -87,3 +88,5 @@ extension XCTWaiter {
return .incomplete
}
}

#endif
3 changes: 3 additions & 0 deletions Sources/XCTest/Public/Asynchronous/XCTWaiter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//
// XCTWaiter.swift
//
#if !DISABLE_XCTWAITER

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import CoreFoundation
Expand Down Expand Up @@ -479,3 +480,5 @@ extension XCTWaiter: ManageableWaiter {
}
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// Methods on XCTestCase for testing asynchronous operations
//

#if !DISABLE_XCTWAITER

public extension XCTestCase {

/// Creates a point of synchronization in the flow of a test. Only one
Expand Down Expand Up @@ -265,3 +267,4 @@ internal extension XCTestCase {
expected: false)
}
}
#endif
3 changes: 3 additions & 0 deletions Sources/XCTest/Public/Asynchronous/XCTestExpectation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//
// XCTestExpectation.swift
//
#if !DISABLE_XCTWAITER

/// Expectations represent specific conditions in asynchronous testing.
open class XCTestExpectation: @unchecked Sendable {
Expand Down Expand Up @@ -320,3 +321,5 @@ extension XCTestExpectation: CustomStringConvertible {
return expectationDescription
}
}

#endif
23 changes: 23 additions & 0 deletions Sources/XCTest/Public/XCAbstractTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,43 @@ open class XCTest {
/// testRunClass. If the test has not yet been run, this will be nil.
open private(set) var testRun: XCTestRun? = nil

#if DISABLE_XCTWAITER
internal var performTask: Task<Void, Never>?

internal func _performAsync(_ run: XCTestRun) async {
fatalError("Must be overridden by subclasses.")
}
internal func _runAsync() async {
guard let testRunType = testRunClass as? XCTestRun.Type else {
fatalError("XCTest.testRunClass must be a kind of XCTestRun.")
}
testRun = testRunType.init(test: self)
await _performAsync(testRun!)
}
#endif

/// The method through which tests are executed. Must be overridden by
/// subclasses.
#if DISABLE_XCTWAITER
@available(*, unavailable)
#endif
open func perform(_ run: XCTestRun) {
fatalError("Must be overridden by subclasses.")
}

/// Creates an instance of the `testRunClass` and passes it as a parameter
/// to `perform()`.
#if DISABLE_XCTWAITER
@available(*, unavailable)
#endif
open func run() {
#if !DISABLE_XCTWAITER
guard let testRunType = testRunClass as? XCTestRun.Type else {
fatalError("XCTest.testRunClass must be a kind of XCTestRun.")
}
testRun = testRunType.init(test: self)
perform(testRun!)
#endif
}

/// Async setup method called before the invocation of `setUpWithError` for each test method in the class.
Expand Down
Loading