Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ba60a44
Expectation handlers need to be @Sendable. #454
grynspan Jun 26, 2023
a7a07a2
Add @MainActor to waitForExpectations(timeout:handler:) #428
grynspan Jun 25, 2023
5607291
Fix typo in CHECK
grynspan Jun 26, 2023
33f65a5
Fix whitespace and try asyncTest() directly again to see if the compi…
grynspan Jun 26, 2023
6f50082
Disable @MainActor test functions due to SILGen crash.
grynspan Jun 26, 2023
e889f20
Oops, wrong checks
grynspan Jun 26, 2023
a1bdcfe
Reenable one of the @MainActor tests and remove the other as redundant
grynspan Jun 26, 2023
333c1ea
Fix count in final checks.
grynspan Jun 27, 2023
6682df5
Merge pull request #455 from apple/jgrynspan/sendable-handlers
grynspan Jun 27, 2023
c902f0f
Merge pull request #452 from apple/jgrynspan/waitForExpectations-shou…
grynspan Jun 27, 2023
543e0d8
Bump the macOS deployment target to 10.13 in the Xcode project.
grynspan Jul 5, 2023
aba63a7
Merge pull request #459 from apple/jgrynspan/bump-macos-deployment-ta…
grynspan Jul 7, 2023
035f8d4
Remove outdated documentation for LinuxMain.swift.
grynspan Oct 5, 2023
a61b37e
Merge pull request #465 from apple/jgrynspan/114360933-remove-linuxma…
grynspan Oct 6, 2023
4e33224
Stop importing CF as a framework
etcwilde Oct 6, 2023
191097e
Merge pull request #467 from apple/ewilde/stop-importing-cf-as-a-fram…
etcwilde Oct 9, 2023
0a568ed
Make `XCTMain()` return its exit code instead of calling `exit()`.
grynspan Oct 24, 2023
efdd66f
Restore existing overloads for source compatibility's sake and clean …
grynspan Oct 24, 2023
e2fb647
Merge pull request #468 from apple/jgrynspan/XCTMain-signature
grynspan Oct 25, 2023
ff2e043
Don't use Core Foundation in tests.
grynspan Nov 27, 2023
ea5c74d
Merge pull request #471 from apple/jgrynspan/116586683-no-core-founda…
grynspan Jan 4, 2024
2bc0f37
Support Windows ARM64 builds
hjyamauchi Feb 13, 2024
be9cfd4
Merge pull request #475 from hjyamauchi/arm64
shahmishal Mar 1, 2024
e7ea525
WebAssembly Support
kateinoigakukun Feb 24, 2024
ae2cb67
Guard out `awaitUsingExpectation` for SWIFT_CONCURRENCY_WAITER mode
kateinoigakukun Mar 12, 2024
e428651
Guard out `SWIFT_CONCURRENCY_WAITER` specific member field
kateinoigakukun Mar 12, 2024
99a5c70
Run test suite, setUp, and tearDown in the main actor
kateinoigakukun Mar 12, 2024
c63a84f
Rename `USE_SWIFT_CONCURRENCY_WAITER` to `DISABLE_XCTWAITER`
kateinoigakukun Mar 12, 2024
9decbc4
Make entire `XCTWaiter` and `XCTestExpectation` unavailable for WASI
kateinoigakukun Mar 12, 2024
2a46822
Remove DispatchShims.swift and replace it with a conditional typealias
kateinoigakukun Mar 12, 2024
da82bb9
Keep running tearDown blocks on the main actor consistently
kateinoigakukun Mar 12, 2024
e0c3868
Merge pull request #478 from kateinoigakukun/yt/xctest-wasm-upstream
shahmishal Mar 13, 2024
6cb8b12
Add new include path into Foundation when building tests
jmschonfeld Jun 24, 2024
1bf0255
Fix build failure
jmschonfeld Jun 24, 2024
1c66d4d
Update swift-syntax links to swiftlang/swift-syntax
ahoppen Jun 25, 2024
333ba0d
Merge pull request #493 from jmschonfeld/support-swiftfoundation
parkera Jun 25, 2024
2a8fe20
Merge pull request #494 from ahoppen/swiftlang-migration
shahmishal Jun 25, 2024
9512507
[Linux] Enable build IDs.
al45tair Jun 26, 2024
7a9df99
Merge pull request #496 from al45tair/eng/PR-130582882
al45tair Jul 23, 2024
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
28 changes: 27 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,29 @@ 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()

include(SwiftSupport)
include(GNUInstallDirs)
include(CheckLinkerFlag)

if(CMAKE_SYSTEM_NAME STREQUAL Linux
OR CMAKE_SYSTEM_NAME STREQUAL FreeBSD
OR CMAKE_SYSTEM_NAME STREQUAL OpenBSD)
enable_language(C)
check_linker_flag(C "LINKER:--build-id=sha1" LINKER_SUPPORTS_BUILD_ID)
endif()

add_library(XCTest
Sources/XCTest/Private/WallClockTimeMetric.swift
Expand Down Expand Up @@ -49,6 +65,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 All @@ -65,6 +87,10 @@ set_target_properties(XCTest PROPERTIES
Swift_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/swift
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/swift)

if(LINKER_SUPPORTS_BUILD_ID)
target_link_options(XCTest PRIVATE "LINKER:--build-id=sha1")
endif()


if(ENABLE_TESTING)
enable_testing()
Expand Down
38 changes: 1 addition & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,7 @@ For general information about using XCTest, see:

The Swift Package Manager integrates directly with XCTest to provide a streamlined experience for unit testing SwiftPM packages. If you are using XCTest within a SwiftPM package, unit test files are located within the package's `Tests` subdirectory, and you can build and run the full test suite in one step by running `swift test`.

For more information about using XCTest with SwiftPM, see its [documentation](https://github.com/apple/swift-package-manager).

### Test Method Discovery

Unlike the version of XCTest included with Xcode, this version does not use the Objective-C runtime to automatically discover test methods because that runtime is not available on all platforms Swift supports. This means that in certain configurations, the full set of test methods must be explicitly provided to XCTest.

When using XCTest via SwiftPM on macOS, this is not necessary because SwiftPM uses the version of XCTest included with Xcode to run tests. But when using this version of XCTest _without_ SwiftPM, or _with_ SwiftPM on a platform other than macOS (including Linux), the full set of test methods cannot be discovered automatically, and your test target must tell XCTest about them explicitly.

The recommended way to do this is to create a static property in each of your `XCTestCase` subclasses. By convention, this property is named `allTests`, and should contain all of the tests in the class. For example:

```swift
class TestNSURL : XCTestCase {
static var allTests = {
return [
("test_bestNumber", test_bestNumber),
("test_URLStrings", test_URLStrings),
("test_fileURLWithPath", test_fileURLWithPath),
// Other tests go here
]
}()

func test_bestNumber() {
// Write your test here. Most of the XCTAssert functions you are familiar with are available.
XCTAssertTrue(theBestNumber == 42, "The number is wrong")
}

// Other tests go here
}
```

After creating an `allTests` property in each `XCTestCase` subclass, you must tell XCTest about those classes' tests.

If the project is a SwiftPM package which supports macOS, the easiest way to do this is to run `swift test --generate-linuxmain` from a macOS machine. This command generates files within the package's `Tests` subdirectory which contains the necessary source code for passing all test classes and methods to XCTest. These files should be committed to source control and re-generated whenever `XCTestCase` subclasses or test methods are added to or removed from your package's test suite.

If the project is a SwiftPM package but does not support macOS, you may edit the package's default `LinuxMain.swift` file manually to add all `XCTestCase` subclasses.

If the project is not a SwiftPM package, follow the steps in the next section to create an executable which calls the `XCTMain` function manually.
For more information about using XCTest with SwiftPM, see its [documentation](https://github.com/swiftlang/swift-package-manager).

### Standalone Command Line Usage

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 @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
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 All @@ -19,7 +21,7 @@ open class XCTNSNotificationExpectation: XCTestExpectation {
/// - Returns: `true` if the expectation should be fulfilled, `false` if it should not.
///
/// - SeeAlso: `XCTNSNotificationExpectation.handler`
public typealias Handler = (Notification) -> Bool
public typealias Handler = @Sendable (Notification) -> Bool

private let queue = DispatchQueue(label: "org.swift.XCTest.XCTNSNotificationExpectation")

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 All @@ -18,7 +20,7 @@ open class XCTNSPredicateExpectation: XCTestExpectation {
/// - Returns: `true` if the expectation should be fulfilled, `false` if it should not.
///
/// - SeeAlso: `XCTNSPredicateExpectation.handler`
public typealias Handler = () -> Bool
public typealias Handler = @Sendable () -> Bool

private let queue = DispatchQueue(label: "org.swift.XCTest.XCTNSPredicateExpectation")

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 @@ -41,7 +43,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 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