@@ -43,6 +43,18 @@ func isDownloadContentError(_ err: Error?, key: OneWaySynchronizerItemKey, under
43
43
}
44
44
}
45
45
46
+ func isCancelError( _ err: Error ? ) -> Bool {
47
+ guard let err = err as? OwsError else {
48
+ return false
49
+ }
50
+ switch err {
51
+ case . cancelledError:
52
+ return true
53
+ default :
54
+ return false
55
+ }
56
+ }
57
+
46
58
class TestItem : OneWaySynchronizerItemDescription {
47
59
48
60
func vss_shouldBeUpdated( with remoteItem: TestItem ) -> Bool {
@@ -51,11 +63,19 @@ class TestItem: OneWaySynchronizerItemDescription {
51
63
52
64
typealias K = String
53
65
54
- var owsPrimaryKey : String
66
+ var owsPrimaryKey : OneWaySynchronizerItemKey
67
+ var owsDownloadOrder : OneWaySynchronizerItemDownloadOrder
55
68
let downloadError : Error ?
56
69
70
+ init ( key: String , order: Int , downloadError: Error ? = nil ) {
71
+ owsPrimaryKey = key
72
+ owsDownloadOrder = order
73
+ self . downloadError = downloadError
74
+ }
75
+
57
76
init ( key: String , downloadError: Error ? = nil ) {
58
77
owsPrimaryKey = key
78
+ owsDownloadOrder = 0
59
79
self . downloadError = downloadError
60
80
}
61
81
}
@@ -69,11 +89,11 @@ class TestProcessor: OneWaySynchronizerProcessor {
69
89
70
90
var beginCalled = 0
71
91
var endCalled = 0
72
-
73
92
var shouldBeUpdated = Set < String > ( )
74
-
75
93
var downloadedPreview = Set < String > ( )
76
94
var downloaded = Set < String > ( )
95
+ var downloadedOrder = [ String] ( )
96
+ var downloadedPreviewOrder = [ String] ( )
77
97
var removed = Set < String > ( )
78
98
79
99
var existingKeys = Array < K > ( )
@@ -131,18 +151,24 @@ class TestProcessor: OneWaySynchronizerProcessor {
131
151
func owsDownloadItemPreview( forDescription description: OneWaySynchronizerItemDescription , completion: @escaping OwsSimpleCompletion ) {
132
152
objc_sync_enter ( self ) ; defer { objc_sync_exit ( self ) }
133
153
downloadedPreview. insert ( description. owsPrimaryKey)
154
+ downloadedPreviewOrder. append ( description. owsPrimaryKey)
134
155
completion ( nil )
135
156
}
136
157
137
158
func owsDownloadItem( forDescription description: OneWaySynchronizerItemDescription , completion: @escaping OwsSimpleCompletion ) {
138
159
objc_sync_enter ( self ) ; defer { objc_sync_exit ( self ) }
139
160
downloaded. insert ( description. owsPrimaryKey)
161
+ downloadedOrder. append ( description. owsPrimaryKey)
140
162
if !existingKeys. contains ( description. owsPrimaryKey) {
141
163
existingKeys. append ( description. owsPrimaryKey)
142
164
}
143
165
completion ( ( description as! TestItem ) . downloadError)
144
166
}
145
167
168
+ func owsPrepareDownload( of descriptions: [ OneWaySynchronizerItemDescription ] , completion: @escaping OwsItemsCompletion ) {
169
+ completion ( nil , descriptions)
170
+ }
171
+
146
172
}
147
173
148
174
class OneWaySynchronizerDemoTests : XCTestCase {
@@ -165,8 +191,9 @@ class OneWaySynchronizerDemoTests: XCTestCase {
165
191
let expectation = XCTestExpectation ( description: " sync " )
166
192
var failed : [ Error ] ?
167
193
let processor = TestProcessor ( fetchList: [
168
- TestItem ( key: " A " ) ,
169
- TestItem ( key: " B " )
194
+ TestItem ( key: " A " , order: 1 ) ,
195
+ TestItem ( key: " B " , order: 0 ) ,
196
+ TestItem ( key: " C " , order: 2 )
170
197
] )
171
198
let service = OneWaySynchronizer ( processor: processor)
172
199
service. concurrency = 1
@@ -194,6 +221,7 @@ class OneWaySynchronizerDemoTests: XCTestCase {
194
221
XCTAssert ( processor. endCalled == 1 )
195
222
XCTAssert ( failed == nil )
196
223
XCTAssert ( Set ( processor. fetchList. map ( { $0. owsPrimaryKey} ) ) == processor. downloaded)
224
+ XCTAssert ( processor. downloadedOrder == [ " B " , " A " , " C " ] )
197
225
XCTAssert ( processor. removeItemsCalled == 0 )
198
226
XCTAssert ( processor. shouldBeUpdatedCalled == 0 )
199
227
XCTAssert ( processor. removed. count == 0 )
@@ -205,8 +233,9 @@ class OneWaySynchronizerDemoTests: XCTestCase {
205
233
let expectation = XCTestExpectation ( description: " sync " )
206
234
var failed : [ Error ] ?
207
235
let processor = TestProcessor ( fetchList: [
208
- TestItem ( key: " A " ) ,
209
- TestItem ( key: " B " )
236
+ TestItem ( key: " A " , order: 1 ) ,
237
+ TestItem ( key: " B " , order: 0 ) ,
238
+ TestItem ( key: " C " , order: 2 )
210
239
] )
211
240
let service = OneWaySynchronizer ( processor: processor)
212
241
service. concurrency = 1
@@ -219,10 +248,12 @@ class OneWaySynchronizerDemoTests: XCTestCase {
219
248
wait ( for: [ expectation] , timeout: 10 )
220
249
XCTAssert ( failed == nil )
221
250
XCTAssert ( Set ( processor. fetchList. map ( { $0. owsPrimaryKey} ) ) == processor. downloaded)
251
+ XCTAssert ( processor. downloadedOrder == [ " B " , " A " , " C " ] )
252
+ XCTAssert ( processor. downloadedPreviewOrder == [ " B " , " A " , " C " ] )
222
253
XCTAssert ( processor. removeItemsCalled == 0 )
223
254
XCTAssert ( processor. shouldBeUpdatedCalled == 0 )
224
255
XCTAssert ( processor. removed. count == 0 )
225
- XCTAssert ( processor. downloadedPreview == Set ( [ " A " , " B " ] ) )
256
+ XCTAssert ( processor. downloadedPreview == Set ( [ " A " , " B " , " C " ] ) )
226
257
}
227
258
228
259
func testSuccessOnlyWithPreview( ) {
@@ -277,6 +308,55 @@ class OneWaySynchronizerDemoTests: XCTestCase {
277
308
XCTAssert ( processor. downloadedPreview. count == 0 )
278
309
}
279
310
311
+ func testCancellation( ) {
312
+
313
+ let startExpectation = XCTestExpectation ( description: " start " )
314
+ let endExpectation = XCTestExpectation ( description: " start " )
315
+
316
+ let expectation = XCTestExpectation ( description: " sync " )
317
+ var failed : [ Error ] ?
318
+ let processor = TestProcessor ( fetchList: [
319
+ TestItem ( key: " A " , order: 1 ) ,
320
+ TestItem ( key: " B " , order: 0 ) ,
321
+ TestItem ( key: " C " , order: 2 )
322
+ ] )
323
+ let service = OneWaySynchronizer ( processor: processor)
324
+ service. concurrency = 1
325
+ let localObserver = NotificationCenter . default. addObserver ( forName: . OneWaySynchronizerDidChangeSyncStatusNotification, object: service, queue: OperationQueue . main) { ( notification) in
326
+ XCTAssert ( Thread . isMainThread)
327
+ if ( notification. object as! OneWaySynchronizer ) . isSyncing {
328
+ startExpectation. fulfill ( )
329
+ }
330
+ else {
331
+ endExpectation. fulfill ( )
332
+ }
333
+ }
334
+
335
+ service. sync { ( error) in
336
+ failed = error
337
+ expectation. fulfill ( )
338
+ }
339
+
340
+ service. cancel ( ) // cancel ASAP
341
+
342
+ wait ( for: [ expectation] , timeout: 10 )
343
+ wait ( for: [ startExpectation, endExpectation] , timeout: 10 )
344
+
345
+ NotificationCenter . default. removeObserver ( localObserver)
346
+
347
+ XCTAssert ( processor. beginCalled == 1 )
348
+ XCTAssert ( processor. endCalled == 1 )
349
+ XCTAssert ( failed != nil )
350
+ XCTAssert ( failed!. count == 1 )
351
+ XCTAssert ( isCancelError ( failed![ 0 ] ) )
352
+ XCTAssert ( processor. downloaded. count == 0 )
353
+ XCTAssert ( processor. downloadedOrder. count == 0 )
354
+ XCTAssert ( processor. removeItemsCalled == 0 )
355
+ XCTAssert ( processor. shouldBeUpdatedCalled == 0 )
356
+ XCTAssert ( processor. removed. count == 0 )
357
+ XCTAssert ( processor. downloadedPreview. count == 0 )
358
+ }
359
+
280
360
func testFetchFailure( ) {
281
361
282
362
let expectation = XCTestExpectation ( description: " sync " )
0 commit comments