]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/Regressions/SOSTestDevice.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / Regressions / SOSTestDevice.c
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
31 #include <Security/SecureObjectSync/SOSCloudCircle.h>
32 #include <Security/SecureObjectSync/SOSEngine.h>
33 #include <Security/SecureObjectSync/SOSPeer.h>
34 #include <Security/SecBase64.h>
35 #include <Security/SecItem.h>
36 #include <Security/SecItemPriv.h>
37 #include <corecrypto/ccsha2.h>
38 #include <securityd/SecItemServer.h>
39 #include <securityd/SecItemDataSource.h>
40 #include <utilities/SecCFWrappers.h>
41 #include <utilities/SecFileLocations.h>
42 #include <utilities/SecIOFormat.h>
43
44 #include <stdint.h>
45 #include <AssertMacros.h>
46
47 CFStringRef SOSMessageCopyDigestHex(SOSMessageRef message) {
48 uint8_t digest[CCSHA1_OUTPUT_SIZE];
49 // TODO: Pass in real sequenceNumber.
50 CFDataRef msgData = SOSMessageCreateData(message, 0, NULL);
51 if (!msgData) return NULL;
52 ccdigest(ccsha1_di(), CFDataGetLength(msgData), CFDataGetBytePtr(msgData), digest);
53 CFMutableStringRef hex = CFStringCreateMutable(0, 2 * sizeof(digest));
54 for (unsigned int ix = 0; ix < sizeof(digest); ++ix) {
55 CFStringAppendFormat(hex, 0, CFSTR("%02X"), digest[ix]);
56 }
57 CFReleaseSafe(msgData);
58 return hex;
59 }
60
61 static void SOSTestDeviceDestroy(CFTypeRef cf) {
62 SOSTestDeviceRef td = (SOSTestDeviceRef)cf;
63 CFReleaseSafe(td->peers);
64 if (td->ds)
65 SOSDataSourceRelease(td->ds, NULL);
66 if (td->dsf)
67 SOSDataSourceFactoryRelease(td->dsf);
68 CFReleaseSafe(td->db);
69 }
70
71 void SOSTestDeviceDestroyEngine(CFMutableDictionaryRef testDevices) {
72
73 CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs"));
74
75 CFArrayForEach(deviceIDs, ^(const void *value) {
76 CFStringRef sourceID = (CFStringRef)value;
77 SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID);
78 SOSEngineClearCache(SOSDataSourceGetSharedEngine(source->ds, NULL));
79 });
80 }
81
82 CFStringRef SOSTestDeviceGetID(SOSTestDeviceRef td) {
83 CFStringRef engineID = NULL;
84 SOSEngineRef engine = SOSDataSourceGetSharedEngine(td->ds, NULL);
85 if (engine)
86 engineID = SOSEngineGetMyID(engine);
87 return engineID;
88 }
89
90 void SOSTestDeviceForEachPeerID(SOSTestDeviceRef td, void(^peerBlock)(CFStringRef peerID, bool *stop)) {
91 SOSPeerRef peer;
92 bool stop = false;
93 CFArrayForEachC(td->peers, peer) {
94 peerBlock(SOSPeerGetID(peer), &stop);
95 if (stop)
96 break;
97 }
98 }
99
100 static CFStringRef SOSTestDeviceCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
101 SOSTestDeviceRef td = (SOSTestDeviceRef)cf;
102 CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorDefault, 0);
103 CFStringAppendFormat(result, NULL, CFSTR("<SOSTestDevice %@"), td->ds->engine);
104 SOSTestDeviceForEachPeerID(td, ^(CFStringRef peerID, bool *stop) {
105 SOSPeerRef peer = SOSEngineCopyPeerWithID(td->ds->engine, peerID, NULL);
106 CFStringAppendFormat(result, NULL, CFSTR("\n%@"), peer);
107 CFReleaseSafe(peer);
108 });
109 CFStringAppendFormat(result, NULL, CFSTR(">"));
110 return result;
111 }
112
113 CFGiblisFor(SOSTestDevice)
114
115 static SOSTestDeviceRef SOSTestDeviceCreateInternal(CFAllocatorRef allocator, CFStringRef engineID) {
116 SOSTestDeviceRef td = CFTypeAllocate(SOSTestDevice, struct __OpaqueSOSTestDevice, allocator);
117 td->peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
118 td->mute = false;
119 return td;
120 }
121
122 SOSTestDeviceRef SOSTestDeviceCreateWithDb(CFAllocatorRef allocator, CFStringRef engineID, SecDbRef db) {
123 setup("create device");
124 SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID);
125 CFRetainAssign(td->db, db);
126 td->dsf = SecItemDataSourceFactoryGetShared(td->db);
127 CFStringRef sname = SOSDataSourceFactoryCopyName(td->dsf);
128 CFErrorRef error = NULL;
129 ok (td->ds = SOSDataSourceFactoryCreateDataSource(td->dsf, sname, &error), "%@ create datasource \"%@\" [error: %@]", engineID, sname, error);
130 CFReleaseNull(error);
131 CFReleaseNull(sname);
132 assert(td->ds); // Shut up static analyzer and test generally run in debug mode anyway
133 if (td->ds)
134 SOSEngineCircleChanged(SOSDataSourceGetSharedEngine(td->ds, NULL), engineID, NULL, NULL);
135 return td;
136 }
137
138 SOSTestDeviceRef SOSTestDeviceCreateWithDbNamed(CFAllocatorRef allocator, CFStringRef engineID, CFStringRef dbName) {
139 CFURLRef url = SecCopyURLForFileInKeychainDirectory(dbName);
140 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
141 SecDbRef db = SecKeychainDbCreate(path);
142 SOSTestDeviceRef td = SOSTestDeviceCreateWithDb(allocator, engineID, db);
143 CFReleaseSafe(db);
144 CFReleaseSafe(path);
145 CFReleaseSafe(url);
146 return td;
147 }
148
149 SOSTestDeviceRef SOSTestDeviceCreateWithTestDataSource(CFAllocatorRef allocator, CFStringRef engineID) {
150 setup("create device");
151 SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID);
152
153 td->ds = SOSTestDataSourceCreate();
154 CFErrorRef error = NULL;
155 ok(td->ds->engine = SOSEngineCreate(td->ds, &error), "create engine: %@", error);
156 SOSEngineCircleChanged(td->ds->engine, engineID, NULL, NULL);
157 CFReleaseNull(error);
158 return td;
159 }
160
161 CFSetRef SOSViewsCopyTestV0Default() {
162 const void *values[] = { kSOSViewKeychainV0 };
163 return CFSetCreate(kCFAllocatorDefault, values, array_size(values), &kCFTypeSetCallBacks);
164 }
165
166 CFSetRef SOSViewsCopyTestV2Default() {
167 const void *values[] = { kSOSViewWiFi, kSOSViewAutofillPasswords, kSOSViewSafariCreditCards, kSOSViewiCloudIdentity, kSOSViewBackupBagV0, kSOSViewOtherSyncable, kSOSViewPCSMasterKey, kSOSViewPCSiCloudDrive, kSOSViewPCSPhotos, kSOSViewPCSCloudKit, kSOSViewPCSEscrow, kSOSViewPCSFDE, kSOSViewPCSMailDrop, kSOSViewPCSiCloudBackup, kSOSViewPCSNotes, kSOSViewPCSiMessage, kSOSViewPCSFeldspar, kSOSViewAppleTV, kSOSViewHomeKit };
168 return CFSetCreate(kCFAllocatorDefault, values, array_size(values), &kCFTypeSetCallBacks);
169 }
170
171 SOSTestDeviceRef SOSTestDeviceSetPeerIDs(SOSTestDeviceRef td, CFArrayRef peerIDs, CFIndex version, CFSetRef defaultViews) {
172 setup("create device");
173 CFStringRef engineID = SOSTestDeviceGetID(td);
174 CFTypeRef peerMeta;
175 CFMutableArrayRef trustedPeersIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
176 CFErrorRef error = NULL;
177 CFArrayForEachC(peerIDs, peerMeta) {
178 CFDataRef keyBag = NULL;
179 CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, NULL, &keyBag, &error);
180 if (!peerID) {
181 fail("SOSPeerMetaGetComponents %@: %@", peerMeta, error);
182 CFReleaseNull(error);
183 } else if (!CFEqualSafe(peerID, engineID)) {
184 if (isString(peerMeta)) {
185 CFTypeRef meta = SOSPeerMetaCreateWithComponents(peerID, defaultViews, keyBag);
186 CFArrayAppendValue(trustedPeersIDs, meta);
187 CFReleaseSafe(meta);
188 } else {
189 CFArrayAppendValue(trustedPeersIDs, peerMeta);
190 }
191 }
192 }
193
194 SOSEngineCircleChanged(td->ds->engine, engineID, trustedPeersIDs, NULL);
195 SOSEngineForEachPeer(td->ds->engine, ^(SOSPeerRef peer) {
196 // TODO: Rewrite this to add version to the peerMeta and remove this hack
197 CFMutableDictionaryRef state = (CFMutableDictionaryRef)SOSPeerCopyState(peer, NULL);
198 CFNumberRef versionNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &version);
199 CFDictionarySetValue(state, CFSTR("version") /* kSOSPeerVersionKey */, versionNumber);
200 CFReleaseSafe(versionNumber);
201 CFErrorRef peerError = NULL;
202 ok(SOSPeerSetState(peer, td->ds->engine, state, &peerError), "SOSPeerSetState: %@", peerError);
203 CFReleaseNull(peerError);
204 CFReleaseSafe(state);
205 CFArrayAppendValue(td->peers, peer);
206 });
207 CFArrayForEachC(trustedPeersIDs, peerMeta) {
208 CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, NULL, NULL, &error);
209 ok(peerID && SOSEnginePeerDidConnect(td->ds->engine, peerID, &error), "tell %@ %@ connected: %@", engineID, peerID, error);
210 CFReleaseNull(error);
211 }
212 CFReleaseSafe(trustedPeersIDs);
213 return td;
214 }
215
216 SOSTestDeviceRef SOSTestDeviceSetMute(SOSTestDeviceRef td, bool mute) {
217 td->mute = mute;
218 return td;
219 }
220
221 bool SOSTestDeviceIsMute(SOSTestDeviceRef td) {
222 return td->mute;
223 }
224
225 CFDataRef SOSTestDeviceCreateMessage(SOSTestDeviceRef td, CFStringRef peerID) {
226 setup("create message");
227 CFErrorRef error = NULL;
228 SOSEnginePeerMessageSentBlock sent = NULL;
229 CFDataRef msgData;
230
231 ok(msgData = SOSEngineCreateMessageToSyncToPeer(td->ds->engine, peerID, &sent, &error),
232 "create message to %@: %@", peerID, error);
233 if (sent)
234 sent(true);
235
236 Block_release(sent);
237 return msgData;
238 }
239
240 #if 0
241 CFDictionaryRef SOSTestDeviceCreateMessages(SOSTestDeviceRef td) {
242 CFTypeRef peer = NULL;
243 CFMutableDictionaryRef messages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
244 CFArrayForEachC(td->peers, peer) {
245 CFStringRef peerID = SOSPeerGetID((SOSPeerRef)peer);
246 CFDataRef msg = SOSTestDeviceCreateMessage(td, peerID);
247 if (msg) {
248 CFDictionaryAddValue(messages, peerID, msg);
249 CFRelease(msg);
250 }
251 }
252 return messages;
253 }
254 #endif
255
256 bool SOSTestDeviceHandleMessage(SOSTestDeviceRef td, CFStringRef peerID, CFDataRef msgData) {
257 setup("handle message");
258 if (!msgData) return false;
259 CFErrorRef error = NULL;
260 bool handled;
261 SOSMessageRef message;
262
263 ok(message = SOSMessageCreateWithData(kCFAllocatorDefault, msgData, &error), "decode message %@: %@", msgData, error);
264 CFReleaseNull(error);
265 pass("handling %@->%@ %@", peerID, SOSEngineGetMyID(SOSDataSourceGetSharedEngine(td->ds, &error)), message);
266 ok(handled = SOSEngineHandleMessage(SOSDataSourceGetSharedEngine(td->ds, &error), peerID, msgData, &error),
267 "handled from %@ %@: %@", peerID, message, error);
268 CFReleaseNull(error);
269
270 CFReleaseNull(message);
271 return handled;
272 }
273
274 void SOSTestDeviceAddGenericItem(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) {
275 __block CFErrorRef error = NULL;
276 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
277 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, account, server);
278 ok(SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error), "%@ added API object %@", SOSTestDeviceGetID(td), error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
279 CFReleaseSafe(object);
280 CFReleaseNull(error);
281 }))
282 fail("ds transaction %@", error);
283 CFReleaseNull(error);
284 }
285
286 void SOSTestDeviceAddGenericItemTombstone(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) {
287 __block CFErrorRef error = NULL;
288 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
289 SOSObjectRef object = SOSDataSourceCreateGenericItemWithData(td->ds, account, server, true, NULL);
290 SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error);
291 CFReleaseSafe(object);
292 CFReleaseNull(error);
293 }))
294 fail("ds transaction %@", error);
295 CFReleaseNull(error);
296 }
297
298 void SOSTestDeviceAddGenericItemWithData(SOSTestDeviceRef td, CFStringRef account, CFStringRef server, CFDataRef data) {
299 __block CFErrorRef error = NULL;
300 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
301 SOSObjectRef object = SOSDataSourceCreateGenericItemWithData(td->ds, account, server, false, data);
302 SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error);
303 CFReleaseSafe(object);
304 CFReleaseNull(error);
305 }))
306 fail("ds transaction %@", error);
307 CFReleaseNull(error);
308 }
309
310 void SOSTestDeviceAddRemoteGenericItem(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) {
311 __block CFErrorRef error = NULL;
312 if (!SOSDataSourceWithAPI(td->ds, false, &error, ^(SOSTransactionRef txn, bool *commit) {
313 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, account, server);
314 ok(SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error), "%@ added remote object %@", SOSTestDeviceGetID(td), error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
315 CFReleaseSafe(object);
316 CFReleaseNull(error);
317 }))
318 fail("ds transaction %@", error);
319 CFReleaseNull(error);
320 }
321
322 bool SOSTestDeviceAddGenericItems(SOSTestDeviceRef td, CFIndex count, CFStringRef account, CFStringRef server) {
323 __block bool didAdd = false;
324 __block CFErrorRef error = NULL;
325 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
326 bool success = true;
327 CFIndex ix = 0;
328 for (; success && ix < count; ++ix) {
329 CFStringRef accountStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@%" PRIdCFIndex), account, ix);
330 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, accountStr, server);
331 success = SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error);
332 CFReleaseNull(accountStr);
333 CFReleaseSafe(object);
334 }
335 ok(success, "%@ added %" PRIdCFIndex " API objects %@", SOSTestDeviceGetID(td), ix, error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
336 didAdd = success && ix == count;
337 CFReleaseNull(error);
338 }))
339 fail("ds transaction %@", error);
340 CFReleaseNull(error);
341 return didAdd;
342 }
343
344 CFMutableDictionaryRef SOSTestDeviceListCreate(bool realDb, CFIndex version, CFArrayRef deviceIDs) {
345 CFMutableDictionaryRef testDevices = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
346 CFStringRef deviceID;
347 CFSetRef defaultViews = realDb ? SOSViewsCopyTestV2Default() : SOSViewsCopyTestV0Default();
348 CFArrayForEachC(deviceIDs, deviceID) {
349 SOSTestDeviceRef device;
350 if (!realDb) {
351 device = SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault, deviceID);
352 } else {
353 device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
354 }
355 SOSTestDeviceSetPeerIDs(device, deviceIDs, version, defaultViews);
356 CFDictionarySetValue(testDevices, deviceID, device);
357 CFReleaseSafe(device);
358 }
359 CFReleaseSafe(defaultViews);
360
361 CFDictionarySetValue(testDevices, CFSTR("@devicesIDs"), deviceIDs);
362 return testDevices;
363 }
364
365 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)) {
366 CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs"));
367 const CFIndex edgeCount = CFArrayGetCount(deviceIDs) * (CFArrayGetCount(deviceIDs) - 1);
368 CFIndex deviceIX = 0;
369 __block CFIndex noMsgSentCount = 0;
370 __block CFIndex msgSentSinceLastChangeCount = 0;
371 __block bool done = false;
372 do {
373 CFStringRef sourceID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, deviceIX++);
374 if (deviceIX >= CFArrayGetCount(deviceIDs))
375 deviceIX = 0;
376
377 SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID);
378 SOSTestDeviceForEachPeerID(source, ^(CFStringRef destID, bool *stop) {
379 SOSTestDeviceRef dest = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, destID);
380 if (dest) {
381 SOSPeerRef peer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(source->ds, NULL), destID, NULL);
382 SOSManifestRef preCreateManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), peer, NULL);
383 if (pre && pre(source, dest))
384 msgSentSinceLastChangeCount = 0;
385
386 CFDataRef msg = SOSTestDeviceCreateMessage(source, destID);
387 SOSMessageRef message = NULL;
388 bool handled = false;
389 if (msg && CFDataGetLength(msg) > 0) {
390 if (!source->mute) {
391 msgSentSinceLastChangeCount++;
392 handled = SOSTestDeviceHandleMessage(dest, sourceID, msg);
393 if (handled) {
394 noMsgSentCount = 0;
395 }
396
397 CFErrorRef error = NULL;
398 message = SOSMessageCreateWithData(kCFAllocatorDefault, msg, &error);
399 ok(handled, "%s %@->%@ %@", name, sourceID, destID, message);
400 CFReleaseNull(error);
401 }
402 } else {
403 msgSentSinceLastChangeCount++;
404 SOSManifestRef sourceManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), peer, NULL);
405 pass("%s %@->%@ done L:%@", name, sourceID, destID, sourceManifest);
406 CFReleaseSafe(sourceManifest);
407 noMsgSentCount++;
408 }
409 CFReleaseSafe(msg);
410 if (post && post(source, dest, message))
411 msgSentSinceLastChangeCount = 0;
412
413 CFReleaseNull(message);
414 if (preCreateManifest) {
415 SOSManifestRef postHandleManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), peer, NULL);
416 if (postHandleManifest && !CFEqual(preCreateManifest, postHandleManifest)) {
417 //CFStringPerformWithCString(destID, ^(const char *destStr) { diag("device %s changed", destStr); });
418 msgSentSinceLastChangeCount = 0;
419 }
420 CFReleaseSafe(postHandleManifest);
421 CFRelease(preCreateManifest);
422 }
423 CFReleaseSafe(peer);
424 }
425 if (noMsgSentCount >= edgeCount) {
426 *stop = done = true;
427 } else if (msgSentSinceLastChangeCount >= /* 3 */9 * edgeCount + 1) {
428 fail("%s %" PRIdCFIndex" peers never stopped syncing %" PRIdCFIndex" messages since last change", name, CFArrayGetCount(deviceIDs), msgSentSinceLastChangeCount);
429 *stop = done = true;
430 }
431 });
432 } while (!done);
433 }
434
435 bool SOSTestDeviceListInSync(const char *name, const char *test_directive, const char *test_reason, CFMutableDictionaryRef testDevices) {
436 bool inSync = true;
437 CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs"));
438 for (CFIndex len = CFArrayGetCount(deviceIDs), source_ix = 0; source_ix < len; ++source_ix) {
439 CFStringRef sourceID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, source_ix);
440 SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID);
441 for (CFIndex dest_ix = source_ix + 1; dest_ix < len; ++dest_ix) {
442 CFStringRef destID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, dest_ix);
443 SOSTestDeviceRef dest = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, destID);
444
445 SOSPeerRef sourcePeer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(source->ds, NULL), destID, NULL);
446 SOSManifestRef sourceManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), sourcePeer, NULL);
447
448 SOSPeerRef destPeer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(dest->ds, NULL), sourceID, NULL);
449 SOSManifestRef destManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), destPeer, NULL);
450
451 if (!CFEqualSafe(sourceManifest, destManifest)) {
452 fail("%s %@ manifest %@ != %@ manifest %@", name, sourceID, sourceManifest, destID, destManifest);
453 inSync = false;
454 }
455 CFReleaseSafe(sourcePeer);
456 CFReleaseSafe(sourceManifest);
457 CFReleaseSafe(destPeer);
458 CFReleaseSafe(destManifest);
459 }
460 }
461 if (inSync)
462 pass("%s all peers in sync", name);
463 return inSync;
464 }
465
466 void SOSTestDeviceListTestSync(const char *name, const char *test_directive, const char *test_reason, CFIndex version, bool use_db,
467 bool(^pre)(SOSTestDeviceRef source, SOSTestDeviceRef dest),
468 bool(^post)(SOSTestDeviceRef source, SOSTestDeviceRef dest, SOSMessageRef message), ...) {
469 va_list args;
470 va_start(args, post);
471 // Optionally prefix each peer with name to make them more unique.
472 CFArrayRef deviceIDs = CFArrayCreateForVC(kCFAllocatorDefault, &kCFTypeArrayCallBacks, args);
473 CFMutableDictionaryRef testDevices = SOSTestDeviceListCreate(use_db, version, deviceIDs);
474 CFReleaseSafe(deviceIDs);
475 SOSTestDeviceListSync(name, test_directive, test_reason, testDevices, pre, post);
476 SOSTestDeviceListInSync(name, test_directive, test_reason, testDevices);
477 SOSTestDeviceDestroyEngine(testDevices);
478 CFReleaseSafe(testDevices);
479 }