@@ -22,9 +22,11 @@ final class Tests: XCTestCase {
22
22
let items = ( 0 ... Int . random ( in: 10 ... 20 ) ) . map { _ in GoodItem ( ) }
23
23
let lifecycle = Lifecycle ( )
24
24
lifecycle. register ( items)
25
- lifecycle. start ( configuration: . init( shutdownSignal: nil ) ) { error in
26
- XCTAssertNil ( error, " not expecting error " )
27
- lifecycle. shutdown ( )
25
+ lifecycle. start ( configuration: . init( shutdownSignal: nil ) ) { startError in
26
+ XCTAssertNil ( startError, " not expecting error " )
27
+ lifecycle. shutdown { shutdownErrors in
28
+ XCTAssertNil ( shutdownErrors, " not expecting error " )
29
+ }
28
30
}
29
31
lifecycle. wait ( )
30
32
items. forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
@@ -118,104 +120,146 @@ final class Tests: XCTestCase {
118
120
items. suffix ( started + 1 ) . forEach { XCTAssertEqual ( $0. state, . idle, " expected item to be idle, but \( $0. state) " ) }
119
121
}
120
122
123
+ func testShutdownWhenIdle( ) {
124
+ let lifecycle = Lifecycle ( )
125
+ lifecycle. register ( GoodItem ( ) )
126
+
127
+ let sempahpore1 = DispatchSemaphore ( value: 0 )
128
+ lifecycle. shutdown { errors in
129
+ XCTAssertNil ( errors)
130
+ sempahpore1. signal ( )
131
+ }
132
+ lifecycle. wait ( )
133
+ XCTAssertEqual ( . success, sempahpore1. wait ( timeout: . now( ) + 1 ) )
134
+
135
+ let sempahpore2 = DispatchSemaphore ( value: 0 )
136
+ lifecycle. shutdown { errors in
137
+ XCTAssertNil ( errors)
138
+ sempahpore2. signal ( )
139
+ }
140
+ lifecycle. wait ( )
141
+ XCTAssertEqual ( . success, sempahpore2. wait ( timeout: . now( ) + 1 ) )
142
+ }
143
+
144
+ func testShutdownWhenShutdown( ) {
145
+ let lifecycle = Lifecycle ( )
146
+ lifecycle. register ( GoodItem ( ) )
147
+ let sempahpore1 = DispatchSemaphore ( value: 0 )
148
+ lifecycle. start { _ in
149
+ lifecycle. shutdown { errors in
150
+ XCTAssertNil ( errors)
151
+ sempahpore1. signal ( )
152
+ }
153
+ }
154
+ lifecycle. wait ( )
155
+ XCTAssertEqual ( . success, sempahpore1. wait ( timeout: . now( ) + 1 ) )
156
+
157
+ let sempahpore2 = DispatchSemaphore ( value: 0 )
158
+ lifecycle. shutdown { errors in
159
+ XCTAssertNil ( errors)
160
+ sempahpore2. signal ( )
161
+ }
162
+ lifecycle. wait ( )
163
+ XCTAssertEqual ( . success, sempahpore2. wait ( timeout: . now( ) + 1 ) )
164
+ }
165
+
121
166
func testShutdownDuringHangingStart( ) {
122
167
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 ( )
168
+ let blockStartSemaphore = DispatchSemaphore ( value: 0 )
130
169
var startCalls = [ String] ( )
131
170
var stopCalls = [ String] ( )
132
171
133
172
do {
134
173
let id = UUID ( ) . uuidString
135
174
lifecycle. register ( label: id,
136
175
start: {
137
- dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
138
176
startCalls. append ( id)
139
- blockStartGroup . wait ( )
177
+ blockStartSemaphore . wait ( )
140
178
} ,
141
179
shutdown: {
142
- dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
143
180
XCTAssertTrue ( startCalls. contains ( id) )
144
181
stopCalls. append ( id)
145
- waitForShutdownGroup. leave ( )
146
- } )
182
+ } )
147
183
}
148
184
do {
149
185
let id = UUID ( ) . uuidString
150
186
lifecycle. register ( label: id,
151
187
start: {
152
- dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
153
188
startCalls. append ( id)
154
189
} ,
155
190
shutdown: {
156
- dispatchPrecondition ( condition: DispatchPredicate . onQueue ( testQueue) )
157
191
XCTAssertTrue ( startCalls. contains ( id) )
158
192
stopCalls. append ( id)
159
- } )
193
+ } )
160
194
}
161
- lifecycle. start ( configuration: . init( callbackQueue : testQueue , shutdownSignal: nil ) ) { error in
195
+ lifecycle. start ( configuration: . init( shutdownSignal: nil ) ) { error in
162
196
XCTAssertNil ( error)
163
197
}
164
198
lifecycle. shutdown ( )
165
- blockStartGroup. leave ( )
166
- waitForShutdownGroup. wait ( )
199
+ blockStartSemaphore. signal ( )
167
200
lifecycle. wait ( )
168
- XCTAssertEqual ( startCalls. count, stopCalls. count)
169
201
XCTAssertEqual ( startCalls. count, 1 )
202
+ XCTAssertEqual ( stopCalls. count, 1 )
170
203
}
171
204
172
- func testBadStartup ( ) {
205
+ func testShutdownErrors ( ) {
173
206
class BadItem : LifecycleItem {
174
- let label : String = UUID ( ) . uuidString
207
+ let label = UUID ( ) . uuidString
175
208
176
209
func start( callback: ( Error ? ) -> Void ) {
177
- callback ( TestError ( ) )
210
+ callback ( nil )
178
211
}
179
212
180
213
func shutdown( callback: ( Error ? ) -> Void ) {
181
- callback ( nil )
214
+ callback ( TestError ( ) )
182
215
}
183
216
}
184
217
185
- let items : [ LifecycleItem ] = [ GoodItem ( ) , GoodItem ( ) , BadItem ( ) , GoodItem ( ) ]
218
+ var shutdownErrors : [ String : Error ] ?
219
+ let shutdownSemaphore = DispatchSemaphore ( value: 0 )
220
+ let items : [ LifecycleItem ] = [ GoodItem ( ) , BadItem ( ) , BadItem ( ) , GoodItem ( ) , BadItem ( ) ]
186
221
let lifecycle = Lifecycle ( )
187
222
lifecycle. register ( items)
188
223
lifecycle. start ( configuration: . init( shutdownSignal: nil ) ) { error in
189
- XCTAssert ( error is TestError , " expected error to match " )
224
+ XCTAssertNil ( error, " not expecting error " )
225
+ lifecycle. shutdown { errors in
226
+ shutdownErrors = errors
227
+ shutdownSemaphore. signal ( )
228
+ }
190
229
}
191
230
lifecycle. wait ( )
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) " ) }
231
+ XCTAssertEqual ( . success, shutdownSemaphore. wait ( timeout: . now( ) + 1 ) )
232
+
233
+ let goodItems = items. compactMap { $0 as? GoodItem }
234
+ goodItems. forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
235
+ let badItems = items. compactMap { $0 as? BadItem }
236
+ XCTAssertEqual ( shutdownErrors? . count, badItems. count, " expected shutdown errors " )
237
+ badItems. forEach { XCTAssert ( shutdownErrors ? [ $0. label] is TestError , " expected error to match " ) }
195
238
}
196
239
197
- func testBadShutdown ( ) {
240
+ func testStartupErrors ( ) {
198
241
class BadItem : LifecycleItem {
199
242
let label : String = UUID ( ) . uuidString
200
243
201
244
func start( callback: ( Error ? ) -> Void ) {
202
- callback ( nil )
245
+ callback ( TestError ( ) )
203
246
}
204
247
205
248
func shutdown( callback: ( Error ? ) -> Void ) {
206
- callback ( TestError ( ) )
249
+ callback ( nil )
207
250
}
208
251
}
209
252
210
- let items : [ LifecycleItem ] = [ GoodItem ( ) , BadItem ( ) , GoodItem ( ) ]
253
+ let items : [ LifecycleItem ] = [ GoodItem ( ) , GoodItem ( ) , BadItem ( ) , GoodItem ( ) ]
211
254
let lifecycle = Lifecycle ( )
212
255
lifecycle. register ( items)
213
256
lifecycle. start ( configuration: . init( shutdownSignal: nil ) ) { error in
214
- XCTAssertNil ( error, " not expecting error " )
215
- lifecycle. shutdown ( )
257
+ XCTAssert ( error is TestError , " expected error to match " )
216
258
}
217
259
lifecycle. wait ( )
218
- items. compactMap { $0 as? GoodItem } . forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
260
+ let badItemIndex = items. firstIndex { $0 as? BadItem != nil } !
261
+ items. prefix ( badItemIndex) . compactMap { $0 as? GoodItem } . forEach { XCTAssertEqual ( $0. state, . shutdown, " expected item to be shutdown, but \( $0. state) " ) }
262
+ items. suffix ( from: badItemIndex + 1 ) . compactMap { $0 as? GoodItem } . forEach { XCTAssertEqual ( $0. state, . idle, " expected item to be idle, but \( $0. state) " ) }
219
263
}
220
264
221
265
func testStartAndWait( ) {
0 commit comments