]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Copyright (c) 2014 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | // Test syncing between SecItemDataSource and SOSTestDataSource | |
26 | ||
27 | #include "SOSTestDevice.h" | |
28 | #include "SOSTestDataSource.h" | |
29 | #include <test/testmore.h> | |
30 | ||
5c19dc3a A |
31 | #include <Security/SecureObjectSync/SOSCloudCircle.h> |
32 | #include <Security/SecureObjectSync/SOSEngine.h> | |
33 | #include <Security/SecureObjectSync/SOSPeer.h> | |
fa7225c8 | 34 | #include <Security/SecureObjectSync/SOSViews.h> |
d8f41ccd A |
35 | #include <Security/SecBase64.h> |
36 | #include <Security/SecItem.h> | |
37 | #include <Security/SecItemPriv.h> | |
38 | #include <corecrypto/ccsha2.h> | |
39 | #include <securityd/SecItemServer.h> | |
40 | #include <securityd/SecItemDataSource.h> | |
41 | #include <utilities/SecCFWrappers.h> | |
42 | #include <utilities/SecFileLocations.h> | |
43 | #include <utilities/SecIOFormat.h> | |
44 | ||
45 | #include <stdint.h> | |
46 | #include <AssertMacros.h> | |
47 | ||
48 | CFStringRef SOSMessageCopyDigestHex(SOSMessageRef message) { | |
49 | uint8_t digest[CCSHA1_OUTPUT_SIZE]; | |
50 | // TODO: Pass in real sequenceNumber. | |
51 | CFDataRef msgData = SOSMessageCreateData(message, 0, NULL); | |
52 | if (!msgData) return NULL; | |
53 | ccdigest(ccsha1_di(), CFDataGetLength(msgData), CFDataGetBytePtr(msgData), digest); | |
54 | CFMutableStringRef hex = CFStringCreateMutable(0, 2 * sizeof(digest)); | |
55 | for (unsigned int ix = 0; ix < sizeof(digest); ++ix) { | |
56 | CFStringAppendFormat(hex, 0, CFSTR("%02X"), digest[ix]); | |
57 | } | |
58 | CFReleaseSafe(msgData); | |
59 | return hex; | |
60 | } | |
61 | ||
62 | static void SOSTestDeviceDestroy(CFTypeRef cf) { | |
63 | SOSTestDeviceRef td = (SOSTestDeviceRef)cf; | |
64 | CFReleaseSafe(td->peers); | |
65 | if (td->ds) | |
66 | SOSDataSourceRelease(td->ds, NULL); | |
67 | if (td->dsf) | |
5c19dc3a | 68 | SOSDataSourceFactoryRelease(td->dsf); |
d8f41ccd A |
69 | CFReleaseSafe(td->db); |
70 | } | |
71 | ||
5c19dc3a A |
72 | void SOSTestDeviceDestroyEngine(CFMutableDictionaryRef testDevices) { |
73 | ||
74 | CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs")); | |
75 | ||
76 | CFArrayForEach(deviceIDs, ^(const void *value) { | |
77 | CFStringRef sourceID = (CFStringRef)value; | |
78 | SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID); | |
e3d460c9 A |
79 | SOSEngineRef engine = SOSDataSourceGetSharedEngine(source->ds, NULL); |
80 | SOSEngineClearCache(engine); | |
81 | SOSEngineDispose(engine); | |
5c19dc3a A |
82 | }); |
83 | } | |
84 | ||
d8f41ccd A |
85 | CFStringRef SOSTestDeviceGetID(SOSTestDeviceRef td) { |
86 | CFStringRef engineID = NULL; | |
87 | SOSEngineRef engine = SOSDataSourceGetSharedEngine(td->ds, NULL); | |
88 | if (engine) | |
89 | engineID = SOSEngineGetMyID(engine); | |
90 | return engineID; | |
91 | } | |
92 | ||
93 | void SOSTestDeviceForEachPeerID(SOSTestDeviceRef td, void(^peerBlock)(CFStringRef peerID, bool *stop)) { | |
94 | SOSPeerRef peer; | |
95 | bool stop = false; | |
96 | CFArrayForEachC(td->peers, peer) { | |
97 | peerBlock(SOSPeerGetID(peer), &stop); | |
98 | if (stop) | |
99 | break; | |
100 | } | |
101 | } | |
102 | ||
d87e1158 | 103 | static CFStringRef SOSTestDeviceCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { |
d8f41ccd A |
104 | SOSTestDeviceRef td = (SOSTestDeviceRef)cf; |
105 | CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorDefault, 0); | |
106 | CFStringAppendFormat(result, NULL, CFSTR("<SOSTestDevice %@"), td->ds->engine); | |
107 | SOSTestDeviceForEachPeerID(td, ^(CFStringRef peerID, bool *stop) { | |
5c19dc3a | 108 | SOSPeerRef peer = SOSEngineCopyPeerWithID(td->ds->engine, peerID, NULL); |
d8f41ccd A |
109 | CFStringAppendFormat(result, NULL, CFSTR("\n%@"), peer); |
110 | CFReleaseSafe(peer); | |
111 | }); | |
112 | CFStringAppendFormat(result, NULL, CFSTR(">")); | |
113 | return result; | |
114 | } | |
115 | ||
116 | CFGiblisFor(SOSTestDevice) | |
117 | ||
118 | static SOSTestDeviceRef SOSTestDeviceCreateInternal(CFAllocatorRef allocator, CFStringRef engineID) { | |
119 | SOSTestDeviceRef td = CFTypeAllocate(SOSTestDevice, struct __OpaqueSOSTestDevice, allocator); | |
120 | td->peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); | |
d87e1158 | 121 | td->mute = false; |
d8f41ccd A |
122 | return td; |
123 | } | |
124 | ||
125 | SOSTestDeviceRef SOSTestDeviceCreateWithDb(CFAllocatorRef allocator, CFStringRef engineID, SecDbRef db) { | |
126 | setup("create device"); | |
127 | SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID); | |
128 | CFRetainAssign(td->db, db); | |
129 | td->dsf = SecItemDataSourceFactoryGetShared(td->db); | |
5c19dc3a | 130 | CFStringRef sname = SOSDataSourceFactoryCopyName(td->dsf); |
d8f41ccd | 131 | CFErrorRef error = NULL; |
5c19dc3a A |
132 | ok (td->ds = SOSDataSourceFactoryCreateDataSource(td->dsf, sname, &error), "%@ create datasource \"%@\" [error: %@]", engineID, sname, error); |
133 | CFReleaseNull(error); | |
134 | CFReleaseNull(sname); | |
d8f41ccd A |
135 | assert(td->ds); // Shut up static analyzer and test generally run in debug mode anyway |
136 | if (td->ds) | |
137 | SOSEngineCircleChanged(SOSDataSourceGetSharedEngine(td->ds, NULL), engineID, NULL, NULL); | |
138 | return td; | |
139 | } | |
140 | ||
141 | SOSTestDeviceRef SOSTestDeviceCreateWithDbNamed(CFAllocatorRef allocator, CFStringRef engineID, CFStringRef dbName) { | |
142 | CFURLRef url = SecCopyURLForFileInKeychainDirectory(dbName); | |
143 | CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); | |
144 | SecDbRef db = SecKeychainDbCreate(path); | |
145 | SOSTestDeviceRef td = SOSTestDeviceCreateWithDb(allocator, engineID, db); | |
146 | CFReleaseSafe(db); | |
147 | CFReleaseSafe(path); | |
148 | CFReleaseSafe(url); | |
149 | return td; | |
150 | } | |
151 | ||
6b200bc3 A |
152 | SOSTestDeviceRef SOSTestDeviceCreateWithTestDataSource(CFAllocatorRef allocator, CFStringRef engineID, |
153 | void(^prepop)(SOSDataSourceRef ds)) { | |
d8f41ccd A |
154 | setup("create device"); |
155 | SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID); | |
156 | ||
157 | td->ds = SOSTestDataSourceCreate(); | |
6b200bc3 A |
158 | if (prepop) |
159 | prepop(td->ds); | |
d8f41ccd A |
160 | CFErrorRef error = NULL; |
161 | ok(td->ds->engine = SOSEngineCreate(td->ds, &error), "create engine: %@", error); | |
162 | SOSEngineCircleChanged(td->ds->engine, engineID, NULL, NULL); | |
163 | CFReleaseNull(error); | |
164 | return td; | |
165 | } | |
166 | ||
6b200bc3 | 167 | |
5c19dc3a A |
168 | CFSetRef SOSViewsCopyTestV0Default() { |
169 | const void *values[] = { kSOSViewKeychainV0 }; | |
170 | return CFSetCreate(kCFAllocatorDefault, values, array_size(values), &kCFTypeSetCallBacks); | |
171 | } | |
172 | ||
fa7225c8 A |
173 | CFSetRef SOSViewsCopyTestV2Default() { // this was originally listing all the views - not just the defaults - but those used to be the default. So we'll programatically get all - the actual test depends on that. |
174 | return SOSViewCopyViewSet(kViewSetAll); | |
5c19dc3a A |
175 | } |
176 | ||
177 | SOSTestDeviceRef SOSTestDeviceSetPeerIDs(SOSTestDeviceRef td, CFArrayRef peerIDs, CFIndex version, CFSetRef defaultViews) { | |
d8f41ccd A |
178 | setup("create device"); |
179 | CFStringRef engineID = SOSTestDeviceGetID(td); | |
5c19dc3a | 180 | CFTypeRef peerMeta; |
d8f41ccd A |
181 | CFMutableArrayRef trustedPeersIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); |
182 | CFErrorRef error = NULL; | |
5c19dc3a A |
183 | CFArrayForEachC(peerIDs, peerMeta) { |
184 | CFDataRef keyBag = NULL; | |
185 | CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, NULL, &keyBag, &error); | |
186 | if (!peerID) { | |
187 | fail("SOSPeerMetaGetComponents %@: %@", peerMeta, error); | |
d8f41ccd | 188 | CFReleaseNull(error); |
5c19dc3a A |
189 | } else if (!CFEqualSafe(peerID, engineID)) { |
190 | if (isString(peerMeta)) { | |
191 | CFTypeRef meta = SOSPeerMetaCreateWithComponents(peerID, defaultViews, keyBag); | |
192 | CFArrayAppendValue(trustedPeersIDs, meta); | |
193 | CFReleaseSafe(meta); | |
194 | } else { | |
195 | CFArrayAppendValue(trustedPeersIDs, peerMeta); | |
196 | } | |
d8f41ccd A |
197 | } |
198 | } | |
199 | ||
200 | SOSEngineCircleChanged(td->ds->engine, engineID, trustedPeersIDs, NULL); | |
5c19dc3a A |
201 | SOSEngineForEachPeer(td->ds->engine, ^(SOSPeerRef peer) { |
202 | // TODO: Rewrite this to add version to the peerMeta and remove this hack | |
203 | CFMutableDictionaryRef state = (CFMutableDictionaryRef)SOSPeerCopyState(peer, NULL); | |
204 | CFNumberRef versionNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &version); | |
205 | CFDictionarySetValue(state, CFSTR("version") /* kSOSPeerVersionKey */, versionNumber); | |
206 | CFReleaseSafe(versionNumber); | |
207 | CFErrorRef peerError = NULL; | |
208 | ok(SOSPeerSetState(peer, td->ds->engine, state, &peerError), "SOSPeerSetState: %@", peerError); | |
209 | CFReleaseNull(peerError); | |
210 | CFReleaseSafe(state); | |
211 | CFArrayAppendValue(td->peers, peer); | |
212 | }); | |
213 | CFArrayForEachC(trustedPeersIDs, peerMeta) { | |
214 | CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, NULL, NULL, &error); | |
215 | ok(peerID && SOSEnginePeerDidConnect(td->ds->engine, peerID, &error), "tell %@ %@ connected: %@", engineID, peerID, error); | |
d8f41ccd A |
216 | CFReleaseNull(error); |
217 | } | |
218 | CFReleaseSafe(trustedPeersIDs); | |
219 | return td; | |
220 | } | |
221 | ||
d87e1158 A |
222 | SOSTestDeviceRef SOSTestDeviceSetMute(SOSTestDeviceRef td, bool mute) { |
223 | td->mute = mute; | |
224 | return td; | |
225 | } | |
226 | ||
227 | bool SOSTestDeviceIsMute(SOSTestDeviceRef td) { | |
228 | return td->mute; | |
229 | } | |
230 | ||
fa7225c8 A |
231 | bool SOSTestDeviceSetEngineState(SOSTestDeviceRef td, CFDataRef derEngineState) { |
232 | CFErrorRef localError = NULL; | |
233 | SOSTestEngineSaveWithDER(td->ds->engine, derEngineState, &localError); | |
234 | return true; | |
235 | } | |
236 | ||
6b200bc3 A |
237 | bool SOSTestDeviceEngineSave(SOSTestDeviceRef td, CFErrorRef *error) { |
238 | __block bool rx = false; | |
239 | if (!SOSDataSourceWithAPI(td->ds, true, error, ^(SOSTransactionRef txn, bool *commit) { | |
240 | rx = SOSTestEngineSave(td->ds->engine, txn, error); | |
241 | })) | |
242 | fail("ds transaction %@", error ? *error : NULL); | |
243 | return rx; | |
244 | } | |
fa7225c8 | 245 | |
6b200bc3 A |
246 | bool SOSTestDeviceEngineLoad(SOSTestDeviceRef td, CFErrorRef *error) { |
247 | __block bool rx = false; | |
248 | if (!SOSDataSourceWithAPI(td->ds, true, error, ^(SOSTransactionRef txn, bool *commit) { | |
249 | rx = SOSTestEngineLoad(td->ds->engine, txn, error); | |
250 | })) | |
251 | fail("ds transaction %@", error ? *error : NULL); | |
252 | return rx; | |
253 | } | |
fa7225c8 | 254 | |
d8f41ccd A |
255 | CFDataRef SOSTestDeviceCreateMessage(SOSTestDeviceRef td, CFStringRef peerID) { |
256 | setup("create message"); | |
257 | CFErrorRef error = NULL; | |
258 | SOSEnginePeerMessageSentBlock sent = NULL; | |
259 | CFDataRef msgData; | |
5c19dc3a | 260 | |
d8f41ccd A |
261 | ok(msgData = SOSEngineCreateMessageToSyncToPeer(td->ds->engine, peerID, &sent, &error), |
262 | "create message to %@: %@", peerID, error); | |
263 | if (sent) | |
264 | sent(true); | |
265 | ||
5c19dc3a | 266 | Block_release(sent); |
d8f41ccd A |
267 | return msgData; |
268 | } | |
269 | ||
270 | #if 0 | |
271 | CFDictionaryRef SOSTestDeviceCreateMessages(SOSTestDeviceRef td) { | |
272 | CFTypeRef peer = NULL; | |
273 | CFMutableDictionaryRef messages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); | |
274 | CFArrayForEachC(td->peers, peer) { | |
275 | CFStringRef peerID = SOSPeerGetID((SOSPeerRef)peer); | |
276 | CFDataRef msg = SOSTestDeviceCreateMessage(td, peerID); | |
277 | if (msg) { | |
278 | CFDictionaryAddValue(messages, peerID, msg); | |
279 | CFRelease(msg); | |
280 | } | |
281 | } | |
282 | return messages; | |
283 | } | |
284 | #endif | |
285 | ||
286 | bool SOSTestDeviceHandleMessage(SOSTestDeviceRef td, CFStringRef peerID, CFDataRef msgData) { | |
287 | setup("handle message"); | |
288 | if (!msgData) return false; | |
289 | CFErrorRef error = NULL; | |
290 | bool handled; | |
291 | SOSMessageRef message; | |
292 | ||
293 | ok(message = SOSMessageCreateWithData(kCFAllocatorDefault, msgData, &error), "decode message %@: %@", msgData, error); | |
294 | CFReleaseNull(error); | |
5c19dc3a | 295 | pass("handling %@->%@ %@", peerID, SOSEngineGetMyID(SOSDataSourceGetSharedEngine(td->ds, &error)), message); |
d8f41ccd A |
296 | ok(handled = SOSEngineHandleMessage(SOSDataSourceGetSharedEngine(td->ds, &error), peerID, msgData, &error), |
297 | "handled from %@ %@: %@", peerID, message, error); | |
298 | CFReleaseNull(error); | |
299 | ||
300 | CFReleaseNull(message); | |
301 | return handled; | |
302 | } | |
303 | ||
304 | void SOSTestDeviceAddGenericItem(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) { | |
305 | __block CFErrorRef error = NULL; | |
306 | if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) { | |
307 | SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, account, server); | |
308 | ok(SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error), "%@ added API object %@", SOSTestDeviceGetID(td), error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok")); | |
309 | CFReleaseSafe(object); | |
310 | CFReleaseNull(error); | |
311 | })) | |
312 | fail("ds transaction %@", error); | |
313 | CFReleaseNull(error); | |
314 | } | |
315 | ||
5c19dc3a A |
316 | void SOSTestDeviceAddGenericItemTombstone(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) { |
317 | __block CFErrorRef error = NULL; | |
318 | if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) { | |
319 | SOSObjectRef object = SOSDataSourceCreateGenericItemWithData(td->ds, account, server, true, NULL); | |
320 | SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error); | |
321 | CFReleaseSafe(object); | |
322 | CFReleaseNull(error); | |
323 | })) | |
324 | fail("ds transaction %@", error); | |
325 | CFReleaseNull(error); | |
326 | } | |
327 | ||
328 | void SOSTestDeviceAddGenericItemWithData(SOSTestDeviceRef td, CFStringRef account, CFStringRef server, CFDataRef data) { | |
329 | __block CFErrorRef error = NULL; | |
330 | if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) { | |
331 | SOSObjectRef object = SOSDataSourceCreateGenericItemWithData(td->ds, account, server, false, data); | |
332 | SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error); | |
333 | CFReleaseSafe(object); | |
334 | CFReleaseNull(error); | |
335 | })) | |
336 | fail("ds transaction %@", error); | |
337 | CFReleaseNull(error); | |
338 | } | |
339 | ||
d8f41ccd A |
340 | void SOSTestDeviceAddRemoteGenericItem(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) { |
341 | __block CFErrorRef error = NULL; | |
342 | if (!SOSDataSourceWithAPI(td->ds, false, &error, ^(SOSTransactionRef txn, bool *commit) { | |
343 | SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, account, server); | |
344 | ok(SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error), "%@ added remote object %@", SOSTestDeviceGetID(td), error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok")); | |
345 | CFReleaseSafe(object); | |
346 | CFReleaseNull(error); | |
347 | })) | |
348 | fail("ds transaction %@", error); | |
349 | CFReleaseNull(error); | |
350 | } | |
351 | ||
352 | bool SOSTestDeviceAddGenericItems(SOSTestDeviceRef td, CFIndex count, CFStringRef account, CFStringRef server) { | |
353 | __block bool didAdd = false; | |
354 | __block CFErrorRef error = NULL; | |
355 | if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) { | |
356 | bool success = true; | |
357 | CFIndex ix = 0; | |
358 | for (; success && ix < count; ++ix) { | |
359 | CFStringRef accountStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@%" PRIdCFIndex), account, ix); | |
360 | SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, accountStr, server); | |
361 | success = SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error); | |
5c19dc3a | 362 | CFReleaseNull(accountStr); |
d8f41ccd A |
363 | CFReleaseSafe(object); |
364 | } | |
365 | ok(success, "%@ added %" PRIdCFIndex " API objects %@", SOSTestDeviceGetID(td), ix, error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok")); | |
366 | didAdd = success && ix == count; | |
367 | CFReleaseNull(error); | |
368 | })) | |
369 | fail("ds transaction %@", error); | |
370 | CFReleaseNull(error); | |
371 | return didAdd; | |
372 | } | |
373 | ||
6b200bc3 A |
374 | void SOSTestDeviceAddV0EngineStateWithData(SOSDataSourceRef ds, CFDataRef engineStateData) { |
375 | __block CFErrorRef error = NULL; | |
376 | const CFStringRef kSOSEngineState = CFSTR("engine-state"); | |
377 | ||
378 | if (!SOSDataSourceWithAPI(ds, true, &error, ^(SOSTransactionRef txn, bool *commit) { | |
379 | SOSObjectRef object = SOSDataSourceCreateV0EngineStateWithData(ds, engineStateData); | |
380 | ||
381 | // Note that state doesn't use SOSDataSourceMergeObject | |
382 | SOSDataSourceSetStateWithKey(ds, txn, kSOSEngineState, kSecAttrAccessibleAlwaysPrivate, engineStateData, &error); | |
383 | CFReleaseSafe(object); | |
384 | CFReleaseNull(error); | |
385 | })) | |
386 | fail("ds transaction %@", error); | |
387 | ||
388 | CFReleaseNull(error); | |
389 | } | |
390 | ||
391 | CFMutableDictionaryRef SOSTestDeviceListCreate(bool realDb, CFIndex version, CFArrayRef deviceIDs, | |
392 | void(^prepop)(SOSDataSourceRef ds)) { | |
d8f41ccd A |
393 | CFMutableDictionaryRef testDevices = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); |
394 | CFStringRef deviceID; | |
5c19dc3a | 395 | CFSetRef defaultViews = realDb ? SOSViewsCopyTestV2Default() : SOSViewsCopyTestV0Default(); |
d8f41ccd A |
396 | CFArrayForEachC(deviceIDs, deviceID) { |
397 | SOSTestDeviceRef device; | |
5c19dc3a | 398 | if (!realDb) { |
6b200bc3 | 399 | device = SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault, deviceID, prepop); |
5c19dc3a | 400 | } else { |
d8f41ccd | 401 | device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID); |
5c19dc3a A |
402 | } |
403 | SOSTestDeviceSetPeerIDs(device, deviceIDs, version, defaultViews); | |
d8f41ccd A |
404 | CFDictionarySetValue(testDevices, deviceID, device); |
405 | CFReleaseSafe(device); | |
406 | } | |
5c19dc3a A |
407 | CFReleaseSafe(defaultViews); |
408 | ||
d8f41ccd A |
409 | CFDictionarySetValue(testDevices, CFSTR("@devicesIDs"), deviceIDs); |
410 | return testDevices; | |
411 | } | |
412 | ||
413 | void SOSTestDeviceListSync(const char *name, const char *test_directive, const char *test_reason, CFMutableDictionaryRef testDevices, bool(^pre)(SOSTestDeviceRef source, SOSTestDeviceRef dest), bool(^post)(SOSTestDeviceRef source, SOSTestDeviceRef dest, SOSMessageRef message)) { | |
414 | CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs")); | |
415 | const CFIndex edgeCount = CFArrayGetCount(deviceIDs) * (CFArrayGetCount(deviceIDs) - 1); | |
416 | CFIndex deviceIX = 0; | |
417 | __block CFIndex noMsgSentCount = 0; | |
418 | __block CFIndex msgSentSinceLastChangeCount = 0; | |
419 | __block bool done = false; | |
420 | do { | |
421 | CFStringRef sourceID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, deviceIX++); | |
422 | if (deviceIX >= CFArrayGetCount(deviceIDs)) | |
423 | deviceIX = 0; | |
424 | ||
425 | SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID); | |
426 | SOSTestDeviceForEachPeerID(source, ^(CFStringRef destID, bool *stop) { | |
427 | SOSTestDeviceRef dest = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, destID); | |
428 | if (dest) { | |
5c19dc3a A |
429 | SOSPeerRef peer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(source->ds, NULL), destID, NULL); |
430 | SOSManifestRef preCreateManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), peer, NULL); | |
431 | if (pre && pre(source, dest)) | |
432 | msgSentSinceLastChangeCount = 0; | |
433 | ||
d8f41ccd A |
434 | CFDataRef msg = SOSTestDeviceCreateMessage(source, destID); |
435 | SOSMessageRef message = NULL; | |
436 | bool handled = false; | |
d8f41ccd | 437 | if (msg && CFDataGetLength(msg) > 0) { |
d87e1158 | 438 | if (!source->mute) { |
5c19dc3a | 439 | msgSentSinceLastChangeCount++; |
d87e1158 A |
440 | handled = SOSTestDeviceHandleMessage(dest, sourceID, msg); |
441 | if (handled) { | |
442 | noMsgSentCount = 0; | |
443 | } | |
d8f41ccd | 444 | |
d87e1158 A |
445 | CFErrorRef error = NULL; |
446 | message = SOSMessageCreateWithData(kCFAllocatorDefault, msg, &error); | |
447 | ok(handled, "%s %@->%@ %@", name, sourceID, destID, message); | |
448 | CFReleaseNull(error); | |
449 | } | |
d8f41ccd | 450 | } else { |
5c19dc3a A |
451 | msgSentSinceLastChangeCount++; |
452 | SOSManifestRef sourceManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), peer, NULL); | |
d8f41ccd A |
453 | pass("%s %@->%@ done L:%@", name, sourceID, destID, sourceManifest); |
454 | CFReleaseSafe(sourceManifest); | |
455 | noMsgSentCount++; | |
d8f41ccd A |
456 | } |
457 | CFReleaseSafe(msg); | |
5c19dc3a A |
458 | if (post && post(source, dest, message)) |
459 | msgSentSinceLastChangeCount = 0; | |
460 | ||
461 | CFReleaseNull(message); | |
462 | if (preCreateManifest) { | |
463 | SOSManifestRef postHandleManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), peer, NULL); | |
464 | if (postHandleManifest && !CFEqual(preCreateManifest, postHandleManifest)) { | |
465 | //CFStringPerformWithCString(destID, ^(const char *destStr) { diag("device %s changed", destStr); }); | |
d8f41ccd | 466 | msgSentSinceLastChangeCount = 0; |
5c19dc3a A |
467 | } |
468 | CFReleaseSafe(postHandleManifest); | |
469 | CFRelease(preCreateManifest); | |
d8f41ccd | 470 | } |
5c19dc3a | 471 | CFReleaseSafe(peer); |
d8f41ccd A |
472 | } |
473 | if (noMsgSentCount >= edgeCount) { | |
474 | *stop = done = true; | |
5c19dc3a | 475 | } else if (msgSentSinceLastChangeCount >= /* 3 */9 * edgeCount + 1) { |
d8f41ccd A |
476 | fail("%s %" PRIdCFIndex" peers never stopped syncing %" PRIdCFIndex" messages since last change", name, CFArrayGetCount(deviceIDs), msgSentSinceLastChangeCount); |
477 | *stop = done = true; | |
478 | } | |
479 | }); | |
480 | } while (!done); | |
481 | } | |
482 | ||
483 | bool SOSTestDeviceListInSync(const char *name, const char *test_directive, const char *test_reason, CFMutableDictionaryRef testDevices) { | |
484 | bool inSync = true; | |
485 | CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs")); | |
5c19dc3a A |
486 | for (CFIndex len = CFArrayGetCount(deviceIDs), source_ix = 0; source_ix < len; ++source_ix) { |
487 | CFStringRef sourceID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, source_ix); | |
488 | SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID); | |
489 | for (CFIndex dest_ix = source_ix + 1; dest_ix < len; ++dest_ix) { | |
490 | CFStringRef destID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, dest_ix); | |
491 | SOSTestDeviceRef dest = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, destID); | |
492 | ||
493 | SOSPeerRef sourcePeer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(source->ds, NULL), destID, NULL); | |
494 | SOSManifestRef sourceManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), sourcePeer, NULL); | |
495 | ||
496 | SOSPeerRef destPeer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(dest->ds, NULL), sourceID, NULL); | |
497 | SOSManifestRef destManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), destPeer, NULL); | |
498 | ||
499 | if (!CFEqualSafe(sourceManifest, destManifest)) { | |
500 | fail("%s %@ manifest %@ != %@ manifest %@", name, sourceID, sourceManifest, destID, destManifest); | |
501 | inSync = false; | |
502 | } | |
503 | CFReleaseSafe(sourcePeer); | |
504 | CFReleaseSafe(sourceManifest); | |
505 | CFReleaseSafe(destPeer); | |
506 | CFReleaseSafe(destManifest); | |
d8f41ccd | 507 | } |
d8f41ccd | 508 | } |
d8f41ccd A |
509 | if (inSync) |
510 | pass("%s all peers in sync", name); | |
511 | return inSync; | |
512 | } | |
513 | ||
514 | void SOSTestDeviceListTestSync(const char *name, const char *test_directive, const char *test_reason, CFIndex version, bool use_db, | |
515 | bool(^pre)(SOSTestDeviceRef source, SOSTestDeviceRef dest), | |
516 | bool(^post)(SOSTestDeviceRef source, SOSTestDeviceRef dest, SOSMessageRef message), ...) { | |
517 | va_list args; | |
518 | va_start(args, post); | |
519 | // Optionally prefix each peer with name to make them more unique. | |
520 | CFArrayRef deviceIDs = CFArrayCreateForVC(kCFAllocatorDefault, &kCFTypeArrayCallBacks, args); | |
6b200bc3 | 521 | CFMutableDictionaryRef testDevices = SOSTestDeviceListCreate(use_db, version, deviceIDs, NULL); |
d8f41ccd A |
522 | CFReleaseSafe(deviceIDs); |
523 | SOSTestDeviceListSync(name, test_directive, test_reason, testDevices, pre, post); | |
524 | SOSTestDeviceListInSync(name, test_directive, test_reason, testDevices); | |
5c19dc3a | 525 | SOSTestDeviceDestroyEngine(testDevices); |
d8f41ccd A |
526 | CFReleaseSafe(testDevices); |
527 | } |