]> git.saurik.com Git - apple/security.git/blob - keychain/SecureObjectSync/Regressions/SOSTestDevice.c
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / SecureObjectSync / 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 <regressions/test/testmore.h>
30
31 #include <Security/SecureObjectSync/SOSCloudCircle.h>
32 #include "keychain/SecureObjectSync/SOSEngine.h"
33 #include "keychain/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, NULL);
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 void(^prepop)(SOSDataSourceRef ds)) {
154 setup("create device");
155 SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID);
156
157 td->ds = SOSTestDataSourceCreate();
158 if (prepop)
159 prepop(td->ds);
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
167
168 CFSetRef SOSViewsCopyTestV0Default() {
169 const void *values[] = { kSOSViewKeychainV0 };
170 return CFSetCreate(kCFAllocatorDefault, values, array_size(values), &kCFTypeSetCallBacks);
171 }
172
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);
175 }
176
177 SOSTestDeviceRef SOSTestDeviceSetPeerIDs(SOSTestDeviceRef td, CFArrayRef peerIDs, CFIndex version, CFSetRef defaultViews) {
178 setup("create device");
179 CFStringRef engineID = SOSTestDeviceGetID(td);
180 CFTypeRef peerMeta;
181 CFMutableArrayRef trustedPeersIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
182 CFErrorRef error = NULL;
183 CFArrayForEachC(peerIDs, peerMeta) {
184 CFDataRef keyBag = NULL;
185 CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, NULL, &keyBag, &error);
186 if (!peerID) {
187 fail("SOSPeerMetaGetComponents %@: %@", peerMeta, error);
188 CFReleaseNull(error);
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 }
197 }
198 }
199
200 SOSEngineCircleChanged(td->ds->engine, engineID, trustedPeersIDs, NULL);
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);
216 CFReleaseNull(error);
217 }
218 CFReleaseSafe(trustedPeersIDs);
219 return td;
220 }
221
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
231 bool SOSTestDeviceSetEngineState(SOSTestDeviceRef td, CFDataRef derEngineState) {
232 CFErrorRef localError = NULL;
233 SOSTestEngineSaveWithDER(td->ds->engine, derEngineState, &localError);
234 return true;
235 }
236
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 }
245
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 }
254
255 CFDataRef SOSTestDeviceCreateMessage(SOSTestDeviceRef td, CFStringRef peerID) {
256 setup("create message");
257 CFErrorRef error = NULL;
258 SOSEnginePeerMessageSentCallback* sent = NULL;
259 CFDataRef msgData;
260 CFMutableArrayRef attributeList = NULL;
261 ok(msgData = SOSEngineCreateMessageToSyncToPeer(td->ds->engine, peerID, &attributeList, &sent, &error),
262 "create message to %@: %@", peerID, error);
263
264 SOSEngineMessageCallCallback(sent, true);
265 SOSEngineFreeMessageCallback(sent);
266
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);
295 pass("handling %@->%@ %@", peerID, SOSEngineGetMyID(SOSDataSourceGetSharedEngine(td->ds, &error)), message);
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
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
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);
362 CFReleaseNull(accountStr);
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
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)) {
393 CFMutableDictionaryRef testDevices = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
394 CFStringRef deviceID;
395 CFSetRef defaultViews = realDb ? SOSViewsCopyTestV2Default() : SOSViewsCopyTestV0Default();
396 CFArrayForEachC(deviceIDs, deviceID) {
397 SOSTestDeviceRef device;
398 if (!realDb) {
399 device = SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault, deviceID, prepop);
400 } else {
401 device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
402 }
403 SOSTestDeviceSetPeerIDs(device, deviceIDs, version, defaultViews);
404 CFDictionarySetValue(testDevices, deviceID, device);
405 CFReleaseSafe(device);
406 }
407 CFReleaseSafe(defaultViews);
408
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) {
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
434 CFDataRef msg = SOSTestDeviceCreateMessage(source, destID);
435 SOSMessageRef message = NULL;
436 bool handled = false;
437 if (msg && CFDataGetLength(msg) > 0) {
438 if (!source->mute) {
439 msgSentSinceLastChangeCount++;
440 handled = SOSTestDeviceHandleMessage(dest, sourceID, msg);
441 if (handled) {
442 noMsgSentCount = 0;
443 }
444
445 CFErrorRef error = NULL;
446 message = SOSMessageCreateWithData(kCFAllocatorDefault, msg, &error);
447 ok(handled, "%s %@->%@ %@", name, sourceID, destID, message);
448 CFReleaseNull(error);
449 }
450 } else {
451 msgSentSinceLastChangeCount++;
452 SOSManifestRef sourceManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), peer, NULL);
453 pass("%s %@->%@ done L:%@", name, sourceID, destID, sourceManifest);
454 CFReleaseSafe(sourceManifest);
455 noMsgSentCount++;
456 }
457 CFReleaseSafe(msg);
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); });
466 msgSentSinceLastChangeCount = 0;
467 }
468 CFReleaseSafe(postHandleManifest);
469 CFRelease(preCreateManifest);
470 }
471 CFReleaseSafe(peer);
472 }
473 if (noMsgSentCount >= edgeCount) {
474 *stop = done = true;
475 } else if (msgSentSinceLastChangeCount >= /* 3 */9 * edgeCount + 1) {
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"));
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);
507 }
508 }
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);
521 CFMutableDictionaryRef testDevices = SOSTestDeviceListCreate(use_db, version, deviceIDs, NULL);
522 CFReleaseSafe(deviceIDs);
523 SOSTestDeviceListSync(name, test_directive, test_reason, testDevices, pre, post);
524 SOSTestDeviceListInSync(name, test_directive, test_reason, testDevices);
525 SOSTestDeviceDestroyEngine(testDevices);
526 CFReleaseSafe(testDevices);
527 }