blob: 3a305e8358a7857536c04d9cd960ef76c05f9e84 [file] [log] [blame]
// Copyright 2020 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/sync/invalidations/fcm_handler.h"
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/gcm_driver/fake_gcm_driver.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/sync/invalidations/fcm_registration_token_observer.h"
#include "components/sync/invalidations/invalidations_listener.h"
#include "components/sync/invalidations/switches.h"
#include "google_apis/gcm/engine/account_mapping.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using instance_id::InstanceID;
using testing::_;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;
using testing::WithArg;
namespace syncer {
namespace {
const char kDefaultSenderId[] = "fake_sender_id";
const char kSyncInvalidationsAppId[] = "com.google.chrome.sync.invalidations";
const int kTokenValidationPeriodMinutesDefault = 60 * 24;
class MockInstanceID : public InstanceID {
public:
MockInstanceID() : InstanceID("app_id", /*gcm_driver=*/nullptr) {}
~MockInstanceID() override = default;
MOCK_METHOD(void, GetID, (GetIDCallback callback), (override));
MOCK_METHOD(void,
GetCreationTime,
(GetCreationTimeCallback callback),
(override));
MOCK_METHOD(void,
GetToken,
(const std::string& authorized_entity,
const std::string& scope,
base::TimeDelta time_to_live,
std::set<Flags> flags,
GetTokenCallback callback),
(override));
MOCK_METHOD(void,
ValidateToken,
(const std::string& authorized_entity,
const std::string& scope,
const std::string& token,
ValidateTokenCallback callback),
(override));
protected:
MOCK_METHOD(void,
DeleteTokenImpl,
(const std::string& authorized_entity,
const std::string& scope,
DeleteTokenCallback callback),
(override));
MOCK_METHOD(void, DeleteIDImpl, (DeleteIDCallback callback), (override));
};
class MockInstanceIDDriver : public instance_id::InstanceIDDriver {
public:
MockInstanceIDDriver() : InstanceIDDriver(/*gcm_driver=*/nullptr) {}
~MockInstanceIDDriver() override = default;
MOCK_METHOD(InstanceID*,
GetInstanceID,
(const std::string& app_id),
(override));
MOCK_METHOD(void, RemoveInstanceID, (const std::string& app_id), (override));
MOCK_METHOD(bool,
ExistsInstanceID,
(const std::string& app_id),
(const override));
};
class MockListener : public InvalidationsListener {
public:
MOCK_METHOD(void,
OnInvalidationReceived,
(const std::string& payload),
(override));
};
class MockTokenObserver : public FCMRegistrationTokenObserver {
public:
MOCK_METHOD(void, OnFCMRegistrationTokenChanged, (), (override));
};
class FCMHandlerTest : public testing::Test {
public:
FCMHandlerTest()
: fcm_handler_(&fake_gcm_driver_,
&mock_instance_id_driver_,
kDefaultSenderId,
kSyncInvalidationsAppId) {
// This is called in the FCMHandler.
ON_CALL(mock_instance_id_driver_, GetInstanceID(kSyncInvalidationsAppId))
.WillByDefault(Return(&mock_instance_id_));
override_features_.InitWithFeatures(
/*enabled_features=*/{switches::kSyncSendInterestedDataTypes,
switches::kUseSyncInvalidations},
/*disabled_features=*/{});
}
protected:
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::test::ScopedFeatureList override_features_;
gcm::FakeGCMDriver fake_gcm_driver_;
NiceMock<MockInstanceIDDriver> mock_instance_id_driver_;
NiceMock<MockInstanceID> mock_instance_id_;
FCMHandler fcm_handler_;
};
TEST_F(FCMHandlerTest, ShouldReturnValidToken) {
// Check that the handler gets the token through GetToken.
EXPECT_CALL(mock_instance_id_, GetToken)
.WillOnce(WithArg<4>(Invoke([](InstanceID::GetTokenCallback callback) {
std::move(callback).Run("token", InstanceID::Result::SUCCESS);
})));
fcm_handler_.StartListening();
EXPECT_EQ("token", fcm_handler_.GetFCMRegistrationToken());
}
TEST_F(FCMHandlerTest, ShouldPropagatePayloadToListener) {
const std::string kPayloadValue = "some_payload";
NiceMock<MockListener> mock_listener;
fcm_handler_.AddListener(&mock_listener);
gcm::IncomingMessage gcm_message;
gcm_message.raw_data = kPayloadValue;
EXPECT_CALL(mock_listener, OnInvalidationReceived(kPayloadValue));
fcm_handler_.OnMessage(kSyncInvalidationsAppId, gcm_message);
fcm_handler_.RemoveListener(&mock_listener);
}
TEST_F(FCMHandlerTest, ShouldNotifyOnTokenChange) {
NiceMock<MockTokenObserver> mock_token_observer;
fcm_handler_.AddTokenObserver(&mock_token_observer);
// Check that the handler gets the token through GetToken.
ON_CALL(mock_instance_id_, GetToken)
.WillByDefault(
WithArg<4>(Invoke([](InstanceID::GetTokenCallback callback) {
std::move(callback).Run("token", InstanceID::Result::SUCCESS);
})));
EXPECT_CALL(mock_token_observer, OnFCMRegistrationTokenChanged());
fcm_handler_.StartListening();
fcm_handler_.RemoveTokenObserver(&mock_token_observer);
}
TEST_F(FCMHandlerTest, ShouldScheduleTokenValidationAndActOnNewToken) {
NiceMock<MockTokenObserver> mock_token_observer;
fcm_handler_.AddTokenObserver(&mock_token_observer);
// Check that the handler gets the token through GetToken and notifies the
// observer.
EXPECT_CALL(mock_instance_id_, GetToken)
.WillOnce(WithArg<4>(Invoke([](InstanceID::GetTokenCallback callback) {
std::move(callback).Run("token", InstanceID::Result::SUCCESS);
})));
EXPECT_CALL(mock_token_observer, OnFCMRegistrationTokenChanged()).Times(1);
fcm_handler_.StartListening();
// Adjust the time and check that validation will happen in time.
// The old token is invalid, so token observer should be informed.
task_environment_.FastForwardBy(
base::TimeDelta::FromMinutes(kTokenValidationPeriodMinutesDefault) -
base::TimeDelta::FromSeconds(1));
// When it is time, validation happens.
EXPECT_CALL(mock_instance_id_, GetToken)
.WillOnce(WithArg<4>(Invoke([](InstanceID::GetTokenCallback callback) {
std::move(callback).Run("new token", InstanceID::Result::SUCCESS);
})));
EXPECT_CALL(mock_token_observer, OnFCMRegistrationTokenChanged()).Times(1);
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
fcm_handler_.RemoveTokenObserver(&mock_token_observer);
}
TEST_F(FCMHandlerTest, ShouldScheduleTokenValidationAndNotActOnSameToken) {
NiceMock<MockTokenObserver> mock_token_observer;
fcm_handler_.AddTokenObserver(&mock_token_observer);
// Check that the handler gets the token through GetToken and notifies the
// observer.
EXPECT_CALL(mock_instance_id_, GetToken)
.WillOnce(WithArg<4>(Invoke([](InstanceID::GetTokenCallback callback) {
std::move(callback).Run("token", InstanceID::Result::SUCCESS);
})));
EXPECT_CALL(mock_token_observer, OnFCMRegistrationTokenChanged()).Times(1);
fcm_handler_.StartListening();
// Adjust the time and check that validation will happen in time.
// The old token is valid, so token observer should not be informed.
task_environment_.FastForwardBy(
base::TimeDelta::FromMinutes(kTokenValidationPeriodMinutesDefault) -
base::TimeDelta::FromSeconds(1));
// When it is time, validation happens.
EXPECT_CALL(mock_instance_id_, GetToken)
.WillOnce(WithArg<4>(Invoke([](InstanceID::GetTokenCallback callback) {
std::move(callback).Run("token", InstanceID::Result::SUCCESS);
})));
EXPECT_CALL(mock_token_observer, OnFCMRegistrationTokenChanged()).Times(0);
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
fcm_handler_.RemoveTokenObserver(&mock_token_observer);
}
TEST_F(FCMHandlerTest, ShouldClearTokenOnStopListeningPermanently) {
// Check that the handler gets the token through GetToken.
EXPECT_CALL(mock_instance_id_, GetToken)
.WillOnce(WithArg<4>(Invoke([](InstanceID::GetTokenCallback callback) {
std::move(callback).Run("token", InstanceID::Result::SUCCESS);
})));
fcm_handler_.StartListening();
NiceMock<MockTokenObserver> mock_token_observer;
fcm_handler_.AddTokenObserver(&mock_token_observer);
EXPECT_CALL(mock_instance_id_driver_,
ExistsInstanceID(kSyncInvalidationsAppId))
.WillOnce(Return(true));
// Token should be cleared when StopListeningPermanently() is called.
EXPECT_CALL(mock_token_observer, OnFCMRegistrationTokenChanged());
fcm_handler_.StopListeningPermanently();
EXPECT_EQ("", fcm_handler_.GetFCMRegistrationToken());
fcm_handler_.RemoveTokenObserver(&mock_token_observer);
}
} // namespace
} // namespace syncer