]> git.saurik.com Git - apple/security.git/blame - OSX/sec/SOSCircle/Regressions/SOSTestDevice.c
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / Regressions / SOSTestDevice.c
CommitLineData
d8f41ccd
A
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"
866f8763 29#include <regressions/test/testmore.h>
d8f41ccd 30
5c19dc3a
A
31#include <Security/SecureObjectSync/SOSCloudCircle.h>
32#include <Security/SecureObjectSync/SOSEngine.h>
33#include <Security/SecureObjectSync/SOSPeer.h>
fa7225c8 34#include <Security/SecureObjectSync/SOSViews.h>
d8f41ccd
A
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
48CFStringRef 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
62static 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)
5c19dc3a 68 SOSDataSourceFactoryRelease(td->dsf);
d8f41ccd
A
69 CFReleaseSafe(td->db);
70}
71
5c19dc3a
A
72void 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);
e3d460c9
A
79 SOSEngineRef engine = SOSDataSourceGetSharedEngine(source->ds, NULL);
80 SOSEngineClearCache(engine);
81 SOSEngineDispose(engine);
5c19dc3a
A
82 });
83}
84
d8f41ccd
A
85CFStringRef 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
93void 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
d87e1158 103static CFStringRef SOSTestDeviceCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
d8f41ccd
A
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) {
5c19dc3a 108 SOSPeerRef peer = SOSEngineCopyPeerWithID(td->ds->engine, peerID, NULL);
d8f41ccd
A
109 CFStringAppendFormat(result, NULL, CFSTR("\n%@"), peer);
110 CFReleaseSafe(peer);
111 });
112 CFStringAppendFormat(result, NULL, CFSTR(">"));
113 return result;
114}
115
116CFGiblisFor(SOSTestDevice)
117
118static SOSTestDeviceRef SOSTestDeviceCreateInternal(CFAllocatorRef allocator, CFStringRef engineID) {
119 SOSTestDeviceRef td = CFTypeAllocate(SOSTestDevice, struct __OpaqueSOSTestDevice, allocator);
120 td->peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
d87e1158 121 td->mute = false;
d8f41ccd
A
122 return td;
123}
124
125SOSTestDeviceRef 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);
5c19dc3a 130 CFStringRef sname = SOSDataSourceFactoryCopyName(td->dsf);
d8f41ccd 131 CFErrorRef error = NULL;
5c19dc3a
A
132 ok (td->ds = SOSDataSourceFactoryCreateDataSource(td->dsf, sname, &error), "%@ create datasource \"%@\" [error: %@]", engineID, sname, error);
133 CFReleaseNull(error);
134 CFReleaseNull(sname);
d8f41ccd
A
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
141SOSTestDeviceRef SOSTestDeviceCreateWithDbNamed(CFAllocatorRef allocator, CFStringRef engineID, CFStringRef dbName) {
142 CFURLRef url = SecCopyURLForFileInKeychainDirectory(dbName);
143 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
866f8763 144 SecDbRef db = SecKeychainDbCreate(path, NULL);
d8f41ccd
A
145 SOSTestDeviceRef td = SOSTestDeviceCreateWithDb(allocator, engineID, db);
146 CFReleaseSafe(db);
147 CFReleaseSafe(path);
148 CFReleaseSafe(url);
149 return td;
150}
151
6b200bc3
A
152SOSTestDeviceRef SOSTestDeviceCreateWithTestDataSource(CFAllocatorRef allocator, CFStringRef engineID,
153 void(^prepop)(SOSDataSourceRef ds)) {
d8f41ccd
A
154 setup("create device");
155 SOSTestDeviceRef td = SOSTestDeviceCreateInternal(allocator, engineID);
156
157 td->ds = SOSTestDataSourceCreate();
6b200bc3
A
158 if (prepop)
159 prepop(td->ds);
d8f41ccd
A
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
6b200bc3 167
5c19dc3a
A
168CFSetRef SOSViewsCopyTestV0Default() {
169 const void *values[] = { kSOSViewKeychainV0 };
170 return CFSetCreate(kCFAllocatorDefault, values, array_size(values), &kCFTypeSetCallBacks);
171}
172
fa7225c8
A
173CFSetRef 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);
5c19dc3a
A
175}
176
177SOSTestDeviceRef SOSTestDeviceSetPeerIDs(SOSTestDeviceRef td, CFArrayRef peerIDs, CFIndex version, CFSetRef defaultViews) {
d8f41ccd
A
178 setup("create device");
179 CFStringRef engineID = SOSTestDeviceGetID(td);
5c19dc3a 180 CFTypeRef peerMeta;
d8f41ccd
A
181 CFMutableArrayRef trustedPeersIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
182 CFErrorRef error = NULL;
5c19dc3a
A
183 CFArrayForEachC(peerIDs, peerMeta) {
184 CFDataRef keyBag = NULL;
185 CFStringRef peerID = SOSPeerMetaGetComponents(peerMeta, NULL, &keyBag, &error);
186 if (!peerID) {
187 fail("SOSPeerMetaGetComponents %@: %@", peerMeta, error);
d8f41ccd 188 CFReleaseNull(error);
5c19dc3a
A
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 }
d8f41ccd
A
197 }
198 }
199
200 SOSEngineCircleChanged(td->ds->engine, engineID, trustedPeersIDs, NULL);
5c19dc3a
A
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);
d8f41ccd
A
216 CFReleaseNull(error);
217 }
218 CFReleaseSafe(trustedPeersIDs);
219 return td;
220}
221
d87e1158
A
222SOSTestDeviceRef SOSTestDeviceSetMute(SOSTestDeviceRef td, bool mute) {
223 td->mute = mute;
224 return td;
225}
226
227bool SOSTestDeviceIsMute(SOSTestDeviceRef td) {
228 return td->mute;
229}
230
fa7225c8
A
231bool SOSTestDeviceSetEngineState(SOSTestDeviceRef td, CFDataRef derEngineState) {
232 CFErrorRef localError = NULL;
233 SOSTestEngineSaveWithDER(td->ds->engine, derEngineState, &localError);
234 return true;
235}
236
6b200bc3
A
237bool 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}
fa7225c8 245
6b200bc3
A
246bool 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}
fa7225c8 254
d8f41ccd
A
255CFDataRef SOSTestDeviceCreateMessage(SOSTestDeviceRef td, CFStringRef peerID) {
256 setup("create message");
257 CFErrorRef error = NULL;
258 SOSEnginePeerMessageSentBlock sent = NULL;
259 CFDataRef msgData;
866f8763
A
260 CFMutableArrayRef attributeList = NULL;
261 ok(msgData = SOSEngineCreateMessageToSyncToPeer(td->ds->engine, peerID, &attributeList, &sent, &error),
d8f41ccd
A
262 "create message to %@: %@", peerID, error);
263 if (sent)
264 sent(true);
265
5c19dc3a 266 Block_release(sent);
d8f41ccd
A
267 return msgData;
268}
269
270#if 0
271CFDictionaryRef 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
286bool 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);
5c19dc3a 295 pass("handling %@->%@ %@", peerID, SOSEngineGetMyID(SOSDataSourceGetSharedEngine(td->ds, &error)), message);
d8f41ccd
A
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
304void 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
5c19dc3a
A
316void 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
328void 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
d8f41ccd
A
340void 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
352bool 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);
5c19dc3a 362 CFReleaseNull(accountStr);
d8f41ccd
A
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
6b200bc3
A
374void 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
391CFMutableDictionaryRef SOSTestDeviceListCreate(bool realDb, CFIndex version, CFArrayRef deviceIDs,
392 void(^prepop)(SOSDataSourceRef ds)) {
d8f41ccd
A
393 CFMutableDictionaryRef testDevices = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
394 CFStringRef deviceID;
5c19dc3a 395 CFSetRef defaultViews = realDb ? SOSViewsCopyTestV2Default() : SOSViewsCopyTestV0Default();
d8f41ccd
A
396 CFArrayForEachC(deviceIDs, deviceID) {
397 SOSTestDeviceRef device;
5c19dc3a 398 if (!realDb) {
6b200bc3 399 device = SOSTestDeviceCreateWithTestDataSource(kCFAllocatorDefault, deviceID, prepop);
5c19dc3a 400 } else {
d8f41ccd 401 device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
5c19dc3a
A
402 }
403 SOSTestDeviceSetPeerIDs(device, deviceIDs, version, defaultViews);
d8f41ccd
A
404 CFDictionarySetValue(testDevices, deviceID, device);
405 CFReleaseSafe(device);
406 }
5c19dc3a
A
407 CFReleaseSafe(defaultViews);
408
d8f41ccd
A
409 CFDictionarySetValue(testDevices, CFSTR("@devicesIDs"), deviceIDs);
410 return testDevices;
411}
412
413void 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) {
5c19dc3a
A
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
d8f41ccd
A
434 CFDataRef msg = SOSTestDeviceCreateMessage(source, destID);
435 SOSMessageRef message = NULL;
436 bool handled = false;
d8f41ccd 437 if (msg && CFDataGetLength(msg) > 0) {
d87e1158 438 if (!source->mute) {
5c19dc3a 439 msgSentSinceLastChangeCount++;
d87e1158
A
440 handled = SOSTestDeviceHandleMessage(dest, sourceID, msg);
441 if (handled) {
442 noMsgSentCount = 0;
443 }
d8f41ccd 444
d87e1158
A
445 CFErrorRef error = NULL;
446 message = SOSMessageCreateWithData(kCFAllocatorDefault, msg, &error);
447 ok(handled, "%s %@->%@ %@", name, sourceID, destID, message);
448 CFReleaseNull(error);
449 }
d8f41ccd 450 } else {
5c19dc3a
A
451 msgSentSinceLastChangeCount++;
452 SOSManifestRef sourceManifest = SOSEngineCopyLocalPeerManifest(SOSDataSourceGetSharedEngine(source->ds, NULL), peer, NULL);
d8f41ccd
A
453 pass("%s %@->%@ done L:%@", name, sourceID, destID, sourceManifest);
454 CFReleaseSafe(sourceManifest);
455 noMsgSentCount++;
d8f41ccd
A
456 }
457 CFReleaseSafe(msg);
5c19dc3a
A
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); });
d8f41ccd 466 msgSentSinceLastChangeCount = 0;
5c19dc3a
A
467 }
468 CFReleaseSafe(postHandleManifest);
469 CFRelease(preCreateManifest);
d8f41ccd 470 }
5c19dc3a 471 CFReleaseSafe(peer);
d8f41ccd
A
472 }
473 if (noMsgSentCount >= edgeCount) {
474 *stop = done = true;
5c19dc3a 475 } else if (msgSentSinceLastChangeCount >= /* 3 */9 * edgeCount + 1) {
d8f41ccd
A
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
483bool 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"));
5c19dc3a
A
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);
d8f41ccd 507 }
d8f41ccd 508 }
d8f41ccd
A
509 if (inSync)
510 pass("%s all peers in sync", name);
511 return inSync;
512}
513
514void 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);
6b200bc3 521 CFMutableDictionaryRef testDevices = SOSTestDeviceListCreate(use_db, version, deviceIDs, NULL);
d8f41ccd
A
522 CFReleaseSafe(deviceIDs);
523 SOSTestDeviceListSync(name, test_directive, test_reason, testDevices, pre, post);
524 SOSTestDeviceListInSync(name, test_directive, test_reason, testDevices);
5c19dc3a 525 SOSTestDeviceDestroyEngine(testDevices);
d8f41ccd
A
526 CFReleaseSafe(testDevices);
527}