Skip to content

Instantly share code, notes, and snippets.

@sidepelican
Created November 20, 2021 11:36
Show Gist options
  • Save sidepelican/e1b802f22bd5e94294ff05154d7ef849 to your computer and use it in GitHub Desktop.
Save sidepelican/e1b802f22bd5e94294ff05154d7ef849 to your computer and use it in GitHub Desktop.
#if !canImport(ObjectiveC)
import Foundation
import XCTest
// INFO:
//typealias XCTestCaseClosure = (XCTestCase) throws -> Void
//typealias XCTestCaseEntry = (testCaseClass: XCTestCase.Type, allTests: [(String, XCTestCaseClosure)])
func testCase<T: XCTestCase>(_ allTests: [(String, (T) -> () async throws -> Void)]) -> XCTestCaseEntry {
let tests: [(String, XCTestCaseClosure)] = allTests.map { ($0.0, asyncTest($0.1)) }
return (T.self, tests)
}
private func asyncTest<T: XCTestCase>(
_ testClosureGenerator: @escaping (T) -> () async throws -> Void
) -> XCTestCaseClosure {
return { testCaseType in
guard let testCase = testCaseType as? T else {
fatalError("Attempt to invoke test on class \(T.self) with incompatible instance type \(type(of: testCaseType))")
}
let testClosure = testClosureGenerator(testCase)
try awaitUsingExpectation(testClosure)
}
}
private func awaitUsingExpectation(
_ closure: @escaping () async throws -> Void
) throws -> Void {
let expectation = XCTestExpectation(description: "async test completion")
let thrownErrorWrapper = ThrownErrorWrapper()
Task {
defer { expectation.fulfill() }
do {
try await closure()
} catch {
thrownErrorWrapper.error = error
}
}
_ = XCTWaiter.wait(for: [expectation], timeout: asyncTestTimeout)
if let error = thrownErrorWrapper.error {
throw error
}
}
private final class ThrownErrorWrapper: @unchecked Sendable {
private let queue = DispatchQueue(label: "LinuxSupport.ThrownErrorWrapper")
private var _error: Error?
var error: Error? {
get {
queue.sync { _error }
}
set {
queue.sync { _error = newValue }
}
}
}
// This time interval is set to a very large value due to their being no real native timeout functionality within corelibs-xctest.
// With the introduction of async/await support, the framework now relies on XCTestExpectations internally to coordinate the addition async portions of setup and tear down.
// This time interval is the timeout corelibs-xctest uses with XCTestExpectations.
private let asyncTestTimeout: TimeInterval = 60 * 60 * 24 * 30
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment