Skip to content

Commit

Permalink
Merge pull request #478 from kateinoigakukun/yt/xctest-wasm-upstream
Browse files Browse the repository at this point in the history
Add support for WASI platform
  • Loading branch information
shahmishal committed Mar 13, 2024
2 parents be9cfd4 + da82bb9 commit e0c3868
Show file tree
Hide file tree
Showing 14 changed files with 353 additions and 54 deletions.
16 changes: 15 additions & 1 deletion CMakeLists.txt
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
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
Expand Up @@ -12,29 +12,42 @@ extension XCTestCase {
/// Supports async and sync throwing methods.
final class TeardownBlocksState {

#if DISABLE_XCTWAITER
typealias TeardownBlock = @Sendable @MainActor () 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
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
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
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
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
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
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
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

0 comments on commit e0c3868

Please sign in to comment.