]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/Regressions/SOSTestDevice.c
Security-57740.31.2.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/SecureObjectSync/SOSViews.h>
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)
68 SOSDataSourceFactoryRelease(td->dsf);
69 CFReleaseSafe(td->db);
70 }
71
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);
79 SOSEngineRef engine = SOSDataSourceGetSharedEngine(source->ds, NULL);
80 SOSEngineClearCache(engine);
81 SOSEngineDispose(engine);
82 });
83 }
84
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
103 static CFStringRef SOSTestDeviceCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
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) {
108 SOSPeerRef peer = SOSEngineCopyPeerWithID(td->ds->engine, peerID, NULL);
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);
121 td->mute = false;
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);
130 CFStringRef sname = SOSDataSourceFactoryCopyName(td->dsf);
131 CFErrorRef error = NULL;
132 ok (td->ds = SOSDataSourceFactoryCreateDataSource(td->dsf, sname, &error), "%@ create datasource \"%@\" [error: %@]", engineID, sname, error);
133 CFReleaseNull(error);
134 CFReleaseNull(sname);
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
152 SOSTestDeviceRef SOSTestDeviceCreateWithTestDataSource(CFAllocatorRef allocator, CFStringRef engineID) {
153 setup("create device");
154 SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID);
155
156 td->ds = SOSTestDataSourceCreate();
157 CFErrorRef error = NULL;
158 ok(td->ds->engine = SOSEngineCreate(td->ds, &error), "create engine: %@", error);
159 SOSEngineCircleChanged(td->ds->engine, engineID, NULL, NULL);
160 CFReleaseNull(error);
161 return td;
162 }
163
164 CFSetRef SOSViewsCopyTestV0Default() {
165 const void *values[] = { kSOSViewKeychainV0 };
166 return CFSetCreate(kCFAllocatorDefault, values, array_size(values), &kCFTypeSetCallBacks);
167 }
168
169 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.
170 return SOSViewCopyViewSet(kViewSetAll);
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 bool SOSTestDeviceSetEngineState(SOSTestDeviceRef td, CFDataRef derEngineState) {
228 CFErrorRef localError = NULL;
229 SOSTestEngineSaveWithDER(td->ds->engine, derEngineState, &localError);
230 return true;
231 }
232
233
234
235 CFDataRef SOSTestDeviceCreateMessage(SOSTestDeviceRef td, CFStringRef peerID) {
236 setup("create message");
237 CFErrorRef error = NULL;
238 SOSEnginePeerMessageSentBlock sent = NULL;
239 CFDataRef msgData;
240
241 ok(msgData = SOSEngineCreateMessageToSyncToPeer(td->ds->engine, peerID, &sent, &error),
242 "create message to %@: %@", peerID, error);
243 if (sent)
244 sent(true);
245
246 Block_release(sent);
247 return msgData;
248 }
249
250 #if 0
251 CFDictionaryRef SOSTestDeviceCreateMessages(SOSTestDeviceRef td) {
252 CFTypeRef peer = NULL;
253 CFMutableDictionaryRef messages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
254 CFArrayForEachC(td->peers, peer) {
255 CFStringRef peerID = SOSPeerGetID((SOSPeerRef)peer);
256 CFDataRef msg = SOSTestDeviceCreateMessage(td, peerID);
257 if (msg) {
258 CFDictionaryAddValue(messages, peerID, msg);
259 CFRelease(msg);
260 }
261 }
262 return messages;
263 }
264 #endif
265
266 bool SOSTestDeviceHandleMessage(SOSTestDeviceRef td, CFStringRef peerID, CFDataRef msgData) {
267 setup("handle message");
268 if (!msgData) return false;
269 CFErrorRef error = NULL;
270 bool handled;
271 SOSMessageRef message;
272
273 ok(message = SOSMessageCreateWithData(kCFAllocatorDefault, msgData, &error), "decode message %@: %@", msgData, error);
274 CFReleaseNull(error);
275 pass("handling %@->%@ %@", peerID, SOSEngineGetMyID(SOSDataSourceGetSharedEngine(td->ds, &error)), message);
276 ok(handled = SOSEngineHandleMessage(SOSDataSourceGetSharedEngine(td->ds, &error), peerID, msgData, &error),
277 "handled from %@ %@: %@", peerID, message, error);
278 CFReleaseNull(error);
279
280 CFReleaseNull(message);
281 return handled;
282 }
283
284 void SOSTestDeviceAddGenericItem(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) {
285 __block CFErrorRef error = NULL;
286 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
287 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, account, server);
288 ok(SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error), "%@ added API object %@", SOSTestDeviceGetID(td), error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
289 CFReleaseSafe(object);
290 CFReleaseNull(error);
291 }))
292 fail("ds transaction %@", error);
293 CFReleaseNull(error);
294 }
295
296 void SOSTestDeviceAddGenericItemTombstone(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) {
297 __block CFErrorRef error = NULL;
298 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
299 SOSObjectRef object = SOSDataSourceCreateGenericItemWithData(td->ds, account, server, true, NULL);
300 SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error);
301 CFReleaseSafe(object);
302 CFReleaseNull(error);
303 }))
304 fail("ds transaction %@", error);
305 CFReleaseNull(error);
306 }
307
308 void SOSTestDeviceAddGenericItemWithData(SOSTestDeviceRef td, CFStringRef account, CFStringRef server, CFDataRef data) {
309 __block CFErrorRef error = NULL;
310 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
311 SOSObjectRef object = SOSDataSourceCreateGenericItemWithData(td->ds, account, server, false, data);
312 SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error);
313 CFReleaseSafe(object);
314 CFReleaseNull(error);
315 }))
316 fail("ds transaction %@", error);
317 CFReleaseNull(error);
318 }
319
320 void SOSTestDeviceAddRemoteGenericItem(SOSTestDeviceRef td, CFStringRef account, CFStringRef server) {
321 __block CFErrorRef error = NULL;
322 if (!SOSDataSourceWithAPI(td->ds, false, &error, ^(SOSTransactionRef txn, bool *commit) {
323 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, account, server);
324 ok(SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error), "%@ added remote object %@", SOSTestDeviceGetID(td), error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
325 CFReleaseSafe(object);
326 CFReleaseNull(error);
327 }))
328 fail("ds transaction %@", error);
329 CFReleaseNull(error);
330 }
331
332 bool SOSTestDeviceAddGenericItems(SOSTestDeviceRef td, CFIndex count, CFStringRef account, CFStringRef server) {
333 __block bool didAdd = false;
334 __block CFErrorRef error = NULL;
335 if (!SOSDataSourceWithAPI(td->ds, true, &error, ^(SOSTransactionRef txn, bool *commit) {
336 bool success = true;
337 CFIndex ix = 0;
338 for (; success && ix < count; ++ix) {
339 CFStringRef accountStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@%" PRIdCFIndex), account, ix);
340 SOSObjectRef object = SOSDataSourceCreateGenericItem(td->ds, accountStr, server);
341 success = SOSDataSourceMergeObject(td->ds, txn, object, NULL, &error);
342 CFReleaseNull(accountStr);
343 CFReleaseSafe(object);
344 }
345 ok(success, "%@ added %" PRIdCFIndex " API objects %@", SOSTestDeviceGetID(td), ix, error ? (CFTypeRef)error : (CFTypeRef)CFSTR("ok"));
346 didAdd = success && ix == count;
347 CFReleaseNull(error);
348 }))
349 fail("ds transaction %@", error);
350 CFReleaseNull(error);
351 return didAdd;
352 }
353
354 CFMutableDictionaryRef SOSTestDeviceListCreate(bool realDb, CFIndex version, CFArrayRef deviceIDs) {
355 CFMutableDictionaryRef testDevices = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
356 CFStringRef deviceID;
357 CFSetRef defaultViews = realDb ? SOSViewsCopyTestV2Default() : SOSViewsCopyTestV0Default();
358 CFArrayForEachC(deviceIDs, deviceID) {
359 SOSTestDeviceRef device;
360 if (!realDb) {
361 device = SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault, deviceID);
362 } else {
363 device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
364 }
365 SOSTestDeviceSetPeerIDs(device, deviceIDs, version, defaultViews);
366 CFDictionarySetValue(testDevices, deviceID, device);
367 CFReleaseSafe(device);
368 }
369 CFReleaseSafe(defaultViews);
370
371 CFDictionarySetValue(testDevices, CFSTR("@devicesIDs"), deviceIDs);
372 return testDevices;
373 }
374
375 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)) {
376 CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs"));
377 const CFIndex edgeCount = CFArrayGetCount(deviceIDs) * (CFArrayGetCount(deviceIDs) - 1);
378 CFIndex deviceIX = 0;
379 __block CFIndex noMsgSentCount = 0;
380 __block CFIndex msgSentSinceLastChangeCount = 0;
381 __block bool done = false;
382 do {
383 CFStringRef sourceID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, deviceIX++);
384 if (deviceIX >= CFArrayGetCount(deviceIDs))
385 deviceIX = 0;
386
387 SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID);
388 SOSTestDeviceForEachPeerID(source, ^(CFStringRef destID, bool *stop) {
389 SOSTestDeviceRef dest = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, destID);
390 if (dest) {
391 SOSPeerRef peer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(source->ds, NULL), destID, NULL);
392 SOSManifestRef preCreateManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), peer, NULL);
393 if (pre && pre(source, dest))
394 msgSentSinceLastChangeCount = 0;
395
396 CFDataRef msg = SOSTestDeviceCreateMessage(source, destID);
397 SOSMessageRef message = NULL;
398 bool handled = false;
399 if (msg && CFDataGetLength(msg) > 0) {
400 if (!source->mute) {
401 msgSentSinceLastChangeCount++;
402 handled = SOSTestDeviceHandleMessage(dest, sourceID, msg);
403 if (handled) {
404 noMsgSentCount = 0;
405 }
406
407 CFErrorRef error = NULL;
408 message = SOSMessageCreateWithData(kCFAllocatorDefault, msg, &error);
409 ok(handled, "%s %@->%@ %@", name, sourceID, destID, message);
410 CFReleaseNull(error);
411 }
412 } else {
413 msgSentSinceLastChangeCount++;
414 SOSManifestRef sourceManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), peer, NULL);
415 pass("%s %@->%@ done L:%@", name, sourceID, destID, sourceManifest);
416 CFReleaseSafe(sourceManifest);
417 noMsgSentCount++;
418 }
419 CFReleaseSafe(msg);
420 if (post && post(source, dest, message))
421 msgSentSinceLastChangeCount = 0;
422
423 CFReleaseNull(message);
424 if (preCreateManifest) {
425 SOSManifestRef postHandleManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), peer, NULL);
426 if (postHandleManifest && !CFEqual(preCreateManifest, postHandleManifest)) {
427 //CFStringPerformWithCString(destID, ^(const char *destStr) { diag("device %s changed", destStr); });
428 msgSentSinceLastChangeCount = 0;
429 }
430 CFReleaseSafe(postHandleManifest);
431 CFRelease(preCreateManifest);
432 }
433 CFReleaseSafe(peer);
434 }
435 if (noMsgSentCount >= edgeCount) {
436 *stop = done = true;
437 } else if (msgSentSinceLastChangeCount >= /* 3 */9 * edgeCount + 1) {
438 fail("%s %" PRIdCFIndex" peers never stopped syncing %" PRIdCFIndex" messages since last change", name, CFArrayGetCount(deviceIDs), msgSentSinceLastChangeCount);
439 *stop = done = true;
440 }
441 });
442 } while (!done);
443 }
444
445 bool SOSTestDeviceListInSync(const char *name, const char *test_directive, const char *test_reason, CFMutableDictionaryRef testDevices) {
446 bool inSync = true;
447 CFArrayRef deviceIDs = (CFArrayRef)CFDictionaryGetValue(testDevices, CFSTR("@devicesIDs"));
448 for (CFIndex len = CFArrayGetCount(deviceIDs), source_ix = 0; source_ix < len; ++source_ix) {
449 CFStringRef sourceID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, source_ix);
450 SOSTestDeviceRef source = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, sourceID);
451 for (CFIndex dest_ix = source_ix + 1; dest_ix < len; ++dest_ix) {
452 CFStringRef destID = (CFStringRef)CFArrayGetValueAtIndex(deviceIDs, dest_ix);
453 SOSTestDeviceRef dest = (SOSTestDeviceRef)CFDictionaryGetValue(testDevices, destID);
454
455 SOSPeerRef sourcePeer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(source->ds, NULL), destID, NULL);
456 SOSManifestRef sourceManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), sourcePeer, NULL);
457
458 SOSPeerRef destPeer = SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(dest->ds, NULL), sourceID, NULL);
459 SOSManifestRef destManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest->ds, NULL), destPeer, NULL);
460
461 if (!CFEqualSafe(sourceManifest, destManifest)) {
462 fail("%s %@ manifest %@ != %@ manifest %@", name, sourceID, sourceManifest, destID, destManifest);
463 inSync = false;
464 }
465 CFReleaseSafe(sourcePeer);
466 CFReleaseSafe(sourceManifest);
467 CFReleaseSafe(destPeer);
468 CFReleaseSafe(destManifest);
469 }
470 }
471 if (inSync)
472 pass("%s all peers in sync", name);
473 return inSync;
474 }
475
476 void SOSTestDeviceListTestSync(const char *name, const char *test_directive, const char *test_reason, CFIndex version, bool use_db,
477 bool(^pre)(SOSTestDeviceRef source, SOSTestDeviceRef dest),
478 bool(^post)(SOSTestDeviceRef source, SOSTestDeviceRef dest, SOSMessageRef message), ...) {
479 va_list args;
480 va_start(args, post);
481 // Optionally prefix each peer with name to make them more unique.
482 CFArrayRef deviceIDs = CFArrayCreateForVC(kCFAllocatorDefault, &kCFTypeArrayCallBacks, args);
483 CFMutableDictionaryRef testDevices = SOSTestDeviceListCreate(use_db, version, deviceIDs);
484 CFReleaseSafe(deviceIDs);
485 SOSTestDeviceListSync(name, test_directive, test_reason, testDevices, pre, post);
486 SOSTestDeviceListInSync(name, test_directive, test_reason, testDevices);
487 SOSTestDeviceDestroyEngine(testDevices);
488 CFReleaseSafe(testDevices);
489 }