@@ -45,14 +45,128 @@ final class Tests: XCTestCase {
45
45
items. forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
46
46
}
47
47
48
- func testImmediateShutdown( ) {
49
- let items = ( 0 ... Int . random ( in: 10 ... 20 ) ) . map { _ in GoodItem ( ) }
48
+ func testDispatchQueues( ) {
49
+ let lifecycle = Lifecycle ( )
50
+ let testQueue = DispatchQueue ( label: UUID ( ) . uuidString)
51
+
52
+ lifecycle. register ( label: UUID ( ) . uuidString,
53
+ start: {
54
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
55
+ } ,
56
+ shutdown: {
57
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
58
+ } )
59
+ lifecycle. register ( label: UUID ( ) . uuidString,
60
+ start: {
61
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
62
+ } ,
63
+ shutdown: {
64
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
65
+ } )
66
+ lifecycle. start ( configuration: . init( callbackQueue: testQueue, shutdownSignal: nil ) ) { error in
67
+ XCTAssertNil ( error)
68
+ lifecycle. shutdown ( )
69
+ }
70
+ lifecycle. wait ( )
71
+ }
72
+
73
+ func testShutdownWhileStarting( ) {
74
+ class Item : LifecycleItem {
75
+ let startedCallback : ( ) -> Void
76
+ var state = State . idle
77
+
78
+ let label = UUID ( ) . uuidString
79
+
80
+ init ( _ startedCallback: @escaping ( ) -> Void ) {
81
+ self . startedCallback = startedCallback
82
+ }
83
+
84
+ func start( callback: @escaping ( Error ? ) -> Void ) {
85
+ DispatchQueue . global ( ) . asyncAfter ( deadline: . now( ) + 0.05 ) {
86
+ self . state = . started
87
+ self . startedCallback ( )
88
+ callback ( nil )
89
+ }
90
+ }
91
+
92
+ func shutdown( callback: ( Error ? ) -> Void ) {
93
+ self . state = . shutdown
94
+ callback ( nil )
95
+ }
96
+
97
+ enum State {
98
+ case idle
99
+ case started
100
+ case shutdown
101
+ }
102
+ }
103
+ var started = 0
104
+ let startSempahore = DispatchSemaphore ( value: 0 )
105
+ let items = ( 0 ... Int . random ( in: 10 ... 20 ) ) . map { _ in Item {
106
+ started += 1
107
+ startSempahore. signal ( )
108
+ } }
50
109
let lifecycle = Lifecycle ( )
51
110
lifecycle. register ( items)
52
111
lifecycle. start ( configuration: . init( shutdownSignal: nil ) ) { _ in }
112
+ startSempahore. wait ( )
53
113
lifecycle. shutdown ( )
54
114
lifecycle. wait ( )
55
- items. forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
115
+ XCTAssertGreaterThan ( started, 0 , " expected some start " )
116
+ XCTAssertLessThan ( started, items. count, " exppcts partial start " )
117
+ items. prefix ( started) . forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
118
+ items. suffix ( started + 1 ) . forEach { XCTAssertEqual ( $0. state, . idle, " expected item to be idle, but \( $0. state) " ) }
119
+ }
120
+
121
+ func testShutdownDuringHangingStart( ) {
122
+ let lifecycle = Lifecycle ( )
123
+ let testQueue = DispatchQueue ( label: UUID ( ) . uuidString)
124
+ let blockStartGroup = DispatchGroup ( )
125
+ let waitForShutdownGroup = DispatchGroup ( )
126
+ let shutdownCompleteGroup = DispatchGroup ( )
127
+ blockStartGroup. enter ( )
128
+ waitForShutdownGroup. enter ( )
129
+ shutdownCompleteGroup. enter ( )
130
+ var startCalls = [ String] ( )
131
+ var stopCalls = [ String] ( )
132
+
133
+ do {
134
+ let id = UUID ( ) . uuidString
135
+ lifecycle. register ( label: id,
136
+ start: {
137
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
138
+ startCalls. append ( id)
139
+ blockStartGroup. wait ( )
140
+ } ,
141
+ shutdown: {
142
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
143
+ XCTAssertTrue ( startCalls. contains ( id) )
144
+ stopCalls. append ( id)
145
+ waitForShutdownGroup. leave ( )
146
+ } )
147
+ }
148
+ do {
149
+ let id = UUID ( ) . uuidString
150
+ lifecycle. register ( label: id,
151
+ start: {
152
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
153
+ startCalls. append ( id)
154
+ } ,
155
+ shutdown: {
156
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
157
+ XCTAssertTrue ( startCalls. contains ( id) )
158
+ stopCalls. append ( id)
159
+ } )
160
+ }
161
+ lifecycle. start ( configuration: . init( callbackQueue: testQueue, shutdownSignal: nil ) ) { error in
162
+ XCTAssertNil ( error)
163
+ }
164
+ lifecycle. shutdown ( )
165
+ blockStartGroup. leave ( )
166
+ waitForShutdownGroup. wait ( )
167
+ lifecycle. wait ( )
168
+ XCTAssertEqual ( startCalls. count, stopCalls. count)
169
+ XCTAssertEqual ( startCalls. count, 1 )
56
170
}
57
171
58
172
func testBadStartup( ) {
@@ -68,15 +182,16 @@ final class Tests: XCTestCase {
68
182
}
69
183
}
70
184
71
- let items : [ LifecycleItem ] = [ GoodItem ( ) , BadItem ( ) , GoodItem ( ) ]
185
+ let items : [ LifecycleItem ] = [ GoodItem ( ) , GoodItem ( ) , BadItem ( ) , GoodItem ( ) ]
72
186
let lifecycle = Lifecycle ( )
73
187
lifecycle. register ( items)
74
188
lifecycle. start ( configuration: . init( shutdownSignal: nil ) ) { error in
75
189
XCTAssert ( error is TestError , " expected error to match " )
76
190
}
77
191
lifecycle. wait ( )
78
- let goodItems = items. compactMap { $0 as? GoodItem }
79
- goodItems. forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
192
+ let badItemIndex = items. firstIndex { $0 as? BadItem != nil } !
193
+ items. prefix ( badItemIndex) . compactMap { $0 as? GoodItem } . forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
194
+ items. suffix ( from: badItemIndex + 1 ) . compactMap { $0 as? GoodItem } . forEach { XCTAssertEqual ( $0. state, . idle, " expected item to be idle, but \( $0. state) " ) }
80
195
}
81
196
82
197
func testBadShutdown( ) {
@@ -386,11 +501,9 @@ final class Tests: XCTestCase {
386
501
let item = GoodItem ( )
387
502
lifecycle. register ( label: " test " ,
388
503
start: . async { callback in
389
- print ( " start " )
390
504
item. start ( callback: callback)
391
505
} ,
392
506
shutdown: . async { callback in
393
- print ( " shutdown " )
394
507
item. shutdown ( callback: callback)
395
508
} )
396
509
@@ -407,7 +520,6 @@ final class Tests: XCTestCase {
407
520
408
521
let item = GoodItem ( )
409
522
lifecycle. registerShutdown ( label: " test " , . async { callback in
410
- print ( " shutdown " )
411
523
item. shutdown ( callback: callback)
412
524
} )
413
525
0 commit comments