2 * Copyright (c) 2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // Test syncing between SecItemDataSource and SOSTestDataSource
27 #include "SOSTestDevice.h"
28 #include "SOSTestDataSource.h"
29 #include <regressions/test/testmore.h>
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>
46 #include <AssertMacros.h>
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
]);
58 CFReleaseSafe(msgData
);
62 static void SOSTestDeviceDestroy(CFTypeRef cf
) {
63 SOSTestDeviceRef td
= (SOSTestDeviceRef
)cf
;
64 CFReleaseSafe(td
->peers
);
66 SOSDataSourceRelease(td
->ds
, NULL
);
68 SOSDataSourceFactoryRelease(td
->dsf
);
69 CFReleaseSafe(td
->db
);
72 void SOSTestDeviceDestroyEngine(CFMutableDictionaryRef testDevices
) {
74 CFArrayRef deviceIDs
= (CFArrayRef
)CFDictionaryGetValue(testDevices
, CFSTR("@devicesIDs"));
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
);
85 CFStringRef
SOSTestDeviceGetID(SOSTestDeviceRef td
) {
86 CFStringRef engineID
= NULL
;
87 SOSEngineRef engine
= SOSDataSourceGetSharedEngine(td
->ds
, NULL
);
89 engineID
= SOSEngineGetMyID(engine
);
93 void SOSTestDeviceForEachPeerID(SOSTestDeviceRef td
, void(^peerBlock
)(CFStringRef peerID
, bool *stop
)) {
96 CFArrayForEachC(td
->peers
, peer
) {
97 peerBlock(SOSPeerGetID(peer
), &stop
);
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
);
112 CFStringAppendFormat(result
, NULL
, CFSTR(">"));
116 CFGiblisFor(SOSTestDevice
)
118 static SOSTestDeviceRef
SOSTestDeviceCreateInternal(CFAllocatorRef allocator
, CFStringRef engineID
) {
119 SOSTestDeviceRef td
= CFTypeAllocate(SOSTestDevice
, struct __OpaqueSOSTestDevice
, allocator
);
120 td
->peers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
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
137 SOSEngineCircleChanged(SOSDataSourceGetSharedEngine(td
->ds
, NULL
), engineID
, NULL
, NULL
);
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
);
152 SOSTestDeviceRef
SOSTestDeviceCreateWithTestDataSource(CFAllocatorRef allocator
, CFStringRef engineID
,
153 void(^prepop
)(SOSDataSourceRef ds
)) {
154 setup("create device");
155 SOSTestDeviceRef td
= SOSTestDeviceCreateInternal(allocator
, engineID
);
157 td
->ds
= SOSTestDataSourceCreate();
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
);
168 CFSetRef
SOSViewsCopyTestV0Default() {
169 const void *values
[] = { kSOSViewKeychainV0
};
170 return CFSetCreate(kCFAllocatorDefault
, values
, array_size(values
), &kCFTypeSetCallBacks
);
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
);
177 SOSTestDeviceRef
SOSTestDeviceSetPeerIDs(SOSTestDeviceRef td
, CFArrayRef peerIDs
, CFIndex version
, CFSetRef defaultViews
) {
178 setup("create device");
179 CFStringRef engineID
= SOSTestDeviceGetID(td
);
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
);
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
);
195 CFArrayAppendValue(trustedPeersIDs
, peerMeta
);
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
);
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
);
218 CFReleaseSafe(trustedPeersIDs
);
222 SOSTestDeviceRef
SOSTestDeviceSetMute(SOSTestDeviceRef td
, bool mute
) {
227 bool SOSTestDeviceIsMute(SOSTestDeviceRef td
) {
231 bool SOSTestDeviceSetEngineState(SOSTestDeviceRef td
, CFDataRef derEngineState
) {
232 CFErrorRef localError
= NULL
;
233 SOSTestEngineSaveWithDER(td
->ds
->engine
, derEngineState
, &localError
);
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
);
242 fail("ds transaction %@", error
? *error
: NULL
);
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
);
251 fail("ds transaction %@", error
? *error
: NULL
);
255 CFDataRef
SOSTestDeviceCreateMessage(SOSTestDeviceRef td
, CFStringRef peerID
) {
256 setup("create message");
257 CFErrorRef error
= NULL
;
258 SOSEnginePeerMessageSentCallback
* sent
= NULL
;
260 CFMutableArrayRef attributeList
= NULL
;
261 ok(msgData
= SOSEngineCreateMessageToSyncToPeer(td
->ds
->engine
, peerID
, &attributeList
, &sent
, &error
),
262 "create message to %@: %@", peerID
, error
);
264 SOSEngineMessageCallCallback(sent
, true);
265 SOSEngineFreeMessageCallback(sent
);
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
);
278 CFDictionaryAddValue(messages
, peerID
, msg
);
286 bool SOSTestDeviceHandleMessage(SOSTestDeviceRef td
, CFStringRef peerID
, CFDataRef msgData
) {
287 setup("handle message");
288 if (!msgData
) return false;
289 CFErrorRef error
= NULL
;
291 SOSMessageRef message
;
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
);
300 CFReleaseNull(message
);
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
);
312 fail("ds transaction %@", error
);
313 CFReleaseNull(error
);
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
);
324 fail("ds transaction %@", error
);
325 CFReleaseNull(error
);
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
);
336 fail("ds transaction %@", error
);
337 CFReleaseNull(error
);
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
);
348 fail("ds transaction %@", error
);
349 CFReleaseNull(error
);
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
) {
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
);
365 ok(success
, "%@ added %" PRIdCFIndex
" API objects %@", SOSTestDeviceGetID(td
), ix
, error
? (CFTypeRef
)error
: (CFTypeRef
)CFSTR("ok"));
366 didAdd
= success
&& ix
== count
;
367 CFReleaseNull(error
);
369 fail("ds transaction %@", error
);
370 CFReleaseNull(error
);
374 void SOSTestDeviceAddV0EngineStateWithData(SOSDataSourceRef ds
, CFDataRef engineStateData
) {
375 __block CFErrorRef error
= NULL
;
376 const CFStringRef kSOSEngineState
= CFSTR("engine-state");
378 if (!SOSDataSourceWithAPI(ds
, true, &error
, ^(SOSTransactionRef txn
, bool *commit
) {
379 SOSObjectRef object
= SOSDataSourceCreateV0EngineStateWithData(ds
, engineStateData
);
381 // Note that state doesn't use SOSDataSourceMergeObject
382 SOSDataSourceSetStateWithKey(ds
, txn
, kSOSEngineState
, kSecAttrAccessibleAlwaysPrivate
, engineStateData
, &error
);
383 CFReleaseSafe(object
);
384 CFReleaseNull(error
);
386 fail("ds transaction %@", error
);
388 CFReleaseNull(error
);
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
;
399 device
= SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault
, deviceID
, prepop
);
401 device
= SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault
, deviceID
, deviceID
);
403 SOSTestDeviceSetPeerIDs(device
, deviceIDs
, version
, defaultViews
);
404 CFDictionarySetValue(testDevices
, deviceID
, device
);
405 CFReleaseSafe(device
);
407 CFReleaseSafe(defaultViews
);
409 CFDictionarySetValue(testDevices
, CFSTR("@devicesIDs"), deviceIDs
);
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;
421 CFStringRef sourceID
= (CFStringRef
)CFArrayGetValueAtIndex(deviceIDs
, deviceIX
++);
422 if (deviceIX
>= CFArrayGetCount(deviceIDs
))
425 SOSTestDeviceRef source
= (SOSTestDeviceRef
)CFDictionaryGetValue(testDevices
, sourceID
);
426 SOSTestDeviceForEachPeerID(source
, ^(CFStringRef destID
, bool *stop
) {
427 SOSTestDeviceRef dest
= (SOSTestDeviceRef
)CFDictionaryGetValue(testDevices
, destID
);
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;
434 CFDataRef msg
= SOSTestDeviceCreateMessage(source
, destID
);
435 SOSMessageRef message
= NULL
;
436 bool handled
= false;
437 if (msg
&& CFDataGetLength(msg
) > 0) {
439 msgSentSinceLastChangeCount
++;
440 handled
= SOSTestDeviceHandleMessage(dest
, sourceID
, msg
);
445 CFErrorRef error
= NULL
;
446 message
= SOSMessageCreateWithData(kCFAllocatorDefault
, msg
, &error
);
447 ok(handled
, "%s %@->%@ %@", name
, sourceID
, destID
, message
);
448 CFReleaseNull(error
);
451 msgSentSinceLastChangeCount
++;
452 SOSManifestRef sourceManifest
= SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source
->ds
, NULL
), peer
, NULL
);
453 pass("%s %@->%@ done L:%@", name
, sourceID
, destID
, sourceManifest
);
454 CFReleaseSafe(sourceManifest
);
458 if (post
&& post(source
, dest
, message
))
459 msgSentSinceLastChangeCount
= 0;
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;
468 CFReleaseSafe(postHandleManifest
);
469 CFRelease(preCreateManifest
);
473 if (noMsgSentCount
>= edgeCount
) {
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
);
483 bool SOSTestDeviceListInSync(const char *name
, const char *test_directive
, const char *test_reason
, CFMutableDictionaryRef testDevices
) {
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
);
493 SOSPeerRef sourcePeer
= SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(source
->ds
, NULL
), destID
, NULL
);
494 SOSManifestRef sourceManifest
= SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source
->ds
, NULL
), sourcePeer
, NULL
);
496 SOSPeerRef destPeer
= SOSEngineCopyPeerWithID(SOSDataSourceGetSharedEngine(dest
->ds
, NULL
), sourceID
, NULL
);
497 SOSManifestRef destManifest
= SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(dest
->ds
, NULL
), destPeer
, NULL
);
499 if (!CFEqualSafe(sourceManifest
, destManifest
)) {
500 fail("%s %@ manifest %@ != %@ manifest %@", name
, sourceID
, sourceManifest
, destID
, destManifest
);
503 CFReleaseSafe(sourcePeer
);
504 CFReleaseSafe(sourceManifest
);
505 CFReleaseSafe(destPeer
);
506 CFReleaseSafe(destManifest
);
510 pass("%s all peers in sync", name
);
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
), ...) {
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
);