@@ -4,122 +4,122 @@ import XCTest
4
4
5
5
// `@MainActor` introduces issues gathering tests on Linux
6
6
#if !os(Linux)
7
- @MainActor
8
- final class CompatibilityTests : XCTestCase {
9
- // Actions can be re-entrantly sent into the store if an action is sent that holds an object
10
- // which sends an action on deinit. In order to prevent a simultaneous access exception for this
11
- // case we need to use `withExtendedLifetime` on the buffered actions when clearing them out.
12
- func testCaseStudy_ActionReentranceFromClearedBufferCausingDeinitAction( ) {
13
- let cancelID = UUID ( )
14
-
15
- struct State : Equatable { }
16
- enum Action : Equatable {
17
- case start
18
- case kickOffAction
19
- case actionSender( OnDeinit )
20
- case stop
21
-
22
- var description : String {
23
- switch self {
24
- case . start:
25
- return " start "
26
- case . kickOffAction:
27
- return " kickOffAction "
28
- case . actionSender:
29
- return " actionSender "
30
- case . stop:
31
- return " stop "
7
+ @MainActor
8
+ final class CompatibilityTests : XCTestCase {
9
+ // Actions can be re-entrantly sent into the store if an action is sent that holds an object
10
+ // which sends an action on deinit. In order to prevent a simultaneous access exception for this
11
+ // case we need to use `withExtendedLifetime` on the buffered actions when clearing them out.
12
+ func testCaseStudy_ActionReentranceFromClearedBufferCausingDeinitAction( ) {
13
+ let cancelID = UUID ( )
14
+
15
+ struct State : Equatable { }
16
+ enum Action : Equatable {
17
+ case start
18
+ case kickOffAction
19
+ case actionSender( OnDeinit )
20
+ case stop
21
+
22
+ var description : String {
23
+ switch self {
24
+ case . start:
25
+ return " start "
26
+ case . kickOffAction:
27
+ return " kickOffAction "
28
+ case . actionSender:
29
+ return " actionSender "
30
+ case . stop:
31
+ return " stop "
32
+ }
32
33
}
33
34
}
34
- }
35
35
let ( signal, observer) = Signal < Action , Never > . pipe ( )
36
36
37
- var handledActions : [ String ] = [ ]
37
+ var handledActions : [ String ] = [ ]
38
38
39
- let reducer = AnyReducer < State , Action , Void > { state, action, env in
40
- handledActions. append ( action. description)
39
+ let reducer = AnyReducer < State , Action , Void > { state, action, env in
40
+ handledActions. append ( action. description)
41
41
42
- switch action {
43
- case . start:
42
+ switch action {
43
+ case . start:
44
44
return signal. producer
45
- . eraseToEffect ( )
46
- . cancellable ( id: cancelID)
45
+ . eraseToEffect ( )
46
+ . cancellable ( id: cancelID)
47
47
48
- case . kickOffAction:
48
+ case . kickOffAction:
49
49
return EffectTask ( value: . actionSender( OnDeinit { observer. send ( value: . stop) } ) )
50
50
51
- case . actionSender:
52
- return . none
51
+ case . actionSender:
52
+ return . none
53
53
54
- case . stop:
55
- return . cancel( id: cancelID)
54
+ case . stop:
55
+ return . cancel( id: cancelID)
56
+ }
56
57
}
57
- }
58
58
59
- let store = Store (
60
- initialState: . init( ) ,
61
- reducer: reducer,
62
- environment: ( )
63
- )
64
-
65
- let viewStore = ViewStore ( store)
66
-
67
- viewStore. send ( . start)
68
- viewStore. send ( . kickOffAction)
69
-
70
- XCTAssertEqual (
71
- handledActions,
72
- [
73
- " start " ,
74
- " kickOffAction " ,
75
- " actionSender " ,
76
- " stop " ,
77
- ]
78
- )
79
- }
59
+ let store = Store (
60
+ initialState: . init( ) ,
61
+ reducer: reducer,
62
+ environment: ( )
63
+ )
64
+
65
+ let viewStore = ViewStore ( store)
66
+
67
+ viewStore. send ( . start)
68
+ viewStore. send ( . kickOffAction)
69
+
70
+ XCTAssertEqual (
71
+ handledActions,
72
+ [
73
+ " start " ,
74
+ " kickOffAction " ,
75
+ " actionSender " ,
76
+ " stop " ,
77
+ ]
78
+ )
79
+ }
80
80
81
- // Actions can be re-entrantly sent into the store while observing changes to the store's state.
82
- // In such cases we need to take special care that those re-entrant actions are handled _after_
83
- // the original action.
84
- //
85
- // In particular, this means that in the implementation of `Store.send` we need to flip
86
- // `isSending` to false _after_ the store's state mutation is made so that re-entrant actions
87
- // are buffered rather than immediately handled.
88
- func testCaseStudy_ActionReentranceFromStateObservation( ) {
89
- let store = Store < Int , Int > (
90
- initialState: 0 ,
91
- reducer: . init { state, action, _ in
92
- state = action
93
- return . none
94
- } ,
95
- environment: ( )
96
- )
97
-
98
- let viewStore = ViewStore ( store)
81
+ // Actions can be re-entrantly sent into the store while observing changes to the store's state.
82
+ // In such cases we need to take special care that those re-entrant actions are handled _after_
83
+ // the original action.
84
+ //
85
+ // In particular, this means that in the implementation of `Store.send` we need to flip
86
+ // `isSending` to false _after_ the store's state mutation is made so that re-entrant actions
87
+ // are buffered rather than immediately handled.
88
+ func testCaseStudy_ActionReentranceFromStateObservation( ) {
89
+ let store = Store < Int , Int > (
90
+ initialState: 0 ,
91
+ reducer: . init { state, action, _ in
92
+ state = action
93
+ return . none
94
+ } ,
95
+ environment: ( )
96
+ )
97
+
98
+ let viewStore = ViewStore ( store)
99
99
100
100
viewStore. produced. producer
101
101
. startWithValues { value in
102
- if value == 1 {
103
- viewStore. send ( 0 )
102
+ if value == 1 {
103
+ viewStore. send ( 0 )
104
+ }
104
105
}
105
- }
106
106
107
- var stateChanges : [ Int ] = [ ]
107
+ var stateChanges : [ Int ] = [ ]
108
108
viewStore. produced. producer
109
109
. startWithValues { stateChanges. append ( $0) }
110
110
111
- XCTAssertEqual ( stateChanges, [ 0 ] )
112
- viewStore. send ( 1 )
113
- XCTAssertEqual ( stateChanges, [ 0 , 1 , 0 ] )
111
+ XCTAssertEqual ( stateChanges, [ 0 ] )
112
+ viewStore. send ( 1 )
113
+ XCTAssertEqual ( stateChanges, [ 0 , 1 , 0 ] )
114
+ }
114
115
}
115
- }
116
116
117
- private final class OnDeinit : Equatable {
118
- private let onDeinit : ( ) -> Void
119
- init ( onDeinit: @escaping ( ) -> Void ) {
120
- self . onDeinit = onDeinit
117
+ private final class OnDeinit : Equatable {
118
+ private let onDeinit : ( ) -> Void
119
+ init ( onDeinit: @escaping ( ) -> Void ) {
120
+ self . onDeinit = onDeinit
121
+ }
122
+ deinit { self . onDeinit ( ) }
123
+ static func == ( lhs: OnDeinit , rhs: OnDeinit ) -> Bool { true }
121
124
}
122
- deinit { self . onDeinit ( ) }
123
- static func == ( lhs: OnDeinit , rhs: OnDeinit ) -> Bool { true }
124
- }
125
125
#endif
0 commit comments