// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/gcm_driver/gcm_client_impl.h"

#include <stdint.h>

#include <initializer_list>
#include <memory>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/gcm_driver/features.h"
#include "google_apis/gcm/base/fake_encryptor.h"
#include "google_apis/gcm/base/mcs_message.h"
#include "google_apis/gcm/base/mcs_util.h"
#include "google_apis/gcm/engine/checkin_request.h"
#include "google_apis/gcm/engine/fake_connection_factory.h"
#include "google_apis/gcm/engine/fake_connection_handler.h"
#include "google_apis/gcm/engine/gservices_settings.h"
#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
#include "google_apis/gcm/protocol/android_checkin.pb.h"
#include "google_apis/gcm/protocol/checkin.pb.h"
#include "google_apis/gcm/protocol/mcs.pb.h"
#include "net/test/gtest_util.h"
#include "net/test/scoped_disable_exit_on_dfatal.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest-spi.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/leveldatabase/leveldb_chrome.h"

namespace gcm {
namespace {

enum LastEvent {
  NONE,
  LOADING_COMPLETED,
  REGISTRATION_COMPLETED,
  UNREGISTRATION_COMPLETED,
  MESSAGE_SEND_ERROR,
  MESSAGE_SEND_ACK,
  MESSAGE_RECEIVED,
  MESSAGES_DELETED,
};

const char kChromeVersion[] = "45.0.0.1";
const uint64_t kDeviceAndroidId = 54321;
const uint64_t kDeviceSecurityToken = 12345;
const uint64_t kDeviceAndroidId2 = 11111;
const uint64_t kDeviceSecurityToken2 = 2222;
const int64_t kSettingsCheckinInterval = 16 * 60 * 60;
const char kProductCategoryForSubtypes[] = "com.chrome.macosx";
const char kExtensionAppId[] = "abcdefghijklmnopabcdefghijklmnop";
const char kRegistrationId[] = "reg_id";
const char kSubtypeAppId[] = "app_id";
const char kSender[] = "project_id";
const char kSender2[] = "project_id2";
const char kRegistrationResponsePrefix[] = "token=";
const char kUnregistrationResponsePrefix[] = "deleted=";
const char kRawData[] = "example raw data";

const char kInstanceID[] = "iid_1";
const char kScope[] = "GCM";
const char kDeleteTokenResponse[] = "token=foo";
const int kTestTokenInvalidationPeriod = 5;
const char kMessageId[] = "0:12345%5678";

const char kRegisterUrl[] = "https://android.clients.google.com/c2dm/register3";

// Helper for building arbitrary data messages.
MCSMessage BuildDownstreamMessage(
    const std::string& project_id,
    const std::string& category,
    const std::string& subtype,
    const std::map<std::string, std::string>& data,
    const std::string& raw_data) {
  mcs_proto::DataMessageStanza data_message;
  data_message.set_from(project_id);
  data_message.set_category(category);
  for (auto iter = data.begin(); iter != data.end(); ++iter) {
    mcs_proto::AppData* app_data = data_message.add_app_data();
    app_data->set_key(iter->first);
    app_data->set_value(iter->second);
  }
  if (!subtype.empty()) {
    mcs_proto::AppData* app_data = data_message.add_app_data();
    app_data->set_key("subtype");
    app_data->set_value(subtype);
  }
  data_message.set_raw_data(raw_data);
  data_message.set_persistent_id(kMessageId);
  return MCSMessage(kDataMessageStanzaTag, data_message);
}

GCMClient::AccountTokenInfo MakeAccountToken(const std::string& email,
                                             const std::string& token) {
  GCMClient::AccountTokenInfo account_token;
  account_token.email = email;
  account_token.access_token = token;
  return account_token;
}

class FakeMCSClient : public MCSClient {
 public:
  FakeMCSClient(base::Clock* clock,
                ConnectionFactory* connection_factory,
                GCMStore* gcm_store,
                scoped_refptr<base::SequencedTaskRunner> io_task_runner,
                GCMStatsRecorder* recorder);
  ~FakeMCSClient() override;
  void Login(uint64_t android_id, uint64_t security_token) override;
  void SendMessage(const MCSMessage& message) override;

  uint64_t last_android_id() const { return last_android_id_; }
  uint64_t last_security_token() const { return last_security_token_; }
  uint8_t last_message_tag() const { return last_message_tag_; }
  const mcs_proto::DataMessageStanza& last_data_message_stanza() const {
    return last_data_message_stanza_;
  }

 private:
  uint64_t last_android_id_;
  uint64_t last_security_token_;
  uint8_t last_message_tag_;
  mcs_proto::DataMessageStanza last_data_message_stanza_;
};

FakeMCSClient::FakeMCSClient(
    base::Clock* clock,
    ConnectionFactory* connection_factory,
    GCMStore* gcm_store,
    scoped_refptr<base::SequencedTaskRunner> io_task_runner,
    GCMStatsRecorder* recorder)
    : MCSClient("",
                clock,
                connection_factory,
                gcm_store,
                io_task_runner,
                recorder),
      last_android_id_(0u),
      last_security_token_(0u),
      last_message_tag_(kNumProtoTypes) {}

FakeMCSClient::~FakeMCSClient() {
}

void FakeMCSClient::Login(uint64_t android_id, uint64_t security_token) {
  last_android_id_ = android_id;
  last_security_token_ = security_token;
}

void FakeMCSClient::SendMessage(const MCSMessage& message) {
  last_message_tag_ = message.tag();
  if (last_message_tag_ == kDataMessageStanzaTag) {
    last_data_message_stanza_.CopyFrom(
        reinterpret_cast<const mcs_proto::DataMessageStanza&>(
            message.GetProtobuf()));
  }
}

class AutoAdvancingTestClock : public base::Clock {
 public:
  explicit AutoAdvancingTestClock(base::TimeDelta auto_increment_time_delta);

  AutoAdvancingTestClock(const AutoAdvancingTestClock&) = delete;
  AutoAdvancingTestClock& operator=(const AutoAdvancingTestClock&) = delete;

  ~AutoAdvancingTestClock() override;

  base::Time Now() const override;
  void Advance(base::TimeDelta delta);
  int call_count() const { return call_count_; }

 private:
  mutable int call_count_;
  base::TimeDelta auto_increment_time_delta_;
  mutable base::Time now_;
};

AutoAdvancingTestClock::AutoAdvancingTestClock(
    base::TimeDelta auto_increment_time_delta)
    : call_count_(0), auto_increment_time_delta_(auto_increment_time_delta) {
}

AutoAdvancingTestClock::~AutoAdvancingTestClock() {
}

base::Time AutoAdvancingTestClock::Now() const {
  call_count_++;
  now_ += auto_increment_time_delta_;
  return now_;
}

void AutoAdvancingTestClock::Advance(base::TimeDelta delta) {
  now_ += delta;
}

class FakeGCMInternalsBuilder : public GCMInternalsBuilder {
 public:
  explicit FakeGCMInternalsBuilder(base::TimeDelta clock_step);
  ~FakeGCMInternalsBuilder() override;

  base::Clock* GetClock() override;
  std::unique_ptr<MCSClient> BuildMCSClient(
      const std::string& version,
      base::Clock* clock,
      ConnectionFactory* connection_factory,
      GCMStore* gcm_store,
      scoped_refptr<base::SequencedTaskRunner> io_task_runner,
      GCMStatsRecorder* recorder) override;
  std::unique_ptr<ConnectionFactory> BuildConnectionFactory(
      const std::vector<GURL>& endpoints,
      const net::BackoffEntry::Policy& backoff_policy,
      base::RepeatingCallback<void(
          mojo::PendingReceiver<network::mojom::ProxyResolvingSocketFactory>)>
          get_socket_factory_callback,
      scoped_refptr<base::SequencedTaskRunner> io_task_runner,
      GCMStatsRecorder* recorder,
      network::NetworkConnectionTracker* network_connection_tracker) override;

 private:
  AutoAdvancingTestClock clock_;
};

FakeGCMInternalsBuilder::FakeGCMInternalsBuilder(base::TimeDelta clock_step)
    : clock_(clock_step) {}

FakeGCMInternalsBuilder::~FakeGCMInternalsBuilder() = default;

base::Clock* FakeGCMInternalsBuilder::GetClock() {
  return &clock_;
}

std::unique_ptr<MCSClient> FakeGCMInternalsBuilder::BuildMCSClient(
    const std::string& version,
    base::Clock* clock,
    ConnectionFactory* connection_factory,
    GCMStore* gcm_store,
    scoped_refptr<base::SequencedTaskRunner> io_task_runner,
    GCMStatsRecorder* recorder) {
  return base::WrapUnique<MCSClient>(new FakeMCSClient(
      clock, connection_factory, gcm_store, io_task_runner, recorder));
}

std::unique_ptr<ConnectionFactory>
FakeGCMInternalsBuilder::BuildConnectionFactory(
    const std::vector<GURL>& endpoints,
    const net::BackoffEntry::Policy& backoff_policy,
    base::RepeatingCallback<void(
        mojo::PendingReceiver<network::mojom::ProxyResolvingSocketFactory>)>
        get_socket_factory_callback,
    scoped_refptr<base::SequencedTaskRunner> io_task_runner,
    GCMStatsRecorder* recorder,
    network::NetworkConnectionTracker* network_connection_tracker) {
  return base::WrapUnique<ConnectionFactory>(new FakeConnectionFactory());
}

}  // namespace

class GCMClientImplTest : public testing::Test,
                          public GCMClient::Delegate {
 public:
  GCMClientImplTest();
  ~GCMClientImplTest() override;

  void SetUp() override;
  void TearDown() override;

  void SetFeatureParams(const base::Feature& feature,
                        const base::FieldTrialParams& params);

  void InitializeInvalidationFieldTrial();

  void BuildGCMClient(base::TimeDelta clock_step);
  void InitializeGCMClient();
  void StartGCMClient();
  void Register(const std::string& app_id,
                const std::vector<std::string>& senders);
  void Unregister(const std::string& app_id);
  void ReceiveMessageFromMCS(const MCSMessage& message);
  void ReceiveOnMessageSentToMCS(
      const std::string& app_id,
      const std::string& message_id,
      const MCSClient::MessageSendStatus status);
  void FailCheckin(net::HttpStatusCode response_code);
  void CompleteCheckin(uint64_t android_id,
                       uint64_t security_token,
                       const std::string& digest,
                       const std::map<std::string, std::string>& settings);
  void CompleteCheckinImpl(uint64_t android_id,
                           uint64_t security_token,
                           const std::string& digest,
                           const std::map<std::string, std::string>& settings,
                           net::HttpStatusCode response_code);
  void CompleteRegistration(const std::string& registration_id);
  void CompleteUnregistration(const std::string& app_id);

  bool ExistsRegistration(const std::string& app_id) const;
  void AddRegistration(const std::string& app_id,
                       const std::vector<std::string>& sender_ids,
                       const std::string& registration_id);

  // GCMClient::Delegate overrides (for verification).
  void OnRegisterFinished(scoped_refptr<RegistrationInfo> registration_info,
                          const std::string& registration_id,
                          GCMClient::Result result) override;
  void OnUnregisterFinished(scoped_refptr<RegistrationInfo> registration_info,
                            GCMClient::Result result) override;
  void OnSendFinished(const std::string& app_id,
                      const std::string& message_id,
                      GCMClient::Result result) override {}
  void OnMessageReceived(const std::string& registration_id,
                         const IncomingMessage& message) override;
  void OnMessagesDeleted(const std::string& app_id) override;
  void OnMessageSendError(
      const std::string& app_id,
      const gcm::GCMClient::SendErrorDetails& send_error_details) override;
  void OnSendAcknowledged(const std::string& app_id,
                          const std::string& message_id) override;
  void OnGCMReady(const std::vector<AccountMapping>& account_mappings,
                  const base::Time& last_token_fetch_time) override;
  void OnActivityRecorded() override {}
  void OnConnected(const net::IPEndPoint& ip_endpoint) override {}
  void OnDisconnected() override {}
  void OnStoreReset() override {}

  GCMClientImpl* gcm_client() const { return gcm_client_.get(); }
  GCMClientImpl::State gcm_client_state() const {
    return gcm_client_->state_;
  }
  FakeMCSClient* mcs_client() const {
    return static_cast<FakeMCSClient*>(gcm_client_->mcs_client_.get());
  }
  ConnectionFactory* connection_factory() const {
    return gcm_client_->connection_factory_.get();
  }

  const GCMClientImpl::CheckinInfo& device_checkin_info() const {
    return gcm_client_->device_checkin_info_;
  }

  const CheckinRequest& checkin_request() const {
    return *gcm_client_->checkin_request_;
  }

  base::test::ScopedFeatureList& scoped_feature_list() {
    return scoped_feature_list_;
  }

  void reset_last_event() {
    last_event_ = NONE;
    last_app_id_.clear();
    last_registration_id_.clear();
    last_message_id_.clear();
    last_result_ = GCMClient::UNKNOWN_ERROR;
    last_account_mappings_.clear();
    last_token_fetch_time_ = base::Time();
  }

  LastEvent last_event() const { return last_event_; }
  const std::string& last_app_id() const { return last_app_id_; }
  const std::string& last_registration_id() const {
    return last_registration_id_;
  }
  const std::string& last_message_id() const { return last_message_id_; }
  GCMClient::Result last_result() const { return last_result_; }
  const IncomingMessage& last_message() const { return last_message_; }
  const GCMClient::SendErrorDetails& last_error_details() const {
    return last_error_details_;
  }
  const base::Time& last_token_fetch_time() const {
    return last_token_fetch_time_;
  }
  const std::vector<AccountMapping>& last_account_mappings() {
    return last_account_mappings_;
  }

  const GServicesSettings& gservices_settings() const {
    return gcm_client_->gservices_settings_;
  }

  const base::FilePath& temp_directory_path() const {
    return temp_directory_.GetPath();
  }

  base::FilePath gcm_store_path() const {
    // Pass an non-existent directory as store path to match the exact
    // behavior in the production code. Currently GCMStoreImpl checks if
    // the directory exist or not to determine the store existence.
    return temp_directory_.GetPath().Append(FILE_PATH_LITERAL("GCM Store"));
  }

  int64_t CurrentTime();

  // Tooling.
  void PumpLoopUntilIdle();
  bool CreateUniqueTempDir();
  AutoAdvancingTestClock* clock() const {
    return static_cast<AutoAdvancingTestClock*>(gcm_client_->clock_);
  }
  network::TestURLLoaderFactory* url_loader_factory() {
    return &test_url_loader_factory_;
  }

  void FastForwardBy(const base::TimeDelta& duration) {
    task_environment_.FastForwardBy(duration);
  }

 private:
  base::test::TaskEnvironment task_environment_{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

  // Must be declared first so that it is destroyed last. Injected to
  // GCM client.
  base::ScopedTempDir temp_directory_;

  // Variables used for verification.
  LastEvent last_event_;
  std::string last_app_id_;
  std::string last_registration_id_;
  std::string last_message_id_;
  GCMClient::Result last_result_;
  IncomingMessage last_message_;
  GCMClient::SendErrorDetails last_error_details_;
  base::Time last_token_fetch_time_;
  std::vector<AccountMapping> last_account_mappings_;

  std::unique_ptr<GCMClientImpl> gcm_client_;

  // Injected to GCM client.
  network::TestURLLoaderFactory test_url_loader_factory_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

GCMClientImplTest::GCMClientImplTest()
    : last_event_(NONE), last_result_(GCMClient::UNKNOWN_ERROR) {}

GCMClientImplTest::~GCMClientImplTest() = default;

void GCMClientImplTest::SetUp() {
  testing::Test::SetUp();
  ASSERT_TRUE(CreateUniqueTempDir());
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  StartGCMClient();
  InitializeInvalidationFieldTrial();
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken, std::string(),
                      std::map<std::string, std::string>()));
}

void GCMClientImplTest::TearDown() {
  gcm_client_.reset();
  PumpLoopUntilIdle();
  testing::Test::TearDown();
}

void GCMClientImplTest::SetFeatureParams(const base::Feature& feature,
                                         const base::FieldTrialParams& params) {
  scoped_feature_list_.InitAndEnableFeatureWithParameters(feature, params);

  base::FieldTrialParams actual_params;
  EXPECT_TRUE(base::GetFieldTrialParamsByFeature(
      features::kInvalidateTokenFeature, &actual_params));
  EXPECT_EQ(params, actual_params);
}

void GCMClientImplTest::InitializeInvalidationFieldTrial() {
  std::map<std::string, std::string> params;
  params[features::kParamNameTokenInvalidationPeriodDays] =
      base::NumberToString(kTestTokenInvalidationPeriod);
  ASSERT_NO_FATAL_FAILURE(
      SetFeatureParams(features::kInvalidateTokenFeature, std::move(params)));
}

void GCMClientImplTest::PumpLoopUntilIdle() {
  task_environment_.RunUntilIdle();
}

bool GCMClientImplTest::CreateUniqueTempDir() {
  return temp_directory_.CreateUniqueTempDir();
}

void GCMClientImplTest::BuildGCMClient(base::TimeDelta clock_step) {
  gcm_client_ =
      std::make_unique<GCMClientImpl>(base::WrapUnique<GCMInternalsBuilder>(
          new FakeGCMInternalsBuilder(clock_step)));
}

void GCMClientImplTest::FailCheckin(net::HttpStatusCode response_code) {
  std::map<std::string, std::string> settings;
  CompleteCheckinImpl(0, 0, GServicesSettings::CalculateDigest(settings),
                      settings, response_code);
}

void GCMClientImplTest::CompleteCheckin(
    uint64_t android_id,
    uint64_t security_token,
    const std::string& digest,
    const std::map<std::string, std::string>& settings) {
  CompleteCheckinImpl(android_id, security_token, digest, settings,
                      net::HTTP_OK);
}

void GCMClientImplTest::CompleteCheckinImpl(
    uint64_t android_id,
    uint64_t security_token,
    const std::string& digest,
    const std::map<std::string, std::string>& settings,
    net::HttpStatusCode response_code) {
  checkin_proto::AndroidCheckinResponse response;
  response.set_stats_ok(true);
  response.set_android_id(android_id);
  response.set_security_token(security_token);

  // For testing G-services settings.
  if (!digest.empty()) {
    response.set_digest(digest);
    for (auto it = settings.begin(); it != settings.end(); ++it) {
      checkin_proto::GservicesSetting* setting = response.add_setting();
      setting->set_name(it->first);
      setting->set_value(it->second);
    }
    response.set_settings_diff(false);
  }

  std::string response_string;
  response.SerializeToString(&response_string);

  EXPECT_TRUE(url_loader_factory()->SimulateResponseForPendingRequest(
      gservices_settings().GetCheckinURL(),
      network::URLLoaderCompletionStatus(net::OK),
      network::CreateURLResponseHead(response_code), response_string));
  // Give a chance for GCMStoreImpl::Backend to finish persisting data.
  PumpLoopUntilIdle();
}

void GCMClientImplTest::CompleteRegistration(
    const std::string& registration_id) {
  std::string response(kRegistrationResponsePrefix);
  response.append(registration_id);

  EXPECT_TRUE(url_loader_factory()->SimulateResponseForPendingRequest(
      GURL(kRegisterUrl), network::URLLoaderCompletionStatus(net::OK),
      network::CreateURLResponseHead(net::HTTP_OK), response));

  // Give a chance for GCMStoreImpl::Backend to finish persisting data.
  PumpLoopUntilIdle();
}

void GCMClientImplTest::CompleteUnregistration(
    const std::string& app_id) {
  std::string response(kUnregistrationResponsePrefix);
  response.append(app_id);

  EXPECT_TRUE(url_loader_factory()->SimulateResponseForPendingRequest(
      GURL(kRegisterUrl), network::URLLoaderCompletionStatus(net::OK),
      network::CreateURLResponseHead(net::HTTP_OK), response));

  // Give a chance for GCMStoreImpl::Backend to finish persisting data.
  PumpLoopUntilIdle();
}

bool GCMClientImplTest::ExistsRegistration(const std::string& app_id) const {
  return ExistsGCMRegistrationInMap(gcm_client_->registrations_, app_id);
}

void GCMClientImplTest::AddRegistration(
    const std::string& app_id,
    const std::vector<std::string>& sender_ids,
    const std::string& registration_id) {
  auto registration = base::MakeRefCounted<GCMRegistrationInfo>();
  registration->app_id = app_id;
  registration->sender_ids = sender_ids;
  gcm_client_->registrations_.emplace(std::move(registration), registration_id);
}

void GCMClientImplTest::InitializeGCMClient() {
  clock()->Advance(base::Milliseconds(1));

  // Actual initialization.
  GCMClient::ChromeBuildInfo chrome_build_info;
  chrome_build_info.version = kChromeVersion;
  chrome_build_info.product_category_for_subtypes = kProductCategoryForSubtypes;
  gcm_client_->Initialize(
      chrome_build_info, gcm_store_path(),
      task_environment_.GetMainThreadTaskRunner(),
      base::SingleThreadTaskRunner::GetCurrentDefault(), base::DoNothing(),
      base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
          &test_url_loader_factory_),
      network::TestNetworkConnectionTracker::GetInstance(),
      base::WrapUnique<Encryptor>(new FakeEncryptor), this);
}

void GCMClientImplTest::StartGCMClient() {
  // Start loading and check-in.
  gcm_client_->Start(GCMClient::IMMEDIATE_START);

  PumpLoopUntilIdle();
}

void GCMClientImplTest::Register(const std::string& app_id,
                                 const std::vector<std::string>& senders) {
  auto gcm_info = base::MakeRefCounted<GCMRegistrationInfo>();
  gcm_info->app_id = app_id;
  gcm_info->sender_ids = senders;
  gcm_client()->Register(std::move(gcm_info));
}

void GCMClientImplTest::Unregister(const std::string& app_id) {
  auto gcm_info = base::MakeRefCounted<GCMRegistrationInfo>();
  gcm_info->app_id = app_id;
  gcm_client()->Unregister(std::move(gcm_info));
}

void GCMClientImplTest::ReceiveMessageFromMCS(const MCSMessage& message) {
  gcm_client_->recorder_.RecordConnectionInitiated(std::string());
  gcm_client_->recorder_.RecordConnectionSuccess();
  gcm_client_->OnMessageReceivedFromMCS(message);
}

void GCMClientImplTest::ReceiveOnMessageSentToMCS(
      const std::string& app_id,
      const std::string& message_id,
      const MCSClient::MessageSendStatus status) {
  gcm_client_->OnMessageSentToMCS(0LL, app_id, message_id, status);
}

void GCMClientImplTest::OnGCMReady(
    const std::vector<AccountMapping>& account_mappings,
    const base::Time& last_token_fetch_time) {
  last_event_ = LOADING_COMPLETED;
  last_account_mappings_ = account_mappings;
  last_token_fetch_time_ = last_token_fetch_time;
}

void GCMClientImplTest::OnMessageReceived(const std::string& registration_id,
                                          const IncomingMessage& message) {
  last_event_ = MESSAGE_RECEIVED;
  last_app_id_ = registration_id;
  last_message_ = message;
}

void GCMClientImplTest::OnRegisterFinished(
    scoped_refptr<RegistrationInfo> registration_info,
    const std::string& registration_id,
    GCMClient::Result result) {
  last_event_ = REGISTRATION_COMPLETED;
  last_app_id_ = registration_info->app_id;
  last_registration_id_ = registration_id;
  last_result_ = result;
}

void GCMClientImplTest::OnUnregisterFinished(
    scoped_refptr<RegistrationInfo> registration_info,
    GCMClient::Result result) {
  last_event_ = UNREGISTRATION_COMPLETED;
  last_app_id_ = registration_info->app_id;
  last_result_ = result;
}

void GCMClientImplTest::OnMessagesDeleted(const std::string& app_id) {
  last_event_ = MESSAGES_DELETED;
  last_app_id_ = app_id;
}

void GCMClientImplTest::OnMessageSendError(
    const std::string& app_id,
    const gcm::GCMClient::SendErrorDetails& send_error_details) {
  last_event_ = MESSAGE_SEND_ERROR;
  last_app_id_ = app_id;
  last_error_details_ = send_error_details;
}

void GCMClientImplTest::OnSendAcknowledged(const std::string& app_id,
                                           const std::string& message_id) {
  last_event_ = MESSAGE_SEND_ACK;
  last_app_id_ = app_id;
  last_message_id_ = message_id;
}

int64_t GCMClientImplTest::CurrentTime() {
  return clock()->Now().ToInternalValue() / base::Time::kMicrosecondsPerSecond;
}

TEST_F(GCMClientImplTest, LoadingCompleted) {
  EXPECT_EQ(LOADING_COMPLETED, last_event());
  EXPECT_EQ(kDeviceAndroidId, mcs_client()->last_android_id());
  EXPECT_EQ(kDeviceSecurityToken, mcs_client()->last_security_token());

  // Checking freshly loaded CheckinInfo.
  EXPECT_EQ(kDeviceAndroidId, device_checkin_info().android_id);
  EXPECT_EQ(kDeviceSecurityToken, device_checkin_info().secret);
  EXPECT_TRUE(device_checkin_info().accounts_set);
}

TEST_F(GCMClientImplTest, LoadingBusted) {
  // Close the GCM store.
  gcm_client()->Stop();
  PumpLoopUntilIdle();

  // Mess up the store.
  EXPECT_TRUE(leveldb_chrome::CorruptClosedDBForTesting(gcm_store_path()));

  // Restart GCM client. The store should be reset and the loading should
  // complete successfully.
  reset_last_event();
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  StartGCMClient();
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId2, kDeviceSecurityToken2, std::string(),
                      std::map<std::string, std::string>()));

  EXPECT_EQ(LOADING_COMPLETED, last_event());
  EXPECT_EQ(kDeviceAndroidId2, mcs_client()->last_android_id());
  EXPECT_EQ(kDeviceSecurityToken2, mcs_client()->last_security_token());
}

TEST_F(GCMClientImplTest, LoadingWithEmptyDirectory) {
  // Close the GCM store.
  gcm_client()->Stop();
  PumpLoopUntilIdle();

  // Make the store directory empty, to simulate a previous destroy store
  // operation failing to delete the store directory.
  ASSERT_TRUE(base::DeletePathRecursively(gcm_store_path()));
  ASSERT_TRUE(base::CreateDirectory(gcm_store_path()));

  base::HistogramTester histogram_tester;

  // Restart GCM client. The store should be considered to not exist.
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  gcm_client()->Start(GCMClient::DELAYED_START);
  PumpLoopUntilIdle();
  // Since the store does not exist, the database should not have been opened.
  histogram_tester.ExpectTotalCount("GCM.Database.Open", 0);
  // Without a store, DELAYED_START loading should only reach INITIALIZED state.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // The store directory should still exist (and be empty). If not, then the
  // DELAYED_START load has probably reset the store, rather than leaving that
  // to the next IMMEDIATE_START load as expected.
  ASSERT_TRUE(base::DirectoryExists(gcm_store_path()));
  ASSERT_FALSE(
      base::PathExists(gcm_store_path().Append(FILE_PATH_LITERAL("CURRENT"))));

  // IMMEDIATE_START loading should successfully create a new store despite the
  // empty directory.
  reset_last_event();
  StartGCMClient();
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId2, kDeviceSecurityToken2, std::string(),
                      std::map<std::string, std::string>()));
  EXPECT_EQ(LOADING_COMPLETED, last_event());
  EXPECT_EQ(GCMClientImpl::READY, gcm_client_state());
  EXPECT_EQ(kDeviceAndroidId2, mcs_client()->last_android_id());
  EXPECT_EQ(kDeviceSecurityToken2, mcs_client()->last_security_token());
}

TEST_F(GCMClientImplTest, DestroyStoreWhenNotNeeded) {
  // Close the GCM store.
  gcm_client()->Stop();
  PumpLoopUntilIdle();

  // Restart GCM client. The store is loaded successfully.
  reset_last_event();
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  gcm_client()->Start(GCMClient::DELAYED_START);
  PumpLoopUntilIdle();

  EXPECT_EQ(GCMClientImpl::LOADED, gcm_client_state());
  EXPECT_TRUE(device_checkin_info().android_id);
  EXPECT_TRUE(device_checkin_info().secret);

  // Fast forward the clock to trigger the store destroying logic.
  FastForwardBy(base::Milliseconds(300000));
  PumpLoopUntilIdle();

  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());
  EXPECT_FALSE(device_checkin_info().android_id);
  EXPECT_FALSE(device_checkin_info().secret);
}

TEST_F(GCMClientImplTest, SerializeAndDeserialize) {
  std::vector<std::string> senders{"sender"};
  auto gcm_info = base::MakeRefCounted<GCMRegistrationInfo>();
  gcm_info->app_id = kExtensionAppId;
  gcm_info->sender_ids = senders;
  gcm_info->last_validated = clock()->Now();

  auto gcm_info_deserialized = base::MakeRefCounted<GCMRegistrationInfo>();
  std::string gcm_registration_id_deserialized;
  {
    std::string serialized_key = gcm_info->GetSerializedKey();
    std::string serialized_value =
        gcm_info->GetSerializedValue(kRegistrationId);

    ASSERT_TRUE(gcm_info_deserialized->Deserialize(
        serialized_key, serialized_value, &gcm_registration_id_deserialized));
  }

  EXPECT_EQ(gcm_info->app_id, gcm_info_deserialized->app_id);
  EXPECT_EQ(gcm_info->sender_ids, gcm_info_deserialized->sender_ids);
  EXPECT_EQ(gcm_info->last_validated, gcm_info_deserialized->last_validated);
  EXPECT_EQ(kRegistrationId, gcm_registration_id_deserialized);

  auto instance_id_info = base::MakeRefCounted<InstanceIDTokenInfo>();
  instance_id_info->app_id = kExtensionAppId;
  instance_id_info->last_validated = clock()->Now();
  instance_id_info->authorized_entity = "different_sender";
  instance_id_info->scope = "scope";

  auto instance_id_info_deserialized =
      base::MakeRefCounted<InstanceIDTokenInfo>();
  std::string instance_id_registration_id_deserialized;
  {
    std::string serialized_key = instance_id_info->GetSerializedKey();
    std::string serialized_value =
        instance_id_info->GetSerializedValue(kRegistrationId);

    ASSERT_TRUE(instance_id_info_deserialized->Deserialize(
        serialized_key, serialized_value,
        &instance_id_registration_id_deserialized));
  }

  EXPECT_EQ(instance_id_info->app_id, instance_id_info_deserialized->app_id);
  EXPECT_EQ(instance_id_info->last_validated,
            instance_id_info_deserialized->last_validated);
  EXPECT_EQ(instance_id_info->authorized_entity,
            instance_id_info_deserialized->authorized_entity);
  EXPECT_EQ(instance_id_info->scope, instance_id_info_deserialized->scope);
  EXPECT_EQ(kRegistrationId, instance_id_registration_id_deserialized);
}

TEST_F(GCMClientImplTest, RegisterApp) {
  EXPECT_FALSE(ExistsRegistration(kExtensionAppId));

  std::vector<std::string> senders;
  senders.push_back("sender");
  Register(kExtensionAppId, senders);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));
}

TEST_F(GCMClientImplTest, RegisterAppFromCache) {
  EXPECT_FALSE(ExistsRegistration(kExtensionAppId));

  std::vector<std::string> senders;
  senders.push_back("sender");
  Register(kExtensionAppId, senders);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id"));
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));

  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());

  // Recreate GCMClient in order to load from the persistent store.
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  StartGCMClient();

  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));
}

TEST_F(GCMClientImplTest, RegisterPreviousSenderAgain) {
  EXPECT_FALSE(ExistsRegistration(kExtensionAppId));

  // Register a sender.
  std::vector<std::string> senders;
  senders.push_back("sender");
  Register(kExtensionAppId, senders);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));

  reset_last_event();

  // Register a different sender. Different registration ID from previous one
  // should be returned.
  std::vector<std::string> senders2;
  senders2.push_back("sender2");
  Register(kExtensionAppId, senders2);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id2"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id2", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));

  reset_last_event();

  // Register the 1st sender again. Different registration ID from previous one
  // should be returned.
  std::vector<std::string> senders3;
  senders3.push_back("sender");
  Register(kExtensionAppId, senders3);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));
}

TEST_F(GCMClientImplTest, RegisterAgainWhenTokenIsFresh) {
  // Register a sender.
  std::vector<std::string> senders;
  senders.push_back("sender");
  Register(kExtensionAppId, senders);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id"));
  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));

  reset_last_event();

  // Advance time by (kTestTokenInvalidationPeriod)/2
  clock()->Advance(base::Days(kTestTokenInvalidationPeriod / 2));

  // Register the same sender again. The same registration ID as the
  // previous one should be returned, and we should *not* send a
  // registration request to the GCM server.
  Register(kExtensionAppId, senders);
  PumpLoopUntilIdle();

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));
}

TEST_F(GCMClientImplTest, RegisterAgainWhenTokenIsStale) {
  // Register a sender.
  std::vector<std::string> senders;
  senders.push_back("sender");
  Register(kExtensionAppId, senders);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));

  reset_last_event();

  // Advance time by kTestTokenInvalidationPeriod
  clock()->Advance(base::Days(kTestTokenInvalidationPeriod));

  // Register the same sender again. Different registration ID from the
  // previous one should be returned.
  Register(kExtensionAppId, senders);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id2"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("reg_id2", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));
}

TEST_F(GCMClientImplTest, UnregisterApp) {
  EXPECT_FALSE(ExistsRegistration(kExtensionAppId));

  std::vector<std::string> senders;
  senders.push_back("sender");
  Register(kExtensionAppId, senders);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id"));
  EXPECT_TRUE(ExistsRegistration(kExtensionAppId));

  Unregister(kExtensionAppId);
  ASSERT_NO_FATAL_FAILURE(CompleteUnregistration(kExtensionAppId));

  EXPECT_EQ(UNREGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_FALSE(ExistsRegistration(kExtensionAppId));
}

// Tests that stopping the GCMClient also deletes pending registration requests.
// This is tested by checking that url fetcher contained in the request was
// deleted.
TEST_F(GCMClientImplTest, DeletePendingRequestsWhenStopping) {
  std::vector<std::string> senders;
  senders.push_back("sender");
  Register(kExtensionAppId, senders);

  gcm_client()->Stop();
  PumpLoopUntilIdle();
  EXPECT_EQ(0, url_loader_factory()->NumPending());
}

TEST_F(GCMClientImplTest, DispatchDownstreamMessage) {
  // Register to receive messages from kSender and kSender2 only.
  std::vector<std::string> senders;
  senders.push_back(kSender);
  senders.push_back(kSender2);
  AddRegistration(kExtensionAppId, senders, "reg_id");

  std::map<std::string, std::string> expected_data;
  expected_data["message_type"] = "gcm";
  expected_data["key"] = "value";
  expected_data["key2"] = "value2";

  // Message for kSender will be received.
  MCSMessage message(BuildDownstreamMessage(
      kSender, kExtensionAppId, std::string() /* subtype */, expected_data,
      std::string() /* raw_data */));
  EXPECT_TRUE(message.IsValid());
  ReceiveMessageFromMCS(message);

  expected_data.erase(expected_data.find("message_type"));
  EXPECT_EQ(MESSAGE_RECEIVED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(expected_data.size(), last_message().data.size());
  EXPECT_EQ(expected_data, last_message().data);
  EXPECT_EQ(kSender, last_message().sender_id);

  reset_last_event();

  // Message for kSender2 will be received.
  MCSMessage message2(BuildDownstreamMessage(
      kSender2, kExtensionAppId, std::string() /* subtype */, expected_data,
      std::string() /* raw_data */));
  EXPECT_TRUE(message2.IsValid());
  ReceiveMessageFromMCS(message2);

  EXPECT_EQ(MESSAGE_RECEIVED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(expected_data.size(), last_message().data.size());
  EXPECT_EQ(expected_data, last_message().data);
  EXPECT_EQ(kSender2, last_message().sender_id);
}

TEST_F(GCMClientImplTest, DispatchDownstreamMessageRawData) {
  std::vector<std::string> senders(1, kSender);
  AddRegistration(kExtensionAppId, senders, "reg_id");

  std::map<std::string, std::string> expected_data;

  MCSMessage message(BuildDownstreamMessage(kSender, kExtensionAppId,
                                            std::string() /* subtype */,
                                            expected_data, kRawData));
  EXPECT_TRUE(message.IsValid());
  ReceiveMessageFromMCS(message);

  EXPECT_EQ(MESSAGE_RECEIVED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(expected_data.size(), last_message().data.size());
  EXPECT_EQ(kSender, last_message().sender_id);
  EXPECT_EQ(kRawData, last_message().raw_data);
}

TEST_F(GCMClientImplTest, DispatchDownstreamMessageSendError) {
  std::map<std::string, std::string> expected_data = {
      {"message_type", "send_error"}, {"error_details", "some details"}};

  MCSMessage message(BuildDownstreamMessage(
      kSender, kExtensionAppId, std::string() /* subtype */, expected_data,
      std::string() /* raw_data */));
  EXPECT_TRUE(message.IsValid());
  ReceiveMessageFromMCS(message);

  EXPECT_EQ(MESSAGE_SEND_ERROR, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(kMessageId, last_error_details().message_id);
  EXPECT_EQ(1UL, last_error_details().additional_data.size());
  auto iter = last_error_details().additional_data.find("error_details");
  EXPECT_TRUE(iter != last_error_details().additional_data.end());
  EXPECT_EQ("some details", iter->second);
}

TEST_F(GCMClientImplTest, DispatchDownstreamMessgaesDeleted) {
  std::map<std::string, std::string> expected_data;
  expected_data["message_type"] = "deleted_messages";
  MCSMessage message(BuildDownstreamMessage(
      kSender, kExtensionAppId, std::string() /* subtype */, expected_data,
      std::string() /* raw_data */));
  EXPECT_TRUE(message.IsValid());
  ReceiveMessageFromMCS(message);

  EXPECT_EQ(MESSAGES_DELETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
}

TEST_F(GCMClientImplTest, SendMessage) {
  OutgoingMessage message;
  message.id = "007";
  message.time_to_live = 500;
  message.data["key"] = "value";
  gcm_client()->Send(kExtensionAppId, kSender, message);

  EXPECT_EQ(kDataMessageStanzaTag, mcs_client()->last_message_tag());
  EXPECT_EQ(kExtensionAppId,
            mcs_client()->last_data_message_stanza().category());
  EXPECT_EQ(kSender, mcs_client()->last_data_message_stanza().to());
  EXPECT_EQ(500, mcs_client()->last_data_message_stanza().ttl());
  EXPECT_EQ(CurrentTime(), mcs_client()->last_data_message_stanza().sent());
  EXPECT_EQ("007", mcs_client()->last_data_message_stanza().id());
  EXPECT_EQ("gcm@chrome.com", mcs_client()->last_data_message_stanza().from());
  EXPECT_EQ(kSender, mcs_client()->last_data_message_stanza().to());
  EXPECT_EQ("key", mcs_client()->last_data_message_stanza().app_data(0).key());
  EXPECT_EQ("value",
            mcs_client()->last_data_message_stanza().app_data(0).value());
}

TEST_F(GCMClientImplTest, SendMessageAcknowledged) {
  ReceiveOnMessageSentToMCS(kExtensionAppId, "007", MCSClient::SENT);
  EXPECT_EQ(MESSAGE_SEND_ACK, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("007", last_message_id());
}

class GCMClientImplCheckinTest : public GCMClientImplTest {
 public:
  GCMClientImplCheckinTest();
  ~GCMClientImplCheckinTest() override;

  void SetUp() override;
};

GCMClientImplCheckinTest::GCMClientImplCheckinTest() {
}

GCMClientImplCheckinTest::~GCMClientImplCheckinTest() {
}

void GCMClientImplCheckinTest::SetUp() {
  testing::Test::SetUp();
  // Creating unique temp directory that will be used by GCMStore shared between
  // GCM Client and G-services settings.
  ASSERT_TRUE(CreateUniqueTempDir());
  // Time will be advancing one hour every time it is checked.
  BuildGCMClient(base::Seconds(kSettingsCheckinInterval));
  InitializeGCMClient();
  StartGCMClient();
}

TEST_F(GCMClientImplCheckinTest, GServicesSettingsAfterInitialCheckin) {
  std::map<std::string, std::string> settings;
  settings["checkin_interval"] = base::NumberToString(kSettingsCheckinInterval);
  settings["checkin_url"] = "http://alternative.url/checkin";
  settings["gcm_hostname"] = "alternative.gcm.host";
  settings["gcm_secure_port"] = "7777";
  settings["gcm_registration_url"] = "http://alternative.url/registration";
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
                      GServicesSettings::CalculateDigest(settings), settings));
  EXPECT_EQ(base::Seconds(kSettingsCheckinInterval),
            gservices_settings().GetCheckinInterval());
  EXPECT_EQ(GURL("http://alternative.url/checkin"),
            gservices_settings().GetCheckinURL());
  EXPECT_EQ(GURL("http://alternative.url/registration"),
            gservices_settings().GetRegistrationURL());
  EXPECT_EQ(GURL("https://alternative.gcm.host:7777"),
            gservices_settings().GetMCSMainEndpoint());
  EXPECT_EQ(GURL("https://alternative.gcm.host:443"),
            gservices_settings().GetMCSFallbackEndpoint());
}

// This test only checks that periodic checkin happens.
TEST_F(GCMClientImplCheckinTest, PeriodicCheckin) {
  std::map<std::string, std::string> settings;
  settings["checkin_interval"] = base::NumberToString(kSettingsCheckinInterval);
  settings["checkin_url"] = "http://alternative.url/checkin";
  settings["gcm_hostname"] = "alternative.gcm.host";
  settings["gcm_secure_port"] = "7777";
  settings["gcm_registration_url"] = "http://alternative.url/registration";
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
                      GServicesSettings::CalculateDigest(settings), settings));

  EXPECT_EQ(2, clock()->call_count());

  PumpLoopUntilIdle();
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
                      GServicesSettings::CalculateDigest(settings), settings));
}

TEST_F(GCMClientImplCheckinTest, LoadGSettingsFromStore) {
  std::map<std::string, std::string> settings;
  settings["checkin_interval"] = base::NumberToString(kSettingsCheckinInterval);
  settings["checkin_url"] = "http://alternative.url/checkin";
  settings["gcm_hostname"] = "alternative.gcm.host";
  settings["gcm_secure_port"] = "7777";
  settings["gcm_registration_url"] = "http://alternative.url/registration";
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
                      GServicesSettings::CalculateDigest(settings), settings));

  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  StartGCMClient();

  EXPECT_EQ(base::Seconds(kSettingsCheckinInterval),
            gservices_settings().GetCheckinInterval());
  EXPECT_EQ(GURL("http://alternative.url/checkin"),
            gservices_settings().GetCheckinURL());
  EXPECT_EQ(GURL("http://alternative.url/registration"),
            gservices_settings().GetRegistrationURL());
  EXPECT_EQ(GURL("https://alternative.gcm.host:7777"),
            gservices_settings().GetMCSMainEndpoint());
  EXPECT_EQ(GURL("https://alternative.gcm.host:443"),
            gservices_settings().GetMCSFallbackEndpoint());
}

// This test only checks that periodic checkin happens.
TEST_F(GCMClientImplCheckinTest, CheckinWithAccountsEmpty) {
  std::map<std::string, std::string> settings;
  settings["checkin_interval"] = base::NumberToString(kSettingsCheckinInterval);
  settings["checkin_url"] = "http://alternative.url/checkin";
  settings["gcm_hostname"] = "alternative.gcm.host";
  settings["gcm_secure_port"] = "7777";
  settings["gcm_registration_url"] = "http://alternative.url/registration";
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
                      GServicesSettings::CalculateDigest(settings), settings));

  std::vector<GCMClient::AccountTokenInfo> account_tokens;
  account_tokens.push_back(MakeAccountToken("test_user1@gmail.com", "token1"));
  account_tokens.push_back(MakeAccountToken("test_user2@gmail.com", "token2"));
  gcm_client()->SetAccountTokens(account_tokens);

  EXPECT_TRUE(device_checkin_info().accounts_set);

  PumpLoopUntilIdle();
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken,
                      GServicesSettings::CalculateDigest(settings), settings));

  EXPECT_TRUE(device_checkin_info().accounts_set);
}

TEST_F(GCMClientImplCheckinTest, ResetStoreWhenCheckinRejected) {
  base::HistogramTester histogram_tester;
  std::map<std::string, std::string> settings;
  ASSERT_NO_FATAL_FAILURE(FailCheckin(net::HTTP_UNAUTHORIZED));
  PumpLoopUntilIdle();

  // Store should have been destroyed. Restart client and verify the initial
  // checkin response is persisted.
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  StartGCMClient();
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId2, kDeviceSecurityToken2,
                      GServicesSettings::CalculateDigest(settings), settings));

  EXPECT_EQ(LOADING_COMPLETED, last_event());
  EXPECT_EQ(kDeviceAndroidId2, mcs_client()->last_android_id());
  EXPECT_EQ(kDeviceSecurityToken2, mcs_client()->last_security_token());
}

class GCMClientImplStartAndStopTest : public GCMClientImplTest {
 public:
  GCMClientImplStartAndStopTest();
  ~GCMClientImplStartAndStopTest() override;

  void SetUp() override;

  void DefaultCompleteCheckin();
};

GCMClientImplStartAndStopTest::GCMClientImplStartAndStopTest() {
}

GCMClientImplStartAndStopTest::~GCMClientImplStartAndStopTest() {
}

void GCMClientImplStartAndStopTest::SetUp() {
  testing::Test::SetUp();
  ASSERT_TRUE(CreateUniqueTempDir());
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
}

void GCMClientImplStartAndStopTest::DefaultCompleteCheckin() {
  ASSERT_NO_FATAL_FAILURE(
      CompleteCheckin(kDeviceAndroidId, kDeviceSecurityToken, std::string(),
                      std::map<std::string, std::string>()));
  PumpLoopUntilIdle();
}

TEST_F(GCMClientImplStartAndStopTest, StartStopAndRestart) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Delay start the GCM.
  gcm_client()->Start(GCMClient::DELAYED_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Stop the GCM.
  gcm_client()->Stop();
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Restart the GCM without delay.
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIAL_DEVICE_CHECKIN, gcm_client_state());
}

TEST_F(GCMClientImplStartAndStopTest, DelayedStartAndStopImmediately) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Delay start the GCM and then stop it immediately.
  gcm_client()->Start(GCMClient::DELAYED_START);
  gcm_client()->Stop();
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());
}

TEST_F(GCMClientImplStartAndStopTest, ImmediateStartAndStopImmediately) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Start the GCM and then stop it immediately.
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  gcm_client()->Stop();
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());
}

TEST_F(GCMClientImplStartAndStopTest, DelayedStartStopAndRestart) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Delay start the GCM and then stop and restart it immediately.
  gcm_client()->Start(GCMClient::DELAYED_START);
  gcm_client()->Stop();
  gcm_client()->Start(GCMClient::DELAYED_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());
}

TEST_F(GCMClientImplStartAndStopTest, ImmediateStartStopAndRestart) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Start the GCM and then stop and restart it immediately.
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  gcm_client()->Stop();
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIAL_DEVICE_CHECKIN, gcm_client_state());
}

TEST_F(GCMClientImplStartAndStopTest, ImmediateStartAndThenImmediateStart) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Start the GCM immediately and complete the checkin.
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIAL_DEVICE_CHECKIN, gcm_client_state());
  ASSERT_NO_FATAL_FAILURE(DefaultCompleteCheckin());
  EXPECT_EQ(GCMClientImpl::READY, gcm_client_state());

  // Stop the GCM.
  gcm_client()->Stop();
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Start the GCM immediately. GCMClientImpl should be in READY state.
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::READY, gcm_client_state());
}

TEST_F(GCMClientImplStartAndStopTest, ImmediateStartAndThenDelayStart) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Start the GCM immediately and complete the checkin.
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIAL_DEVICE_CHECKIN, gcm_client_state());
  ASSERT_NO_FATAL_FAILURE(DefaultCompleteCheckin());
  EXPECT_EQ(GCMClientImpl::READY, gcm_client_state());

  // Stop the GCM.
  gcm_client()->Stop();
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Delay start the GCM. GCMClientImpl should be in LOADED state.
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  gcm_client()->Start(GCMClient::DELAYED_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::LOADED, gcm_client_state());
}

TEST_F(GCMClientImplStartAndStopTest, DelayedStartRace) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Delay start the GCM, then start it immediately while it's still loading.
  gcm_client()->Start(GCMClient::DELAYED_START);
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIAL_DEVICE_CHECKIN, gcm_client_state());
  ASSERT_NO_FATAL_FAILURE(DefaultCompleteCheckin());
  EXPECT_EQ(GCMClientImpl::READY, gcm_client_state());
}

TEST_F(GCMClientImplStartAndStopTest, DelayedStart) {
  // GCMClientImpl should be in INITIALIZED state at first.
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Delay start the GCM. The store will not be loaded and GCMClientImpl should
  // still be in INITIALIZED state.
  gcm_client()->Start(GCMClient::DELAYED_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Start the GCM immediately and complete the checkin.
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIAL_DEVICE_CHECKIN, gcm_client_state());
  ASSERT_NO_FATAL_FAILURE(DefaultCompleteCheckin());
  EXPECT_EQ(GCMClientImpl::READY, gcm_client_state());

  // Registration.
  std::vector<std::string> senders;
  senders.push_back("sender");
  Register(kExtensionAppId, senders);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("reg_id"));
  EXPECT_EQ(GCMClientImpl::READY, gcm_client_state());

  // Stop the GCM.
  gcm_client()->Stop();
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::INITIALIZED, gcm_client_state());

  // Delay start the GCM. GCM is indeed started without delay because the
  // registration record has been found.
  BuildGCMClient(base::TimeDelta());
  InitializeGCMClient();
  gcm_client()->Start(GCMClient::DELAYED_START);
  PumpLoopUntilIdle();
  EXPECT_EQ(GCMClientImpl::READY, gcm_client_state());
}

// Test for known account mappings and last token fetching time being passed
// to OnGCMReady.
TEST_F(GCMClientImplStartAndStopTest, OnGCMReadyAccountsAndTokenFetchingTime) {
  // Start the GCM and wait until it is ready.
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();
  ASSERT_NO_FATAL_FAILURE(DefaultCompleteCheckin());

  base::Time expected_time = base::Time::Now();
  gcm_client()->SetLastTokenFetchTime(expected_time);
  AccountMapping expected_mapping;
  expected_mapping.account_id = CoreAccountId::FromGaiaId(GaiaId("accId"));
  expected_mapping.email = "email@gmail.com";
  expected_mapping.status = AccountMapping::MAPPED;
  expected_mapping.status_change_timestamp = expected_time;
  gcm_client()->UpdateAccountMapping(expected_mapping);
  PumpLoopUntilIdle();

  // Stop the GCM.
  gcm_client()->Stop();
  PumpLoopUntilIdle();

  // Restart the GCM.
  gcm_client()->Start(GCMClient::IMMEDIATE_START);
  PumpLoopUntilIdle();

  EXPECT_EQ(LOADING_COMPLETED, last_event());
  EXPECT_EQ(expected_time, last_token_fetch_time());
  ASSERT_EQ(1UL, last_account_mappings().size());
  const AccountMapping& actual_mapping = last_account_mappings()[0];
  EXPECT_EQ(expected_mapping.account_id, actual_mapping.account_id);
  EXPECT_EQ(expected_mapping.email, actual_mapping.email);
  EXPECT_EQ(expected_mapping.status, actual_mapping.status);
  EXPECT_EQ(expected_mapping.status_change_timestamp,
            actual_mapping.status_change_timestamp);
}


class GCMClientInstanceIDTest : public GCMClientImplTest {
 public:
  GCMClientInstanceIDTest();
  ~GCMClientInstanceIDTest() override;

  void AddInstanceID(const std::string& app_id,
                     const std::string& instance_id);
  void RemoveInstanceID(const std::string& app_id);
  void GetToken(const std::string& app_id,
                const std::string& authorized_entity,
                const std::string& scope);
  void DeleteToken(const std::string& app_id,
                   const std::string& authorized_entity,
                   const std::string& scope);
  void CompleteDeleteToken();
  bool ExistsToken(const std::string& app_id,
                   const std::string& authorized_entity,
                   const std::string& scope) const;
};

GCMClientInstanceIDTest::GCMClientInstanceIDTest() {
}

GCMClientInstanceIDTest::~GCMClientInstanceIDTest() {
}

void GCMClientInstanceIDTest::AddInstanceID(const std::string& app_id,
                                            const std::string& instance_id) {
  gcm_client()->AddInstanceIDData(app_id, instance_id, "123");
}

void GCMClientInstanceIDTest::RemoveInstanceID(const std::string& app_id) {
  gcm_client()->RemoveInstanceIDData(app_id);
}

void GCMClientInstanceIDTest::GetToken(const std::string& app_id,
                                       const std::string& authorized_entity,
                                       const std::string& scope) {
  auto instance_id_info = base::MakeRefCounted<InstanceIDTokenInfo>();
  instance_id_info->app_id = app_id;
  instance_id_info->authorized_entity = authorized_entity;
  instance_id_info->scope = scope;
  gcm_client()->Register(std::move(instance_id_info));
}

void GCMClientInstanceIDTest::DeleteToken(const std::string& app_id,
                                          const std::string& authorized_entity,
                                          const std::string& scope) {
  auto instance_id_info = base::MakeRefCounted<InstanceIDTokenInfo>();
  instance_id_info->app_id = app_id;
  instance_id_info->authorized_entity = authorized_entity;
  instance_id_info->scope = scope;
  gcm_client()->Unregister(std::move(instance_id_info));
}

void GCMClientInstanceIDTest::CompleteDeleteToken() {
  std::string response(kDeleteTokenResponse);

  EXPECT_TRUE(url_loader_factory()->SimulateResponseForPendingRequest(
      GURL(kRegisterUrl), network::URLLoaderCompletionStatus(net::OK),
      network::CreateURLResponseHead(net::HTTP_OK), response));

  // Give a chance for GCMStoreImpl::Backend to finish persisting data.
  PumpLoopUntilIdle();
}

bool GCMClientInstanceIDTest::ExistsToken(const std::string& app_id,
                                          const std::string& authorized_entity,
                                          const std::string& scope) const {
  auto instance_id_info = base::MakeRefCounted<InstanceIDTokenInfo>();
  instance_id_info->app_id = app_id;
  instance_id_info->authorized_entity = authorized_entity;
  instance_id_info->scope = scope;
  return gcm_client()->registrations_.count(std::move(instance_id_info)) > 0;
}

TEST_F(GCMClientInstanceIDTest, GetToken) {
  AddInstanceID(kExtensionAppId, kInstanceID);

  // Get a token.
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender, kScope));
  GetToken(kExtensionAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token1"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("token1", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender, kScope));

  // Get another token.
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender2, kScope));
  GetToken(kExtensionAppId, kSender2, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token2"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("token2", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender2, kScope));
  // The 1st token still exists.
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender, kScope));
}

// Most tests in this file use kExtensionAppId which is special-cased by
// InstanceIDUsesSubtypeForAppId in gcm_client_impl.cc. This test uses
// kSubtypeAppId to cover the alternate case.
TEST_F(GCMClientInstanceIDTest, GetTokenWithSubtype) {
  ASSERT_EQ(GCMClientImpl::READY, gcm_client_state());

  AddInstanceID(kSubtypeAppId, kInstanceID);

  EXPECT_FALSE(ExistsToken(kSubtypeAppId, kSender, kScope));

  // Get a token.
  GetToken(kSubtypeAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token1"));
  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kSubtypeAppId, last_app_id());
  EXPECT_EQ("token1", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsToken(kSubtypeAppId, kSender, kScope));

  // Delete the token.
  DeleteToken(kSubtypeAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteDeleteToken());
  EXPECT_FALSE(ExistsToken(kSubtypeAppId, kSender, kScope));
}

TEST_F(GCMClientInstanceIDTest, DeleteInvalidToken) {
  AddInstanceID(kExtensionAppId, kInstanceID);

  // Delete an invalid token.
  DeleteToken(kExtensionAppId, "Foo@#$", kScope);
  PumpLoopUntilIdle();

  EXPECT_EQ(UNREGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(GCMClient::INVALID_PARAMETER, last_result());

  reset_last_event();

  // Delete a non-existing token.
  DeleteToken(kExtensionAppId, kSender, kScope);
  PumpLoopUntilIdle();

  EXPECT_EQ(UNREGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(GCMClient::INVALID_PARAMETER, last_result());
}

TEST_F(GCMClientInstanceIDTest, DeleteSingleToken) {
  AddInstanceID(kExtensionAppId, kInstanceID);

  // Get a token.
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender, kScope));
  GetToken(kExtensionAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token1"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("token1", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender, kScope));

  reset_last_event();

  // Get another token.
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender2, kScope));
  GetToken(kExtensionAppId, kSender2, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token2"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("token2", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender2, kScope));
  // The 1st token still exists.
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender, kScope));

  reset_last_event();

  // Delete the 2nd token.
  DeleteToken(kExtensionAppId, kSender2, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteDeleteToken());

  EXPECT_EQ(UNREGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  // The 2nd token is gone while the 1st token still exists.
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender, kScope));
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender2, kScope));

  reset_last_event();

  // Delete the 1st token.
  DeleteToken(kExtensionAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteDeleteToken());

  EXPECT_EQ(UNREGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  // Both tokens are gone now.
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender, kScope));
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender, kScope));

  reset_last_event();

  // Trying to delete the token again will get an error.
  DeleteToken(kExtensionAppId, kSender, kScope);
  PumpLoopUntilIdle();

  EXPECT_EQ(UNREGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(GCMClient::INVALID_PARAMETER, last_result());
}

TEST_F(GCMClientInstanceIDTest, DeleteAllTokens) {
  AddInstanceID(kExtensionAppId, kInstanceID);

  // Get a token.
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender, kScope));
  GetToken(kExtensionAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token1"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("token1", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender, kScope));

  reset_last_event();

  // Get another token.
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender2, kScope));
  GetToken(kExtensionAppId, kSender2, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token2"));

  EXPECT_EQ(REGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ("token2", last_registration_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender2, kScope));
  // The 1st token still exists.
  EXPECT_TRUE(ExistsToken(kExtensionAppId, kSender, kScope));

  reset_last_event();

  // Delete all tokens.
  DeleteToken(kExtensionAppId, "*", "*");
  ASSERT_NO_FATAL_FAILURE(CompleteDeleteToken());

  EXPECT_EQ(UNREGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
  // All tokens are gone now.
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender, kScope));
  EXPECT_FALSE(ExistsToken(kExtensionAppId, kSender, kScope));
}

TEST_F(GCMClientInstanceIDTest, DeleteAllTokensBeforeGetAnyToken) {
  AddInstanceID(kExtensionAppId, kInstanceID);

  // Delete all tokens without getting a token first.
  DeleteToken(kExtensionAppId, "*", "*");
  // No need to call CompleteDeleteToken since unregistration request should
  // not be triggered.
  PumpLoopUntilIdle();

  EXPECT_EQ(UNREGISTRATION_COMPLETED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(GCMClient::SUCCESS, last_result());
}

TEST_F(GCMClientInstanceIDTest, DispatchDownstreamMessageWithoutSubtype) {
  AddInstanceID(kExtensionAppId, kInstanceID);
  GetToken(kExtensionAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token1"));

  std::map<std::string, std::string> expected_data;

  MCSMessage message(BuildDownstreamMessage(
      kSender, kExtensionAppId, std::string() /* subtype */, expected_data,
      std::string() /* raw_data */));
  EXPECT_TRUE(message.IsValid());
  ReceiveMessageFromMCS(message);

  EXPECT_EQ(MESSAGE_RECEIVED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(expected_data.size(), last_message().data.size());
  EXPECT_EQ(expected_data, last_message().data);
  EXPECT_EQ(kSender, last_message().sender_id);
}

TEST_F(GCMClientInstanceIDTest, DispatchDownstreamMessageWithSubtype) {
  AddInstanceID(kSubtypeAppId, kInstanceID);
  GetToken(kSubtypeAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token1"));

  std::map<std::string, std::string> expected_data;

  MCSMessage message(BuildDownstreamMessage(
      kSender, kProductCategoryForSubtypes, kSubtypeAppId /* subtype */,
      expected_data, std::string() /* raw_data */));
  EXPECT_TRUE(message.IsValid());
  ReceiveMessageFromMCS(message);

  EXPECT_EQ(MESSAGE_RECEIVED, last_event());
  EXPECT_EQ(kSubtypeAppId, last_app_id());
  EXPECT_EQ(expected_data.size(), last_message().data.size());
  EXPECT_EQ(expected_data, last_message().data);
  EXPECT_EQ(kSender, last_message().sender_id);
}

TEST_F(GCMClientInstanceIDTest, DispatchDownstreamMessageWithFakeSubtype) {
  // Victim non-extension registration.
  AddInstanceID(kSubtypeAppId, "iid_1");
  GetToken(kSubtypeAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token1"));

  // Malicious extension registration.
  AddInstanceID(kExtensionAppId, "iid_2");
  GetToken(kExtensionAppId, kSender, kScope);
  ASSERT_NO_FATAL_FAILURE(CompleteRegistration("token2"));

  std::map<std::string, std::string> expected_data;

  // Message for kExtensionAppId should be delivered to the extension rather
  // than the victim app, despite the malicious subtype property attempting to
  // impersonate victim app.
  MCSMessage message(BuildDownstreamMessage(
      kSender, kExtensionAppId /* category */, kSubtypeAppId /* subtype */,
      expected_data, std::string() /* raw_data */));
  EXPECT_TRUE(message.IsValid());
  ReceiveMessageFromMCS(message);

  EXPECT_EQ(MESSAGE_RECEIVED, last_event());
  EXPECT_EQ(kExtensionAppId, last_app_id());
  EXPECT_EQ(expected_data.size(), last_message().data.size());
  EXPECT_EQ(expected_data, last_message().data);
  EXPECT_EQ(kSender, last_message().sender_id);
}

}  // namespace gcm
