@@ -4,122 +4,122 @@ import XCTest
44
55// `@MainActor` introduces issues gathering tests on Linux
66#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+ }
3233 }
3334 }
34- }
3535 let ( signal, observer) = Signal < Action , Never > . pipe ( )
3636
37- var handledActions : [ String ] = [ ]
37+ var handledActions : [ String ] = [ ]
3838
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)
4141
42- switch action {
43- case . start:
42+ switch action {
43+ case . start:
4444 return signal. producer
45- . eraseToEffect ( )
46- . cancellable ( id: cancelID)
45+ . eraseToEffect ( )
46+ . cancellable ( id: cancelID)
4747
48- case . kickOffAction:
48+ case . kickOffAction:
4949 return EffectTask ( value: . actionSender( OnDeinit { observer. send ( value: . stop) } ) )
5050
51- case . actionSender:
52- return . none
51+ case . actionSender:
52+ return . none
5353
54- case . stop:
55- return . cancel( id: cancelID)
54+ case . stop:
55+ return . cancel( id: cancelID)
56+ }
5657 }
57- }
5858
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+ }
8080
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)
9999
100100 viewStore. produced. producer
101101 . startWithValues { value in
102- if value == 1 {
103- viewStore. send ( 0 )
102+ if value == 1 {
103+ viewStore. send ( 0 )
104+ }
104105 }
105- }
106106
107- var stateChanges : [ Int ] = [ ]
107+ var stateChanges : [ Int ] = [ ]
108108 viewStore. produced. producer
109109 . startWithValues { stateChanges. append ( $0) }
110110
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+ }
114115 }
115- }
116116
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 }
121124 }
122- deinit { self . onDeinit ( ) }
123- static func == ( lhs: OnDeinit , rhs: OnDeinit ) -> Bool { true }
124- }
125125#endif
0 commit comments