Skip to content

Commit 9911316

Browse files
committed
Disable tests that use @MainActor on Linux
## Changes - Disable tests that use `@MainActor` on Linux because of an issue gathering tests: https://github.com/apple/swift-corelibs-xctest/issues/424 - Remove Swift installation from SwiftPM Linux job, as Swift is already included in `ubuntu-latest` (`ubuntu-20.04`): https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2004-Readme.md#language-and-runtime
1 parent a254af7 commit 9911316

14 files changed

+1968
-1961
lines changed

.github/workflows/ci.yml

+1-12
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,8 @@ jobs:
4545
name: SwiftPM Linux
4646
runs-on: ubuntu-latest
4747
steps:
48-
- name: Install Swift
49-
run: |
50-
eval "$(curl -sL https://swiftenv.fuller.li/install.sh)"
51-
- name: Checkout
52-
uses: actions/checkout@v2
53-
- name: Pull dependencies
54-
run: |
55-
swift package resolve
48+
- uses: actions/checkout@v2
5649
- name: Swift version
5750
run: swift --version
58-
- name: Options
59-
run: swift test --help
60-
- name: List Tests
61-
run: swift test --enable-test-discovery --list-tests
6251
- name: Test via SwiftPM
6352
run: swift test --enable-test-discovery

Tests/ComposableArchitectureTests/ComposableArchitectureTests.swift

+128-125
Original file line numberDiff line numberDiff line change
@@ -2,154 +2,157 @@ import ComposableArchitecture
22
import ReactiveSwift
33
import XCTest
44

5-
@MainActor
6-
final class ComposableArchitectureTests: XCTestCase {
7-
func testScheduling() async {
8-
enum CounterAction: Equatable {
9-
case incrAndSquareLater
10-
case incrNow
11-
case squareNow
12-
}
5+
// `@MainActor` introduces issues gathering tests on Linux
6+
#if !os(Linux)
7+
@MainActor
8+
final class ComposableArchitectureTests: XCTestCase {
9+
func testScheduling() async {
10+
enum CounterAction: Equatable {
11+
case incrAndSquareLater
12+
case incrNow
13+
case squareNow
14+
}
1315

14-
let counterReducer = Reducer<Int, CounterAction, DateScheduler> {
15-
state, action, scheduler in
16-
switch action {
17-
case .incrAndSquareLater:
18-
return .merge(
19-
Effect(value: .incrNow).deferred(for: 2, scheduler: scheduler),
20-
Effect(value: .squareNow).deferred(for: 1, scheduler: scheduler),
21-
Effect(value: .squareNow).deferred(for: 2, scheduler: scheduler)
22-
)
23-
case .incrNow:
24-
state += 1
25-
return .none
26-
case .squareNow:
27-
state *= state
28-
return .none
16+
let counterReducer = Reducer<Int, CounterAction, DateScheduler> {
17+
state, action, scheduler in
18+
switch action {
19+
case .incrAndSquareLater:
20+
return .merge(
21+
Effect(value: .incrNow).deferred(for: 2, scheduler: scheduler),
22+
Effect(value: .squareNow).deferred(for: 1, scheduler: scheduler),
23+
Effect(value: .squareNow).deferred(for: 2, scheduler: scheduler)
24+
)
25+
case .incrNow:
26+
state += 1
27+
return .none
28+
case .squareNow:
29+
state *= state
30+
return .none
31+
}
2932
}
33+
34+
let mainQueue = TestScheduler()
35+
36+
let store = TestStore(
37+
initialState: 2,
38+
reducer: counterReducer,
39+
environment: mainQueue
40+
)
41+
42+
await store.send(.incrAndSquareLater)
43+
await mainQueue.advance(by: 1)
44+
await store.receive(.squareNow) { $0 = 4 }
45+
await mainQueue.advance(by: 1)
46+
await store.receive(.incrNow) { $0 = 5 }
47+
await store.receive(.squareNow) { $0 = 25 }
48+
49+
await store.send(.incrAndSquareLater)
50+
await mainQueue.advance(by: 2)
51+
await store.receive(.squareNow) { $0 = 625 }
52+
await store.receive(.incrNow) { $0 = 626 }
53+
await store.receive(.squareNow) { $0 = 391876 }
3054
}
3155

32-
let mainQueue = TestScheduler()
33-
34-
let store = TestStore(
35-
initialState: 2,
36-
reducer: counterReducer,
37-
environment: mainQueue
38-
)
39-
40-
await store.send(.incrAndSquareLater)
41-
await mainQueue.advance(by: 1)
42-
await store.receive(.squareNow) { $0 = 4 }
43-
await mainQueue.advance(by: 1)
44-
await store.receive(.incrNow) { $0 = 5 }
45-
await store.receive(.squareNow) { $0 = 25 }
46-
47-
await store.send(.incrAndSquareLater)
48-
await mainQueue.advance(by: 2)
49-
await store.receive(.squareNow) { $0 = 625 }
50-
await store.receive(.incrNow) { $0 = 626 }
51-
await store.receive(.squareNow) { $0 = 391876 }
52-
}
56+
func testSimultaneousWorkOrdering() {
57+
let testScheduler = TestScheduler()
5358

54-
func testSimultaneousWorkOrdering() {
55-
let testScheduler = TestScheduler()
59+
var values: [Int] = []
60+
testScheduler.schedule(after: .seconds(0), interval: .seconds(1)) { values.append(1) }
61+
testScheduler.schedule(after: .seconds(0), interval: .seconds(2)) { values.append(42) }
5662

57-
var values: [Int] = []
58-
testScheduler.schedule(after: .seconds(0), interval: .seconds(1)) { values.append(1) }
59-
testScheduler.schedule(after: .seconds(0), interval: .seconds(2)) { values.append(42) }
63+
XCTAssertNoDifference(values, [])
64+
testScheduler.advance()
65+
XCTAssertNoDifference(values, [1, 42])
66+
testScheduler.advance(by: 2)
67+
XCTAssertNoDifference(values, [1, 42, 1, 42, 1])
68+
}
6069

61-
XCTAssertNoDifference(values, [])
62-
testScheduler.advance()
63-
XCTAssertNoDifference(values, [1, 42])
64-
testScheduler.advance(by: 2)
65-
XCTAssertNoDifference(values, [1, 42, 1, 42, 1])
66-
}
70+
func testLongLivingEffects() async {
71+
typealias Environment = (
72+
startEffect: Effect<Void, Never>,
73+
stopEffect: Effect<Never, Never>
74+
)
6775

68-
func testLongLivingEffects() async {
69-
typealias Environment = (
70-
startEffect: Effect<Void, Never>,
71-
stopEffect: Effect<Never, Never>
72-
)
73-
74-
enum Action { case end, incr, start }
75-
76-
let reducer = Reducer<Int, Action, Environment> { state, action, environment in
77-
switch action {
78-
case .end:
79-
return environment.stopEffect.fireAndForget()
80-
case .incr:
81-
state += 1
82-
return .none
83-
case .start:
84-
return environment.startEffect.map { Action.incr }
76+
enum Action { case end, incr, start }
77+
78+
let reducer = Reducer<Int, Action, Environment> { state, action, environment in
79+
switch action {
80+
case .end:
81+
return environment.stopEffect.fireAndForget()
82+
case .incr:
83+
state += 1
84+
return .none
85+
case .start:
86+
return environment.startEffect.map { Action.incr }
87+
}
8588
}
86-
}
8789

88-
let subject = Signal<Void, Never>.pipe()
90+
let subject = Signal<Void, Never>.pipe()
8991

90-
let store = TestStore(
91-
initialState: 0,
92-
reducer: reducer,
93-
environment: (
94-
startEffect: subject.output.producer.eraseToEffect(),
95-
stopEffect: .fireAndForget { subject.input.sendCompleted() }
92+
let store = TestStore(
93+
initialState: 0,
94+
reducer: reducer,
95+
environment: (
96+
startEffect: subject.output.producer.eraseToEffect(),
97+
stopEffect: .fireAndForget { subject.input.sendCompleted() }
98+
)
9699
)
97-
)
98100

99-
await store.send(.start)
100-
await store.send(.incr) { $0 = 1 }
101-
subject.input.send(value: ())
102-
await store.receive(.incr) { $0 = 2 }
103-
await store.send(.end)
104-
}
101+
await store.send(.start)
102+
await store.send(.incr) { $0 = 1 }
103+
subject.input.send(value: ())
104+
await store.receive(.incr) { $0 = 2 }
105+
await store.send(.end)
106+
}
105107

106-
func testCancellation() async {
107-
let mainQueue = TestScheduler()
108+
func testCancellation() async {
109+
let mainQueue = TestScheduler()
108110

109-
enum Action: Equatable {
110-
case cancel
111-
case incr
112-
case response(Int)
113-
}
111+
enum Action: Equatable {
112+
case cancel
113+
case incr
114+
case response(Int)
115+
}
114116

115-
struct Environment {
116-
let fetch: (Int) async -> Int
117-
}
117+
struct Environment {
118+
let fetch: (Int) async -> Int
119+
}
118120

119-
let reducer = Reducer<Int, Action, Environment> { state, action, environment in
120-
enum CancelID {}
121+
let reducer = Reducer<Int, Action, Environment> { state, action, environment in
122+
enum CancelID {}
121123

122-
switch action {
123-
case .cancel:
124-
return .cancel(id: CancelID.self)
124+
switch action {
125+
case .cancel:
126+
return .cancel(id: CancelID.self)
125127

126-
case .incr:
127-
state += 1
128-
return .task { [state] in
129-
try await mainQueue.sleep(for: .seconds(1))
130-
return .response(await environment.fetch(state))
131-
}
132-
.cancellable(id: CancelID.self)
128+
case .incr:
129+
state += 1
130+
return .task { [state] in
131+
try await mainQueue.sleep(for: .seconds(1))
132+
return .response(await environment.fetch(state))
133+
}
134+
.cancellable(id: CancelID.self)
133135

134-
case let .response(value):
135-
state = value
136-
return .none
136+
case let .response(value):
137+
state = value
138+
return .none
139+
}
137140
}
138-
}
139141

140-
let store = TestStore(
141-
initialState: 0,
142-
reducer: reducer,
143-
environment: Environment(
144-
fetch: { value in value * value }
142+
let store = TestStore(
143+
initialState: 0,
144+
reducer: reducer,
145+
environment: Environment(
146+
fetch: { value in value * value }
147+
)
145148
)
146-
)
147149

148-
await store.send(.incr) { $0 = 1 }
149-
await mainQueue.advance(by: .seconds(1))
150-
await store.receive(.response(1))
150+
await store.send(.incr) { $0 = 1 }
151+
await mainQueue.advance(by: .seconds(1))
152+
await store.receive(.response(1))
151153

152-
await store.send(.incr) { $0 = 2 }
153-
await store.send(.cancel)
154+
await store.send(.incr) { $0 = 2 }
155+
await store.send(.cancel)
156+
}
154157
}
155-
}
158+
#endif

0 commit comments

Comments
 (0)