| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/ntp_snippets/breaking_news/breaking_news_gcm_app_handler.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/bind_helpers.h" |
| #include "base/json/json_reader.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/test/test_mock_time_task_runner.h" |
| #include "base/time/clock.h" |
| #include "base/time/tick_clock.h" |
| #include "base/timer/timer.h" |
| #include "build/build_config.h" |
| #include "components/gcm_driver/gcm_driver.h" |
| #include "components/gcm_driver/instance_id/instance_id.h" |
| #include "components/gcm_driver/instance_id/instance_id_driver.h" |
| #include "components/ntp_snippets/breaking_news/breaking_news_metrics.h" |
| #include "components/ntp_snippets/breaking_news/subscription_manager.h" |
| #include "components/ntp_snippets/features.h" |
| #include "components/ntp_snippets/pref_names.h" |
| #include "components/ntp_snippets/remote/test_utils.h" |
| #include "components/ntp_snippets/time_serialization.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/variations/variations_params_manager.h" |
| #include "google_apis/gcm/engine/account_mapping.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| using base::Clock; |
| using base::TestMockTimeTaskRunner; |
| using base::TickClock; |
| using base::TimeTicks; |
| using gcm::InstanceIDHandler; |
| using instance_id::InstanceID; |
| using instance_id::InstanceIDDriver; |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::AtMost; |
| using testing::ElementsAre; |
| using testing::IsEmpty; |
| using testing::NiceMock; |
| using testing::SaveArg; |
| using testing::StrictMock; |
| |
| namespace ntp_snippets { |
| |
| namespace { |
| |
| // The action key in pushed GCM message. |
| const char kPushedActionKey[] = "action"; |
| |
| class MockSubscriptionManager : public SubscriptionManager { |
| public: |
| MockSubscriptionManager() = default; |
| ~MockSubscriptionManager() override = default; |
| |
| MOCK_METHOD1(Subscribe, void(const std::string& token)); |
| MOCK_METHOD0(Unsubscribe, void()); |
| MOCK_METHOD0(IsSubscribed, bool()); |
| MOCK_METHOD1(Resubscribe, void(const std::string& new_token)); |
| MOCK_METHOD0(NeedsToResubscribe, bool()); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockSubscriptionManager); |
| }; |
| |
| class MockInstanceIDDriver : public InstanceIDDriver { |
| public: |
| MockInstanceIDDriver() : InstanceIDDriver(/*gcm_driver=*/nullptr){}; |
| ~MockInstanceIDDriver() override = default; |
| |
| MOCK_METHOD1(GetInstanceID, InstanceID*(const std::string& app_id)); |
| MOCK_METHOD1(RemoveInstanceID, void(const std::string& app_id)); |
| MOCK_CONST_METHOD1(ExistsInstanceID, bool(const std::string& app_id)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockInstanceIDDriver); |
| }; |
| |
| class MockInstanceID : public InstanceID { |
| public: |
| MockInstanceID() : InstanceID("app_id", /*gcm_driver=*/nullptr) {} |
| ~MockInstanceID() override = default; |
| |
| MOCK_METHOD1(GetID, void(const GetIDCallback& callback)); |
| MOCK_METHOD1(GetCreationTime, void(const GetCreationTimeCallback& callback)); |
| MOCK_METHOD5(GetToken, |
| void(const std::string& authorized_entity, |
| const std::string& scope, |
| const std::map<std::string, std::string>& options, |
| bool is_lazy, |
| const GetTokenCallback& callback)); |
| MOCK_METHOD4(ValidateToken, |
| void(const std::string& authorized_entity, |
| const std::string& scope, |
| const std::string& token, |
| const ValidateTokenCallback& callback)); |
| |
| protected: |
| MOCK_METHOD3(DeleteTokenImpl, |
| void(const std::string& authorized_entity, |
| const std::string& scope, |
| const DeleteTokenCallback& callback)); |
| MOCK_METHOD1(DeleteIDImpl, void(const DeleteIDCallback& callback)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockInstanceID); |
| }; |
| |
| class MockGCMDriver : public gcm::GCMDriver { |
| public: |
| MockGCMDriver() |
| : GCMDriver(/*store_path=*/base::FilePath(), |
| /*blocking_task_runner=*/nullptr) {} |
| ~MockGCMDriver() override = default; |
| |
| MOCK_METHOD4(ValidateRegistration, |
| void(const std::string& app_id, |
| const std::vector<std::string>& sender_ids, |
| const std::string& registration_id, |
| const ValidateRegistrationCallback& callback)); |
| MOCK_METHOD0(OnSignedIn, void()); |
| MOCK_METHOD0(OnSignedOut, void()); |
| MOCK_METHOD1(AddConnectionObserver, |
| void(gcm::GCMConnectionObserver* observer)); |
| MOCK_METHOD1(RemoveConnectionObserver, |
| void(gcm::GCMConnectionObserver* observer)); |
| MOCK_METHOD0(Enable, void()); |
| MOCK_METHOD0(Disable, void()); |
| MOCK_CONST_METHOD0(GetGCMClientForTesting, gcm::GCMClient*()); |
| MOCK_CONST_METHOD0(IsStarted, bool()); |
| MOCK_CONST_METHOD0(IsConnected, bool()); |
| MOCK_METHOD2(GetGCMStatistics, |
| void(const GetGCMStatisticsCallback& callback, |
| ClearActivityLogs clear_logs)); |
| MOCK_METHOD2(SetGCMRecording, |
| void(const GetGCMStatisticsCallback& callback, bool recording)); |
| MOCK_METHOD1(SetAccountTokens, |
| void(const std::vector<gcm::GCMClient::AccountTokenInfo>& |
| account_tokens)); |
| MOCK_METHOD1(UpdateAccountMapping, |
| void(const gcm::AccountMapping& account_mapping)); |
| MOCK_METHOD1(RemoveAccountMapping, void(const std::string& account_id)); |
| MOCK_METHOD0(GetLastTokenFetchTime, base::Time()); |
| MOCK_METHOD1(SetLastTokenFetchTime, void(const base::Time& time)); |
| MOCK_METHOD1(WakeFromSuspendForHeartbeat, void(bool wake)); |
| MOCK_METHOD0(GetInstanceIDHandlerInternal, InstanceIDHandler*()); |
| MOCK_METHOD2(AddHeartbeatInterval, |
| void(const std::string& scope, int interval_ms)); |
| MOCK_METHOD1(RemoveHeartbeatInterval, void(const std::string& scope)); |
| |
| protected: |
| MOCK_METHOD1(EnsureStarted, |
| gcm::GCMClient::Result(gcm::GCMClient::StartMode start_mode)); |
| MOCK_METHOD2(RegisterImpl, |
| void(const std::string& app_id, |
| const std::vector<std::string>& sender_ids)); |
| MOCK_METHOD1(UnregisterImpl, void(const std::string& app_id)); |
| MOCK_METHOD3(SendImpl, |
| void(const std::string& app_id, |
| const std::string& receiver_id, |
| const gcm::OutgoingMessage& message)); |
| MOCK_METHOD2(RecordDecryptionFailure, |
| void(const std::string& app_id, |
| gcm::GCMDecryptionResult result)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockGCMDriver); |
| }; |
| |
| class MockOnNewRemoteSuggestionCallback { |
| public: |
| // Workaround for gMock's lack of support for movable-only arguments. |
| void WrappedRun(std::unique_ptr<RemoteSuggestion> remote_suggestion) { |
| Run(remote_suggestion.get()); |
| } |
| |
| BreakingNewsListener::OnNewRemoteSuggestionCallback Get() { |
| return base::Bind(&MockOnNewRemoteSuggestionCallback::WrappedRun, |
| base::Unretained(this)); |
| } |
| |
| MOCK_METHOD1(Run, void(RemoteSuggestion* remote_suggestion)); |
| }; |
| |
| void ParseJson( |
| const std::string& json, |
| const BreakingNewsGCMAppHandler::SuccessCallback& success_callback, |
| const BreakingNewsGCMAppHandler::ErrorCallback& error_callback) { |
| base::JSONReader json_reader; |
| std::unique_ptr<base::Value> value = json_reader.ReadToValue(json); |
| if (value) { |
| success_callback.Run(std::move(value)); |
| } else { |
| error_callback.Run(json_reader.GetErrorMessage()); |
| } |
| } |
| |
| ACTION_TEMPLATE(InvokeCallbackArgument, |
| HAS_1_TEMPLATE_PARAMS(int, k), |
| AND_1_VALUE_PARAMS(p0)) { |
| std::get<k>(args).Run(p0); |
| } |
| |
| ACTION_TEMPLATE(InvokeCallbackArgument, |
| HAS_1_TEMPLATE_PARAMS(int, k), |
| AND_2_VALUE_PARAMS(p0, p1)) { |
| std::get<k>(args).Run(p0, p1); |
| } |
| |
| base::TimeDelta GetTokenValidationPeriod() { |
| return base::TimeDelta::FromHours(24); |
| } |
| |
| base::TimeDelta GetForcedSubscriptionPeriod() { |
| return base::TimeDelta::FromDays(7); |
| } |
| |
| const char kBreakingNewsGCMAppID[] = "com.google.breakingnews.gcm"; |
| |
| std::string BoolToString(bool value) { |
| return value ? "true" : "false"; |
| } |
| |
| } // namespace |
| |
| class BreakingNewsGCMAppHandlerTest : public testing::Test { |
| public: |
| BreakingNewsGCMAppHandlerTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME), |
| start_time_(scoped_task_environment_.GetMockClock()->Now()) { |
| // Our app handler obtains InstanceID through InstanceIDDriver. We mock |
| // InstanceIDDriver and return MockInstanceID through it. |
| mock_instance_id_driver_ = |
| std::make_unique<StrictMock<MockInstanceIDDriver>>(); |
| mock_instance_id_ = std::make_unique<StrictMock<MockInstanceID>>(); |
| mock_gcm_driver_ = std::make_unique<StrictMock<MockGCMDriver>>(); |
| BreakingNewsGCMAppHandler::RegisterProfilePrefs( |
| utils_.pref_service()->registry()); |
| |
| // This is called in BreakingNewsGCMAppHandler. |
| EXPECT_CALL(*mock_instance_id_driver_, GetInstanceID(kBreakingNewsGCMAppID)) |
| .WillRepeatedly(Return(mock_instance_id_.get())); |
| } |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> MakeHandler() { |
| // TODO(vitaliii): Initialize MockSubscriptionManager in the constructor, so |
| // that one could set up expectations before creating the handler. |
| auto wrapped_mock_subscription_manager = |
| std::make_unique<NiceMock<MockSubscriptionManager>>(); |
| mock_subscription_manager_ = wrapped_mock_subscription_manager.get(); |
| |
| auto token_validation_timer = std::make_unique<base::OneShotTimer>( |
| scoped_task_environment_.GetMockTickClock()); |
| token_validation_timer->SetTaskRunner( |
| scoped_task_environment_.GetMainThreadTaskRunner()); |
| |
| auto forced_subscription_timer = std::make_unique<base::OneShotTimer>( |
| scoped_task_environment_.GetMockTickClock()); |
| forced_subscription_timer->SetTaskRunner( |
| scoped_task_environment_.GetMainThreadTaskRunner()); |
| |
| return std::make_unique<BreakingNewsGCMAppHandler>( |
| mock_gcm_driver_.get(), mock_instance_id_driver_.get(), pref_service(), |
| std::move(wrapped_mock_subscription_manager), base::Bind(&ParseJson), |
| scoped_task_environment_.GetMockClock(), |
| std::move(token_validation_timer), |
| std::move(forced_subscription_timer)); |
| } |
| |
| void SetFeatureParams(bool enable_token_validation, |
| bool enable_forced_subscription) { |
| // VariationParamsManager supports only one |
| // |SetVariationParamsWithFeatureAssociations| at a time, so we clear |
| // previous settings first to make this explicit. |
| params_manager_.ClearAllVariationParams(); |
| params_manager_.SetVariationParamsWithFeatureAssociations( |
| kBreakingNewsPushFeature.name, |
| { |
| {"enable_token_validation", BoolToString(enable_token_validation)}, |
| {"enable_forced_subscription", |
| BoolToString(enable_forced_subscription)}, |
| }, |
| {kBreakingNewsPushFeature.name}); |
| } |
| |
| PrefService* pref_service() { return utils_.pref_service(); } |
| NiceMock<MockSubscriptionManager>* mock_subscription_manager() { |
| return mock_subscription_manager_; |
| } |
| StrictMock<MockInstanceID>* mock_instance_id() { |
| return mock_instance_id_.get(); |
| } |
| |
| void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); } |
| |
| void FastForwardBy(const base::TimeDelta& delta) { |
| scoped_task_environment_.FastForwardBy(delta); |
| } |
| |
| base::Time GetDummyNow() { return start_time_; } |
| |
| private: |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| const base::Time start_time_; |
| variations::testing::VariationParamsManager params_manager_; |
| test::RemoteSuggestionsTestUtils utils_; |
| NiceMock<MockSubscriptionManager>* mock_subscription_manager_; |
| std::unique_ptr<StrictMock<MockGCMDriver>> mock_gcm_driver_; |
| std::unique_ptr<StrictMock<MockInstanceIDDriver>> mock_instance_id_driver_; |
| std::unique_ptr<StrictMock<MockInstanceID>> mock_instance_id_; |
| }; |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldValidateTokenImmediatelyIfValidationIsDue) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // Last validation was long time ago. |
| const base::Time last_validation = |
| GetDummyNow() - 10 * GetTokenValidationPeriod(); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| // Check that the handler validates the token through GetToken. ValidateToken |
| // always returns true on Android, so it's not useful. Instead, the handler |
| // must check that the result from GetToken is unchanged. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| RunUntilIdle(); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldScheduleTokenValidationIfNotYetDue) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // The next validation will be soon. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(1); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| // Check that handler does not validate the token yet. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)).Times(0); |
| EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| FastForwardBy(time_to_validation - base::TimeDelta::FromSeconds(1)); |
| |
| // But when it is time, validation happens. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, ShouldNotValidateTokenBeforeListening) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // Last validation was long time ago. |
| const base::Time last_validation = |
| GetDummyNow() - 10 * GetTokenValidationPeriod(); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| |
| // Check that handler does not validate the token before StartListening even |
| // though a validation is due. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)).Times(0); |
| EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0); |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| FastForwardBy(10 * GetTokenValidationPeriod()); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldNotValidateTokenAfterStopListening) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // The next validation will be soon. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(1); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| // Check that handler does not validate the token after StopListening even |
| // though a validation is due. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)).Times(0); |
| EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0); |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| handler->StopListening(); |
| FastForwardBy(10 * GetTokenValidationPeriod()); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldRescheduleTokenValidationWhenRetrievingToken) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // The next validation will be soon. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(1); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| // There is no token yet, thus, handler retrieves it on StartListening. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| // Check that the validation schedule has changed. "Old validation" should not |
| // happen because the token was retrieved recently. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)).Times(0); |
| FastForwardBy(GetTokenValidationPeriod() - base::TimeDelta::FromSeconds(1)); |
| |
| // The new validation should happen. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldScheduleNewTokenValidationAfterValidation) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // The next validation will be soon. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(1); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| // Handler validates the token. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0); |
| FastForwardBy(time_to_validation); |
| |
| // Check that the next validation is scheduled in time. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)).Times(0); |
| FastForwardBy(GetTokenValidationPeriod() - base::TimeDelta::FromSeconds(1)); |
| |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldResubscribeWithNewTokenIfOldIsInvalidAfterValidation) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // Last validation was long time ago. |
| const base::Time last_validation = |
| GetDummyNow() - 10 * GetTokenValidationPeriod(); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "old_token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| // Check that handler resubscribes with the new token after a validation, if |
| // old is invalid. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("new_token", InstanceID::Result::SUCCESS)); |
| EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0); |
| EXPECT_CALL(*mock_subscription_manager(), Resubscribe("new_token")); |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe(_)).Times(0); |
| RunUntilIdle(); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldDoNothingIfOldTokenIsValidAfterValidation) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // Last validation was long time ago. |
| const base::Time last_validation = |
| GetDummyNow() - 10 * GetTokenValidationPeriod(); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| // Check that provider does not resubscribe if the old token is still valid |
| // after validation. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| EXPECT_CALL(*mock_instance_id(), ValidateToken(_, _, _, _)).Times(0); |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe(_)).Times(0); |
| EXPECT_CALL(*mock_subscription_manager(), Resubscribe(_)).Times(0); |
| RunUntilIdle(); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldIgnoreTokenReceivedAfterStopListening) { |
| // The last validation time is used to ensure that GetToken callback is |
| // ignored. Thus, enable validation. |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // The next validation won't be soon. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromDays(5); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| // Save the GetToken callback during the subscription. |
| base::RepeatingCallback<void(const std::string&, InstanceID::Result)> |
| get_token_callback; |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce(SaveArg<4>(&get_token_callback)); |
| handler->StartListening( |
| base::BindRepeating( |
| [](std::unique_ptr<RemoteSuggestion> remote_suggestion) {}), |
| base::DoNothing()); |
| |
| // The client stops listening for the push updates. |
| handler->StopListening(); |
| |
| // The GCM returns the new token (it does not know that we are not interested |
| // anymore). Imitate an asynchronous call via time delay. |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| get_token_callback.Run("new_token", InstanceID::Result::SUCCESS); |
| |
| // The new token must be completely ignored. It should not be saved. |
| EXPECT_EQ("", pref_service()->GetString( |
| prefs::kBreakingNewsGCMSubscriptionTokenCache)); |
| // The validation should not be rescheduled. |
| EXPECT_EQ(last_validation, |
| DeserializeTime(pref_service()->GetInt64( |
| prefs::kBreakingNewsGCMLastTokenValidationTime))); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldIgnoreTokenReceivedForValidationAfterStopListening) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // The next validation will be soon. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(1); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "old_token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening( |
| base::BindRepeating( |
| [](std::unique_ptr<RemoteSuggestion> remote_suggestion) {}), |
| base::DoNothing()); |
| |
| FastForwardBy(time_to_validation - base::TimeDelta::FromSeconds(1)); |
| |
| // Save the GetToken callback during the validation. |
| base::RepeatingCallback<void(const std::string&, InstanceID::Result)> |
| get_token_callback; |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce(SaveArg<4>(&get_token_callback)); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| |
| // The client stops listening for the push updates. |
| handler->StopListening(); |
| |
| // The GCM returns the new token (it does not know that we are not interested |
| // anymore). |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| get_token_callback.Run("new_token", InstanceID::Result::SUCCESS); |
| |
| // The new token must be completely ignored. It should not be saved. |
| EXPECT_EQ("old_token", pref_service()->GetString( |
| prefs::kBreakingNewsGCMSubscriptionTokenCache)); |
| // The validation should not occur. |
| EXPECT_EQ(last_validation, |
| DeserializeTime(pref_service()->GetInt64( |
| prefs::kBreakingNewsGCMLastTokenValidationTime))); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| IsListeningShouldReturnFalseBeforeListening) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| EXPECT_FALSE(handler->IsListening()); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| IsListeningShouldReturnTrueAfterStartListening) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| ASSERT_FALSE(handler->IsListening()); |
| |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillRepeatedly( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| EXPECT_TRUE(handler->IsListening()); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| IsListeningShouldReturnFalseAfterStopListening) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| ASSERT_FALSE(handler->IsListening()); |
| |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillRepeatedly( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| ASSERT_TRUE(handler->IsListening()); |
| |
| handler->StopListening(); |
| |
| EXPECT_FALSE(handler->IsListening()); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| IsListeningShouldReturnTrueAfterSecondStartListening) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| ASSERT_FALSE(handler->IsListening()); |
| |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillRepeatedly( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| ASSERT_TRUE(handler->IsListening()); |
| |
| handler->StopListening(); |
| ASSERT_FALSE(handler->IsListening()); |
| |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| EXPECT_TRUE(handler->IsListening()); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, ShouldForceSubscribeImmediatelyIfDue) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/true); |
| |
| // Last subscription was long time ago. |
| const base::Time last_subscription = |
| GetDummyNow() - 10 * GetForcedSubscriptionPeriod(); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastForcedSubscriptionTime, |
| SerializeTime(last_subscription)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")); |
| RunUntilIdle(); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldScheduleForcedSubscribtionIfNotYetDue) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/true); |
| |
| // The next forced subscription will be soon. |
| const base::TimeDelta time_to_subscription = base::TimeDelta::FromHours(1); |
| const base::Time last_subscription = |
| GetDummyNow() - (GetForcedSubscriptionPeriod() - time_to_subscription); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastForcedSubscriptionTime, |
| SerializeTime(last_subscription)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| // Check that handler does not force subscribe yet. |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| // TODO(vitaliii): Consider making FakeSubscriptionManager, because |
| // IsSubscribed() affects forced subscriptions. Currently we have to carefully |
| // avoid the initial subscription. |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")).Times(0); |
| FastForwardBy(time_to_subscription - base::TimeDelta::FromSeconds(1)); |
| |
| // But when it is time, forced subscription happens. |
| testing::Mock::VerifyAndClearExpectations(mock_subscription_manager()); |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, ShouldNotForceSubscribeBeforeListening) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/true); |
| |
| // Last subscription was long time ago. |
| const base::Time last_subscription = |
| GetDummyNow() - 10 * GetForcedSubscriptionPeriod(); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastForcedSubscriptionTime, |
| SerializeTime(last_subscription)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| // Check that handler does not force subscribe before StartListening even |
| // though a forced subscription is due. |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")).Times(0); |
| FastForwardBy(10 * GetForcedSubscriptionPeriod()); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldNotForceSubscribeAfterStopListening) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/true); |
| |
| // The next forced subscription will be soon. |
| const base::TimeDelta time_to_subscription = base::TimeDelta::FromHours(1); |
| const base::Time last_subscription = |
| GetDummyNow() - (GetForcedSubscriptionPeriod() - time_to_subscription); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastForcedSubscriptionTime, |
| SerializeTime(last_subscription)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| // Check that handler does not force subscribe after StopListening even |
| // though a forced subscription is due. |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| handler->StopListening(); |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")).Times(0); |
| FastForwardBy(10 * GetForcedSubscriptionPeriod()); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldScheduleNewForcedSubscriptionAfterForcedSubscription) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/true); |
| |
| // The next forced subscription will be soon. |
| const base::TimeDelta time_to_subscription = base::TimeDelta::FromHours(1); |
| const base::Time last_subscription = |
| GetDummyNow() - (GetForcedSubscriptionPeriod() - time_to_subscription); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastForcedSubscriptionTime, |
| SerializeTime(last_subscription)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| // Handler force subscribes. |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")); |
| FastForwardBy(time_to_subscription); |
| |
| // Check that the next forced subscription is scheduled in time. |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")).Times(0); |
| FastForwardBy(GetForcedSubscriptionPeriod() - |
| base::TimeDelta::FromSeconds(1)); |
| |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| TokenValidationAndForcedSubscriptionShouldNotAffectEachOther) { |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/true); |
| |
| // The next forced subscription will be soon. |
| const base::TimeDelta time_to_subscription = base::TimeDelta::FromHours(1); |
| const base::Time last_subscription = |
| GetDummyNow() - (GetForcedSubscriptionPeriod() - time_to_subscription); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastForcedSubscriptionTime, |
| SerializeTime(last_subscription)); |
| |
| // The next validation will be sooner. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromMinutes(30); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| // Check that the next validation is scheduled in time. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)).Times(0); |
| FastForwardBy(time_to_validation - base::TimeDelta::FromSeconds(1)); |
| |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| |
| // Check that the next forced subscription is scheduled in time. |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")).Times(0); |
| FastForwardBy((time_to_subscription - time_to_validation) - |
| base::TimeDelta::FromSeconds(1)); |
| |
| EXPECT_CALL(*mock_subscription_manager(), Subscribe("token")); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, ShouldReportMissingAction) { |
| base::HistogramTester histogram_tester; |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| handler->OnMessage("com.google.breakingnews.gcm", gcm::IncomingMessage()); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "NewTabPage.ContentSuggestions.BreakingNews.ReceivedMessageAction"), |
| ElementsAre(base::Bucket( |
| /*min=*/static_cast<int>(metrics::ReceivedMessageAction::NO_ACTION), |
| /*count=*/1))); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, ShouldReportInvalidAction) { |
| base::HistogramTester histogram_tester; |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| gcm::IncomingMessage message; |
| message.data[kPushedActionKey] = "invalid_action"; |
| |
| handler->OnMessage("com.google.breakingnews.gcm", message); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "NewTabPage.ContentSuggestions.BreakingNews.ReceivedMessageAction"), |
| ElementsAre( |
| base::Bucket(/*min=*/static_cast<int>( |
| metrics::ReceivedMessageAction::INVALID_ACTION), |
| /*count=*/1))); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, ShouldReportPushToRefreshAction) { |
| base::HistogramTester histogram_tester; |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| gcm::IncomingMessage message; |
| message.data[kPushedActionKey] = "push-to-refresh"; |
| |
| handler->OnMessage("com.google.breakingnews.gcm", message); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "NewTabPage.ContentSuggestions.BreakingNews.ReceivedMessageAction"), |
| ElementsAre( |
| base::Bucket(/*min=*/static_cast<int>( |
| metrics::ReceivedMessageAction::PUSH_TO_REFRESH), |
| /*count=*/1))); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, ShouldReportPushByValueAction) { |
| base::HistogramTester histogram_tester; |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| gcm::IncomingMessage message; |
| message.data[kPushedActionKey] = "push-by-value"; |
| message.data["payload"] = "news"; |
| |
| handler->OnMessage("com.google.breakingnews.gcm", message); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "NewTabPage.ContentSuggestions.BreakingNews.ReceivedMessageAction"), |
| ElementsAre(base::Bucket( |
| /*min=*/static_cast<int>( |
| metrics::ReceivedMessageAction::PUSH_BY_VALUE), |
| /*count=*/1))); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldCallOnNewRemoteSuggestionCallbackOnPushByValueMessage) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| std::string json = R"( |
| {"categories" : [{ |
| "id": 1, |
| "localizedTitle": "section title", |
| "suggestions" : [{ |
| "ids" : ["http://url.com"], |
| "title" : "Pushed Dummy Title", |
| "snippet" : "Pushed Dummy Snippet", |
| "fullPageUrl" : "http://url.com", |
| "creationTime" : "2017-01-01T00:00:01.000Z", |
| "expirationTime" : "2100-01-01T00:00:01.000Z", |
| "attribution" : "Pushed Dummy Publisher", |
| "imageUrl" : "https://www.google.com/favicon.ico" |
| }] |
| }]} |
| )"; |
| |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| MockOnNewRemoteSuggestionCallback mock_on_new_remote_suggestion_callback; |
| |
| handler->StartListening(mock_on_new_remote_suggestion_callback.Get(), |
| base::DoNothing()); |
| |
| gcm::IncomingMessage message; |
| message.data[kPushedActionKey] = "push-by-value"; |
| message.data["payload"] = json; |
| |
| EXPECT_CALL(mock_on_new_remote_suggestion_callback, Run(_)).Times(1); |
| handler->OnMessage("com.google.breakingnews.gcm", message); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldCallOnRefreshRequestedCallbackOnPushToRefreshMessage) { |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| base::MockCallback<BreakingNewsListener::OnRefreshRequestedCallback> |
| on_refresh_requested_callback; |
| |
| handler->StartListening(base::DoNothing(), |
| on_refresh_requested_callback.Get()); |
| |
| gcm::IncomingMessage message; |
| message.data[kPushedActionKey] = "push-to-refresh"; |
| |
| EXPECT_CALL(on_refresh_requested_callback, Run()).Times(1); |
| handler->OnMessage("com.google.breakingnews.gcm", message); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, ShouldReportTokenRetrievalResult) { |
| base::HistogramTester histogram_tester; |
| SetFeatureParams(/*enable_token_validation=*/false, |
| /*enable_forced_subscription=*/false); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce(InvokeCallbackArgument<4>(/*token=*/"", |
| InstanceID::Result::NETWORK_ERROR)); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "NewTabPage.ContentSuggestions.BreakingNews.TokenRetrievalResult"), |
| ElementsAre(base::Bucket( |
| /*min=*/static_cast<int>(InstanceID::Result::NETWORK_ERROR), |
| /*count=*/1))); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldReportTimeSinceLastTokenValidation) { |
| base::HistogramTester histogram_tester; |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // The next validation will be soon. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(1); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| // Check that handler does not report the metric before the validation. |
| FastForwardBy(time_to_validation - base::TimeDelta::FromSeconds(1)); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "NewTabPage.ContentSuggestions.BreakingNews.TokenRetrievalResult"), |
| IsEmpty()); |
| |
| // But when the validation happens, the time is reported. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| |
| histogram_tester.ExpectTimeBucketCount( |
| "NewTabPage.ContentSuggestions.BreakingNews.TimeSinceLastTokenValidation", |
| GetTokenValidationPeriod(), /*count=*/1); |
| // |ExpectTimeBucketCount| allows other buckets. Let's ensure that there are |
| // none. |
| histogram_tester.ExpectTotalCount( |
| "NewTabPage.ContentSuggestions.BreakingNews.TimeSinceLastTokenValidation", |
| /*count=*/1); |
| } |
| |
| TEST_F(BreakingNewsGCMAppHandlerTest, |
| ShouldReportWhetherTokenWasValidBeforeValidation) { |
| base::HistogramTester histogram_tester; |
| SetFeatureParams(/*enable_token_validation=*/true, |
| /*enable_forced_subscription=*/false); |
| |
| // The next validation will be soon. |
| const base::TimeDelta time_to_validation = base::TimeDelta::FromHours(1); |
| const base::Time last_validation = |
| GetDummyNow() - (GetTokenValidationPeriod() - time_to_validation); |
| pref_service()->SetInt64(prefs::kBreakingNewsGCMLastTokenValidationTime, |
| SerializeTime(last_validation)); |
| // Omit receiving the token by putting it there directly. |
| pref_service()->SetString(prefs::kBreakingNewsGCMSubscriptionTokenCache, |
| "token"); |
| |
| std::unique_ptr<BreakingNewsGCMAppHandler> handler = MakeHandler(); |
| handler->StartListening(base::DoNothing(), base::DoNothing()); |
| |
| // Check that handler does not report the metric before the validation. |
| FastForwardBy(time_to_validation - base::TimeDelta::FromSeconds(1)); |
| EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.ContentSuggestions." |
| "BreakingNews." |
| "WasTokenValidBeforeValidation"), |
| IsEmpty()); |
| |
| // But when the validation happens, the old token validness is reported. |
| EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _, _)) |
| .WillOnce( |
| InvokeCallbackArgument<4>("token", InstanceID::Result::SUCCESS)); |
| FastForwardBy(base::TimeDelta::FromSeconds(1)); |
| |
| EXPECT_THAT(histogram_tester.GetAllSamples("NewTabPage.ContentSuggestions." |
| "BreakingNews." |
| "WasTokenValidBeforeValidation"), |
| ElementsAre(base::Bucket( |
| /*min=*/1, |
| /*count=*/1))); |
| } |
| |
| } // namespace ntp_snippets |