diff options
author | Paul Beusterien <paulbeusterien@google.com> | 2017-05-15 12:27:07 -0700 |
---|---|---|
committer | Paul Beusterien <paulbeusterien@google.com> | 2017-05-15 12:27:07 -0700 |
commit | 98ba64449a632518bd2b86fe8d927f4a960d3ddc (patch) | |
tree | 131d9c4272fa6179fcda6c5a33fcb3b1bd57ad2e /Example/Core/Tests | |
parent | 32461366c9e204a527ca05e6e9b9404a2454ac51 (diff) |
Initial
Diffstat (limited to 'Example/Core/Tests')
-rw-r--r-- | Example/Core/Tests/FIRAppAssociationRegistrationUnitTests.m | 193 | ||||
-rw-r--r-- | Example/Core/Tests/FIRAppTest.m | 582 | ||||
-rw-r--r-- | Example/Core/Tests/FIRBundleUtilTest.m | 86 | ||||
-rw-r--r-- | Example/Core/Tests/FIRConfigurationTest.m | 31 | ||||
-rw-r--r-- | Example/Core/Tests/FIRLoggerTest.m | 265 | ||||
-rw-r--r-- | Example/Core/Tests/FIROptionsTest.m | 468 | ||||
-rw-r--r-- | Example/Core/Tests/FIRTestCase.h | 45 | ||||
-rw-r--r-- | Example/Core/Tests/FIRTestCase.m | 47 | ||||
-rw-r--r-- | Example/Core/Tests/Tests-Info.plist | 22 |
9 files changed, 1739 insertions, 0 deletions
diff --git a/Example/Core/Tests/FIRAppAssociationRegistrationUnitTests.m b/Example/Core/Tests/FIRAppAssociationRegistrationUnitTests.m new file mode 100644 index 0000000..9649c99 --- /dev/null +++ b/Example/Core/Tests/FIRAppAssociationRegistrationUnitTests.m @@ -0,0 +1,193 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import <XCTest/XCTest.h> + +#import "FIRAppAssociationRegistration.h" + +/** @var kKey + @brief A unique string key. + */ +static NSString *kKey = @"key"; + +/** @var kKey1 + @brief A unique string key. + */ +static NSString *kKey1 = @"key1"; + +/** @var kKey2 + @brief A unique string key. + */ +static NSString *kKey2 = @"key2"; + +/** @var gCreateNewObject + @brief A block that returns a new object everytime it is called. + */ +static id _Nullable (^gCreateNewObject)() = ^id _Nullable() { + return [[NSObject alloc] init]; +}; + +/** @class FIRAppAssociationRegistrationTests + @brief Tests for @c FIRAppAssociationRegistration + */ +@interface FIRAppAssociationRegistrationTests : XCTestCase +@end + +@implementation FIRAppAssociationRegistrationTests + +- (void)testPassObject { + id host = gCreateNewObject(); + id obj = gCreateNewObject(); + id result = [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey + creationBlock:^id _Nullable() { + return obj; + }]; + XCTAssertEqual(obj, result); +} + +- (void)testPassNil { + id host = gCreateNewObject(); + id obj = [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey + creationBlock:^id _Nullable() { + return nil; + }]; + XCTAssertNil(obj); +} + +- (void)testObjectOwnership { + __weak id weakHost; + __block __weak id weakObj; + @autoreleasepool { + id host = gCreateNewObject(); + weakHost = host; + [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey + creationBlock:^id _Nullable() { + id obj = gCreateNewObject(); + weakObj = obj; + return obj; + }]; + // Verify that neither the host nor the object is released yet, i.e., the host owns the object + // because nothing else retains the object. + XCTAssertNotNil(weakHost); + XCTAssertNotNil(weakObj); + } + // Verify that both the host and the object are released upon exit of the autorelease pool, + // i.e., the host is the sole owner of the object. + XCTAssertNil(weakHost); + XCTAssertNil(weakObj); +} + +- (void)testSameHostSameKey { + id host = gCreateNewObject(); + id obj1 = [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey + creationBlock:gCreateNewObject]; + id obj2 = [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey + creationBlock:gCreateNewObject]; + XCTAssertEqual(obj1, obj2); +} + +- (void)testSameHostDifferentKey { + id host = gCreateNewObject(); + id obj1 = [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey1 + creationBlock:gCreateNewObject]; + id obj2 = [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey2 + creationBlock:gCreateNewObject]; + XCTAssertNotEqual(obj1, obj2); +} + +- (void)testDifferentHostSameKey { + id host1 = gCreateNewObject(); + id obj1 = [FIRAppAssociationRegistration registeredObjectWithHost:host1 + key:kKey + creationBlock:gCreateNewObject]; + id host2 = gCreateNewObject(); + id obj2 = [FIRAppAssociationRegistration registeredObjectWithHost:host2 + key:kKey + creationBlock:gCreateNewObject]; + XCTAssertNotEqual(obj1, obj2); +} + +- (void)testDifferentHostDifferentKey { + id host1 = gCreateNewObject(); + id obj1 = [FIRAppAssociationRegistration registeredObjectWithHost:host1 + key:kKey1 + creationBlock:gCreateNewObject]; + id host2 = gCreateNewObject(); + id obj2 = [FIRAppAssociationRegistration registeredObjectWithHost:host2 + key:kKey2 + creationBlock:gCreateNewObject]; + XCTAssertNotEqual(obj1, obj2); +} + +- (void)testReentrySameHostSameKey { + id host = gCreateNewObject(); + XCTAssertThrows([FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey + creationBlock:^id _Nullable() { + [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey + creationBlock:gCreateNewObject]; + return gCreateNewObject(); + }]); +} + +- (void)testReentrySameHostDifferentKey { + id host = gCreateNewObject(); + [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey1 + creationBlock:^id _Nullable() { + [FIRAppAssociationRegistration registeredObjectWithHost:host + key:kKey2 + creationBlock:gCreateNewObject]; + return gCreateNewObject(); + }]; + // Expect no exception raised. +} + +- (void)testReentryDifferentHostSameKey { + id host1 = gCreateNewObject(); + id host2 = gCreateNewObject(); + [FIRAppAssociationRegistration registeredObjectWithHost:host1 + key:kKey + creationBlock:^id _Nullable() { + [FIRAppAssociationRegistration registeredObjectWithHost:host2 + key:kKey + creationBlock:gCreateNewObject]; + return gCreateNewObject(); + }]; + // Expect no exception raised. +} + +- (void)testReentryDifferentHostDifferentKey { + id host1 = gCreateNewObject(); + id host2 = gCreateNewObject(); + [FIRAppAssociationRegistration registeredObjectWithHost:host1 + key:kKey1 + creationBlock:^id _Nullable() { + [FIRAppAssociationRegistration registeredObjectWithHost:host2 + key:kKey2 + creationBlock:gCreateNewObject]; + return gCreateNewObject(); + }]; + // Expect no exception raised. +} + +@end diff --git a/Example/Core/Tests/FIRAppTest.m b/Example/Core/Tests/FIRAppTest.m new file mode 100644 index 0000000..da97c6c --- /dev/null +++ b/Example/Core/Tests/FIRAppTest.m @@ -0,0 +1,582 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRAppInternal.h" +#import "FIROptionsInternal.h" +#import "FIRTestCase.h" + +NSString *const kFIRTestAppName1 = @"test_app_name_1"; +NSString *const kFIRTestAppName2 = @"test-app-name-2"; + +@interface FIRApp (TestInternal) + +@property(nonatomic) BOOL alreadySentConfigureNotification; +@property(nonatomic) BOOL alreadySentDeleteNotification; + ++ (void)resetApps; +- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options; +- (BOOL)configureCore; ++ (NSError *)errorForInvalidAppID; +- (BOOL)isAppIDValid; ++ (NSString *)actualBundleID; ++ (NSNumber *)mapFromServiceStringToTypeEnum:(NSString *)serviceString; ++ (NSString *)deviceModel; ++ (NSString *)installString; ++ (NSURL *)filePathURLWithName:(NSString *)fileName; ++ (NSString *)stringAtURL:(NSURL *)filePathURL; ++ (BOOL)writeString:(NSString *)string toURL:(NSURL *)filePathURL; ++ (void)logAppInfo:(NSNotification *)notification; ++ (BOOL)validateAppID:(NSString *)appID; ++ (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version; ++ (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version; + +@end + + +@interface FIRAppTest : FIRTestCase + +@property(nonatomic) id appClassMock; +@property(nonatomic) id optionsInstanceMock; +@property(nonatomic) id notificationCenterMock; +@property(nonatomic) FIRApp *app; + +@end + +@implementation FIRAppTest + +- (void)setUp { + [super setUp]; + [FIROptions resetDefaultOptions]; + [FIRApp resetApps]; + _appClassMock = OCMClassMock([FIRApp class]); + _optionsInstanceMock = OCMPartialMock([FIROptions defaultOptions]); + _notificationCenterMock = OCMPartialMock([NSNotificationCenter defaultCenter]); +} + +- (void)tearDown { + [_appClassMock stopMocking]; + [_optionsInstanceMock stopMocking]; + [_notificationCenterMock stopMocking]; + + [super tearDown]; +} + +- (void)testConfigure { + NSDictionary *expectedUserInfo = [self expectedUserInfoWithAppName:kFIRDefaultAppName + isDefaultApp:YES]; + OCMExpect([self.notificationCenterMock postNotificationName:kFIRAppReadyToConfigureSDKNotification + object:[FIRApp class] + userInfo:expectedUserInfo]); + XCTAssertNoThrow([FIRApp configure]); + OCMVerifyAll(self.notificationCenterMock); + + self.app = [FIRApp defaultApp]; + XCTAssertNotNil(self.app); + XCTAssertEqualObjects(self.app.name, kFIRDefaultAppName); + XCTAssertEqualObjects(self.app.options.clientID, kClientID); + XCTAssertTrue([FIRApp allApps].count == 1); + XCTAssertTrue(self.app.alreadySentConfigureNotification); + + // Test if options is nil + id optionsClassMock = OCMClassMock([FIROptions class]); + OCMStub([optionsClassMock defaultOptions]).andReturn(nil); + XCTAssertThrows([FIRApp configure]); +} + +- (void)testConfigureWithOptions { + // nil options + XCTAssertThrows([FIRApp configureWithOptions:nil]); + XCTAssertTrue([FIRApp allApps].count == 0); + + NSDictionary *expectedUserInfo = [self expectedUserInfoWithAppName:kFIRDefaultAppName + isDefaultApp:YES]; + OCMExpect([self.notificationCenterMock postNotificationName:kFIRAppReadyToConfigureSDKNotification + object:[FIRApp class] + userInfo:expectedUserInfo]); + // default options + XCTAssertNoThrow([FIRApp configureWithOptions:[FIROptions defaultOptions]]); + OCMVerifyAll(self.notificationCenterMock); + + self.app = [FIRApp defaultApp]; + XCTAssertNotNil(self.app); + XCTAssertEqualObjects(self.app.name, kFIRDefaultAppName); + XCTAssertEqualObjects(self.app.options.clientID, kClientID); + XCTAssertTrue([FIRApp allApps].count == 1); +} + +- (void)testConfigureWithCustomizedOptions { + // valid customized options + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID + bundleID:kBundleID + GCMSenderID:kGCMSenderID + APIKey:kCustomizedAPIKey + clientID:nil + trackingID:nil + androidClientID:nil + databaseURL:nil + storageBucket:nil + deepLinkURLScheme:nil]; + + NSDictionary *expectedUserInfo = [self expectedUserInfoWithAppName:kFIRDefaultAppName + isDefaultApp:YES]; + OCMExpect([self.notificationCenterMock postNotificationName:kFIRAppReadyToConfigureSDKNotification + object:[FIRApp class] + userInfo:expectedUserInfo]); + + XCTAssertNoThrow([FIRApp configureWithOptions:options]); + OCMVerifyAll(self.notificationCenterMock); + + self.app = [FIRApp defaultApp]; + XCTAssertNotNil(self.app); + XCTAssertEqualObjects(self.app.name, kFIRDefaultAppName); + XCTAssertEqualObjects(self.app.options.googleAppID, kGoogleAppID); + XCTAssertEqualObjects(self.app.options.APIKey, kCustomizedAPIKey); + XCTAssertTrue([FIRApp allApps].count == 1); +} + +- (void)testConfigureWithNameAndOptions { + XCTAssertThrows([FIRApp configureWithName:nil options:[FIROptions defaultOptions]]); + XCTAssertThrows([FIRApp configureWithName:kFIRTestAppName1 options:nil]); + XCTAssertThrows([FIRApp configureWithName:@"" options:[FIROptions defaultOptions]]); + XCTAssertThrows([FIRApp configureWithName:kFIRDefaultAppName + options:[FIROptions defaultOptions]]); + XCTAssertTrue([FIRApp allApps].count == 0); + + NSDictionary *expectedUserInfo = [self expectedUserInfoWithAppName:kFIRTestAppName1 + isDefaultApp:NO]; + OCMExpect([self.notificationCenterMock postNotificationName:kFIRAppReadyToConfigureSDKNotification + object:[FIRApp class] + userInfo:expectedUserInfo]); + XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName1 options:[FIROptions defaultOptions]]); + OCMVerifyAll(self.notificationCenterMock); + + XCTAssertTrue([FIRApp allApps].count == 1); + self.app = [FIRApp appNamed:kFIRTestAppName1]; + XCTAssertNotNil(self.app); + XCTAssertEqualObjects(self.app.name, kFIRTestAppName1); + XCTAssertEqualObjects(self.app.options.clientID, kClientID); + + // Configure the same app again should throw an exception. + XCTAssertThrows([FIRApp configureWithName:kFIRTestAppName1 options:[FIROptions defaultOptions]]); +} + +- (void)testConfigureWithNameAndCustomizedOptions { + FIROptions *options = [FIROptions defaultOptions]; + FIROptions *newOptions = [options copy]; + newOptions.deepLinkURLScheme = kDeepLinkURLScheme; + + NSDictionary *expectedUserInfo1 = [self expectedUserInfoWithAppName:kFIRTestAppName1 + isDefaultApp:NO]; + OCMExpect([self.notificationCenterMock postNotificationName:kFIRAppReadyToConfigureSDKNotification + object:[FIRApp class] + userInfo:expectedUserInfo1]); + XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName1 options:newOptions]); + XCTAssertTrue([FIRApp allApps].count == 1); + self.app = [FIRApp appNamed:kFIRTestAppName1]; + + // Configure a different app with valid customized options + FIROptions *customizedOptions = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID + bundleID:kBundleID + GCMSenderID:kGCMSenderID + APIKey:kCustomizedAPIKey + clientID:nil + trackingID:nil + androidClientID:nil + databaseURL:nil + storageBucket:nil + deepLinkURLScheme:nil]; + + NSDictionary *expectedUserInfo2 = [self expectedUserInfoWithAppName:kFIRTestAppName2 + isDefaultApp:NO]; + OCMExpect([self.notificationCenterMock postNotificationName:kFIRAppReadyToConfigureSDKNotification + object:[FIRApp class] + userInfo:expectedUserInfo2]); + XCTAssertNoThrow([FIRApp configureWithName:kFIRTestAppName2 options:customizedOptions]); + OCMVerifyAll(self.notificationCenterMock); + + XCTAssertTrue([FIRApp allApps].count == 2); + self.app = [FIRApp appNamed:kFIRTestAppName2]; + XCTAssertNotNil(self.app); + XCTAssertEqualObjects(self.app.name, kFIRTestAppName2); + XCTAssertEqualObjects(self.app.options.googleAppID, kGoogleAppID); + XCTAssertEqualObjects(self.app.options.APIKey, kCustomizedAPIKey); +} + +- (void)testValidName { + XCTAssertNoThrow([FIRApp configureWithName:@"aA1_" options:[FIROptions defaultOptions]]); + XCTAssertThrows([FIRApp configureWithName:@"aA1%" options:[FIROptions defaultOptions]]); + XCTAssertThrows([FIRApp configureWithName:@"aA1?" options:[FIROptions defaultOptions]]); + XCTAssertThrows([FIRApp configureWithName:@"aA1!" options:[FIROptions defaultOptions]]); +} + +- (void)testDefaultApp { + self.app = [FIRApp defaultApp]; + XCTAssertNil(self.app); + + [FIRApp configure]; + self.app = [FIRApp defaultApp]; + XCTAssertEqualObjects(self.app.name, kFIRDefaultAppName); + XCTAssertEqualObjects(self.app.options.clientID, kClientID); +} + +- (void)testAppNamed { + self.app = [FIRApp appNamed:kFIRTestAppName1]; + XCTAssertNil(self.app); + + [FIRApp configureWithName:kFIRTestAppName1 options:[FIROptions defaultOptions]]; + self.app = [FIRApp appNamed:kFIRTestAppName1]; + XCTAssertEqualObjects(self.app.name, kFIRTestAppName1); + XCTAssertEqualObjects(self.app.options.clientID, kClientID); +} + +- (void)testDeleteApp { + [FIRApp configure]; + self.app = [FIRApp defaultApp]; + XCTAssertTrue([FIRApp allApps].count == 1); + [self.app deleteApp:^(BOOL success) { + XCTAssertTrue(success); + }]; + OCMVerify([self.notificationCenterMock postNotificationName:kFIRAppDeleteNotification + object:[FIRApp class] + userInfo:[OCMArg any]]); + XCTAssertTrue(self.app.alreadySentDeleteNotification); + XCTAssertTrue([FIRApp allApps].count == 0); +} + +- (void)testErrorForSubspecConfigurationFailure { + NSError *error = [FIRApp errorForSubspecConfigurationFailureWithDomain:kFirebaseAdMobErrorDomain + errorCode:FIRErrorCodeAdMobFailed + service:kFIRServiceAdMob + reason:@"some reason"]; + XCTAssertNotNil(error); + XCTAssert([error.domain isEqualToString:kFirebaseAdMobErrorDomain]); + XCTAssert(error.code == FIRErrorCodeAdMobFailed); + XCTAssert([error.description containsString:@"Configuration failed for"]); +} + +- (void)testGetTokenWithCallback { + [FIRApp configure]; + FIRApp *app = [FIRApp defaultApp]; + + __block BOOL getTokenImplementationWasCalled = NO; + __block BOOL getTokenCallbackWasCalled = NO; + __block BOOL passedRefreshValue = NO; + + [app getTokenForcingRefresh:YES + withCallback:^(NSString *_Nullable token, NSError *_Nullable error) { + getTokenCallbackWasCalled = YES; + }]; + + XCTAssert(getTokenCallbackWasCalled, + @"The callback should be invoked by the base implementation when no block for " + "'getTokenImplementation' has been specified."); + + getTokenCallbackWasCalled = NO; + + app.getTokenImplementation = ^(BOOL refresh, FIRTokenCallback callback) { + getTokenImplementationWasCalled = YES; + passedRefreshValue = refresh; + callback(nil, nil); + }; + [app getTokenForcingRefresh:YES + withCallback:^(NSString *_Nullable token, NSError *_Nullable error) { + getTokenCallbackWasCalled = YES; + }]; + + XCTAssert(getTokenImplementationWasCalled, + @"The 'getTokenImplementation' block was never called."); + XCTAssert(passedRefreshValue, + @"The value for the 'refresh' parameter wasn't passed to the 'getTokenImplementation' " + "block correctly."); + XCTAssert(getTokenCallbackWasCalled, + @"The 'getTokenImplementation' should have invoked the callback. This could be an " + "error in this test, or the callback parameter may not have been passed to the " + "implementation correctly."); + + getTokenImplementationWasCalled = NO; + getTokenCallbackWasCalled = NO; + passedRefreshValue = NO; + + [app getTokenForcingRefresh:NO + withCallback:^(NSString *_Nullable token, NSError *_Nullable error) { + getTokenCallbackWasCalled = YES; + }]; + + XCTAssertFalse(passedRefreshValue, + @"The value for the 'refresh' parameter wasn't passed to the " + "'getTokenImplementation' block correctly."); +} + +- (void)testModifyingOptionsThrows { + [FIRApp configure]; + FIROptions *options = [[FIRApp defaultApp] options]; + XCTAssertTrue(options.isEditingLocked); + + // Modification to every property should result in an exception. + XCTAssertThrows(options.androidClientID = @"should_throw"); + XCTAssertThrows(options.APIKey = @"should_throw"); + XCTAssertThrows(options.bundleID = @"should_throw"); + XCTAssertThrows(options.clientID = @"should_throw"); + XCTAssertThrows(options.databaseURL = @"should_throw"); + XCTAssertThrows(options.deepLinkURLScheme = @"should_throw"); + XCTAssertThrows(options.GCMSenderID = @"should_throw"); + XCTAssertThrows(options.googleAppID = @"should_throw"); + XCTAssertThrows(options.projectID = @"should_throw"); + XCTAssertThrows(options.storageBucket = @"should_throw"); + XCTAssertThrows(options.trackingID = @"should_throw"); +} + +- (void)testOptionsLocking { + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID + GCMSenderID:kGCMSenderID]; + options.projectID = kProjectID; + options.databaseURL = kDatabaseURL; + + // Options should not be locked before they are used to configure a `FIRApp`. + XCTAssertFalse(options.isEditingLocked); + + // The options returned should be locked after configuring `FIRApp`. + [FIRApp configureWithOptions:options]; + FIROptions *optionsCopy = [[FIRApp defaultApp] options]; + XCTAssertTrue(optionsCopy.isEditingLocked); +} + +#pragma mark - App ID v1 + +- (void)testAppIDV1 { + // Missing separator between platform:fingerprint. + XCTAssertFalse([FIRApp validateAppID:@"1:1337:iosdeadbeef"]); + + // Wrong platform "android". + XCTAssertFalse([FIRApp validateAppID:@"1:1337:android:deadbeef"]); + + // The fingerprint, aka 4th field, should only contain hex characters. + XCTAssertFalse([FIRApp validateAppID:@"1:1337:ios:123abcxyz"]); + + // The fingerprint, aka 4th field, is not tested in V1, so a bad value shouldn't cause a failure. + XCTAssertTrue([FIRApp validateAppID:@"1:1337:ios:deadbeef"]); +} + +#pragma mark - App ID v2 + +- (void)testAppIDV2 { + // Missing separator between platform:fingerprint. + XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios5e18052ab54fbfec"]); + + // Unknown versions may contain anything. + XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:123abcxyz"]); + XCTAssertTrue([FIRApp validateAppID:@"2:thisdoesn'teven_m:a:t:t:e:r_"]); + + // Known good fingerprint. + XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:5e18052ab54fbfec"]); + + // Unknown fingerprint, not tested so shouldn't cause a failure. + XCTAssertTrue([FIRApp validateAppID:@"2:1337:ios:deadbeef"]); +} + +#pragma mark - App ID other + +- (void)testAppIDV3 { + // Currently there is no specification for v3, so we would not expect it to fail. + XCTAssertTrue([FIRApp validateAppID:@"3:1337:ios:deadbeef"]); +} + +- (void)testAppIDEmpty { + XCTAssertFalse([FIRApp validateAppID:@""]); +} + +- (void)testAppIDValidationTrue { + // Ensure that isAppIDValid matches validateAppID. + [FIRApp configure]; + OCMStub([self.appClassMock validateAppID:[OCMArg any]]).andReturn(YES); + XCTAssertTrue([[FIRApp defaultApp] isAppIDValid]); +} + +- (void)testAppIDValidationFalse { + // Ensure that isAppIDValid matches validateAppID. + [FIRApp configure]; + OCMStub([self.appClassMock validateAppID:[OCMArg any]]).andReturn(NO); + XCTAssertFalse([[FIRApp defaultApp] isAppIDValid]); +} + +- (void)testAppIDPrefix { + // Unknown numeric-character prefixes should pass. + XCTAssertTrue([FIRApp validateAppID:@"0:"]); + XCTAssertTrue([FIRApp validateAppID:@"01:"]); + XCTAssertTrue([FIRApp validateAppID:@"10:"]); + XCTAssertTrue([FIRApp validateAppID:@"010:"]); + XCTAssertTrue([FIRApp validateAppID:@"3:"]); + XCTAssertTrue([FIRApp validateAppID:@"123:"]); + XCTAssertTrue([FIRApp validateAppID:@"999999999:"]); + + // Non-numeric prefixes should not pass. + XCTAssertFalse([FIRApp validateAppID:@"a:"]); + XCTAssertFalse([FIRApp validateAppID:@"abcsdf0:"]); + XCTAssertFalse([FIRApp validateAppID:@"0aaaa:"]); + XCTAssertFalse([FIRApp validateAppID:@"0aaaa0450:"]); + XCTAssertFalse([FIRApp validateAppID:@"-1:"]); + XCTAssertFalse([FIRApp validateAppID:@"abcsdf:"]); + XCTAssertFalse([FIRApp validateAppID:@"ABDCF:"]); + XCTAssertFalse([FIRApp validateAppID:@" :"]); + XCTAssertFalse([FIRApp validateAppID:@"1 :"]); + XCTAssertFalse([FIRApp validateAppID:@" 1:"]); + XCTAssertFalse([FIRApp validateAppID:@" 123 :"]); + XCTAssertFalse([FIRApp validateAppID:@"1 23:"]); + XCTAssertFalse([FIRApp validateAppID:@"&($*&%(*$&:"]); + XCTAssertFalse([FIRApp validateAppID:@"abCDSF$%%df:"]); + + // Known version prefixes should never pass without the rest of the app ID string present. + XCTAssertFalse([FIRApp validateAppID:@"1:"]); + + // Version must include ":". + XCTAssertFalse([FIRApp validateAppID:@"0"]); + XCTAssertFalse([FIRApp validateAppID:@"01"]); + XCTAssertFalse([FIRApp validateAppID:@"10"]); + XCTAssertFalse([FIRApp validateAppID:@"010"]); + XCTAssertFalse([FIRApp validateAppID:@"3"]); + XCTAssertFalse([FIRApp validateAppID:@"123"]); + XCTAssertFalse([FIRApp validateAppID:@"999999999"]); + XCTAssertFalse([FIRApp validateAppID:@"com.google.bundleID"]); +} + +- (void)testAppIDFormatInvalid { + OCMStub([self.appClassMock actualBundleID]).andReturn(@"com.google.bundleID"); + // Some direct tests of the validateAppIDFormat:withVersion: method. + // Sanity checks first. + NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef"; + NSString *const kGoodVersionV1 = @"1:"; + XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodVersionV1]); + + NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec"; + NSString *const kGoodVersionV2 = @"2:"; + XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]); + + // Version mismatch. + XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodVersionV2]); + XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:@"999:"]); + + // Nil or empty strings. + XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:nil]); + XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:@""]); + XCTAssertFalse([FIRApp validateAppIDFormat:nil withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:nil withVersion:nil]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"" withVersion:@""]); + + // App ID contains only the version prefix. + XCTAssertFalse([FIRApp validateAppIDFormat:kGoodVersionV1 withVersion:kGoodVersionV1]); + // The version is the entire app ID. + XCTAssertFalse([FIRApp validateAppIDFormat:kGoodAppIDV1 withVersion:kGoodAppIDV1]); + + // Versions digits that may make a partial match. + XCTAssertFalse([FIRApp validateAppIDFormat:@"01:1337:ios:deadbeef" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"10:1337:ios:deadbeef" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"11:1337:ios:deadbeef" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"21:1337:ios:5e18052ab54fbfec" + withVersion:kGoodVersionV2]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"22:1337:ios:5e18052ab54fbfec" + withVersion:kGoodVersionV2]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"02:1337:ios:5e18052ab54fbfec" + withVersion:kGoodVersionV2]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"20:1337:ios:5e18052ab54fbfec" + withVersion:kGoodVersionV2]); + + // Extra fields. + XCTAssertFalse([FIRApp validateAppIDFormat:@"ab:1:1337:ios:deadbeef" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"1:ab:1337:ios:deadbeef" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ab:ios:deadbeef" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ios:ab:deadbeef" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFormat:@"1:1337:ios:deadbeef:ab" withVersion:kGoodVersionV1]); +} + +- (void)testAppIDFingerprintInvalid { + OCMStub([self.appClassMock actualBundleID]).andReturn(@"com.google.bundleID"); + // Some direct tests of the validateAppIDFingerprint:withVersion: method. + // Sanity checks first. + NSString *const kGoodAppIDV1 = @"1:1337:ios:deadbeef"; + NSString *const kGoodVersionV1 = @"1:"; + XCTAssertTrue([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV1]); + + NSString *const kGoodAppIDV2 = @"2:1337:ios:5e18052ab54fbfec"; + NSString *const kGoodVersionV2 = @"2:"; + XCTAssertTrue([FIRApp validateAppIDFormat:kGoodAppIDV2 withVersion:kGoodVersionV2]); + + // Version mismatch. + XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV2 withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodVersionV2]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@"999:"]); + + // Nil or empty strings. + XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:nil]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:@""]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:nil withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"" withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:nil withVersion:nil]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"" withVersion:@""]); + + // App ID contains only the version prefix. + XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodVersionV1 withVersion:kGoodVersionV1]); + // The version is the entire app ID. + XCTAssertFalse([FIRApp validateAppIDFingerprint:kGoodAppIDV1 withVersion:kGoodAppIDV1]); + + // Versions digits that may make a partial match. + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"01:1337:ios:deadbeef" + withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"10:1337:ios:deadbeef" + withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"11:1337:ios:deadbeef" + withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"21:1337:ios:5e18052ab54fbfec" + withVersion:kGoodVersionV2]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"22:1337:ios:5e18052ab54fbfec" + withVersion:kGoodVersionV2]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"02:1337:ios:5e18052ab54fbfec" + withVersion:kGoodVersionV2]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"20:1337:ios:5e18052ab54fbfec" + withVersion:kGoodVersionV2]); + // Extra fields. + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"ab:1:1337:ios:deadbeef" + withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:ab:1337:ios:deadbeef" + withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ab:ios:deadbeef" + withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ios:ab:deadbeef" + withVersion:kGoodVersionV1]); + XCTAssertFalse([FIRApp validateAppIDFingerprint:@"1:1337:ios:deadbeef:ab" + withVersion:kGoodVersionV1]); +} + +#pragma mark - Internal Methods + +- (void)testAuthGetUID { + [FIRApp configure]; + + [FIRApp defaultApp].getUIDImplementation = ^NSString *{ return @"highlander"; }; + XCTAssertEqual([[FIRApp defaultApp] getUID], @"highlander"); +} + +#pragma mark - private + +- (NSDictionary <NSString *, NSObject *> *)expectedUserInfoWithAppName:(NSString *)name + isDefaultApp:(BOOL)isDefaultApp { + return @{ + kFIRAppNameKey : name, + kFIRAppIsDefaultAppKey : [NSNumber numberWithBool:isDefaultApp], + kFIRGoogleAppIDKey : kGoogleAppID + }; +} + +@end diff --git a/Example/Core/Tests/FIRBundleUtilTest.m b/Example/Core/Tests/FIRBundleUtilTest.m new file mode 100644 index 0000000..6a3e20a --- /dev/null +++ b/Example/Core/Tests/FIRBundleUtilTest.m @@ -0,0 +1,86 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRBundleUtil.h" + +#import "FIRTestCase.h" + +static NSString *const kResultPath = @"resultPath"; +static NSString *const kResourceName = @"resourceName"; +static NSString *const kFileType = @"fileType"; + +@interface FIRBundleUtilTest : FIRTestCase + +@property(nonatomic, strong) id mockBundle; + +@end + +@implementation FIRBundleUtilTest + +- (void)setUp { + [super setUp]; + self.mockBundle = OCMClassMock([NSBundle class]); +} + +- (void)testRelevantBundles_mainIsFirst { + // Pointer compare to same instance of main bundle. + XCTAssertEqual([NSBundle mainBundle], [FIRBundleUtil relevantBundles][0]); +} + +// TODO: test that adding a bundle appears in "all bundles" +// once the use-case is understood. + +- (void)testFindOptionsDictionaryPath { + [OCMStub([self.mockBundle pathForResource:kResourceName ofType:kFileType]) andReturn:kResultPath]; + XCTAssertEqualObjects( + [FIRBundleUtil optionsDictionaryPathWithResourceName:kResourceName + andFileType:kFileType + inBundles:@[ self.mockBundle ]], + kResultPath); +} + +- (void)testFindOptionsDictionaryPath_notFound { + XCTAssertNil([FIRBundleUtil optionsDictionaryPathWithResourceName:kResourceName + andFileType:kFileType + inBundles:@[ self.mockBundle ]]); +} + +- (void)testFindOptionsDictionaryPath_secondBundle { + NSBundle *mockBundleEmpty = OCMClassMock([NSBundle class]); + [OCMStub([self.mockBundle pathForResource:kResourceName ofType:kFileType]) andReturn:kResultPath]; + + NSArray *bundles = @[ mockBundleEmpty, self.mockBundle ]; + XCTAssertEqualObjects( + [FIRBundleUtil optionsDictionaryPathWithResourceName:kResourceName + andFileType:kFileType + inBundles:bundles], + kResultPath); +} + +- (void)testBundleIdentifierExistsInBundles { + NSString *bundleID = @"com.google.test"; + [OCMStub([self.mockBundle bundleIdentifier]) andReturn:bundleID]; + XCTAssertTrue([FIRBundleUtil hasBundleIdentifier:bundleID inBundles:@[ self.mockBundle ]]); +} + +- (void)testBundleIdentifierExistsInBundles_notExist { + [OCMStub([self.mockBundle bundleIdentifier]) andReturn:@"com.google.test"]; + XCTAssertFalse([FIRBundleUtil hasBundleIdentifier:@"not-exist" inBundles:@[ self.mockBundle ]]); +} + +- (void)testBundleIdentifierExistsInBundles_emptyBundlesArray { + XCTAssertFalse([FIRBundleUtil hasBundleIdentifier:@"com.google.test" inBundles:@[ ]]); +} + +@end diff --git a/Example/Core/Tests/FIRConfigurationTest.m b/Example/Core/Tests/FIRConfigurationTest.m new file mode 100644 index 0000000..2b3ff46 --- /dev/null +++ b/Example/Core/Tests/FIRConfigurationTest.m @@ -0,0 +1,31 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRConfiguration.h" + +#import "FIRTestCase.h" + +@interface FIRConfigurationTest : FIRTestCase + +@end + +@implementation FIRConfigurationTest + +- (void)testSharedInstance { + FIRConfiguration *config = [FIRConfiguration sharedInstance]; + XCTAssertNotNil(config); + XCTAssertNotNil(config.analyticsConfiguration); +} + +@end diff --git a/Example/Core/Tests/FIRLoggerTest.m b/Example/Core/Tests/FIRLoggerTest.m new file mode 100644 index 0000000..e7031a7 --- /dev/null +++ b/Example/Core/Tests/FIRLoggerTest.m @@ -0,0 +1,265 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRLogger.h" +#import "FIRTestCase.h" + +#import <asl.h> + +// The following constants are exposed from FIRLogger for unit tests. +extern NSString *const kFIRDisableDebugModeApplicationArgument; +extern NSString *const kFIREnableDebugModeApplicationArgument; + +extern NSString *const kFIRPersistedDebugModeKey; + +extern const char *kFIRLoggerASLClientFacilityName; + +extern const char *kFIRLoggerCustomASLMessageFormat; + +extern void FIRResetLogger(); + +extern aslclient getFIRLoggerClient(); + +extern dispatch_queue_t getFIRClientQueue(); + +extern BOOL getFIRLoggerDebugMode(); + +// Define the message format again to make sure the format doesn't accidentally change. +static NSString *const kCorrectASLMessageFormat = + @"$((Time)(J.3)) $(Sender)[$(PID)] <$((Level)(str))> $Message"; + +static NSString *const kMessageCode = @"I-COR000001"; + +@interface FIRLoggerTest : FIRTestCase + +@property(nonatomic) NSString *randomLogString; + +@end + +@implementation FIRLoggerTest + +- (void)setUp { + [super setUp]; + FIRResetLogger(); +} + +// Test some stable variables to make sure they weren't accidently changed. +- (void)testStableVariables { + // kFIRLoggerCustomASLMessageFormat. + XCTAssertEqualObjects(kCorrectASLMessageFormat, + [NSString stringWithUTF8String:kFIRLoggerCustomASLMessageFormat]); + + // Strings of type FIRLoggerServices. + XCTAssertEqualObjects(kFIRLoggerABTesting, @"[Firebase/ABTesting]"); + XCTAssertEqualObjects(kFIRLoggerAdMob, @"[Firebase/AdMob]"); + XCTAssertEqualObjects(kFIRLoggerAnalytics, @"[Firebase/Analytics]"); + XCTAssertEqualObjects(kFIRLoggerAuth, @"[Firebase/Auth]"); + XCTAssertEqualObjects(kFIRLoggerCore, @"[Firebase/Core]"); + XCTAssertEqualObjects(kFIRLoggerCrash, @"[Firebase/Crash]"); + XCTAssertEqualObjects(kFIRLoggerDatabase, @"[Firebase/Database]"); + XCTAssertEqualObjects(kFIRLoggerDynamicLinks, @"[Firebase/DynamicLinks]"); + XCTAssertEqualObjects(kFIRLoggerInstanceID, @"[Firebase/InstanceID]"); + XCTAssertEqualObjects(kFIRLoggerInvites, @"[Firebase/Invites]"); + XCTAssertEqualObjects(kFIRLoggerMessaging, @"[Firebase/Messaging]"); + XCTAssertEqualObjects(kFIRLoggerRemoteConfig, @"[Firebase/RemoteConfig]"); + XCTAssertEqualObjects(kFIRLoggerStorage, @"[Firebase/Storage]"); +} + +- (void)testInitializeASLForNonDebugMode { + // Stub. + id processInfoMock = [OCMockObject partialMockForObject:[NSProcessInfo processInfo]]; + NSArray *arguments = @[ kFIRDisableDebugModeApplicationArgument ]; + [[[processInfoMock stub] andReturn:arguments] arguments]; + + // Test. + FIRLogError(kFIRLoggerCore, kMessageCode, @"Some error."); + + // Assert. + NSNumber *debugMode = + [[NSUserDefaults standardUserDefaults] objectForKey:kFIRPersistedDebugModeKey]; + XCTAssertNil(debugMode); + XCTAssertFalse(getFIRLoggerDebugMode()); + + // Stop. + [processInfoMock stopMocking]; +} + +- (void)testInitializeASLForDebugModeWithArgument { + // Stub. + id processInfoMock = [OCMockObject partialMockForObject:[NSProcessInfo processInfo]]; + NSArray *arguments = @[ kFIREnableDebugModeApplicationArgument ]; + [[[processInfoMock stub] andReturn:arguments] arguments]; + + // Test. + FIRLogError(kFIRLoggerCore, kMessageCode, @"Some error."); + + // Assert. + NSNumber *debugMode = + [[NSUserDefaults standardUserDefaults] objectForKey:kFIRPersistedDebugModeKey]; + XCTAssertTrue(debugMode.boolValue); + XCTAssertTrue(getFIRLoggerDebugMode()); + + // Stop. + [processInfoMock stopMocking]; +} + +- (void)testInitializeASLForDebugModeWithUserDefaults { + // Stub. + id userDefaultsMock = [OCMockObject partialMockForObject:[NSUserDefaults standardUserDefaults]]; + NSNumber *debugMode = @YES; + [[[userDefaultsMock stub] andReturnValue:debugMode] boolForKey:kFIRPersistedDebugModeKey]; + + // Test. + FIRLogError(kFIRLoggerCore, kMessageCode, @"Some error."); + + // Assert. + debugMode = [[NSUserDefaults standardUserDefaults] objectForKey:kFIRPersistedDebugModeKey]; + XCTAssertTrue(debugMode.boolValue); + XCTAssertTrue(getFIRLoggerDebugMode()); + + // Stop. + [userDefaultsMock stopMocking]; +} + +- (void)testMessageCodeFormat { + // Valid case. + XCTAssertNoThrow(FIRLogError(kFIRLoggerCore, @"I-APP000001", @"Message.")); + + // An extra dash or missing dash should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"I-APP-000001", @"Message.")); + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"IAPP000001", @"Message.")); + + // Wrong number of digits should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"I-APP00001", @"Message.")); + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"I-APP0000001", @"Message.")); + + // Lowercase should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"I-app000001", @"Message.")); + + // nil or empty message code should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, nil, @"Message.")); + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"", @"Message.")); + + // Android message code should fail. + XCTAssertThrows(FIRLogError(kFIRLoggerCore, @"A-APP000001", @"Message.")); +} + + +- (void)testLoggerInterface { + XCTAssertNoThrow(FIRLogError(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogError(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(FIRLogWarning(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogWarning(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(FIRLogNotice(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogNotice(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(FIRLogInfo(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogInfo(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); + + XCTAssertNoThrow(FIRLogDebug(kFIRLoggerCore, kMessageCode, @"Message.")); + XCTAssertNoThrow(FIRLogDebug(kFIRLoggerCore, kMessageCode, @"Configure %@.", @"blah")); +} + + +// asl_set_filter does not perform as expected in unit test environment with simulator. The +// following test only checks whether the logs have been sent to system with the default settings in +// the unit test environment. +- (void)testSystemLogWithDefaultStatus { +#if !(TARGET_OS_SIMULATOR) + // Test fails on device - b/38130372 + return; +#else + // Sets the time interval that we need to wait in order to fetch all the logs. + NSTimeInterval timeInterval = 0.1f; + // Generates a random string each time and check whether it has been logged. + // Log messages with Notice level and below should be logged to system/device by default. + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogError(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogWarning(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogNotice(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertTrue([self logExists]); + + // Log messages with Info level and above should NOT be logged to system/device by default. + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogInfo(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertFalse([self logExists]); + + self.randomLogString = [NSUUID UUID].UUIDString; + FIRLogDebug(kFIRLoggerCore, kMessageCode, @"%@", self.randomLogString); + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeInterval]]; + XCTAssertFalse([self logExists]); +#endif +} + +// The FIRLoggerLevel enum must match the ASL_LEVEL_* constants, but we manually redefine +// them in FIRLoggerLevel.h since we cannot include <asl.h> (see b/34976089 for more details). +// This test ensures the constants match. +- (void)testFIRLoggerLevelValues { + XCTAssertEqual(FIRLoggerLevelError, ASL_LEVEL_ERR); + XCTAssertEqual(FIRLoggerLevelWarning, ASL_LEVEL_WARNING); + XCTAssertEqual(FIRLoggerLevelNotice, ASL_LEVEL_NOTICE); + XCTAssertEqual(FIRLoggerLevelInfo, ASL_LEVEL_INFO); + XCTAssertEqual(FIRLoggerLevelDebug, ASL_LEVEL_DEBUG); +} + + +// Helper functions. +- (BOOL)logExists { + [self drainFIRClientQueue]; + NSString *correctMsg = [NSString stringWithFormat:@"%@[%@] %@", kFIRLoggerCore, kMessageCode, + self.randomLogString]; + return [self messageWasLogged:correctMsg]; +} + + +- (void)drainFIRClientQueue { + dispatch_semaphore_t workerSemaphore = dispatch_semaphore_create(0); + dispatch_async(getFIRClientQueue(), ^{ + dispatch_semaphore_signal(workerSemaphore); + }); + dispatch_semaphore_wait(workerSemaphore, DISPATCH_TIME_FOREVER); +} + +- (BOOL)messageWasLogged:(NSString *)message { + aslmsg query = asl_new(ASL_TYPE_QUERY); + asl_set_query(query, ASL_KEY_FACILITY, kFIRLoggerASLClientFacilityName, ASL_QUERY_OP_EQUAL); + aslresponse r = asl_search(getFIRLoggerClient(), query); + asl_free(query); + aslmsg m; + const char *val; + NSMutableArray *allMsg = [[NSMutableArray alloc] init]; + while ((m = asl_next(r)) != NULL) { + val = asl_get(m, ASL_KEY_MSG); + if (val) { + [allMsg addObject:[NSString stringWithUTF8String:val]]; + } + } + asl_free(m); + asl_release(r); + return [allMsg containsObject:message]; +} + +@end diff --git a/Example/Core/Tests/FIROptionsTest.m b/Example/Core/Tests/FIROptionsTest.m new file mode 100644 index 0000000..62b6294 --- /dev/null +++ b/Example/Core/Tests/FIROptionsTest.m @@ -0,0 +1,468 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRAppInternal.h" +#import "FIRBundleUtil.h" +#import "FIROptionsInternal.h" + +#import "FIRTestCase.h" + +extern NSString *const kFIRIsMeasurementEnabled; +extern NSString *const kFIRIsAnalyticsCollectionEnabled; +extern NSString *const kFIRIsAnalyticsCollectionDeactivated; +extern NSString *const kFIRLibraryVersionID; + +@interface FIROptions (Test) + +@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary; + +@end + +@interface FIROptionsTest : FIRTestCase + +@end + +@implementation FIROptionsTest + +- (void)setUp { + [super setUp]; + [FIROptions resetDefaultOptions]; +} + +- (void)testInit { + NSDictionary *optionsDictionary = [FIROptions defaultOptionsDictionary]; + FIROptions *options = + [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + [self assertOptionsMatchDefaults:options andProjectID:YES]; + XCTAssertNil(options.deepLinkURLScheme); + XCTAssertTrue(options.usingOptionsFromDefaultPlist); + + options.deepLinkURLScheme = kDeepLinkURLScheme; + XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme); +} + +- (void)testDefaultOptionsDictionaryWithNilFilePath { + id mockBundleUtil = OCMClassMock([FIRBundleUtil class]); + [OCMStub([mockBundleUtil optionsDictionaryPathWithResourceName:kServiceInfoFileName + andFileType:kServiceInfoFileType + inBundles:[FIRBundleUtil relevantBundles]]) + andReturn:nil]; + XCTAssertNil([FIROptions defaultOptionsDictionary]); +} + +- (void)testDefaultOptionsDictionaryWithInvalidSourceFile { + id mockBundleUtil = OCMClassMock([FIRBundleUtil class]); + [OCMStub([mockBundleUtil optionsDictionaryPathWithResourceName:kServiceInfoFileName + andFileType:kServiceInfoFileType + inBundles:[FIRBundleUtil relevantBundles]]) + andReturn:@"invalid.plist"]; + XCTAssertNil([FIROptions defaultOptionsDictionary]); +} + +- (void)testDefaultOptions { + FIROptions *options = [FIROptions defaultOptions]; + [self assertOptionsMatchDefaults:options andProjectID:YES]; + XCTAssertNil(options.deepLinkURLScheme); + XCTAssertTrue(options.usingOptionsFromDefaultPlist); + + options.deepLinkURLScheme = kDeepLinkURLScheme; + XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme); +} + +- (void)testInitCustomizedOptions { + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID + bundleID:kBundleID + GCMSenderID:kGCMSenderID + APIKey:kAPIKey + clientID:kClientID + trackingID:kTrackingID + androidClientID:kAndroidClientID + databaseURL:kDatabaseURL + storageBucket:kStorageBucket + deepLinkURLScheme:kDeepLinkURLScheme]; + [self assertOptionsMatchDefaults:options andProjectID:NO]; + XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme); + XCTAssertFalse(options.usingOptionsFromDefaultPlist); + + FIROptions *options2 = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID + GCMSenderID:kGCMSenderID]; + options2.androidClientID = kAndroidClientID; + options2.APIKey = kAPIKey; + options2.bundleID = kBundleID; + options2.clientID = kClientID; + options2.databaseURL = kDatabaseURL; + options2.deepLinkURLScheme = kDeepLinkURLScheme; + options2.projectID = kProjectID; + options2.storageBucket = kStorageBucket; + options2.trackingID = kTrackingID; + [self assertOptionsMatchDefaults:options2 andProjectID:YES]; + XCTAssertEqualObjects(options2.deepLinkURLScheme, kDeepLinkURLScheme); + XCTAssertFalse(options.usingOptionsFromDefaultPlist); + + // nil GoogleAppID should throw an exception + XCTAssertThrows([[FIROptions alloc] initWithGoogleAppID:nil + bundleID:kBundleID + GCMSenderID:kGCMSenderID + APIKey:kCustomizedAPIKey + clientID:nil + trackingID:nil + androidClientID:nil + databaseURL:nil + storageBucket:nil + deepLinkURLScheme:nil]); +} + +- (void)testinitWithContentsOfFile { + NSString *filePath = + [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; + FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath]; + [self assertOptionsMatchDefaults:options andProjectID:YES]; + XCTAssertNil(options.deepLinkURLScheme); + XCTAssertFalse(options.usingOptionsFromDefaultPlist); + + FIROptions *emptyOptions = [[FIROptions alloc] initWithContentsOfFile:nil]; + XCTAssertNil(emptyOptions); + + FIROptions *invalidOptions = [[FIROptions alloc] initWithContentsOfFile:@"invalid.plist"]; + XCTAssertNil(invalidOptions); +} + +- (void)assertOptionsMatchDefaults:(FIROptions *)options andProjectID:(BOOL)matchProjectID { + XCTAssertEqualObjects(options.googleAppID, kGoogleAppID); + XCTAssertEqualObjects(options.APIKey, kAPIKey); + XCTAssertEqualObjects(options.clientID, kClientID); + XCTAssertEqualObjects(options.trackingID, kTrackingID); + XCTAssertEqualObjects(options.GCMSenderID, kGCMSenderID); + XCTAssertEqualObjects(options.androidClientID, kAndroidClientID); + XCTAssertEqualObjects(options.libraryVersionID, kFIRLibraryVersionID); + XCTAssertEqualObjects(options.databaseURL, kDatabaseURL); + XCTAssertEqualObjects(options.storageBucket, kStorageBucket); + XCTAssertEqualObjects(options.bundleID, kBundleID); + + // Custom `matchProjectID` parameter to be removed once the deprecated `FIROptions` constructor is + // removed. + if (matchProjectID) { + XCTAssertEqualObjects(options.projectID, kProjectID); + } +} + +- (void)testCopyingProperties { + NSMutableString *mutableString; + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID + GCMSenderID:kGCMSenderID]; + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.APIKey = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.APIKey, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.bundleID = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.bundleID, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.clientID = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.clientID, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.trackingID = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.trackingID, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.GCMSenderID = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.GCMSenderID, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.projectID = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.projectID, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.androidClientID = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.androidClientID, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.googleAppID = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.googleAppID, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.databaseURL = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.databaseURL, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.deepLinkURLScheme = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.deepLinkURLScheme, @"1"); + + mutableString = [[NSMutableString alloc] initWithString:@"1"]; + options.storageBucket = mutableString; + [mutableString appendString:@"2"]; + XCTAssertEqualObjects(options.storageBucket, @"1"); +} + +- (void)testCopyWithZone { + // default options + FIROptions *options = [FIROptions defaultOptions]; + options.deepLinkURLScheme = kDeepLinkURLScheme; + XCTAssertEqualObjects(options.deepLinkURLScheme, kDeepLinkURLScheme); + + FIROptions *newOptions = [options copy]; + XCTAssertEqualObjects(newOptions.deepLinkURLScheme, kDeepLinkURLScheme); + + [options setDeepLinkURLScheme:kNewDeepLinkURLScheme]; + XCTAssertEqualObjects(options.deepLinkURLScheme, kNewDeepLinkURLScheme); + XCTAssertEqualObjects(newOptions.deepLinkURLScheme, kDeepLinkURLScheme); + + // customized options + FIROptions *customizedOptions = [[FIROptions alloc] initWithGoogleAppID:kGoogleAppID + bundleID:kBundleID + GCMSenderID:kGCMSenderID + APIKey:kAPIKey + clientID:kClientID + trackingID:kTrackingID + androidClientID:kAndroidClientID + databaseURL:kDatabaseURL + storageBucket:kStorageBucket + deepLinkURLScheme:kDeepLinkURLScheme]; + FIROptions *copyCustomizedOptions = [customizedOptions copy]; + [copyCustomizedOptions setDeepLinkURLScheme:kNewDeepLinkURLScheme]; + XCTAssertEqualObjects(customizedOptions.deepLinkURLScheme, kDeepLinkURLScheme); + XCTAssertEqualObjects(copyCustomizedOptions.deepLinkURLScheme, kNewDeepLinkURLScheme); +} + +- (void)testAnalyticsConstants { + // The keys are public values and should never change. + XCTAssertEqualObjects(kFIRIsMeasurementEnabled, @"IS_MEASUREMENT_ENABLED"); + XCTAssertEqualObjects(kFIRIsAnalyticsCollectionEnabled, @"FIREBASE_ANALYTICS_COLLECTION_ENABLED"); + XCTAssertEqualObjects(kFIRIsAnalyticsCollectionDeactivated, + @"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED"); +} + +- (void)testAnalyticsOptions { + id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); + + // No keys anywhere. + NSDictionary *optionsDictionary = nil; + FIROptions *options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + NSDictionary *mainDictionary = nil; + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + NSDictionary *expectedAnalyticsOptions = @{}; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + optionsDictionary = @{}; + options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + mainDictionary = @{}; + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + expectedAnalyticsOptions = @{}; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + // Main has no keys. + optionsDictionary = @{ + kFIRIsAnalyticsCollectionDeactivated : @YES, + kFIRIsAnalyticsCollectionEnabled : @YES, + kFIRIsMeasurementEnabled : @YES + }; + options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + mainDictionary = @{}; + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + expectedAnalyticsOptions = optionsDictionary; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + // Main overrides all the keys. + optionsDictionary = @{ + kFIRIsAnalyticsCollectionDeactivated : @YES, + kFIRIsAnalyticsCollectionEnabled : @YES, + kFIRIsMeasurementEnabled : @YES + }; + options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + mainDictionary = @{ + kFIRIsAnalyticsCollectionDeactivated : @NO, + kFIRIsAnalyticsCollectionEnabled : @NO, + kFIRIsMeasurementEnabled : @NO + }; + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + expectedAnalyticsOptions = mainDictionary; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + // Keys exist only in main. + optionsDictionary = @{}; + options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + mainDictionary = @{ + kFIRIsAnalyticsCollectionDeactivated : @YES, + kFIRIsAnalyticsCollectionEnabled : @YES, + kFIRIsMeasurementEnabled : @YES + }; + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + expectedAnalyticsOptions = mainDictionary; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + // Main overrides single keys. + optionsDictionary = @{ + kFIRIsAnalyticsCollectionDeactivated : @YES, + kFIRIsAnalyticsCollectionEnabled : @YES, + kFIRIsMeasurementEnabled : @YES + }; + options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + mainDictionary = @{ kFIRIsAnalyticsCollectionDeactivated : @NO }; + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + expectedAnalyticsOptions = @{ + kFIRIsAnalyticsCollectionDeactivated : @NO, // override + kFIRIsAnalyticsCollectionEnabled : @YES, + kFIRIsMeasurementEnabled : @YES + }; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + optionsDictionary = @{ + kFIRIsAnalyticsCollectionDeactivated : @YES, + kFIRIsAnalyticsCollectionEnabled : @YES, + kFIRIsMeasurementEnabled : @YES + }; + options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + mainDictionary = @{ kFIRIsAnalyticsCollectionEnabled : @NO }; + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + expectedAnalyticsOptions = @{ + kFIRIsAnalyticsCollectionDeactivated : @YES, + kFIRIsAnalyticsCollectionEnabled : @NO, // override + kFIRIsMeasurementEnabled : @YES + }; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + optionsDictionary = @{ + kFIRIsAnalyticsCollectionDeactivated : @YES, + kFIRIsAnalyticsCollectionEnabled : @YES, + kFIRIsMeasurementEnabled : @YES + }; + options = [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + mainDictionary = @{ kFIRIsMeasurementEnabled : @NO }; + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + expectedAnalyticsOptions = @{ + kFIRIsAnalyticsCollectionDeactivated : @YES, + kFIRIsAnalyticsCollectionEnabled : @YES, + kFIRIsMeasurementEnabled : @NO // override + }; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); +} + +- (void)testAnalyticsOptions_combinatorial { + // Complete combinatorial test. + id mainBundleMock = OCMPartialMock([NSBundle mainBundle]); + // Possible values for the flags in the plist, where NSNull means the flag is not present. + NSArray *values = @[ [NSNull null], @NO, @YES ]; + + // Sanity checks for the combination generation. + int combinationCount = 0; + NSMutableArray *uniqueMainCombinations = [[NSMutableArray alloc] init]; + NSMutableArray *uniqueOptionsCombinations = [[NSMutableArray alloc] init]; + + // Generate all optout flag combinations for { main plist X GoogleService-info options plist }. + // Options present in the main plist should override options of the same key in the service plist. + for (id mainDeactivated in values) { + for (id mainAnalyticsEnabled in values) { + for (id mainMeasurementEnabled in values) { + for (id optionsDeactivated in values) { + for (id optionsAnalyticsEnabled in values) { + for (id optionsMeasurementEnabled in values) { + @autoreleasepool { + // Fill the GoogleService-info options plist dictionary. + NSMutableDictionary *optionsDictionary = [[NSMutableDictionary alloc] init]; + if (![optionsDeactivated isEqual:[NSNull null]]) { + optionsDictionary[kFIRIsAnalyticsCollectionDeactivated] = optionsDeactivated; + } + if (![optionsAnalyticsEnabled isEqual:[NSNull null]]) { + optionsDictionary[kFIRIsAnalyticsCollectionEnabled] = optionsAnalyticsEnabled; + } + if (![optionsMeasurementEnabled isEqual:[NSNull null]]) { + optionsDictionary[kFIRIsMeasurementEnabled] = optionsMeasurementEnabled; + } + FIROptions *options = + [[FIROptions alloc] initInternalWithOptionsDictionary:optionsDictionary]; + if (![uniqueOptionsCombinations containsObject:optionsDictionary]) { + [uniqueOptionsCombinations addObject:optionsDictionary]; + } + + // Fill the main plist dictionary. + NSMutableDictionary *mainDictionary = [[NSMutableDictionary alloc] init]; + if (![mainDeactivated isEqual:[NSNull null]]) { + mainDictionary[kFIRIsAnalyticsCollectionDeactivated] = mainDeactivated; + } + if (![mainAnalyticsEnabled isEqual:[NSNull null]]) { + mainDictionary[kFIRIsAnalyticsCollectionEnabled] = mainAnalyticsEnabled; + } + if (![mainMeasurementEnabled isEqual:[NSNull null]]) { + mainDictionary[kFIRIsMeasurementEnabled] = mainMeasurementEnabled; + } + OCMExpect([mainBundleMock infoDictionary]).andReturn(mainDictionary); + if (![uniqueMainCombinations containsObject:mainDictionary]) { + [uniqueMainCombinations addObject:mainDictionary]; + } + + // Generate the expected options by adding main values on top of the service options + // values. The main values will replace any existing options values with the same + // key. This is a different way of combining the two sets of flags from the actual + // implementation in FIROptions, with equivalent output. + NSMutableDictionary *expectedAnalyticsOptions = + [[NSMutableDictionary alloc] initWithDictionary:optionsDictionary]; + [expectedAnalyticsOptions addEntriesFromDictionary:mainDictionary]; + XCTAssertEqualObjects(options.analyticsOptionsDictionary, expectedAnalyticsOptions); + + combinationCount++; + } + } + } + } + } + } + } + + // Verify the sanity checks. + XCTAssertEqual(combinationCount, 729); // = 3^6. + + XCTAssertEqual(uniqueOptionsCombinations.count, 27); + int optionsSizeCount[4] = {0}; + for (NSDictionary *dictionary in uniqueOptionsCombinations) { + optionsSizeCount[dictionary.count]++; + } + XCTAssertEqual(optionsSizeCount[0], 1); + XCTAssertEqual(optionsSizeCount[1], 6); + XCTAssertEqual(optionsSizeCount[2], 12); + XCTAssertEqual(optionsSizeCount[3], 8); + + XCTAssertEqual(uniqueMainCombinations.count, 27); + int mainSizeCount[4] = {0}; + for (NSDictionary *dictionary in uniqueMainCombinations) { + mainSizeCount[dictionary.count]++; + } + XCTAssertEqual(mainSizeCount[0], 1); + XCTAssertEqual(mainSizeCount[1], 6); + XCTAssertEqual(mainSizeCount[2], 12); + XCTAssertEqual(mainSizeCount[3], 8); +} + +- (void)testVersionFormat { + NSRegularExpression *sLibraryVersionRegex = + [NSRegularExpression regularExpressionWithPattern:@"^[0-9]{8,}$" options:0 error:NULL]; + NSUInteger numberOfMatches = + [sLibraryVersionRegex numberOfMatchesInString:kFIRLibraryVersionID + options:0 + range:NSMakeRange(0, kFIRLibraryVersionID.length)]; + XCTAssertEqual(numberOfMatches, 1, @"Incorrect library version format."); +} + +@end diff --git a/Example/Core/Tests/FIRTestCase.h b/Example/Core/Tests/FIRTestCase.h new file mode 100644 index 0000000..acc7b16 --- /dev/null +++ b/Example/Core/Tests/FIRTestCase.h @@ -0,0 +1,45 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <OCMock/OCMock.h> +#import <XCTest/XCTest.h> + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const kAPIKey; +extern NSString *const kCustomizedAPIKey; +extern NSString *const kClientID; +extern NSString *const kTrackingID; +extern NSString *const kGCMSenderID; +extern NSString *const kAndroidClientID; +extern NSString *const kGoogleAppID; +extern NSString *const kDatabaseURL; +extern NSString *const kStorageBucket; + +extern NSString *const kDeepLinkURLScheme; +extern NSString *const kNewDeepLinkURLScheme; + +extern NSString *const kBundleID; +extern NSString *const kProjectID; + +/** + * Base test case for Firebase Core SDK tests. + */ +@interface FIRTestCase : XCTestCase + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/Core/Tests/FIRTestCase.m b/Example/Core/Tests/FIRTestCase.m new file mode 100644 index 0000000..2c68b8d --- /dev/null +++ b/Example/Core/Tests/FIRTestCase.m @@ -0,0 +1,47 @@ +// Copyright 2017 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRTestCase.h" + +NSString *const kAPIKey = @"correct_api_key"; +NSString *const kCustomizedAPIKey = @"customized_api_key"; +NSString *const kClientID = @"correct_client_id"; +NSString *const kTrackingID = @"correct_tracking_id"; +NSString *const kGCMSenderID = @"correct_gcm_sender_id"; +NSString *const kAndroidClientID = @"correct_android_client_id"; +NSString *const kGoogleAppID = @"1:123:ios:123abc"; +NSString *const kDatabaseURL = @"https://abc-xyz-123.firebaseio.com"; +NSString *const kStorageBucket = @"project-id-123.storage.firebase.com"; + +NSString *const kDeepLinkURLScheme = @"comgoogledeeplinkurl"; +NSString *const kNewDeepLinkURLScheme = @"newdeeplinkurlfortest"; + +NSString *const kBundleID = @"com.google.FirebaseSDKTests"; +NSString *const kProjectID = @"abc-xyz-123"; + +@interface FIRTestCase () + +@end + +@implementation FIRTestCase + +- (void)setUp { + [super setUp]; +} + +- (void)tearDown { + [super tearDown]; +} + +@end diff --git a/Example/Core/Tests/Tests-Info.plist b/Example/Core/Tests/Tests-Info.plist new file mode 100644 index 0000000..169b6f7 --- /dev/null +++ b/Example/Core/Tests/Tests-Info.plist @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>en</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> +</dict> +</plist> |