Skip to content

Commit f616e68

Browse files
leogdionclaude
andcommitted
fix(tests): disable DispatchQueue-dependent tests on WASM
- Add isWasm boolean (via canImport(Dispatch)) to SundialKitCoreTests and SundialKitNetworkTests - Wrap MockPathMonitor (both targets) in #if canImport(Dispatch) - Guard each test function body with #if canImport(Dispatch)/#else, recording an issue in the #else branch as a safety net - Add .disabled(if: isWasm) trait to NetworkMonitorTests suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 77d0079 commit f616e68

7 files changed

Lines changed: 434 additions & 293 deletions

File tree

Tests/SundialKitCoreTests/MockPathMonitor.swift

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,41 @@ import Foundation
99
@testable import SundialKitCore
1010
@testable import SundialKitNetwork
1111

12-
internal final class MockPathMonitor: PathMonitor, @unchecked Sendable {
13-
internal typealias PathType = MockPath
14-
15-
internal let id: UUID
16-
internal private(set) var pathUpdate: ((MockPath) -> Void)?
17-
internal private(set) var dispatchQueueLabel: String?
18-
internal private(set) var isCancelled = false
19-
internal init(id: UUID) {
20-
self.id = id
21-
}
12+
#if canImport(Dispatch)
2213

23-
internal func onPathUpdate(_ handler: @escaping (MockPath) -> Void) {
24-
pathUpdate = handler
25-
}
14+
internal final class MockPathMonitor: PathMonitor, @unchecked Sendable {
15+
internal typealias PathType = MockPath
16+
17+
internal let id: UUID
18+
internal private(set) var pathUpdate: ((MockPath) -> Void)?
19+
internal private(set) var dispatchQueueLabel: String?
20+
internal private(set) var isCancelled = false
21+
internal init(id: UUID) {
22+
self.id = id
23+
}
24+
25+
internal func onPathUpdate(_ handler: @escaping (MockPath) -> Void) {
26+
pathUpdate = handler
27+
}
2628

27-
internal func start(queue: DispatchQueue) {
28-
dispatchQueueLabel = queue.label
29-
pathUpdate?(
30-
.init(
31-
isConstrained: false,
32-
isExpensive: false,
33-
pathStatus: .satisfied(.wiredEthernet)
29+
internal func start(queue: DispatchQueue) {
30+
dispatchQueueLabel = queue.label
31+
pathUpdate?(
32+
.init(
33+
isConstrained: false,
34+
isExpensive: false,
35+
pathStatus: .satisfied(.wiredEthernet)
36+
)
3437
)
35-
)
36-
}
38+
}
3739

38-
internal func cancel() {
39-
isCancelled = true
40-
}
40+
internal func cancel() {
41+
isCancelled = true
42+
}
4143

42-
internal func sendPath(_ path: MockPath) {
43-
pathUpdate?(path)
44+
internal func sendPath(_ path: MockPath) {
45+
pathUpdate?(path)
46+
}
4447
}
45-
}
48+
49+
#endif
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// WASMSupport.swift
3+
// SundialKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2026 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the "Software"), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
#if canImport(Dispatch)
31+
let isWasm = false
32+
#else
33+
let isWasm = true
34+
#endif

Tests/SundialKitNetworkTests/MockPathMonitor.swift

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,41 @@ import Foundation
99
@testable import SundialKitCore
1010
@testable import SundialKitNetwork
1111

12-
internal final class MockPathMonitor: PathMonitor, @unchecked Sendable {
13-
internal typealias PathType = MockPath
14-
15-
internal let id: UUID
16-
internal private(set) var pathUpdate: ((MockPath) -> Void)?
17-
internal private(set) var dispatchQueueLabel: String?
18-
internal private(set) var isCancelled = false
19-
internal init(id: UUID) {
20-
self.id = id
21-
}
12+
#if canImport(Dispatch)
2213

23-
internal func onPathUpdate(_ handler: @escaping (MockPath) -> Void) {
24-
pathUpdate = handler
25-
}
14+
internal final class MockPathMonitor: PathMonitor, @unchecked Sendable {
15+
internal typealias PathType = MockPath
16+
17+
internal let id: UUID
18+
internal private(set) var pathUpdate: ((MockPath) -> Void)?
19+
internal private(set) var dispatchQueueLabel: String?
20+
internal private(set) var isCancelled = false
21+
internal init(id: UUID) {
22+
self.id = id
23+
}
24+
25+
internal func onPathUpdate(_ handler: @escaping (MockPath) -> Void) {
26+
pathUpdate = handler
27+
}
2628

27-
internal func start(queue: DispatchQueue) {
28-
dispatchQueueLabel = queue.label
29-
pathUpdate?(
30-
.init(
31-
isConstrained: false,
32-
isExpensive: false,
33-
pathStatus: .satisfied(.wiredEthernet)
29+
internal func start(queue: DispatchQueue) {
30+
dispatchQueueLabel = queue.label
31+
pathUpdate?(
32+
.init(
33+
isConstrained: false,
34+
isExpensive: false,
35+
pathStatus: .satisfied(.wiredEthernet)
36+
)
3437
)
35-
)
36-
}
38+
}
3739

38-
internal func cancel() {
39-
isCancelled = true
40-
}
40+
internal func cancel() {
41+
isCancelled = true
42+
}
4143

42-
internal func sendPath(_ path: MockPath) {
43-
pathUpdate?(path)
44+
internal func sendPath(_ path: MockPath) {
45+
pathUpdate?(path)
46+
}
4447
}
45-
}
48+
49+
#endif

Tests/SundialKitNetworkTests/NetworkMonitorTests+ConcurrencyTests.swift

Lines changed: 52 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,70 +17,82 @@ extension NetworkMonitorTests {
1717

1818
@Test("NetworkMonitor handles concurrent property access")
1919
func testConcurrentPropertyAccess() async {
20-
let pathMonitor = MockPathMonitor(id: UUID())
21-
let monitor = NetworkMonitor(monitor: pathMonitor, ping: nil as NeverPing?)
22-
23-
monitor.start(queue: .global())
24-
25-
await withTaskGroup(of: Void.self) { group in
26-
for _ in 0..<100 {
27-
group.addTask {
28-
_ = await monitor.pathStatus
29-
_ = await monitor.isExpensive
30-
_ = await monitor.isConstrained
20+
#if canImport(Dispatch)
21+
let pathMonitor = MockPathMonitor(id: UUID())
22+
let monitor = NetworkMonitor(monitor: pathMonitor, ping: nil as NeverPing?)
23+
24+
monitor.start(queue: .global())
25+
26+
await withTaskGroup(of: Void.self) { group in
27+
for _ in 0..<100 {
28+
group.addTask {
29+
_ = await monitor.pathStatus
30+
_ = await monitor.isExpensive
31+
_ = await monitor.isConstrained
32+
}
3133
}
3234
}
33-
}
3435

35-
// If we get here without crashing, thread safety is working
36-
#expect(true)
36+
// If we get here without crashing, thread safety is working
37+
#expect(true)
38+
#else
39+
Issue.record("This test requires Dispatch and should be disabled on WASM")
40+
#endif
3741
}
3842

3943
@Test("NetworkMonitor handles concurrent observer operations")
4044
func testConcurrentObserverOperations() async {
41-
let pathMonitor = MockPathMonitor(id: UUID())
42-
let monitor = NetworkMonitor(monitor: pathMonitor, ping: nil as NeverPing?)
43-
let observers = (0..<10).map { _ in TestNetworkStateObserver() }
45+
#if canImport(Dispatch)
46+
let pathMonitor = MockPathMonitor(id: UUID())
47+
let monitor = NetworkMonitor(monitor: pathMonitor, ping: nil as NeverPing?)
48+
let observers = (0..<10).map { _ in TestNetworkStateObserver() }
4449

45-
monitor.start(queue: .global())
50+
monitor.start(queue: .global())
4651

47-
await withTaskGroup(of: Void.self) { group in
48-
for observer in observers {
49-
group.addTask {
50-
await monitor.addObserver(observer)
52+
await withTaskGroup(of: Void.self) { group in
53+
for observer in observers {
54+
group.addTask {
55+
await monitor.addObserver(observer)
56+
}
5157
}
52-
}
5358

54-
for observer in observers {
55-
group.addTask {
56-
let observerToRemove = observer
57-
await monitor.removeObservers {
58-
($0 as? TestNetworkStateObserver) === observerToRemove
59+
for observer in observers {
60+
group.addTask {
61+
let observerToRemove = observer
62+
await monitor.removeObservers {
63+
($0 as? TestNetworkStateObserver) === observerToRemove
64+
}
5965
}
6066
}
6167
}
62-
}
6368

64-
// If we get here without crashing, thread safety is working
65-
#expect(true)
69+
// If we get here without crashing, thread safety is working
70+
#expect(true)
71+
#else
72+
Issue.record("This test requires Dispatch and should be disabled on WASM")
73+
#endif
6674
}
6775

6876
// MARK: - Ping Integration Tests
6977

7078
@Test("NetworkMonitor integrates with ping")
7179
func testPingIntegration() async throws {
72-
let pathMonitor = MockPathMonitor(id: UUID())
73-
let ping = MockNetworkPing(id: UUID(), timeInterval: 0.1)
74-
let monitor = NetworkMonitor(monitor: pathMonitor, ping: ping)
80+
#if canImport(Dispatch)
81+
let pathMonitor = MockPathMonitor(id: UUID())
82+
let ping = MockNetworkPing(id: UUID(), timeInterval: 0.1)
83+
let monitor = NetworkMonitor(monitor: pathMonitor, ping: ping)
7584

76-
monitor.start(queue: .global())
85+
monitor.start(queue: .global())
7786

78-
// Wait for potential ping to execute
79-
try await Task.sleep(forMilliseconds: 500)
87+
// Wait for potential ping to execute
88+
try await Task.sleep(forMilliseconds: 500)
8089

81-
monitor.stop()
90+
monitor.stop()
8291

83-
// Test passes if no crashes occurred during ping execution
84-
#expect(true)
92+
// Test passes if no crashes occurred during ping execution
93+
#expect(true)
94+
#else
95+
Issue.record("This test requires Dispatch and should be disabled on WASM")
96+
#endif
8597
}
8698
}

0 commit comments

Comments
 (0)