2 * Copyright (c) 2017 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 #import "keychain/ckks/CKKSAnalyticsLogger.h"
26 #import <Security/SFSQLite.h>
27 #import <Foundation/Foundation.h>
28 #import <XCTest/XCTest.h>
30 static NSString* tablePath = nil;
32 @interface SQLiteTests : XCTestCase
35 @implementation SQLiteTests
41 tablePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"test_table.db"];
48 [[NSFileManager defaultManager] removeItemAtPath:tablePath error:nil];
53 [[NSFileManager defaultManager] removeItemAtPath:tablePath error:nil];
58 - (void)testCreateLoggingDatabase
60 NSString* schema = @"CREATE table test (test_column INTEGER);";
61 SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
62 XCTAssertTrue([sqlTable openWithError:nil]);
63 XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:tablePath]);
67 - (void)testInsertAndDelete
69 NSString* schema = @"CREATE table test (test_column INTEGER);";
70 SFSQLite* sqlTable = [[SFSQLite alloc] initWithPath:tablePath schema:schema];
71 XCTAssertTrue([sqlTable openWithError:nil]);
73 [sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(1)}];
74 [sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(2)}];
75 [sqlTable insertOrReplaceInto:@"test" values:@{@"test_column" : @(3)}];
76 XCTAssertTrue([[sqlTable selectAllFrom:@"test" where:nil bindings:nil] count] == 3);
78 [sqlTable deleteFrom:@"test" where:@"test_column = ?" bindings:@[@3]];
79 XCTAssertTrue([[sqlTable selectAllFrom:@"test" where:nil bindings:nil] count] == 2);
81 [sqlTable executeSQL:@"delete from test"];
82 XCTAssertTrue([[sqlTable selectAllFrom:@"test" where:nil bindings:nil] count] == 0);
87 @interface CKKSAnalyticsLoggerTests : CloudKitKeychainSyncingTestsBase
90 @implementation CKKSAnalyticsLoggerTests
92 - (void)testLoggingJSONGenerated
94 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
96 // We expect a single record to be uploaded.
97 [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
99 [self startCKKSSubsystem];
101 [self addGenericPassword: @"data" account: @"account-delete-me"];
103 OCMVerifyAllWithDelay(self.mockDatabase, 8);
105 NSError* error = nil;
106 NSData* json = [[CKKSAnalyticsLogger logger] getLoggingJSONWithError:&error];
107 XCTAssertNotNil(json, @"failed to generate logging json");
108 XCTAssertNil(error, @"encourntered error getting logging json: %@", error);
110 NSDictionary* dictionary = [NSJSONSerialization JSONObjectWithData:json options:0 error:&error];
111 XCTAssertNotNil(dictionary, @"failed to generate dictionary from json data");
112 XCTAssertNil(error, @"encountered error deserializing json: %@", error);
113 XCTAssertTrue([dictionary isKindOfClass:[NSDictionary class]], @"did not get the class we expected from json deserialization");
116 - (void)testSplunkDefaultTopicNameExists
118 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
119 dispatch_sync(logger.splunkLoggingQueue, ^{
120 XCTAssertNotNil(logger.splunkTopicName);
124 - (void)testSplunkDefaultBagURLExists
126 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
127 dispatch_sync(logger.splunkLoggingQueue, ^{
128 XCTAssertNotNil(logger.splunkBagURL);
132 // <rdar://problem/32983193> test_KeychainCKKS | CKKSTests failed: "Failed subtests: -[CloudKitKeychainSyncingTests testSplunkUploadURLExists]" [j71ap][CoreOSTigris15Z240][bats-e-27-204-1]
134 - (void)testSplunkUploadURLExists
136 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
137 dispatch_sync(logger.splunkLoggingQueue, ^{
138 logger.ignoreServerDisablingMessages = YES;
139 XCTAssertNotNil(logger.splunkUploadURL);
144 - (void)testLastSuccessfulSyncDate
146 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
147 [self startCKKSSubsystem];
148 CKRecord* ckr = [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
149 [self.keychainZone addToZone: ckr];
151 // Trigger a notification (with hilariously fake data)
152 [self.keychainView notifyZoneChange:nil];
154 [[[self.keychainView waitForFetchAndIncomingQueueProcessing] completionHandlerDidRunCondition] wait:4 * NSEC_PER_SEC];
156 NSDate* syncDate = [[CKKSAnalyticsLogger logger] dateOfLastSuccessForEvent:CKKSEventProcessIncomingQueueClassC inView:self.keychainView];
157 XCTAssertNotNil(syncDate, "Failed to get a last successful sync date");
158 NSDate* nowDate = [NSDate dateWithTimeIntervalSinceNow:0];
159 NSTimeInterval timeIntervalSinceSyncDate = [nowDate timeIntervalSinceDate:syncDate];
160 XCTAssertTrue(timeIntervalSinceSyncDate >= 0.0 && timeIntervalSinceSyncDate <= 15.0, "Last sync date does not look like a reasonable one");
163 - (void)testExtraValuesToUploadToServer
165 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
166 [self startCKKSSubsystem];
167 CKRecord* ckr = [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
168 [self.keychainZone addToZone: ckr];
170 // Trigger a notification (with hilariously fake data)
171 [self.keychainView notifyZoneChange:nil];
173 [[[self.keychainView waitForFetchAndIncomingQueueProcessing] completionHandlerDidRunCondition] wait:4 * NSEC_PER_SEC];
175 NSDictionary* extraValues = [[CKKSAnalyticsLogger logger] extraValuesToUploadToServer];
176 XCTAssertTrue([extraValues[@"keychain-TLKs"] boolValue]);
177 XCTAssertTrue([extraValues[@"keychain-inSyncA"] boolValue]);
178 XCTAssertTrue([extraValues[@"keychain-inSyncC"] boolValue]);
179 XCTAssertTrue([extraValues[@"keychain-IQNOE"] boolValue]);
180 XCTAssertTrue([extraValues[@"keychain-OQNOE"] boolValue]);
181 XCTAssertTrue([extraValues[@"keychain-inSync"] boolValue]);
184 - (void)testNilEventDoesNotCrashTheSystem
186 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
187 [logger logSuccessForEventNamed:nil];
190 NSError* error = nil;
191 XCTAssertNoThrow(json = [logger getLoggingJSONWithError:&error]);
192 XCTAssertNotNil(json, @"Failed to get JSON after logging nil event");
193 XCTAssertNil(error, @"Got error when grabbing JSON after logging nil event: %@", error);
196 - (void)testRaceToCreateLoggers
198 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
199 for (NSInteger i = 0; i < 5; i++) {
200 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
201 CKKSAnalyticsLogger* logger = [CKKSAnalyticsLogger logger];
202 [logger logSuccessForEvent:(CKKSAnalyticsFailableEvent*)@"test_event" inView:self.keychainView];
203 dispatch_semaphore_signal(semaphore);
207 for (NSInteger i = 0; i < 5; i++) {
208 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
212 - (void)testSysdiagnoseDump
214 [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
215 [self startCKKSSubsystem];
216 CKRecord* ckr = [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-F9C5-481E-98AC-5A507ACB2D85"];
217 [self.keychainZone addToZone: ckr];
219 // Trigger a notification (with hilariously fake data)
220 [self.keychainView notifyZoneChange:nil];
222 [self.keychainView waitForFetchAndIncomingQueueProcessing];
224 NSError* error = nil;
225 NSString* sysdiagnose = [[CKKSAnalyticsLogger logger] getSysdiagnoseDumpWithError:&error];
226 XCTAssertNil(error, @"encountered an error grabbing CKKS analytics sysdiagnose: %@", error);
227 XCTAssertTrue(sysdiagnose.length > 0, @"failed to get a sysdiagnose from CKKS analytics");