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