diff options
40 files changed, 1015 insertions, 292 deletions
diff --git a/Example/Auth/Tests/FIRUserTests.m b/Example/Auth/Tests/FIRUserTests.m index 7a6c165..8bb6786 100644 --- a/Example/Auth/Tests/FIRUserTests.m +++ b/Example/Auth/Tests/FIRUserTests.m @@ -26,9 +26,11 @@ #import "FIRAuthGlobalWorkQueue.h" #import "FIRAuthOperationType.h" #import "FIRAuthTokenResult.h" +#import "FIREmailLinkSignInResponse.m" #import "FIRSecureTokenService.h" #import "FIRSecureTokenRequest.h" #import "FIRSecureTokenResponse.h" +#import "FIRSignUpNewUserResponse.h" #import "FIRGetAccountInfoRequest.h" #import "FIRGetAccountInfoResponse.h" #import "FIRSetAccountInfoRequest.h" @@ -583,6 +585,57 @@ static const NSTimeInterval kExpectationTimeout = 2; OCMVerifyAll(_mockBackend); } +/** @fn testUpdateEmailWithAuthLinkAccountSuccess + @brief Tests a successful @c updateEmail:completion: call updates provider info. + */ +- (void)testUpdateEmailWithAuthLinkAccountSuccess { + id (^mockUserInfoWithDisplayName)(NSString *) = ^(NSString *displayName) { + id mockGetAccountInfoResponseUser = OCMClassMock([FIRGetAccountInfoResponseUser class]); + OCMStub([mockGetAccountInfoResponseUser localID]).andReturn(kLocalID); + OCMStub([mockGetAccountInfoResponseUser email]).andReturn(kEmail); + OCMStub([mockGetAccountInfoResponseUser displayName]).andReturn(displayName); + OCMStub([mockGetAccountInfoResponseUser passwordHash]).andReturn(kPasswordHash); + return mockGetAccountInfoResponseUser; + }; + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + id userInfoResponse = mockUserInfoWithDisplayName(kGoogleDisplayName); + [self signInWithEmailLinkWithMockUserInfoResponse:userInfoResponse + completion:^(FIRUser *user) { + // Pretend that the display name on the server has been changed since last request. + [self + expectGetAccountInfoWithMockUserInfoResponse:mockUserInfoWithDisplayName(kNewDisplayName)]; + OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIRSetAccountInfoRequest *_Nullable request, + FIRSetAccountInfoResponseCallback callback) { + XCTAssertEqualObjects(request.APIKey, kAPIKey); + XCTAssertEqualObjects(request.accessToken, kAccessToken); + XCTAssertEqualObjects(request.email, kNewEmail); + XCTAssertNil(request.localID); + XCTAssertNil(request.displayName); + XCTAssertNil(request.photoURL); + XCTAssertNil(request.password); + XCTAssertNil(request.providers); + XCTAssertNil(request.deleteAttributes); + XCTAssertNil(request.deleteProviders); + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + id mockSetAccountInfoResponse = OCMClassMock([FIRSetAccountInfoResponse class]); + OCMStub([mockSetAccountInfoResponse email]).andReturn(kNewEmail); + OCMStub([mockSetAccountInfoResponse displayName]).andReturn(kNewDisplayName); + callback(mockSetAccountInfoResponse, nil); + }); + }); + [user updateEmail:kNewEmail completion:^(NSError *_Nullable error) { + XCTAssertNil(error); + XCTAssertEqualObjects(user.email, kNewEmail); + XCTAssertEqualObjects(user.displayName, kNewDisplayName); + XCTAssertFalse(user.isAnonymous); + [expectation fulfill]; + }]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); +} + /** @fn testUpdateEmailFailure @brief Tests the flow of a failed @c updateEmail:completion: call. */ @@ -1572,6 +1625,64 @@ static const NSTimeInterval kExpectationTimeout = 2; OCMVerifyAll(_mockBackend); } +/** @fn testLinkingAnonymousAccountsUpdatesIsAnonymous + @brief Tests the flow of a successful @c linkAndRetrieveDataWithCredential:completion: + invocation for email credential. + */ +- (void)testLinkingAnonymousAccountsUpdatesIsAnonymous { + FIRAuthCredential *linkEmailCredential = + [FIREmailAuthProvider credentialWithEmail:kEmail + link:@"https://google.com?oobCode=aCode&mode=signIn"]; + + id (^mockUserInfoWithDisplayName)(NSString *, BOOL) = ^(NSString *displayName, + BOOL hasProviders) { + NSArray *providers = hasProviders ? @[ @{ + @"providerId": FIREmailAuthProviderID, + @"email": kEmail + } ] : @[]; + FIRGetAccountInfoResponseUser *responseUser = + [[FIRGetAccountInfoResponseUser alloc] initWithDictionary:@{ + @"providerUserInfo": providers, + @"localId": kLocalID, + @"displayName": displayName, + @"email": kEmail + }]; + return responseUser; + }; + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + id userInfoResponse = mockUserInfoWithDisplayName(kGoogleDisplayName, NO); + + [self signInAnonymouslyWithMockGetAccountInfoResponse:userInfoResponse + completion:^(FIRUser *user) { + // Pretend that the display name and providers on the server have been updated. + // Get account info is expected to be invoked twice. + id updatedMockUser = mockUserInfoWithDisplayName(kNewDisplayName, YES); + [self expectGetAccountInfoWithMockUserInfoResponse:updatedMockUser]; + [self expectGetAccountInfoWithMockUserInfoResponse:updatedMockUser]; + OCMExpect([_mockBackend setAccountInfo:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIRSetAccountInfoRequest *_Nullable request, + FIRSetAccountInfoResponseCallback callback) { + id mockSetAccountInfoResponse = OCMClassMock([FIRSetAccountInfoResponse class]); + OCMStub([mockSetAccountInfoResponse email]).andReturn(kNewEmail); + OCMStub([mockSetAccountInfoResponse displayName]).andReturn(kNewDisplayName); + callback(mockSetAccountInfoResponse, nil); + }); + XCTAssertTrue(user.isAnonymous); + + [user linkAndRetrieveDataWithCredential:linkEmailCredential + completion:^(FIRAuthDataResult *_Nullable linkAuthResult, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNil(error); + XCTAssertEqualObjects(user.email, kEmail); + XCTAssertFalse(user.isAnonymous); + [expectation fulfill]; + }]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); +} + /** @fn testlinkEmailAndRetrieveDataSuccess @brief Tests the flow of a successful @c linkAndRetrieveDataWithCredential:completion: invocation for email credential. @@ -2222,6 +2333,34 @@ static const NSTimeInterval kExpectationTimeout = 2; OCMVerifyAll(_mockBackend); } +/** @fn signInAnonymouslyWithMockGetAccountInfoResponse:completion: + @brief Signs in with an anonymous account with mocked backend end calls. + @param mockUserInfoResponse A mocked FIRGetAccountInfoResponseUser object. + @param completion The completion block that takes the newly signed-in user as the only + parameter. + */ +- (void)signInAnonymouslyWithMockGetAccountInfoResponse:(id)mockUserInfoResponse + completion:(void (^)(FIRUser *user))completion { + OCMExpect([_mockBackend signUpNewUser:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIRSignUpNewUserRequest *_Nullable request, + FIRSignupNewUserCallback callback) { + id mockSignUpResponse = OCMClassMock([FIRSignUpNewUserResponse class]); + OCMStub([mockSignUpResponse IDToken]).andReturn(kAccessToken); + OCMStub([mockSignUpResponse approximateExpirationDate]) + .andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]); + OCMStub([mockSignUpResponse refreshToken]).andReturn(kRefreshToken); + callback(mockSignUpResponse, nil); + }); + [self expectGetAccountInfoWithMockUserInfoResponse:mockUserInfoResponse]; + [[FIRAuth auth] signOut:NULL]; + [[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { + XCTAssertNotNil(result.user); + XCTAssertNil(error); + completion(result.user); + }]; +} + /** @fn signInWithEmailPasswordWithMockGetAccountInfoResponse:completion: @brief Signs in with an email and password account with mocked backend end calls. @param mockUserInfoResponse A mocked FIRGetAccountInfoResponseUser object. @@ -2229,7 +2368,7 @@ static const NSTimeInterval kExpectationTimeout = 2; parameter. */ - (void)signInWithEmailPasswordWithMockUserInfoResponse:(id)mockUserInfoResponse - completion:(void (^)(FIRUser *user))completion { + completion:(void (^)(FIRUser *user))completion { OCMExpect([_mockBackend verifyPassword:[OCMArg any] callback:[OCMArg any]]) .andCallBlock2(^(FIRVerifyPasswordRequest *_Nullable request, FIRVerifyPasswordResponseCallback callback) { @@ -2239,7 +2378,7 @@ static const NSTimeInterval kExpectationTimeout = 2; OCMStub([mockVeriyPasswordResponse approximateExpirationDate]) .andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]); OCMStub([mockVeriyPasswordResponse refreshToken]).andReturn(kRefreshToken); - callback(mockVeriyPasswordResponse, nil); + callback(mockVeriyPasswordResponse, nil); }); }); [self expectGetAccountInfoWithMockUserInfoResponse:mockUserInfoResponse]; @@ -2253,6 +2392,35 @@ static const NSTimeInterval kExpectationTimeout = 2; }]; } +/** @fn signInWithEmailLinkWithMockGetAccountInfoResponse:completion: + @brief Signs in with an email link auth account with mocked backend end calls. + @param mockUserInfoResponse A mocked FIRGetAccountInfoResponseUser object. + @param completion The completion block that takes the newly signed-in user as the only + parameter. + */ +- (void)signInWithEmailLinkWithMockUserInfoResponse:(id)mockUserInfoResponse + completion:(void (^)(FIRUser *user))completion { + OCMExpect([_mockBackend emailLinkSignin:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIREmailLinkSignInRequest *_Nullable request, + FIREmailLinkSigninResponseCallback callback) { + id mockVerifyLinkResponse = OCMClassMock([FIREmailLinkSignInResponse class]); + OCMStub([mockVerifyLinkResponse IDToken]).andReturn(kAccessToken); + OCMStub([mockVerifyLinkResponse approximateExpirationDate]) + .andReturn([NSDate dateWithTimeIntervalSinceNow:kAccessTokenTimeToLive]); + OCMStub([mockVerifyLinkResponse refreshToken]).andReturn(kRefreshToken); + callback(mockVerifyLinkResponse, nil); + }); + [self expectGetAccountInfoWithMockUserInfoResponse:mockUserInfoResponse]; + [[FIRAuth auth] signOut:NULL]; + [[FIRAuth auth] signInWithEmail:kEmail + link:@"https://www.google.com?oobCode=aCode&mode=signIn" + completion:^(FIRAuthDataResult *_Nullable result, NSError *_Nullable error) { + XCTAssertNotNil(result.user); + XCTAssertNil(error); + completion(result.user); + }]; +} + /** @fn expectGetAccountInfoWithMockUserInfoResponse: @brief Expects a GetAccountInfo request on the mock backend and calls back with provided fake account data. diff --git a/Example/Core/Tests/FIRLoggerTest.m b/Example/Core/Tests/FIRLoggerTest.m index 0d6d4e2..c1ba37b 100644 --- a/Example/Core/Tests/FIRLoggerTest.m +++ b/Example/Core/Tests/FIRLoggerTest.m @@ -29,8 +29,6 @@ extern NSString *const kFIRPersistedDebugModeKey; extern const char *kFIRLoggerASLClientFacilityName; -extern const char *kFIRLoggerCustomASLMessageFormat; - extern void FIRResetLogger(void); extern aslclient getFIRLoggerClient(void); @@ -39,10 +37,6 @@ extern dispatch_queue_t getFIRClientQueue(void); extern BOOL getFIRLoggerDebugMode(void); -// 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 @@ -71,10 +65,6 @@ static NSString *const kMessageCode = @"I-COR000001"; // 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]"); diff --git a/Example/Messaging/Tests/FIRMessagingPubSubTest.m b/Example/Messaging/Tests/FIRMessagingPubSubTest.m index 3af1402..e1260f5 100644 --- a/Example/Messaging/Tests/FIRMessagingPubSubTest.m +++ b/Example/Messaging/Tests/FIRMessagingPubSubTest.m @@ -78,4 +78,13 @@ static NSString *const kTopicName = @"topic-Name"; XCTAssertTrue([FIRMessagingPubSub isValidTopicWithPrefix:topic]); } +- (void)testRemoveTopicPrefix { + NSString *topic = [NSString stringWithFormat:@"/topics/%@", kTopicName]; + topic = [FIRMessagingPubSub removePrefixFromTopic:topic]; + XCTAssertEqualObjects(topic, kTopicName); + // if the topic doesn't have the prefix, should return topic itself. + topic = [FIRMessagingPubSub removePrefixFromTopic:kTopicName]; + XCTAssertEqualObjects(topic, kTopicName); +} + @end diff --git a/Example/Messaging/Tests/FIRMessagingServiceTest.m b/Example/Messaging/Tests/FIRMessagingServiceTest.m index 073adad..afbae46 100644 --- a/Example/Messaging/Tests/FIRMessagingServiceTest.m +++ b/Example/Messaging/Tests/FIRMessagingServiceTest.m @@ -26,6 +26,11 @@ #import "InternalHeaders/FIRMessagingInternalUtilities.h" #import "NSError+FIRMessaging.h" +static NSString *const kFakeToken = + @"fE1e1PZJFSQ:APA91bFAOjp1ahBWn9rTlbjArwBEm_" + @"yUTTzK6dhIvLqzqqCSabaa4TQVM0pGTmF6r7tmMHPe6VYiGMHuCwJFgj5v97xl78sUNMLwuPPhoci8z_" + @"QGlCrTbxCFGzEUfvA3fGpGgIVQU2W6"; + @interface FIRMessaging () <FIRMessagingClientDelegate> @property(nonatomic, readwrite, strong) FIRMessagingClient *client; @@ -40,22 +45,35 @@ @end - -@interface FIRMessagingServiceTest : XCTestCase +@interface FIRMessagingServiceTest : XCTestCase { + FIRMessaging *_messaging; + id _mockPubSub; +} @end @implementation FIRMessagingServiceTest +- (void)setUp { + _messaging = [FIRMessaging messaging]; + _messaging.defaultFcmToken = kFakeToken; + _mockPubSub = OCMPartialMock(_messaging.pubsub); + [super setUp]; +} + +- (void)tearDown { + [_mockPubSub stopMocking]; + [super tearDown]; +} + - (void)testSubscribe { id mockClient = OCMClassMock([FIRMessagingClient class]); - FIRMessaging *service = [FIRMessaging messaging]; - [service setClient:mockClient]; - [service.pubsub setClient:mockClient]; + [_messaging setClient:mockClient]; + [_mockPubSub setClient:mockClient]; XCTestExpectation *subscribeExpectation = [self expectationWithDescription:@"Should call subscribe on FIRMessagingClient"]; - NSString *token = @"abcdefghijklmn"; + NSString *token = kFakeToken; NSString *topic = @"/topics/some-random-topic"; [[[mockClient stub] @@ -68,12 +86,12 @@ shouldDelete:NO handler:OCMOCK_ANY]; - [service.pubsub subscribeWithToken:token - topic:topic - options:nil - handler:^(NSError *error){ - // not a nil block - }]; + [_mockPubSub subscribeWithToken:token + topic:topic + options:nil + handler:^(NSError *error){ + // not a nil block + }]; // should call updateSubscription [self waitForExpectationsWithTimeout:0.1 @@ -85,14 +103,13 @@ - (void)testUnsubscribe { id mockClient = OCMClassMock([FIRMessagingClient class]); - FIRMessaging *messaging = [FIRMessaging messaging]; - [messaging setClient:mockClient]; - [messaging.pubsub setClient:mockClient]; + [_messaging setClient:mockClient]; + [_mockPubSub setClient:mockClient]; XCTestExpectation *subscribeExpectation = [self expectationWithDescription:@"Should call unsubscribe on FIRMessagingClient"]; - NSString *token = @"abcdefghijklmn"; + NSString *token = kFakeToken; NSString *topic = @"/topics/some-random-topic"; [[[mockClient stub] andDo:^(NSInvocation *invocation) { @@ -109,12 +126,12 @@ shouldDelete:YES handler:OCMOCK_ANY]; - [messaging.pubsub unsubscribeWithToken:token - topic:topic - options:nil - handler:^(NSError *error){ + [_mockPubSub unsubscribeWithToken:token + topic:topic + options:nil + handler:^(NSError *error){ - }]; + }]; // should call updateSubscription [self waitForExpectationsWithTimeout:0.1 @@ -128,8 +145,8 @@ * Test using PubSub without explicitly starting FIRMessagingService. */ - (void)testSubscribeWithoutStart { - [[[FIRMessaging messaging] pubsub] - subscribeWithToken:@"abcdef1234" + [_mockPubSub + subscribeWithToken:kFakeToken topic:@"/topics/hello-world" options:nil handler:^(NSError *error) { @@ -141,19 +158,18 @@ // TODO(chliangGoogle) Investigate why invalid token can't throw assertion but the rest can under // release build. - (void)testSubscribeWithInvalidTopic { - FIRMessaging *messaging = [FIRMessaging messaging]; XCTestExpectation *exceptionExpectation = [self expectationWithDescription:@"Should throw exception for invalid token"]; @try { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" - [messaging.pubsub subscribeWithToken:@"abcdef1234" - topic:nil - options:nil - handler:^(NSError *error) { - XCTFail(@"Should not invoke the handler"); - }]; + [_mockPubSub subscribeWithToken:kFakeToken + topic:nil + options:nil + handler:^(NSError *error) { + XCTFail(@"Should not invoke the handler"); + }]; #pragma clang diagnostic pop } @catch (NSException *exception) { @@ -167,19 +183,17 @@ } - (void)testUnsubscribeWithInvalidTopic { - FIRMessaging *messaging = [FIRMessaging messaging]; - XCTestExpectation *exceptionExpectation = [self expectationWithDescription:@"Should throw exception for invalid token"]; @try { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnonnull" - [messaging.pubsub unsubscribeWithToken:@"abcdef1234" - topic:nil - options:nil - handler:^(NSError *error) { - XCTFail(@"Should not invoke the handler"); - }]; + [_mockPubSub unsubscribeWithToken:kFakeToken + topic:nil + options:nil + handler:^(NSError *error) { + XCTFail(@"Should not invoke the handler"); + }]; #pragma clang diagnostic pop } @catch (NSException *exception) { @@ -193,68 +207,66 @@ } - (void)testSubscribeWithNoTopicPrefix { - FIRMessaging *messaging = [FIRMessaging messaging]; - FIRMessagingPubSub *pubSub = messaging.pubsub; - id mockPubSub = OCMClassMock([FIRMessagingPubSub class]); NSString *topicName = @"topicWithoutPrefix"; NSString *topicNameWithPrefix = [FIRMessagingPubSub addPrefixToTopic:topicName]; - messaging.pubsub = mockPubSub; - messaging.defaultFcmToken = @"fake-default-token"; - OCMExpect([messaging.pubsub subscribeToTopic:[OCMArg isEqual:topicNameWithPrefix] - handler:[OCMArg any]]); - [messaging subscribeToTopic:topicName]; - OCMVerifyAll(mockPubSub); - // Need to swap back since it's a singleton and hence will live beyond the scope of this test. - messaging.pubsub = pubSub; + OCMExpect( + [_mockPubSub subscribeToTopic:[OCMArg isEqual:topicNameWithPrefix] handler:[OCMArg any]]); + [_messaging subscribeToTopic:topicName]; + OCMVerifyAll(_mockPubSub); } - (void)testSubscribeWithTopicPrefix { - FIRMessaging *messaging = [FIRMessaging messaging]; - FIRMessagingPubSub *pubSub = messaging.pubsub; - id mockPubSub = OCMClassMock([FIRMessagingPubSub class]); - NSString *topicName = @"/topics/topicWithoutPrefix"; - messaging.pubsub = mockPubSub; - messaging.defaultFcmToken = @"fake-default-token"; - OCMExpect([messaging.pubsub subscribeToTopic:[OCMArg isEqual:topicName] handler:[OCMArg any]]); - [messaging subscribeToTopic:topicName]; - OCMVerifyAll(mockPubSub); - // Need to swap back since it's a singleton and hence will live beyond the scope of this test. - messaging.pubsub = pubSub; + OCMExpect([_mockPubSub subscribeToTopic:[OCMArg isEqual:topicName] handler:[OCMArg any]]); + [_messaging subscribeToTopic:topicName]; + OCMVerifyAll(_mockPubSub); } - (void)testUnsubscribeWithNoTopicPrefix { - FIRMessaging *messaging = [FIRMessaging messaging]; - FIRMessagingPubSub *pubSub = messaging.pubsub; - id mockPubSub = OCMClassMock([FIRMessagingPubSub class]); - NSString *topicName = @"topicWithoutPrefix"; NSString *topicNameWithPrefix = [FIRMessagingPubSub addPrefixToTopic:topicName]; - messaging.pubsub = mockPubSub; - messaging.defaultFcmToken = @"fake-default-token"; - OCMExpect([messaging.pubsub unsubscribeFromTopic:[OCMArg isEqual:topicNameWithPrefix] - handler:[OCMArg any]]); - [messaging unsubscribeFromTopic:topicName]; - OCMVerifyAll(mockPubSub); - // Need to swap back since it's a singleton and hence will live beyond the scope of this test. - messaging.pubsub = pubSub; + OCMExpect( + [_mockPubSub unsubscribeFromTopic:[OCMArg isEqual:topicNameWithPrefix] handler:[OCMArg any]]); + [_messaging unsubscribeFromTopic:topicName]; + OCMVerifyAll(_mockPubSub); } - (void)testUnsubscribeWithTopicPrefix { - FIRMessaging *messaging = [FIRMessaging messaging]; - FIRMessagingPubSub *pubSub = messaging.pubsub; - id mockPubSub = OCMClassMock([FIRMessagingPubSub class]); - NSString *topicName = @"/topics/topicWithPrefix"; - messaging.pubsub = mockPubSub; - messaging.defaultFcmToken = @"fake-default-token"; - OCMExpect([messaging.pubsub unsubscribeFromTopic:[OCMArg isEqual:topicName] - handler:[OCMArg any]]); - [messaging unsubscribeFromTopic:topicName]; - OCMVerifyAll(mockPubSub); - // Need to swap back since it's a singleton and hence will live beyond the scope of this test. - messaging.pubsub = pubSub; + OCMExpect([_mockPubSub unsubscribeFromTopic:[OCMArg isEqual:topicName] handler:[OCMArg any]]); + [_messaging unsubscribeFromTopic:topicName]; + OCMVerifyAll(_mockPubSub); +} + +- (void)testSubscriptionCompletionHandlerWithSuccess { + OCMStub([_mockPubSub subscribeToTopic:[OCMArg any] + handler:([OCMArg invokeBlockWithArgs:[NSNull null], nil])]); + XCTestExpectation *subscriptionCompletionExpectation = + [self expectationWithDescription:@"Subscription is complete"]; + [_messaging subscribeToTopic:@"news" + completion:^(NSError *error) { + XCTAssertNil(error); + [subscriptionCompletionExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:0.2 + handler:^(NSError *_Nullable error){ + }]; +} + +- (void)testUnsubscribeCompletionHandlerWithSuccess { + OCMStub([_mockPubSub unsubscribeFromTopic:[OCMArg any] + handler:([OCMArg invokeBlockWithArgs:[NSNull null], nil])]); + XCTestExpectation *unsubscriptionCompletionExpectation = + [self expectationWithDescription:@"Unsubscription is complete"]; + [_messaging unsubscribeFromTopic:@"news" + completion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [unsubscriptionCompletionExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:0.2 + handler:^(NSError *_Nullable error){ + }]; } - (void)testFIRMessagingSDKVersionInFIRMessagingService { diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md index 292f8bd..2669fc0 100644 --- a/Firebase/Auth/CHANGELOG.md +++ b/Firebase/Auth/CHANGELOG.md @@ -1,3 +1,7 @@ +# v5.0.2 +- Fix an issue where anonymous accounts weren't correctly promoted to + non-anonymous when linked with passwordless email auth accounts. + # v5.0.1 - Restore 4.x level of support for extensions (#1357). diff --git a/Firebase/Auth/Source/FIRUser.m b/Firebase/Auth/Source/FIRUser.m index 04aa861..3f5bf35 100644 --- a/Firebase/Auth/Source/FIRUser.m +++ b/Firebase/Auth/Source/FIRUser.m @@ -541,7 +541,7 @@ static void callInMainThreadWithAuthDataResultAndError( - (void)updateEmail:(nullable NSString *)email password:(nullable NSString *)password callback:(nonnull FIRUserProfileChangeCallback)callback { - if (password && ![password length]){ + if (password && ![password length]) { callback([FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:kMissingPasswordReason]); return; } @@ -561,11 +561,9 @@ static void callInMainThreadWithAuthDataResultAndError( return; } if (email) { - self->_email = email; + self->_email = [email copy]; } - if (self->_email && password) { - self->_anonymous = NO; - self->_hasEmailPasswordCredential = YES; + if (self->_email) { if (!hadEmailPasswordCredential) { // The list of providers need to be updated for the newly added email-password provider. [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, @@ -586,6 +584,20 @@ static void callInMainThreadWithAuthDataResultAndError( callback(error); return; } + for (FIRGetAccountInfoResponseUser *userAccountInfo in response.users) { + // Set the account to non-anonymous if there are any providers, even if + // they're not email/password ones. + if (userAccountInfo.providerUserInfo.count > 0) { + self->_anonymous = NO; + } + for (FIRGetAccountInfoResponseProviderUserInfo *providerUserInfo in + userAccountInfo.providerUserInfo) { + if ([providerUserInfo.providerID isEqualToString:FIREmailAuthProviderID]) { + self->_hasEmailPasswordCredential = YES; + break; + } + } + } [self updateWithGetAccountInfoResponse:response]; if (![self updateKeychain:&error]) { callback(error); diff --git a/Firebase/Core/CHANGELOG.md b/Firebase/Core/CHANGELOG.md index e1f157a..9a02cb4 100644 --- a/Firebase/Core/CHANGELOG.md +++ b/Firebase/Core/CHANGELOG.md @@ -1,5 +1,9 @@ # Unreleased +# 2018-06-19 -- v5.0.4 -- M28 +- [fixed] Fixed a thread sanitizer error (#1390) +- [fixed] Updated FirebaseCore.podspec so that it works with cocoapods-packager. (#1378) + # 2018-05-29 -- v5.0.2 -- M26 - [changed] Delayed library registration call from `+load` to `+initialize`. (#1305) diff --git a/Firebase/Core/FIRLogger.m b/Firebase/Core/FIRLogger.m index 03f8a79..ae14e9f 100644 --- a/Firebase/Core/FIRLogger.m +++ b/Firebase/Core/FIRLogger.m @@ -54,10 +54,6 @@ NSString *const kFIRPersistedDebugModeKey = @"/google/firebase/debug_mode"; /// ASL client facility name used by FIRLogger. const char *kFIRLoggerASLClientFacilityName = "com.firebase.app.logger"; -/// Message format used by ASL client that matches format of NSLog. -const char *kFIRLoggerCustomASLMessageFormat = - "$((Time)(J.3)) $(Sender)[$(PID)] <$((Level)(str))> $Message"; - /// Keys for the number of errors and warnings logged. NSString *const kFIRLoggerErrorCountKey = @"/google/firebase/count_of_errors_logged"; NSString *const kFIRLoggerWarningCountKey = @"/google/firebase/count_of_warnings_logged"; diff --git a/Firebase/Messaging/FIRMessaging.m b/Firebase/Messaging/FIRMessaging.m index 3c4d999..100c18a 100644 --- a/Firebase/Messaging/FIRMessaging.m +++ b/Firebase/Messaging/FIRMessaging.m @@ -680,6 +680,9 @@ NSString *const kFIRMessagingPlistAutoInitEnabled = #pragma mark - Topics + (NSString *)normalizeTopic:(NSString *)topic { + if (!topic.length) { + return nil; + } if (![FIRMessagingPubSub hasTopicsPrefix:topic]) { topic = [FIRMessagingPubSub addPrefixToTopic:topic]; } @@ -695,24 +698,24 @@ NSString *const kFIRMessagingPlistAutoInitEnabled = - (void)subscribeToTopic:(NSString *)topic completion:(nullable FIRMessagingTopicOperationCompletion)completion { - if (self.defaultFcmToken.length && topic.length) { - NSString *normalizeTopic = [[self class ] normalizeTopic:topic]; - if ([FIRMessagingPubSub hasTopicsPrefix:topic]) { - FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicFormatIsDeprecated, - @"Format '%@' is deprecated. Only '%@' should be used in " - @"subscribeToTopic.", topic, normalizeTopic); - } - if (normalizeTopic.length) { - [self.pubsub subscribeToTopic:normalizeTopic handler:completion]; - } else { - FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging009, - @"Cannot parse topic name %@. Will not subscribe.", topic); - } - } else { - FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging010, - @"Cannot subscribe to topic: %@ with token: %@", topic, - self.defaultFcmToken); + if ([FIRMessagingPubSub hasTopicsPrefix:topic]) { + FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicFormatIsDeprecated, + @"Format '%@' is deprecated. Only '%@' should be used in " + @"subscribeToTopic.", + topic, [FIRMessagingPubSub removePrefixFromTopic:topic]); + } + if (!self.defaultFcmToken.length) { + FIRMessagingLoggerWarn(kFIRMessagingMessageCodeMessaging010, + @"The subscription operation is suspended because you don't have a " + @"token. The operation will resume once you get an FCM token."); + } + NSString *normalizeTopic = [[self class] normalizeTopic:topic]; + if (normalizeTopic.length) { + [self.pubsub subscribeToTopic:normalizeTopic handler:completion]; + return; } + FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging009, + @"Cannot parse topic name %@. Will not subscribe.", topic); } - (void)unsubscribeFromTopic:(NSString *)topic { @@ -721,24 +724,24 @@ NSString *const kFIRMessagingPlistAutoInitEnabled = - (void)unsubscribeFromTopic:(NSString *)topic completion:(nullable FIRMessagingTopicOperationCompletion)completion { - if (self.defaultFcmToken.length && topic.length) { - NSString *normalizeTopic = [[self class] normalizeTopic:topic]; - if ([FIRMessagingPubSub hasTopicsPrefix:topic]) { - FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicFormatIsDeprecated, - @"Format '%@' is deprecated. Only '%@' should be used in " - @"unsubscribeFromTopic.", topic, normalizeTopic); - } - if (normalizeTopic.length) { - [self.pubsub unsubscribeFromTopic:normalizeTopic handler:completion]; - } else { - FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging011, - @"Cannot parse topic name %@. Will not unsubscribe.", topic); - } - } else { - FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging012, - @"Cannot unsubscribe to topic: %@ with token: %@", topic, - self.defaultFcmToken); + if ([FIRMessagingPubSub hasTopicsPrefix:topic]) { + FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicFormatIsDeprecated, + @"Format '%@' is deprecated. Only '%@' should be used in " + @"unsubscribeFromTopic.", + topic, [FIRMessagingPubSub removePrefixFromTopic:topic]); + } + if (!self.defaultFcmToken.length) { + FIRMessagingLoggerWarn(kFIRMessagingMessageCodeMessaging012, + @"The unsubscription operation is suspended because you don't have a " + @"token. The operation will resume once you get an FCM token."); + } + NSString *normalizeTopic = [[self class] normalizeTopic:topic]; + if (normalizeTopic.length) { + [self.pubsub unsubscribeFromTopic:normalizeTopic handler:completion]; + return; } + FIRMessagingLoggerError(kFIRMessagingMessageCodeMessaging011, + @"Cannot parse topic name %@. Will not unsubscribe.", topic); } #pragma mark - Send diff --git a/Firebase/Messaging/FIRMessagingPubSub.h b/Firebase/Messaging/FIRMessagingPubSub.h index 1c615d1..ebb4ca8 100644 --- a/Firebase/Messaging/FIRMessagingPubSub.h +++ b/Firebase/Messaging/FIRMessagingPubSub.h @@ -139,6 +139,15 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)addPrefixToTopic:(NSString *)topic; /** + * Removes the "/topics/" prefix from the topic. + * + * @param topic The topic to remove the prefix from. + * + * @return The new topic name with the "/topics/" prefix removed. + */ + ++ (NSString *)removePrefixFromTopic:(NSString *)topic; +/** * Check if the topic name has "/topics/" prefix. * * @param topic The topic name to verify. diff --git a/Firebase/Messaging/FIRMessagingPubSub.m b/Firebase/Messaging/FIRMessagingPubSub.m index 09491b4..3f954e8 100644 --- a/Firebase/Messaging/FIRMessagingPubSub.m +++ b/Firebase/Messaging/FIRMessagingPubSub.m @@ -231,6 +231,14 @@ static NSString *const kTopicRegexPattern = @"/topics/([a-zA-Z0-9-_.~%]+)"; } } ++ (NSString *)removePrefixFromTopic:(NSString *)topic { + if ([self hasTopicsPrefix:topic]) { + return [topic substringFromIndex:kTopicsPrefix.length]; + } else { + return [topic copy]; + } +} + + (BOOL)hasTopicsPrefix:(NSString *)topic { return [topic hasPrefix:kTopicsPrefix]; } diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 441cd34..068d562 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -51,17 +51,23 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.dependency 'gRPC-ProtoRPC', '~> 1.0' s.dependency 'leveldb-library', '~> 1.18' s.dependency 'Protobuf', '~> 3.1' + s.dependency 'nanopb', '~> 0.3.8' s.frameworks = 'MobileCoreServices' s.library = 'c++' s.pod_target_xcconfig = { - 'GCC_PREPROCESSOR_DEFINITIONS' => 'GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 ' + - 'FIRFirestore_VERSION=' + s.version.to_s + ' PB_FIELD_16BIT', + 'GCC_PREPROCESSOR_DEFINITIONS' => + "FIRFirestore_VERSION=#{s.version} " + + 'GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 ' + + # The nanopb pod sets these defs, so we must too. (We *do* require 16bit + # (or larger) fields, so we'd have to set at least PB_FIELD_16BIT + # anyways.) + 'PB_FIELD_32BIT=1 PB_NO_PACKED_STRUCTS=1', 'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}" ' + '"${PODS_TARGET_SRCROOT}/Firestore/third_party/abseil-cpp" ' + '"${PODS_ROOT}/nanopb" ' + - '"${PODS_TARGET_SRCROOT}/Firestore/Protos/nanopb"' + '"${PODS_TARGET_SRCROOT}/Firestore/Protos/nanopb"', } s.prepare_command = <<-CMD diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 2441b65..68b3a55 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -147,6 +147,19 @@ 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 6161B5032047140C00A99DBB /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; + 618BBEA620B89AAC00B5BCE7 /* target.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */; }; + 618BBEA720B89AAC00B5BCE7 /* maybe_document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */; }; + 618BBEA820B89AAC00B5BCE7 /* mutation.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8220B89AAC00B5BCE7 /* mutation.pb.cc */; }; + 618BBEA920B89AAC00B5BCE7 /* common.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8820B89AAC00B5BCE7 /* common.pb.cc */; }; + 618BBEAA20B89AAC00B5BCE7 /* firestore.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8A20B89AAC00B5BCE7 /* firestore.pb.cc */; }; + 618BBEAB20B89AAC00B5BCE7 /* query.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8C20B89AAC00B5BCE7 /* query.pb.cc */; }; + 618BBEAC20B89AAC00B5BCE7 /* document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8E20B89AAC00B5BCE7 /* document.pb.cc */; }; + 618BBEAD20B89AAC00B5BCE7 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE8F20B89AAC00B5BCE7 /* write.pb.cc */; }; + 618BBEAE20B89AAC00B5BCE7 /* latlng.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */; }; + 618BBEAF20B89AAC00B5BCE7 /* annotations.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */; }; + 618BBEB020B89AAC00B5BCE7 /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; + 618BBEB120B89AAC00B5BCE7 /* status.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */; }; + 61F72C5620BC48FD001A68CB /* serializer_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 61F72C5520BC48FD001A68CB /* serializer_test.cc */; }; 6EDD3B4620BF247500C33877 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6EDD3B4820BF247500C33877 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6EDD3B4920BF247500C33877 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; @@ -274,7 +287,7 @@ 3B843E4A1F3930A400548890 /* remote_store_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = remote_store_spec_test.json; sourceTree = "<group>"; }; 3C81DE3772628FE297055662 /* Pods-Firestore_Example_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.debug.xcconfig"; sourceTree = "<group>"; }; 3F0992A4B83C60841C52E960 /* Pods-Firestore_Example_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS/Pods-Firestore_Example_iOS.release.xcconfig"; sourceTree = "<group>"; }; - 403DBF6EFB541DFD01582AA3 /* path_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = path_test.cc; sourceTree = "<group>"; }; + 403DBF6EFB541DFD01582AA3 /* path_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = path_test.cc; sourceTree = "<group>"; }; 444B7AB3F5A2929070CB1363 /* hard_assert_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = hard_assert_test.cc; sourceTree = "<group>"; }; 54131E9620ADE678001DF3FF /* string_format_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_format_test.cc; sourceTree = "<group>"; }; 54511E8D209805F8005BD28F /* hashing_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hashing_test.cc; sourceTree = "<group>"; }; @@ -411,6 +424,31 @@ 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; }; 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFirestoreSourceTests.mm; sourceTree = "<group>"; }; + 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = target.pb.cc; sourceTree = "<group>"; }; + 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = maybe_document.pb.cc; sourceTree = "<group>"; }; + 618BBE7F20B89AAC00B5BCE7 /* target.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = target.pb.h; sourceTree = "<group>"; }; + 618BBE8020B89AAC00B5BCE7 /* maybe_document.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = maybe_document.pb.h; sourceTree = "<group>"; }; + 618BBE8120B89AAC00B5BCE7 /* mutation.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mutation.pb.h; sourceTree = "<group>"; }; + 618BBE8220B89AAC00B5BCE7 /* mutation.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mutation.pb.cc; sourceTree = "<group>"; }; + 618BBE8620B89AAC00B5BCE7 /* query.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = query.pb.h; sourceTree = "<group>"; }; + 618BBE8720B89AAC00B5BCE7 /* common.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.pb.h; sourceTree = "<group>"; }; + 618BBE8820B89AAC00B5BCE7 /* common.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = common.pb.cc; sourceTree = "<group>"; }; + 618BBE8920B89AAC00B5BCE7 /* firestore.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = firestore.pb.h; sourceTree = "<group>"; }; + 618BBE8A20B89AAC00B5BCE7 /* firestore.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = firestore.pb.cc; sourceTree = "<group>"; }; + 618BBE8B20B89AAC00B5BCE7 /* write.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = write.pb.h; sourceTree = "<group>"; }; + 618BBE8C20B89AAC00B5BCE7 /* query.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = query.pb.cc; sourceTree = "<group>"; }; + 618BBE8D20B89AAC00B5BCE7 /* document.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = document.pb.h; sourceTree = "<group>"; }; + 618BBE8E20B89AAC00B5BCE7 /* document.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = document.pb.cc; sourceTree = "<group>"; }; + 618BBE8F20B89AAC00B5BCE7 /* write.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = write.pb.cc; sourceTree = "<group>"; }; + 618BBE9120B89AAC00B5BCE7 /* latlng.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = latlng.pb.h; sourceTree = "<group>"; }; + 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = latlng.pb.cc; sourceTree = "<group>"; }; + 618BBE9420B89AAC00B5BCE7 /* http.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http.pb.h; sourceTree = "<group>"; }; + 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = annotations.pb.cc; sourceTree = "<group>"; }; + 618BBE9620B89AAC00B5BCE7 /* annotations.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = annotations.pb.h; sourceTree = "<group>"; }; + 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = http.pb.cc; sourceTree = "<group>"; }; + 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = status.pb.cc; sourceTree = "<group>"; }; + 618BBE9A20B89AAC00B5BCE7 /* status.pb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = status.pb.h; sourceTree = "<group>"; }; + 61F72C5520BC48FD001A68CB /* serializer_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serializer_test.cc; sourceTree = "<group>"; }; 69E6C311558EC77729A16CF1 /* Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS/Pods-Firestore_Example_iOS-Firestore_SwiftTests_iOS.debug.xcconfig"; sourceTree = "<group>"; }; 6EDD3B5B20BF247500C33877 /* Firestore_FuzzTests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_FuzzTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 6EDD3B5C20BF247500C33877 /* Firestore_FuzzTests_iOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Firestore_FuzzTests_iOS-Info.plist"; sourceTree = "<group>"; }; @@ -567,6 +605,7 @@ isa = PBXGroup; children = ( 546854A820A36867004BDBD5 /* datastore_test.cc */, + 61F72C5520BC48FD001A68CB /* serializer_test.cc */, ); path = remote; sourceTree = "<group>"; @@ -665,6 +704,7 @@ 6003F581195388D10070C39A = { isa = PBXGroup; children = ( + 618BBE7A20B89AAC00B5BCE7 /* CoreTestsProtos */, 6EDD3B5D20BF24A700C33877 /* FuzzTests */, 543B4F0520A91E4B001F506D /* App */, 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, @@ -769,6 +809,102 @@ name = "Podspec Metadata"; sourceTree = "<group>"; }; + 618BBE7A20B89AAC00B5BCE7 /* CoreTestsProtos */ = { + isa = PBXGroup; + children = ( + 618BBE7B20B89AAC00B5BCE7 /* firestore */, + 618BBE8320B89AAC00B5BCE7 /* google */, + ); + name = CoreTestsProtos; + path = ../Protos/cpp; + sourceTree = "<group>"; + }; + 618BBE7B20B89AAC00B5BCE7 /* firestore */ = { + isa = PBXGroup; + children = ( + 618BBE7C20B89AAC00B5BCE7 /* local */, + ); + path = firestore; + sourceTree = "<group>"; + }; + 618BBE7C20B89AAC00B5BCE7 /* local */ = { + isa = PBXGroup; + children = ( + 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */, + 618BBE8020B89AAC00B5BCE7 /* maybe_document.pb.h */, + 618BBE8220B89AAC00B5BCE7 /* mutation.pb.cc */, + 618BBE8120B89AAC00B5BCE7 /* mutation.pb.h */, + 618BBE7D20B89AAC00B5BCE7 /* target.pb.cc */, + 618BBE7F20B89AAC00B5BCE7 /* target.pb.h */, + ); + path = local; + sourceTree = "<group>"; + }; + 618BBE8320B89AAC00B5BCE7 /* google */ = { + isa = PBXGroup; + children = ( + 618BBE9320B89AAC00B5BCE7 /* api */, + 618BBE8420B89AAC00B5BCE7 /* firestore */, + 618BBE9820B89AAC00B5BCE7 /* rpc */, + 618BBE9020B89AAC00B5BCE7 /* type */, + ); + path = google; + sourceTree = "<group>"; + }; + 618BBE8420B89AAC00B5BCE7 /* firestore */ = { + isa = PBXGroup; + children = ( + 618BBE8520B89AAC00B5BCE7 /* v1beta1 */, + ); + path = firestore; + sourceTree = "<group>"; + }; + 618BBE8520B89AAC00B5BCE7 /* v1beta1 */ = { + isa = PBXGroup; + children = ( + 618BBE8820B89AAC00B5BCE7 /* common.pb.cc */, + 618BBE8720B89AAC00B5BCE7 /* common.pb.h */, + 618BBE8E20B89AAC00B5BCE7 /* document.pb.cc */, + 618BBE8D20B89AAC00B5BCE7 /* document.pb.h */, + 618BBE8A20B89AAC00B5BCE7 /* firestore.pb.cc */, + 618BBE8920B89AAC00B5BCE7 /* firestore.pb.h */, + 618BBE8C20B89AAC00B5BCE7 /* query.pb.cc */, + 618BBE8620B89AAC00B5BCE7 /* query.pb.h */, + 618BBE8F20B89AAC00B5BCE7 /* write.pb.cc */, + 618BBE8B20B89AAC00B5BCE7 /* write.pb.h */, + ); + path = v1beta1; + sourceTree = "<group>"; + }; + 618BBE9020B89AAC00B5BCE7 /* type */ = { + isa = PBXGroup; + children = ( + 618BBE9220B89AAC00B5BCE7 /* latlng.pb.cc */, + 618BBE9120B89AAC00B5BCE7 /* latlng.pb.h */, + ); + path = type; + sourceTree = "<group>"; + }; + 618BBE9320B89AAC00B5BCE7 /* api */ = { + isa = PBXGroup; + children = ( + 618BBE9520B89AAC00B5BCE7 /* annotations.pb.cc */, + 618BBE9620B89AAC00B5BCE7 /* annotations.pb.h */, + 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */, + 618BBE9420B89AAC00B5BCE7 /* http.pb.h */, + ); + path = api; + sourceTree = "<group>"; + }; + 618BBE9820B89AAC00B5BCE7 /* rpc */ = { + isa = PBXGroup; + children = ( + 618BBE9920B89AAC00B5BCE7 /* status.pb.cc */, + 618BBE9A20B89AAC00B5BCE7 /* status.pb.h */, + ); + path = rpc; + sourceTree = "<group>"; + }; 6EDD3B5D20BF24A700C33877 /* FuzzTests */ = { isa = PBXGroup; children = ( @@ -1360,12 +1496,14 @@ "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", "${BUILT_PRODUCTS_DIR}/GoogleTest/GoogleTest.framework", "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", + "${BUILT_PRODUCTS_DIR}/ProtobufCpp/ProtobufCpp.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleTest.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ProtobufCpp.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1639,6 +1777,7 @@ 5492E0CA2021557E00B64F25 /* FSTWatchChangeTests.mm in Sources */, 5492E0AB2021552D00B64F25 /* StringViewTests.mm in Sources */, 5492E03C2021401F00B64F25 /* XCTestCase+Await.mm in Sources */, + 618BBEAF20B89AAC00B5BCE7 /* annotations.pb.cc in Sources */, 5467FB08203E6A44009C9584 /* app_testing.mm in Sources */, 54EB764D202277B30088B8F3 /* array_sorted_map_test.cc in Sources */, B6FB4684208EA0EC00554BA2 /* async_queue_libdispatch_test.mm in Sources */, @@ -1646,11 +1785,13 @@ B6FB467D208E9D3C00554BA2 /* async_queue_test.cc in Sources */, 54740A581FC914F000713A1A /* autoid_test.cc in Sources */, AB380D02201BC69F00D97691 /* bits_test.cc in Sources */, + 618BBEA920B89AAC00B5BCE7 /* common.pb.cc in Sources */, 548DB929200D59F600E00ABC /* comparison_test.cc in Sources */, ABC1D7DC2023A04B00BA84F0 /* credentials_provider_test.cc in Sources */, ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */, AB38D93020236E21000A432D /* database_info_test.cc in Sources */, 546854AA20A36867004BDBD5 /* datastore_test.cc in Sources */, + 618BBEAC20B89AAC00B5BCE7 /* document.pb.cc in Sources */, B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */, AB6B908420322E4D00CC290A /* document_test.cc in Sources */, ABC1D7DD2023A04F00BA84F0 /* empty_credentials_provider_test.cc in Sources */, @@ -1662,28 +1803,37 @@ 54A0352620A3AED0003E0143 /* field_transform_test.mm in Sources */, AB356EF7200EA5EB0089B766 /* field_value_test.cc in Sources */, ABC1D7E42024AFDE00BA84F0 /* firebase_credentials_provider_test.mm in Sources */, + 618BBEAA20B89AAC00B5BCE7 /* firestore.pb.cc in Sources */, AB7BAB342012B519001E0872 /* geo_point_test.cc in Sources */, 73FE5066020EF9B2892C86BF /* hard_assert_test.cc in Sources */, 54511E8E209805F8005BD28F /* hashing_test.cc in Sources */, + 618BBEB020B89AAC00B5BCE7 /* http.pb.cc in Sources */, 54A0353520A3D8CB003E0143 /* iterator_adaptors_test.cc in Sources */, + 618BBEAE20B89AAC00B5BCE7 /* latlng.pb.cc in Sources */, 54995F6F205B6E12004EFFA0 /* leveldb_key_test.cc in Sources */, 54C2294F1FECABAE007D065B /* log_test.cc in Sources */, + 618BBEA720B89AAC00B5BCE7 /* maybe_document.pb.cc in Sources */, AB6B908620322E6D00CC290A /* maybe_document_test.cc in Sources */, + 618BBEA820B89AAC00B5BCE7 /* mutation.pb.cc in Sources */, AB6B908820322E8800CC290A /* no_document_test.cc in Sources */, AB380D04201BC6E400D97691 /* ordered_code_test.cc in Sources */, 5A080105CCBFDB6BF3F3772D /* path_test.cc in Sources */, 549CCA5920A36E1F00BCEB75 /* precondition_test.cc in Sources */, + 618BBEAB20B89AAC00B5BCE7 /* query.pb.cc in Sources */, B686F2B22025000D0028D6BE /* resource_path_test.cc in Sources */, 54740A571FC914BA00713A1A /* secure_random_test.cc in Sources */, + 61F72C5620BC48FD001A68CB /* serializer_test.cc in Sources */, ABA495BB202B7E80008A7851 /* snapshot_version_test.cc in Sources */, 549CCA5220A36DBC00BCEB75 /* sorted_map_test.cc in Sources */, 549CCA5020A36DBC00BCEB75 /* sorted_set_test.cc in Sources */, + 618BBEB120B89AAC00B5BCE7 /* status.pb.cc in Sources */, 54A0352F20A3B3D8003E0143 /* status_test.cc in Sources */, 54A0353020A3B3D8003E0143 /* statusor_test.cc in Sources */, 1CAA9012B25F975D445D5978 /* strerror_test.cc in Sources */, 0535C1B65DADAE1CE47FA3CA /* string_format_apple_test.mm in Sources */, 54131E9720ADE679001DF3FF /* string_format_test.cc in Sources */, AB380CFE201A2F4500D97691 /* string_util_test.cc in Sources */, + 618BBEA620B89AAC00B5BCE7 /* target.pb.cc in Sources */, AB380CFB2019388600D97691 /* target_id_generator_test.cc in Sources */, 54A0352A20A3B3BD003E0143 /* testutil.cc in Sources */, ABF6506C201131F8005F2C74 /* timestamp_test.cc in Sources */, @@ -1692,6 +1842,7 @@ 549CCA5120A36DBC00BCEB75 /* tree_sorted_map_test.cc in Sources */, C80B10E79CDD7EF7843C321E /* type_traits_apple_test.mm in Sources */, ABC1D7DE2023A05300BA84F0 /* user_test.cc in Sources */, + 618BBEAD20B89AAC00B5BCE7 /* write.pb.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1924,6 +2075,7 @@ HEADER_SEARCH_PATHS = ""; IPHONEOS_DEPLOYMENT_TARGET = 8.0; ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ""; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -1957,6 +2109,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ""; IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_CFLAGS = ""; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -2036,10 +2189,61 @@ "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", "\"${PODS_ROOT}/GoogleTest/googletest/include\"", "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", + "\"${PODS_ROOT}/ProtobufCpp/src\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; + OTHER_CFLAGS = ( + "$(inherited)", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/GoogleTest/GoogleTest.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/OCMock/OCMock.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ProtobufCpp/ProtobufCpp.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers\"", + "$(inherited)", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL/openssl.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-ProtoRPC/ProtoRPC.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-RxLibrary/RxLibrary.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/GRPCClient.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers\"", + "-isystem", + "\"${PODS_ROOT}/Headers/Public\"", + "-isystem", + "\"${PODS_ROOT}/Headers/Public/Firebase\"", + "-isystem", + "\"${PODS_ROOT}/Headers/Public/FirebaseAnalytics\"", + "-isystem", + "\"${PODS_ROOT}/Headers/Public/FirebaseInstanceID\"", + "-DPB_FIELD_32BIT", + "-DPB_NO_PACKED_STRUCTS=1", + ); PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; + SYSTEM_HEADER_SEARCH_PATHS = "\"${PODS_ROOT}/nanopb\""; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_iOS.app/Firestore_Example_iOS"; WRAPPER_EXTENSION = xctest; }; @@ -2070,10 +2274,61 @@ "\"${PODS_ROOT}/GoogleTest/googlemock/include\"", "\"${PODS_ROOT}/GoogleTest/googletest/include\"", "\"${PODS_ROOT}/leveldb-library/include\"", + "\"${PODS_ROOT}/../../../Firestore/Protos/cpp\"", + "\"${PODS_ROOT}/ProtobufCpp/src\"", ); INFOPLIST_FILE = "Tests/Tests-Info.plist"; + OTHER_CFLAGS = ( + "$(inherited)", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/GoogleTest/GoogleTest.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/OCMock/OCMock.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/ProtobufCpp/ProtobufCpp.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers\"", + "$(inherited)", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL/openssl.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseAuth/FirebaseAuth.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseFirestore/FirebaseFirestore.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core/grpc.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-ProtoRPC/ProtoRPC.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-RxLibrary/RxLibrary.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC/GRPCClient.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/leveldb-library/leveldb.framework/Headers\"", + "-iquote", + "\"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb.framework/Headers\"", + "-isystem", + "\"${PODS_ROOT}/Headers/Public\"", + "-isystem", + "\"${PODS_ROOT}/Headers/Public/Firebase\"", + "-isystem", + "\"${PODS_ROOT}/Headers/Public/FirebaseAnalytics\"", + "-isystem", + "\"${PODS_ROOT}/Headers/Public/FirebaseInstanceID\"", + "-DPB_FIELD_32BIT", + "-DPB_NO_PACKED_STRUCTS=1", + ); PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; + SYSTEM_HEADER_SEARCH_PATHS = "\"${PODS_ROOT}/nanopb\""; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Firestore_Example_iOS.app/Firestore_Example_iOS"; WRAPPER_EXTENSION = xctest; }; diff --git a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_FuzzTests_iOS.xcscheme b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_FuzzTests_iOS.xcscheme index 039273b..051b1a4 100644 --- a/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_FuzzTests_iOS.xcscheme +++ b/Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_FuzzTests_iOS.xcscheme @@ -5,6 +5,19 @@ <BuildAction parallelizeBuildables = "YES" buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForRunning = "YES" + buildForTesting = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "6EDD3AD120BF247500C33877" + BuildableName = "Firestore_FuzzTests_iOS.xctest" + BlueprintName = "Firestore_FuzzTests_iOS" + ReferencedContainer = "container:Firestore.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> </BuildAction> <TestAction buildConfiguration = "Debug" diff --git a/Firestore/Example/Podfile b/Firestore/Example/Podfile index 8439260..881747a 100644 --- a/Firestore/Example/Podfile +++ b/Firestore/Example/Podfile @@ -22,6 +22,7 @@ target 'Firestore_Example_iOS' do pod 'leveldb-library' pod 'OCMock' pod 'GoogleTest', :podspec => 'GoogleTest.podspec' + pod 'ProtobufCpp', :podspec => 'ProtobufCpp.podspec' end target 'Firestore_IntegrationTests_iOS' do diff --git a/Firestore/Example/ProtobufCpp.podspec b/Firestore/Example/ProtobufCpp.podspec new file mode 100644 index 0000000..c809c06 --- /dev/null +++ b/Firestore/Example/ProtobufCpp.podspec @@ -0,0 +1,74 @@ +# Copyright 2018 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. + +# A Private podspec for Protobuf which exposes the C++ headers (rather than +# only the Obj-C headers). Suitable only for use inside this source tree. + +Pod::Spec.new do |s| + s.name = 'ProtobufCpp' + s.version = '3.5.2' + s.summary = 'Protocol Buffers v.3 runtime library for C++.' + s.homepage = 'https://github.com/google/protobuf' + s.license = '3-Clause BSD License' + s.authors = { 'The Protocol Buffers contributors' => 'protobuf@googlegroups.com' } + s.cocoapods_version = '>= 1.0' + + s.source = { + :git => 'https://github.com/google/protobuf.git', + :tag => "v#{s.version}" + } + + s.source_files = 'src/**/*.{h,cc}' + s.exclude_files = # skip test files. (Yes, the test files are intermixed with + # the source. No there doesn't seem to be a common/simple + # pattern we could use to exclude them; 'test' appears in + # various places throughout the file names and also in a + # non-test file. So, we'll exclude all files that either + # start with 'test' or include test and have a previous + # character that isn't "y" (so that bytestream isn't + # matched.)) + 'src/**/test*.*', + 'src/**/*[^y]test*.*', + 'src/**/testing/**', + 'src/**/mock*', + # skip the javascript handling code. + 'src/**/js/**', + # skip the protoc compiler + 'src/google/protobuf/compiler/**/*' + + s.header_mappings_dir = 'src/' + + # Set a CPP symbol so the code knows to use framework imports. + s.pod_target_xcconfig = { + 'GCC_PREPROCESSOR_DEFINITIONS' => + '$(inherited) ' + + 'GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1 ' + + 'HAVE_PTHREAD=1', + 'HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/ProtobufCpp/src"', + + # Cocoapods flattens header imports, leading to much anguish. The + # following two statements work around this. + # - https://github.com/CocoaPods/CocoaPods/issues/1437 + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } + + # Disable warnings that upstream does not concern itself with + s.compiler_flags = '$(inherited) ' + + '-Wno-comma ' + + '-Wno-shorten-64-to-32' + + s.requires_arc = false + s.library = 'c++' +end diff --git a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm index 3da8083..e6ab720 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBMigrationsTests.mm @@ -42,7 +42,7 @@ using leveldb::Status; @end @implementation FSTLevelDBMigrationsTests { - std::shared_ptr<DB> _db; + std::unique_ptr<DB> _db; } - (void)setUp { @@ -62,12 +62,12 @@ using leveldb::Status; } - (void)testAddsTargetGlobal { - FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; + FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db.get()]; XCTAssertNil(metadata, @"Not expecting metadata yet, we should have an empty db"); LevelDbTransaction transaction(_db.get(), "testAddsTargetGlobal"); [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; transaction.Commit(); - metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; + metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db.get()]; XCTAssertNotNil(metadata, @"Migrations should have added the metadata"); } @@ -107,7 +107,7 @@ using leveldb::Status; LevelDbTransaction transaction(_db.get(), "testCountsQueries"); [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; transaction.Commit(); - FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db]; + FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db.get()]; XCTAssertEqual(expected, metadata.targetCount, @"Failed to count all of the targets we added"); } } diff --git a/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm b/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm index 29f5d6c..5c2718b 100644 --- a/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm +++ b/Firestore/Example/Tests/Local/FSTLevelDBTransactionTests.mm @@ -46,7 +46,7 @@ using firebase::firestore::local::LevelDbTransaction; @end @implementation FSTLevelDBTransactionTests { - std::shared_ptr<DB> _db; + std::unique_ptr<DB> _db; } - (void)setUp { diff --git a/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm b/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm index c6936f7..715f09b 100644 --- a/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm +++ b/Firestore/Example/Tests/Remote/FSTRemoteEventTests.mm @@ -93,8 +93,6 @@ NS_ASSUME_NONNULL_BEGIN * Creates an aggregator initialized with the set of provided FSTWatchChanges. Tests can add further * changes via `handleDocumentChange`, `handleTargetChange` and `handleExistenceFilterChange`. * - * @param snapshotVersion The version at which to create the remote event. This corresponds to the - * snapshot version provided by a NO_CHANGE event. * @param targetMap A map of query data for all active targets. The map must include an entry for * every target referenced by any of the watch changes. * @param outstandingResponses The number of outstanding ACKs a target has to receive before it is diff --git a/Firestore/Source/Core/FSTSyncEngine.mm b/Firestore/Source/Core/FSTSyncEngine.mm index 89cb774..bf7b053 100644 --- a/Firestore/Source/Core/FSTSyncEngine.mm +++ b/Firestore/Source/Core/FSTSyncEngine.mm @@ -28,7 +28,6 @@ #import "Firestore/Source/Core/FSTTransaction.h" #import "Firestore/Source/Core/FSTView.h" #import "Firestore/Source/Core/FSTViewSnapshot.h" -#import "Firestore/Source/Local/FSTEagerGarbageCollector.h" #import "Firestore/Source/Local/FSTLocalStore.h" #import "Firestore/Source/Local/FSTLocalViewChanges.h" #import "Firestore/Source/Local/FSTLocalWriteResult.h" @@ -136,9 +135,6 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; /** Used to track any documents that are currently in limbo. */ @property(nonatomic, strong, readonly) FSTReferenceSet *limboDocumentRefs; -/** The garbage collector used to collect documents that are no longer in limbo. */ -@property(nonatomic, strong, readonly) FSTEagerGarbageCollector *limboCollector; - @end @implementation FSTSyncEngine { @@ -171,10 +167,7 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; _queryViewsByQuery = [NSMutableDictionary dictionary]; _queryViewsByTarget = [NSMutableDictionary dictionary]; - _limboCollector = [[FSTEagerGarbageCollector alloc] init]; _limboDocumentRefs = [[FSTReferenceSet alloc] init]; - [_limboCollector addGarbageSource:_limboDocumentRefs]; - _targetIdGenerator = TargetIdGenerator::SyncEngineTargetIdGenerator(0); _currentUser = initialUser; } @@ -399,8 +392,14 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; [self.queryViewsByQuery removeObjectForKey:queryView.query]; [self.queryViewsByTarget removeObjectForKey:@(queryView.targetID)]; + DocumentKeySet limboKeys = [self.limboDocumentRefs referencedKeysForID:queryView.targetID]; [self.limboDocumentRefs removeReferencesForID:queryView.targetID]; - [self garbageCollectLimboDocuments]; + for (const DocumentKey &key : limboKeys) { + if (![self.limboDocumentRefs containsKey:key]) { + // We removed the last reference for this key. + [self removeLimboTargetForKey:key]; + } + } } /** @@ -462,13 +461,16 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; case FSTLimboDocumentChangeTypeRemoved: LOG_DEBUG("Document no longer in limbo: %s", limboChange.key.ToString()); [self.limboDocumentRefs removeReferenceToKey:limboChange.key forID:targetID]; + if (![self.limboDocumentRefs containsKey:limboChange.key]) { + // We removed the last reference for this key + [self removeLimboTargetForKey:limboChange.key]; + } break; default: HARD_FAIL("Unknown limbo change type: %s", limboChange.type); } } - [self garbageCollectLimboDocuments]; } - (void)trackLimboChange:(FSTLimboDocumentChange *)limboChange { @@ -488,20 +490,16 @@ static const FSTListenSequenceNumber kIrrelevantSequenceNumber = -1; } } -/** Garbage collect the limbo documents that we no longer need to track. */ -- (void)garbageCollectLimboDocuments { - const std::set<DocumentKey> garbage = [self.limboCollector collectGarbage]; - for (const DocumentKey &key : garbage) { - const auto iter = _limboTargetsByKey.find(key); - if (iter == _limboTargetsByKey.end()) { - // This target already got removed, because the query failed. - return; - } - TargetId limboTargetID = iter->second; - [self.remoteStore stopListeningToTargetID:limboTargetID]; - _limboTargetsByKey.erase(key); - _limboKeysByTarget.erase(limboTargetID); +- (void)removeLimboTargetForKey:(const DocumentKey &)key { + const auto iter = _limboTargetsByKey.find(key); + if (iter == _limboTargetsByKey.end()) { + // This target already got removed, because the query failed. + return; } + TargetId limboTargetID = iter->second; + [self.remoteStore stopListeningToTargetID:limboTargetID]; + _limboTargetsByKey.erase(key); + _limboKeysByTarget.erase(limboTargetID); } // Used for testing diff --git a/Firestore/Source/Local/FSTLevelDB.h b/Firestore/Source/Local/FSTLevelDB.h index 95b80a6..a56c133 100644 --- a/Firestore/Source/Local/FSTLevelDB.h +++ b/Firestore/Source/Local/FSTLevelDB.h @@ -95,7 +95,7 @@ NS_ASSUME_NONNULL_BEGIN + (NSString *)descriptionOfStatus:(leveldb::Status)status; /** The native db pointer, allocated during start. */ -@property(nonatomic, assign, readonly) std::shared_ptr<leveldb::DB> ptr; +@property(nonatomic, assign, readonly) leveldb::DB *ptr; @property(nonatomic, readonly) firebase::firestore::local::LevelDbTransaction *currentTransaction; diff --git a/Firestore/Source/Local/FSTLevelDB.mm b/Firestore/Source/Local/FSTLevelDB.mm index 9f75a3e..9dc50a2 100644 --- a/Firestore/Source/Local/FSTLevelDB.mm +++ b/Firestore/Source/Local/FSTLevelDB.mm @@ -17,6 +17,7 @@ #import "Firestore/Source/Local/FSTLevelDB.h" #include <memory> +#include <utility> #import "FIRFirestoreErrors.h" #import "Firestore/Source/Local/FSTLevelDBMigrations.h" @@ -60,6 +61,7 @@ using leveldb::WriteOptions; @implementation FSTLevelDB { std::unique_ptr<LevelDbTransaction> _transaction; + std::unique_ptr<leveldb::DB> _ptr; FSTTransactionRunner _transactionRunner; } @@ -82,6 +84,10 @@ using leveldb::WriteOptions; return self; } +- (leveldb::DB *)ptr { + return _ptr.get(); +} + - (const FSTTransactionRunner &)run { return _transactionRunner; } diff --git a/Firestore/Source/Local/FSTLevelDBMutationQueue.h b/Firestore/Source/Local/FSTLevelDBMutationQueue.h index 034738f..911fa37 100644 --- a/Firestore/Source/Local/FSTLevelDBMutationQueue.h +++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.h @@ -51,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN * Returns one larger than the largest batch ID that has been stored. If there are no mutations * returns 0. Note that batch IDs are global. */ -+ (FSTBatchID)loadNextBatchIDFromDB:(std::shared_ptr<leveldb::DB>)db; ++ (FSTBatchID)loadNextBatchIDFromDB:(leveldb::DB *)db; @end diff --git a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm index 3b4687c..94ab8a5 100644 --- a/Firestore/Source/Local/FSTLevelDBMutationQueue.mm +++ b/Firestore/Source/Local/FSTLevelDBMutationQueue.mm @@ -132,7 +132,7 @@ using leveldb::WriteOptions; self.metadata = metadata; } -+ (FSTBatchID)loadNextBatchIDFromDB:(std::shared_ptr<DB>)db { ++ (FSTBatchID)loadNextBatchIDFromDB:(DB *)db { // TODO(gsoltis): implement Prev() and SeekToLast() on LevelDbTransaction::Iterator, then port // this to a transaction. std::unique_ptr<Iterator> it(db->NewIterator(LevelDbTransaction::DefaultReadOptions())); diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.h b/Firestore/Source/Local/FSTLevelDBQueryCache.h index 2cd6758..f756d9e 100644 --- a/Firestore/Source/Local/FSTLevelDBQueryCache.h +++ b/Firestore/Source/Local/FSTLevelDBQueryCache.h @@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN * Retrieves the global singleton metadata row from the given database, if it exists. * TODO(gsoltis): remove this method once fully ported to transactions. */ -+ (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(std::shared_ptr<leveldb::DB>)db; ++ (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(leveldb::DB *)db; /** * Retrieves the global singleton metadata row using the given transaction, if it exists. diff --git a/Firestore/Source/Local/FSTLevelDBQueryCache.mm b/Firestore/Source/Local/FSTLevelDBQueryCache.mm index f28370a..31c2a2e 100644 --- a/Firestore/Source/Local/FSTLevelDBQueryCache.mm +++ b/Firestore/Source/Local/FSTLevelDBQueryCache.mm @@ -85,7 +85,7 @@ using firebase::firestore::model::DocumentKeySet; return proto; } -+ (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(std::shared_ptr<DB>)db { ++ (nullable FSTPBTargetGlobal *)readTargetMetadataFromDB:(DB *)db { std::string key = [FSTLevelDBTargetGlobalKey key]; std::string value; Status status = db->Get([FSTLevelDB standardReadOptions], key, &value); diff --git a/Firestore/Source/Remote/FSTDatastore.mm b/Firestore/Source/Remote/FSTDatastore.mm index fdbeea3..33d29e9 100644 --- a/Firestore/Source/Remote/FSTDatastore.mm +++ b/Firestore/Source/Remote/FSTDatastore.mm @@ -240,7 +240,7 @@ typedef GRPCProtoCall * (^RPCFactory)(void); error = [FSTDatastore firestoreErrorForError:error]; [self.workerDispatchQueue dispatchAsync:^{ if (error != nil && error.code == FIRFirestoreErrorCodeUnauthenticated) { - _credentials->InvalidateToken(); + self->_credentials->InvalidateToken(); } LOG_DEBUG("RPC CommitRequest completed. Error: %s", error); [FSTDatastore logHeadersForRPC:rpc RPCName:@"CommitRequest"]; @@ -277,7 +277,7 @@ typedef GRPCProtoCall * (^RPCFactory)(void); if (error) { LOG_DEBUG("RPC BatchGetDocuments completed. Error: %s", error); if (error.code == FIRFirestoreErrorCodeUnauthenticated) { - _credentials->InvalidateToken(); + self->_credentials->InvalidateToken(); } [FSTDatastore logHeadersForRPC:rpc RPCName:@"BatchGetDocuments"]; completion(nil, error); diff --git a/Firestore/Source/Remote/FSTRemoteEvent.h b/Firestore/Source/Remote/FSTRemoteEvent.h index 9ea0f9c..02a5289 100644 --- a/Firestore/Source/Remote/FSTRemoteEvent.h +++ b/Firestore/Source/Remote/FSTRemoteEvent.h @@ -176,6 +176,9 @@ initWithSnapshotVersion:(firebase::firestore::model::SnapshotVersion)snapshotVer /** Processes and adds the WatchTargetChange to the current set of changes. */ - (void)handleTargetChange:(FSTWatchTargetChange *)targetChange; +/** Removes the in-memory state for the provided target. */ +- (void)removeTarget:(FSTTargetID)targetID; + /** * Handles existence filters and synthesizes deletes for filter mismatches. Targets that are * invalidated by filter mismatches are added to `targetMismatches`. diff --git a/Firestore/Source/Remote/FSTRemoteStore.mm b/Firestore/Source/Remote/FSTRemoteStore.mm index 54d00c4..4309c74 100644 --- a/Firestore/Source/Remote/FSTRemoteStore.mm +++ b/Firestore/Source/Remote/FSTRemoteStore.mm @@ -405,8 +405,10 @@ static const int kMaxPendingWrites = 10; // Ignore targets that have been removed already. for (FSTBoxedTargetID *targetID in change.targetIDs) { if (self.listenTargets[targetID]) { + int unboxedTargetId = targetID.intValue; [self.listenTargets removeObjectForKey:targetID]; - [self.syncEngine rejectListenWithTargetID:[targetID intValue] error:change.cause]; + [self.watchChangeAggregator removeTarget:unboxedTargetId]; + [self.syncEngine rejectListenWithTargetID:unboxedTargetId error:change.cause]; } } } diff --git a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt index 089d03c..4d39c3d 100644 --- a/Firestore/core/src/firebase/firestore/local/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/local/CMakeLists.txt @@ -17,6 +17,8 @@ cc_library( SOURCES leveldb_key.h leveldb_key.cc + leveldb_transaction.h + leveldb_transaction.cc DEPENDS LevelDB::LevelDB absl_strings diff --git a/Firestore/core/src/firebase/firestore/nanopb/reader.cc b/Firestore/core/src/firebase/firestore/nanopb/reader.cc index 69e3d83..3b102f0 100644 --- a/Firestore/core/src/firebase/firestore/nanopb/reader.cc +++ b/Firestore/core/src/firebase/firestore/nanopb/reader.cc @@ -110,14 +110,13 @@ std::string Reader::ReadString() { pb_istream_t substream; if (!pb_make_string_substream(&stream_, &substream)) { status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); - pb_close_string_substream(&stream_, &substream); return ""; } std::string result(substream.bytes_left, '\0'); if (!pb_read(&substream, reinterpret_cast<pb_byte_t*>(&result[0]), substream.bytes_left)) { - status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); + status_ = Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&substream)); pb_close_string_substream(&stream_, &substream); return ""; } diff --git a/Firestore/core/src/firebase/firestore/nanopb/reader.h b/Firestore/core/src/firebase/firestore/nanopb/reader.h index 2c16ec4..7dd7432 100644 --- a/Firestore/core/src/firebase/firestore/nanopb/reader.h +++ b/Firestore/core/src/firebase/firestore/nanopb/reader.h @@ -150,7 +150,6 @@ T Reader::ReadNestedMessage(const std::function<T(Reader*)>& read_message_fn) { if (!pb_make_string_substream(&stream_, &raw_substream)) { status_ = util::Status(FirestoreErrorCode::DataLoss, PB_GET_ERROR(&stream_)); - pb_close_string_substream(&stream_, &raw_substream); return read_message_fn(this); } Reader substream(raw_substream); diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc index f3773b9..1125fb4 100644 --- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc @@ -656,7 +656,7 @@ TEST_F(SerializerTest, BadFieldValueTagWithOtherValidTagsPresent) { // Craft the bytes. boolean_value has a smaller tag, so it'll get encoded // first, normally implying integer_value should "win". Except that // integer_value isn't a valid tag, so it should be ignored here. - google_firestore_v1beta1_Value_Fake crafty_value{false, int64_t{42}}; + google_firestore_v1beta1_Value_Fake crafty_value{true, int64_t{42}}; std::vector<uint8_t> bytes(128); pb_ostream_t stream = pb_ostream_from_buffer(bytes.data(), bytes.size()); pb_encode(&stream, google_firestore_v1beta1_Value_fields_Fake, &crafty_value); @@ -664,11 +664,12 @@ TEST_F(SerializerTest, BadFieldValueTagWithOtherValidTagsPresent) { // Decode the bytes into the model StatusOr<FieldValue> actual_model_status = serializer.DecodeFieldValue(bytes); + Status s = actual_model_status.status(); EXPECT_OK(actual_model_status); FieldValue actual_model = actual_model_status.ValueOrDie(); // Ensure the decoded model is as expected. - FieldValue expected_model = FieldValue::BooleanValue(false); + FieldValue expected_model = FieldValue::BooleanValue(true); EXPECT_EQ(FieldValue::Type::Boolean, actual_model.type()); EXPECT_EQ(expected_model, actual_model); } diff --git a/Releases/Manifests/5.3.0.json b/Releases/Manifests/5.3.0.json new file mode 100644 index 0000000..ae18cfa --- /dev/null +++ b/Releases/Manifests/5.3.0.json @@ -0,0 +1,4 @@ +{ + "FirebaseCore":"5.0.4", + "FirebaseFirestore":"0.12.4" +}
\ No newline at end of file diff --git a/Releases/Manifests/README b/Releases/Manifests/README new file mode 100644 index 0000000..0c7a631 --- /dev/null +++ b/Releases/Manifests/README @@ -0,0 +1,3 @@ +Each file in this directory corresponds with a Firebase release. The file +includes a list of the podspecs and versions from this repo that updated in the +Firebase release version indicated by the filename. diff --git a/Releases/update-versions.py b/Releases/update-versions.py new file mode 100755 index 0000000..681f044 --- /dev/null +++ b/Releases/update-versions.py @@ -0,0 +1,249 @@ +#!/usr/bin/python + +# Copyright 2018 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. + +"""update-versions.py creates a release branch and commit with version updates. + +With the required --version parameter, this script will update all files in +the repo based on the versions in Releases/Manifests/{version}.json. + +It will create a release branch, push and tag the updates, and push the +updated podspecs to cpdc-internal. +""" + +import argparse +import json +import os +import subprocess +import sys +import tempfile + +test_mode = False # Flag to disable external repo updates + + +def SetupArguments(): + """SetupArguments sets up the set of command-line arguments. + + Returns: + Args: The set of command line arguments + """ + parser = argparse.ArgumentParser(description='Update Pod Versions') + + parser.add_argument('--version', required=True, help='Firebase version') + + parser.add_argument( + '--test_mode', + dest='test_mode', + action='store_true', + help='Log commands instead of updating public repo') + + parser.add_argument( + '--tag_update', + dest='tag_update', + action='store_true', + help='Update the tags only') + + args = parser.parse_args() + return args + + +def LogOrRun(command): + """Log or run a command depending on test_mode value. + + Args: + command: command to log or run. + """ + if test_mode: + print 'Log only: {}'.format(command) + else: + os.system(command) + + +def GetVersionData(git_root, version): + """Update version specifier in FIROptions.m. + + Args: + git_root: root of git checkout. + version: the next version to release. + Returns: + Dictionary with pod keys and version values. + """ + json_file = os.path.join(git_root, 'Releases', 'Manifests', + '{}.json'.format(version)) + if os.path.isfile(json_file): + return json.load(open(json_file)) + else: + sys.exit('Missing version file:{}'.format(json_file)) + + +def CreateReleaseBranch(release_branch): + """Create and push the release branch. + + Args: + release_branch: the name of the git release branch. + """ + os.system('git checkout master') + os.system('git pull') + os.system('git checkout -b {}'.format(release_branch)) + LogOrRun('git push origin {}'.format(release_branch)) + LogOrRun('git branch --set-upstream-to=origin/{} {}'.format(release_branch, + release_branch)) + + +def UpdateFIROptions(git_root, version_data): + """Update version specifier in FIROptions.m. + + Args: + git_root: root of git checkout. + version_data: dictionary of versions to be updated. + """ + core_version = version_data['FirebaseCore'] + major, minor, patch = core_version.split('.') + path = os.path.join(git_root, 'Firebase', 'Core', 'FIROptions.m') + os.system("sed -E -i.bak 's/[[:digit:]]+\"[[:space:]]*\\/\\/ Major/" + "{}\" \\/\\/ Major/' {}".format(major, path)) + os.system("sed -E -i.bak 's/[[:digit:]]+\"[[:space:]]*\\/\\/ Minor/" + "{}\" \\/\\/ Minor/' {}".format(minor.zfill(2), path)) + os.system("sed -E -i.bak 's/[[:digit:]]+\"[[:space:]]*\\/\\/ Build/" + "{}\" \\/\\/ Build/' {}".format(patch.zfill(2), path)) + + +def UpdatePodSpecs(git_root, version_data, firebase_version): + """Update the podspecs with the right version. + + Args: + git_root: root of git checkout. + version_data: dictionary of versions to be updated. + firebase_version: the Firebase version. + """ + core_podspec = os.path.join(git_root, 'FirebaseCore.podspec') + os.system("sed -i.bak -e \"s/\\(Firebase_VERSION=\\).*'/\\1{}'/\" {}".format( + firebase_version, core_podspec)) + for pod, version in version_data.items(): + podspec = os.path.join(git_root, '{}.podspec'.format(pod)) + os.system("sed -i.bak -e \"s/\\(\\.version.*=[[:space:]]*'\\).*'/\\1{}'/\" " + '{}'.format(version, podspec)) + + +def UpdatePodfiles(git_root, version): + """Update Podfile's to reference the latest Firebase pod. + + Args: + git_root: root of git checkout. + version: the next Firebase version to release. + """ + firebase_podfile = os.path.join(git_root, 'Example', 'Podfile') + firestore_podfile = os.path.join(git_root, 'Firestore', 'Example', 'Podfile') + + sed_command = ("sed -i.bak -e \"s#\\(pod " + "'Firebase/Core',[[:space:]]*'\\).*'#\\1{}'#\" {}") + os.system(sed_command.format(version, firebase_podfile)) + os.system(sed_command.format(version, firestore_podfile)) + + +def UpdateTags(version_data, firebase_version, first=False): + """Update tags. + + Args: + version_data: dictionary of versions to be updated. + firebase_version: the Firebase version. + first: set to true the first time the versions are set. + """ + if not first: + LogOrRun("git push --delete origin '{}'".format(firebase_version)) + LogOrRun("git tag --delete '{}'".format(firebase_version)) + LogOrRun("git tag '{}'".format(firebase_version)) + for pod, version in version_data.items(): + name = pod[len('Firebase'):] + tag = '{}-{}'.format(name, version) + if not first: + LogOrRun("git push --delete origin '{}'".format(tag)) + LogOrRun("git tag --delete '{}'".format(tag)) + LogOrRun("git tag '{}'".format(tag)) + LogOrRun('git push origin --tags') + + +def GetCpdcInternal(): + """Find the cpdc-internal repo. + +""" + tmp_file = tempfile.mktemp() + os.system('pod repo list | grep -B2 sso://cpdc-internal | head -1 > {}' + .format(tmp_file)) + with open(tmp_file,'r') as o: + output_var = ''.join(o.readlines()).strip() + os.system('rm -rf {}'.format(tmp_file)) + return output_var + + +def PushPodspecs(version_data): + """Push podspecs to cpdc-internal. + + Args: + version_data: dictionary of versions to be updated. + """ + pods = version_data.keys() + pods.insert(0, pods.pop(pods.index('FirebaseCore'))) # Core should be first + tmp_dir = tempfile.mkdtemp() + for pod in pods: + LogOrRun('pod cache clean {} --all'.format(pod)) + if pod == 'FirebaseFirestore': + warnings_ok = ' --allow-warnings' + else: + warnings_ok = '' + + podspec = '{}.podspec'.format(pod) + json = os.path.join(tmp_dir, '{}.json'.format(podspec)) + os.system('pod ipc spec {} > {}'.format(podspec, json)) + LogOrRun('pod repo push {} {}{}'.format(GetCpdcInternal(), json, + warnings_ok)) + os.system('rm -rf {}'.format(tmp_dir)) + + +def UpdateVersions(): + """UpdateVersions is the main body to create the branch and change versions. + """ + global test_mode + args = SetupArguments() + test_mode = args.test_mode + # Validate version is proper format + major, minor, patch = args.version.split('.') + if (not major.isdigit()) or (not minor.isdigit()) or (not patch.isdigit()): + sys.exit('Invalid version parameter') + + git_root = subprocess.Popen( + ['git', 'rev-parse', '--show-toplevel'], + stdout=subprocess.PIPE).communicate()[0].rstrip().decode('utf-8') + + version_data = GetVersionData(git_root, args.version) + if args.tag_update: + UpdateTags(version_data, args.version) + return + + release_branch = 'release-{}'.format(args.version) + CreateReleaseBranch(release_branch) + UpdateFIROptions(git_root, version_data) + UpdatePodSpecs(git_root, version_data, args.version) + UpdatePodfiles(git_root, args.version) + + LogOrRun('git commit -am "Update versions for Release {}"' + .format(args.version)) + LogOrRun('git push origin {}'.format(release_branch)) + UpdateTags(version_data, args.version, True) + PushPodspecs(version_data) + + +if __name__ == '__main__': + UpdateVersions() diff --git a/scripts/check_test_inclusion.py b/scripts/check_test_inclusion.py index 7f5f354..7f6e43c 100755 --- a/scripts/check_test_inclusion.py +++ b/scripts/check_test_inclusion.py @@ -26,8 +26,6 @@ import sys # Tests that are known not to compile in Xcode and can't be added there. EXCLUDED = frozenset([ - # b/79496027 - "Firestore/core/test/firebase/firestore/remote/serializer_test.cc", ]) diff --git a/scripts/push-pods.sh b/scripts/push-pods.sh deleted file mode 100755 index 9f4c303..0000000 --- a/scripts/push-pods.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2018 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. - -# Push GitHub pods to cpdc-internal-spec. - -# When bootstrapping a repo, FirebaseCore must be pushed first, then -# FirebaseInstanceID, then FirebaseAnalytics, then the rest -# Most of the warnings are tvOS specific. The Firestore one needs -# investigation. - -pod cache clean FirebaseCore --all -#pod cache clean FirebaseAuth --all -#pod cache clean FirebaseDatabase --all -pod cache clean FirebaseFirestore --all -#pod cache clean FirebaseFunctions --all -#pod cache clean FirebaseMessaging --all -#pod cache clean FirebaseStorage --all - -pod repo push cpdc-internal-spec FirebaseCore.podspec -#pod repo push cpdc-internal-spec FirebaseAuth.podspec -#pod repo push cpdc-internal-spec FirebaseDatabase.podspec -pod repo push cpdc-internal-spec FirebaseFirestore.podspec --allow-warnings -#pod repo push cpdc-internal-spec FirebaseFunctions.podspec -#pod repo push cpdc-internal-spec FirebaseMessaging.podspec -#pod repo push cpdc-internal-spec FirebaseStorage.podspec - -# FirebaseFirestore warning (no plan to fix) -# https://github.com/firebase/firebase-ios-sdk/issues/1143 diff --git a/scripts/sync_project.rb b/scripts/sync_project.rb index e34ae31..ba9f98b 100755 --- a/scripts/sync_project.rb +++ b/scripts/sync_project.rb @@ -50,15 +50,13 @@ def sync_firestore() 'CMakeLists.txt', 'InfoPlist.strings', '*.plist', - - # b/79496027 - 'Firestore/core/test/firebase/firestore/remote/serializer_test.cc', ] # Folder groups in the Xcode project that contain tests. s.test_groups = [ 'Tests', 'CoreTests', + 'CoreTestsProtos', 'SwiftTests', ] @@ -66,6 +64,7 @@ def sync_firestore() t.source_files = [ 'Firestore/Example/Tests/**', 'Firestore/core/test/**', + 'Firestore/Protos/cpp/**', 'Firestore/third_party/Immutable/Tests/**', ] t.exclude_files = [ diff --git a/scripts/update-tags.sh b/scripts/update-tags.sh deleted file mode 100755 index 0e5bd8c..0000000 --- a/scripts/update-tags.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2018 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. - -# Update the tags for the Firebase 5.0.0 release process - -# Before running, make sure everything is pushed. - -# This script should be a spec for a more robust Python or Swift script that -# does the following. -# 1. Verify all files are committed -# 2. Verify running on a release branch -# 3. Read the versions from the podspec (or incorporate into even more -# automated version management) - -# Delete any existing tags at origin - -git push --delete origin '5.3.0' -git push --delete origin 'Core-5.0.4' -#git push --delete origin 'Auth-5.0.1' -#git push --delete origin 'Database-5.0.1' -git push --delete origin 'Firestore-0.12.4' -#git push --delete origin 'Functions-2.0.0' -#git push --delete origin 'Messaging-3.0.2' -#git push --delete origin 'Storage-3.0.0' - -# Delete local tags - -git tag --delete '5.3.0' -git tag --delete 'Core-5.0.4' -#git tag --delete 'Auth-5.0.1' -#git tag --delete 'Database-5.0.1' -git tag --delete 'Firestore-0.12.4' -#git tag --delete 'Functions-2.0.0' -#git tag --delete 'Messaging-3.0.2' -#git tag --delete 'Storage-3.0.0' - -# Add and push the tags - -git tag '5.3.0' -git tag 'Core-5.0.4' -#git tag 'Auth-5.0.1' -# git tag 'Database-5.0.1' -git tag 'Firestore-0.12.4' -#git tag 'Functions-2.0.0' -#git tag 'Messaging-3.0.2' -#git tag 'Storage-3.0.0' - -git push origin --tags |