diff --git a/.github/workflows/ci-swiftpm.yml b/.github/workflows/ci-swiftpm.yml index e15c91aa1..174f2fd50 100644 --- a/.github/workflows/ci-swiftpm.yml +++ b/.github/workflows/ci-swiftpm.yml @@ -50,3 +50,16 @@ jobs: - uses: actions/checkout@v4 - run: swift build -Xswiftc -suppress-warnings - run: swift test -Xswiftc -suppress-warnings --enable-test-discovery + + swiftpm_windows: + name: SwiftPM, Windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v3 + - name: Install Swift + uses: compnerd/gha-setup-swift@main + with: + branch: swift-5.9-release + tag: 5.9-RELEASE + - name: Test Windows + run: swift test -Xswiftc -suppress-warnings diff --git a/Sources/Nimble/Matchers/PostNotification.swift b/Sources/Nimble/Matchers/PostNotification.swift index 6f604ae8c..c848b968a 100644 --- a/Sources/Nimble/Matchers/PostNotification.swift +++ b/Sources/Nimble/Matchers/PostNotification.swift @@ -43,7 +43,11 @@ internal class NotificationCollector { } } +#if !os(Windows) private let mainThread = pthread_self() +#else +private let mainThread = Thread.mainThread +#endif private func _postNotifications( _ predicate: Predicate<[Notification]>, diff --git a/Sources/Nimble/Utils/AsyncAwait.swift b/Sources/Nimble/Utils/AsyncAwait.swift index 12547249f..a21dfd2cc 100644 --- a/Sources/Nimble/Utils/AsyncAwait.swift +++ b/Sources/Nimble/Utils/AsyncAwait.swift @@ -1,6 +1,9 @@ #if !os(WASI) +#if canImport(CoreFoundation) import CoreFoundation +#endif + import Dispatch import Foundation diff --git a/Sources/Nimble/Utils/AsyncTimerSequence.swift b/Sources/Nimble/Utils/AsyncTimerSequence.swift index 6bd46b83c..83c2485f9 100644 --- a/Sources/Nimble/Utils/AsyncTimerSequence.swift +++ b/Sources/Nimble/Utils/AsyncTimerSequence.swift @@ -1,6 +1,8 @@ #if !os(WASI) +#if canImport(CoreFoundation) import CoreFoundation +#endif import Dispatch import Foundation diff --git a/Sources/Nimble/Utils/PollAwait.swift b/Sources/Nimble/Utils/PollAwait.swift index 177bd094b..161e9552a 100644 --- a/Sources/Nimble/Utils/PollAwait.swift +++ b/Sources/Nimble/Utils/PollAwait.swift @@ -1,6 +1,8 @@ #if !os(WASI) +#if canImport(CoreFoundation) import CoreFoundation +#endif import Dispatch import Foundation @@ -192,6 +194,7 @@ internal class AwaitPromiseBuilder { let timedOutSem = DispatchSemaphore(value: 0) let semTimedOutOrBlocked = DispatchSemaphore(value: 0) semTimedOutOrBlocked.signal() + #if canImport(CoreFoundation) let runLoop = CFRunLoopGetMain() #if canImport(Darwin) let runLoopMode = CFRunLoopMode.defaultMode.rawValue @@ -209,12 +212,30 @@ internal class AwaitPromiseBuilder { } // potentially interrupt blocking code on run loop to let timeout code run CFRunLoopStop(runLoop) + #else + let runLoop = RunLoop.main + runLoop.perform(inModes: [.default], block: { + if semTimedOutOrBlocked.wait(timeout: .now()) == .success { + timedOutSem.signal() + semTimedOutOrBlocked.signal() + if self.promise.resolveResult(.timedOut) { + RunLoop.main._stop() + } + } + }) + // potentially interrupt blocking code on run loop to let timeout code run + runLoop._stop() + #endif let now = DispatchTime.now() + forcefullyAbortTimeout.dispatchTimeInterval let didNotTimeOut = timedOutSem.wait(timeout: now) != .success let timeoutWasNotTriggered = semTimedOutOrBlocked.wait(timeout: .now()) == .success if didNotTimeOut && timeoutWasNotTriggered { if self.promise.resolveResult(.blockedRunLoop) { + #if canImport(CoreFoundation) CFRunLoopStop(CFRunLoopGetMain()) + #else + RunLoop.main._stop() + #endif } } } @@ -302,7 +323,11 @@ internal class Awaiter { if completionCount < 2 { func completeBlock() { if promise.resolveResult(.completed(result)) { + #if canImport(CoreFoundation) CFRunLoopStop(CFRunLoopGetMain()) + #else + RunLoop.main._stop() + #endif } } @@ -340,12 +365,20 @@ internal class Awaiter { do { if let result = try closure() { if promise.resolveResult(.completed(result)) { + #if canImport(CoreFoundation) CFRunLoopStop(CFRunLoopGetCurrent()) + #else + RunLoop.current._stop() + #endif } } } catch let error { if promise.resolveResult(.errorThrown(error)) { + #if canImport(CoreFoundation) CFRunLoopStop(CFRunLoopGetCurrent()) + #else + RunLoop.current._stop() + #endif } } } diff --git a/Tests/NimbleTests/AsyncAwaitTest.swift b/Tests/NimbleTests/AsyncAwaitTest.swift index 3c13dea76..ff80b8df0 100644 --- a/Tests/NimbleTests/AsyncAwaitTest.swift +++ b/Tests/NimbleTests/AsyncAwaitTest.swift @@ -85,7 +85,7 @@ final class AsyncAwaitTest: XCTestCase { // swiftlint:disable:this type_body_len @MainActor func testToEventuallyOnMain() async { await expect(1).toEventually(equal(1), timeout: .seconds(300)) - await expect { usleep(10); return 1 }.toEventually(equal(1)) + await expect { try? await Task.sleep(nanoseconds: 10_000); return 1 }.toEventually(equal(1)) } @MainActor diff --git a/Tests/NimbleTests/Matchers/ThrowAssertionTest.swift b/Tests/NimbleTests/Matchers/ThrowAssertionTest.swift index 916104e80..8d547d128 100644 --- a/Tests/NimbleTests/Matchers/ThrowAssertionTest.swift +++ b/Tests/NimbleTests/Matchers/ThrowAssertionTest.swift @@ -9,19 +9,19 @@ private let error: Error = NSError(domain: "test", code: 0, userInfo: nil) final class ThrowAssertionTest: XCTestCase { func testPositiveMatch() { - #if arch(x86_64) || arch(arm64) + #if (arch(x86_64) || arch(arm64)) && !os(Windows) expect { () -> Void in fatalError() }.to(throwAssertion()) #endif } func testErrorThrown() { - #if arch(x86_64) || arch(arm64) + #if (arch(x86_64) || arch(arm64)) && !os(Windows) expect { throw error }.toNot(throwAssertion()) #endif } func testPostAssertionCodeNotRun() { - #if arch(x86_64) || arch(arm64) + #if (arch(x86_64) || arch(arm64)) && !os(Windows) var reachedPoint1 = false var reachedPoint2 = false @@ -37,7 +37,7 @@ final class ThrowAssertionTest: XCTestCase { } func testNegativeMatch() { - #if arch(x86_64) || arch(arm64) + #if (arch(x86_64) || arch(arm64)) && !os(Windows) var reachedPoint1 = false expect { reachedPoint1 = true }.toNot(throwAssertion()) @@ -47,7 +47,7 @@ final class ThrowAssertionTest: XCTestCase { } func testPositiveMessage() { - #if arch(x86_64) || arch(arm64) + #if (arch(x86_64) || arch(arm64)) && !os(Windows) failsWithErrorMessage("expected to throw an assertion") { expect { () -> Void? in return }.to(throwAssertion()) } @@ -59,7 +59,7 @@ final class ThrowAssertionTest: XCTestCase { } func testNegativeMessage() { - #if arch(x86_64) || arch(arm64) + #if (arch(x86_64) || arch(arm64)) && !os(Windows) failsWithErrorMessage("expected to not throw an assertion") { expect { () -> Void in fatalError() }.toNot(throwAssertion()) } @@ -67,13 +67,13 @@ final class ThrowAssertionTest: XCTestCase { } func testNonVoidClosure() { - #if arch(x86_64) || arch(arm64) + #if (arch(x86_64) || arch(arm64)) && !os(Windows) expect { () -> Int in fatalError() }.to(throwAssertion()) #endif } func testChainOnThrowAssertion() { - #if arch(x86_64) || arch(arm64) + #if (arch(x86_64) || arch(arm64)) && !os(Windows) expect { () -> Int in return 5 }.toNot(throwAssertion()).to(equal(5)) #endif } diff --git a/Tests/NimbleTests/PollingTest.swift b/Tests/NimbleTests/PollingTest.swift index 2abe01402..0b7a7c394 100644 --- a/Tests/NimbleTests/PollingTest.swift +++ b/Tests/NimbleTests/PollingTest.swift @@ -1,7 +1,9 @@ #if !os(WASI) import Dispatch +#if canImport(CoreFoundation) import CoreFoundation +#endif import Foundation import XCTest import Nimble