--- /dev/null
+/*
+ * Copyright (c) 2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+// Run on 2 devices:
+// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_103_syncupdate -v -- -i alice
+// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_103_syncupdate -v -- -i bob
+// Run on 2 devices:
+// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_101_accountsync -v -- -i
+// /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_101_accountsync -v -- -i
+
+#include <AssertMacros.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <xpc/xpc.h>
+
+#include <Security/SecBase.h>
+#include <Security/SecItem.h>
+#include <Security/SecItemPriv.h>
+
+#include <CKBridge/SOSCloudKeychainClient.h>
+#include <SecureObjectSync/SOSCloudCircle.h>
+
+#include <notify.h>
+#include <dispatch/dispatch.h>
+#include <getopt.h>
+
+#include <utilities/SecCFWrappers.h>
+#include <utilities/debugging.h>
+#include <utilities/SecCFRelease.h>
+
+#include "SOSCircle_regressions.h"
+#include "SOSRegressionUtilities.h"
+#include <CKBridge/SOSCloudKeychainConstants.h>
+
+#define xsecdebug(format...) secerror(format)
+
+static dispatch_group_t sDispatchGroup = NULL;
+
+__unused static const uint64_t putTestInterval = 10ull * NSEC_PER_SEC;
+static const uint64_t leeway = 1ull * NSEC_PER_SEC;
+
+__unused static const uint64_t syncInterval = 60ull * NSEC_PER_SEC; // seconds
+
+__unused static const uint64_t putDelay = 10ull * NSEC_PER_SEC; // seconds; should probably be longer than syncedefaultsd latency (6 sec)
+
+static const uint64_t exitDelay = 180ull * NSEC_PER_SEC; // seconds
+
+static dispatch_queue_t requestqueue;
+
+static dispatch_source_t exitTimerSource;
+
+static uint64_t failCounter = 0;
+static uint64_t putAttemptCounter = 0;
+static uint64_t itemsChangedCount = 0;
+
+
+// MARK: ----- Debug Routines -----
+
+static void tearDown(void)
+{
+ xsecdebug("exit");
+ CFRunLoopStop(CFRunLoopGetMain());
+}
+
+// MARK: ----- Get Cycle Tester -----
+
+static void initCyclerTests(dispatch_group_t dgroup)
+{
+ /*
+ We set up two timer sources:
+ - getRequestsource waits 5 seconds, then gets every 5 seconds
+ - exitTimerSource fires once after 300 seconds (5 minutes)
+ All are created suspended, so they don't start yet
+ */
+
+ uint64_t delay = exitDelay;
+ dispatch_time_t exitFireTime = dispatch_time(DISPATCH_TIME_NOW, delay);
+ exitTimerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, (uintptr_t)NULL, 0, requestqueue);
+ dispatch_source_set_timer(exitTimerSource, exitFireTime, 0ull, leeway);
+
+ dispatch_source_set_event_handler(exitTimerSource,
+ ^{
+ xsecdebug("Test Exit: %lld", failCounter);
+ printf("Test Exit: fail: %lld, total: %lld, changed: %lld\n", failCounter, putAttemptCounter, itemsChangedCount);
+ tearDown();
+ exit(0);
+ });
+}
+
+static void updateSyncingEnabledSwitch()
+{
+ // Set the visual state of switch based on membership in circle
+ CFErrorRef error = NULL;
+ SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error);
+ // [_syncingEnabled setOn:(BOOL)(ccstatus == kSOSCCInCircle) animated:NO];
+ pass("ccstatus: %d, error: %@", ccstatus, error);
+}
+
+static void requestToJoinCircle()
+{
+ // Set the visual state of switch based on membership in circle
+ bool bx;
+ CFErrorRef error = NULL;
+ SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error);
+ pass("ccstatus: %d, error: %@", ccstatus, error);
+ if (ccstatus == kSOSCCInCircle)
+ return;
+
+ if (ccstatus == kSOSCCNotInCircle)
+ {
+ pass("Not in circle, requesting admission");
+ bx = SOSCCRequestToJoinCircle(&error);
+ if (!bx)
+ pass("SOSCCRequestToJoinCircle error: %@", error);
+ }
+ else
+ if (ccstatus == kSOSCCRequestPending)
+ pass("Not in circle, admission pending");
+}
+
+static void handleEnableSyncing(bool turningOn)
+{
+ dispatch_queue_t workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+ if (turningOn) // i.e. we are trying to turn on syncing
+ {
+ pass("Keychain syncing is being turned ON");
+ dispatch_async(workq, ^
+ {
+ CFErrorRef error = NULL;
+ bool bx = SOSCCResetToOffering(&error);
+ if (bx)
+ pass("ResetToOffering OK");
+ else
+ pass("ResetToOffering fail: %@", error);
+
+ requestToJoinCircle();
+ updateSyncingEnabledSwitch();
+ dispatch_group_leave(sDispatchGroup);
+ });
+ }
+ else
+ {
+ pass("Keychain syncing is being turned OFF");
+ CFErrorRef error = NULL;
+ bool bx = SOSCCRemoveThisDeviceFromCircle(&error);
+ updateSyncingEnabledSwitch();
+ if (!bx)
+ pass("SOSCCRemoveThisDeviceFromCircle fail: %@", error);
+ }
+}
+
+// MARK: ----- start of all tests -----
+
+static void initialEstablish(void)
+{
+ dispatch_group_enter(sDispatchGroup);
+ dispatch_group_notify(sDispatchGroup, requestqueue, ^
+ {
+ printf("Exiting via dispatch_group_notify; all work done\n");
+ CFRunLoopStop(CFRunLoopGetMain());
+ // exit(0);
+ });
+handleEnableSyncing(true);
+
+#if 0
+ CFErrorRef error = NULL;
+ bool bx = false;
+ SOSCCStatus sccStatus = SOSCCThisDeviceIsInCircle(&error);
+ if (sccStatus == kSOSCCNotInCircle)
+ bx = SOSCCResetToOffering(&error);
+ else
+ bx = SOSCCRequestToJoinCircle(&error);
+ if (!bx)
+ xsecdebug("circle establish error: %@", error);
+ ok(bx, "Circle established");
+#endif
+}
+
+// MARK: ---------- Main ----------
+
+// define the options table for the command line
+static const struct option options[] =
+{
+ { "verbose", optional_argument, NULL, 'v' },
+ { }
+};
+
+static int kTestCount = 22;
+
+int sc_103_syncupdate(int argc, char *const *argv)
+{
+ extern char *optarg;
+ int arg, argSlot;
+
+ while (argSlot = -1, (arg = getopt_long(argc, (char * const *)argv, "i:v", options, &argSlot)) != -1)
+ switch (arg)
+ {
+ default:
+ secerror("arg: %s", optarg);
+ break;
+ }
+
+ plan_tests(kTestCount);
+
+ SKIP:
+ {
+ skip("Skipping ckdclient tests because CloudKeychainProxy.xpc is not installed", kTestCount, XPCServiceInstalled());
+ sDispatchGroup = dispatch_group_create();
+ requestqueue = dispatch_queue_create("sc_103_syncupdate", DISPATCH_QUEUE_CONCURRENT);
+ initCyclerTests(sDispatchGroup);
+ initialEstablish();
+ }
+
+ dispatch_group_wait(sDispatchGroup, DISPATCH_TIME_FOREVER);
+ pass("Tests are running...");
+ printf("Tests are running...\n");
+ CFRunLoopRun(); // Wait for it...
+ pass("Exit from run loop");
+
+ return 0;
+}
+
+
+