blob: 4eeb96d9a35b19d557bb52628bc2cb503355692b [file] [log] [blame]
// Copyright 2014 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/gcm_driver/gcm_driver_desktop.h"
#include <stdint.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/scoped_temp_dir.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/message_loop/message_loop_current.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
#include "components/gcm_driver/fake_gcm_app_handler.h"
#include "components/gcm_driver/fake_gcm_client.h"
#include "components/gcm_driver/fake_gcm_client_factory.h"
#include "components/gcm_driver/gcm_app_handler.h"
#include "components/gcm_driver/gcm_channel_status_request.h"
#include "components/gcm_driver/gcm_channel_status_syncer.h"
#include "components/gcm_driver/gcm_client_factory.h"
#include "components/gcm_driver/gcm_connection_observer.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/sync/protocol/experiment_status.pb.h"
#include "components/sync/protocol/experiments_specifics.pb.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_test_util.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gcm {
namespace {
const char kTestChannelStatusRequestURL[] = "http://channel.status.request.com";
const char kTestAppID1[] = "TestApp1";
const char kTestAppID2[] = "TestApp2";
const char kUserID1[] = "user1";
const char kScope[] = "GCM";
const char kInstanceID1[] = "IID1";
const char kInstanceID2[] = "IID2";
class FakeGCMConnectionObserver : public GCMConnectionObserver {
public:
FakeGCMConnectionObserver();
~FakeGCMConnectionObserver() override;
// gcm::GCMConnectionObserver implementation:
void OnConnected(const net::IPEndPoint& ip_endpoint) override;
void OnDisconnected() override;
bool connected() const { return connected_; }
private:
bool connected_;
};
FakeGCMConnectionObserver::FakeGCMConnectionObserver() : connected_(false) {
}
FakeGCMConnectionObserver::~FakeGCMConnectionObserver() {
}
void FakeGCMConnectionObserver::OnConnected(
const net::IPEndPoint& ip_endpoint) {
connected_ = true;
}
void FakeGCMConnectionObserver::OnDisconnected() {
connected_ = false;
}
void PumpCurrentLoop() {
base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
}
void PumpUILoop() {
PumpCurrentLoop();
}
std::vector<std::string> ToSenderList(const std::string& sender_ids) {
return base::SplitString(
sender_ids, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
} // namespace
class GCMDriverTest : public testing::Test {
public:
enum WaitToFinish {
DO_NOT_WAIT,
WAIT
};
GCMDriverTest();
~GCMDriverTest() override;
// testing::Test:
void SetUp() override;
void TearDown() override;
GCMDriverDesktop* driver() { return driver_.get(); }
FakeGCMAppHandler* gcm_app_handler() { return gcm_app_handler_.get(); }
FakeGCMConnectionObserver* gcm_connection_observer() {
return gcm_connection_observer_.get();
}
const std::string& registration_id() const { return registration_id_; }
GCMClient::Result registration_result() const { return registration_result_; }
const std::string& send_message_id() const { return send_message_id_; }
GCMClient::Result send_result() const { return send_result_; }
GCMClient::Result unregistration_result() const {
return unregistration_result_;
}
const std::string& p256dh() const { return p256dh_; }
const std::string& auth_secret() const { return auth_secret_; }
void PumpIOLoop();
void ClearResults();
bool HasAppHandlers() const;
FakeGCMClient* GetGCMClient();
void CreateDriver();
void ShutdownDriver();
void AddAppHandlers();
void RemoveAppHandlers();
void Register(const std::string& app_id,
const std::vector<std::string>& sender_ids,
WaitToFinish wait_to_finish);
void Send(const std::string& app_id,
const std::string& receiver_id,
const OutgoingMessage& message,
WaitToFinish wait_to_finish);
void GetEncryptionInfo(const std::string& app_id,
WaitToFinish wait_to_finish);
void Unregister(const std::string& app_id, WaitToFinish wait_to_finish);
void WaitForAsyncOperation();
void RegisterCompleted(const std::string& registration_id,
GCMClient::Result result);
void SendCompleted(const std::string& message_id, GCMClient::Result result);
void GetEncryptionInfoCompleted(const std::string& p256dh,
const std::string& auth_secret);
void UnregisterCompleted(GCMClient::Result result);
const base::Closure& async_operation_completed_callback() const {
return async_operation_completed_callback_;
}
void set_async_operation_completed_callback(const base::Closure& callback) {
async_operation_completed_callback_ = callback;
}
private:
base::ScopedTempDir temp_dir_;
TestingPrefServiceSimple prefs_;
base::test::ScopedTaskEnvironment task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::UI};
base::Thread io_thread_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<GCMDriverDesktop> driver_;
std::unique_ptr<FakeGCMAppHandler> gcm_app_handler_;
std::unique_ptr<FakeGCMConnectionObserver> gcm_connection_observer_;
base::Closure async_operation_completed_callback_;
std::string registration_id_;
GCMClient::Result registration_result_;
std::string send_message_id_;
GCMClient::Result send_result_;
GCMClient::Result unregistration_result_;
std::string p256dh_;
std::string auth_secret_;
DISALLOW_COPY_AND_ASSIGN(GCMDriverTest);
};
GCMDriverTest::GCMDriverTest()
: io_thread_("IOThread"),
registration_result_(GCMClient::UNKNOWN_ERROR),
send_result_(GCMClient::UNKNOWN_ERROR),
unregistration_result_(GCMClient::UNKNOWN_ERROR) {}
GCMDriverTest::~GCMDriverTest() {
}
void GCMDriverTest::SetUp() {
GCMChannelStatusSyncer::RegisterPrefs(prefs_.registry());
io_thread_.Start();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
void GCMDriverTest::TearDown() {
if (!driver_)
return;
ShutdownDriver();
driver_.reset();
PumpIOLoop();
io_thread_.Stop();
}
void GCMDriverTest::PumpIOLoop() {
base::RunLoop run_loop;
io_thread_.task_runner()->PostTaskAndReply(
FROM_HERE, base::BindOnce(&PumpCurrentLoop), run_loop.QuitClosure());
run_loop.Run();
}
void GCMDriverTest::ClearResults() {
registration_id_.clear();
registration_result_ = GCMClient::UNKNOWN_ERROR;
send_message_id_.clear();
send_result_ = GCMClient::UNKNOWN_ERROR;
unregistration_result_ = GCMClient::UNKNOWN_ERROR;
}
bool GCMDriverTest::HasAppHandlers() const {
return !driver_->app_handlers().empty();
}
FakeGCMClient* GCMDriverTest::GetGCMClient() {
return static_cast<FakeGCMClient*>(driver_->GetGCMClientForTesting());
}
void GCMDriverTest::CreateDriver() {
scoped_refptr<net::URLRequestContextGetter> request_context =
new net::TestURLRequestContextGetter(io_thread_.task_runner());
GCMClient::ChromeBuildInfo chrome_build_info;
chrome_build_info.product_category_for_subtypes = "com.chrome.macosx";
driver_ = std::make_unique<GCMDriverDesktop>(
std::unique_ptr<GCMClientFactory>(new FakeGCMClientFactory(
base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner())),
chrome_build_info, kTestChannelStatusRequestURL, "user-agent-string",
&prefs_, temp_dir_.GetPath(), base::DoNothing(),
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_),
network::TestNetworkConnectionTracker::GetInstance(),
base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner(),
task_environment_.GetMainThreadTaskRunner());
gcm_app_handler_.reset(new FakeGCMAppHandler);
gcm_connection_observer_.reset(new FakeGCMConnectionObserver);
driver_->AddConnectionObserver(gcm_connection_observer_.get());
}
void GCMDriverTest::ShutdownDriver() {
if (gcm_connection_observer())
driver()->RemoveConnectionObserver(gcm_connection_observer());
driver()->Shutdown();
}
void GCMDriverTest::AddAppHandlers() {
driver_->AddAppHandler(kTestAppID1, gcm_app_handler_.get());
driver_->AddAppHandler(kTestAppID2, gcm_app_handler_.get());
}
void GCMDriverTest::RemoveAppHandlers() {
driver_->RemoveAppHandler(kTestAppID1);
driver_->RemoveAppHandler(kTestAppID2);
}
void GCMDriverTest::Register(const std::string& app_id,
const std::vector<std::string>& sender_ids,
WaitToFinish wait_to_finish) {
base::RunLoop run_loop;
async_operation_completed_callback_ = run_loop.QuitClosure();
driver_->Register(app_id,
sender_ids,
base::Bind(&GCMDriverTest::RegisterCompleted,
base::Unretained(this)));
if (wait_to_finish == WAIT)
run_loop.Run();
}
void GCMDriverTest::Send(const std::string& app_id,
const std::string& receiver_id,
const OutgoingMessage& message,
WaitToFinish wait_to_finish) {
base::RunLoop run_loop;
async_operation_completed_callback_ = run_loop.QuitClosure();
driver_->Send(app_id,
receiver_id,
message,
base::Bind(&GCMDriverTest::SendCompleted,
base::Unretained(this)));
if (wait_to_finish == WAIT)
run_loop.Run();
}
void GCMDriverTest::GetEncryptionInfo(const std::string& app_id,
WaitToFinish wait_to_finish) {
base::RunLoop run_loop;
async_operation_completed_callback_ = run_loop.QuitClosure();
driver_->GetEncryptionInfo(
app_id, base::Bind(&GCMDriverTest::GetEncryptionInfoCompleted,
base::Unretained(this)));
if (wait_to_finish == WAIT)
run_loop.Run();
}
void GCMDriverTest::Unregister(const std::string& app_id,
WaitToFinish wait_to_finish) {
base::RunLoop run_loop;
async_operation_completed_callback_ = run_loop.QuitClosure();
driver_->Unregister(app_id,
base::Bind(&GCMDriverTest::UnregisterCompleted,
base::Unretained(this)));
if (wait_to_finish == WAIT)
run_loop.Run();
}
void GCMDriverTest::WaitForAsyncOperation() {
base::RunLoop run_loop;
async_operation_completed_callback_ = run_loop.QuitClosure();
run_loop.Run();
}
void GCMDriverTest::RegisterCompleted(const std::string& registration_id,
GCMClient::Result result) {
registration_id_ = registration_id;
registration_result_ = result;
if (!async_operation_completed_callback_.is_null())
async_operation_completed_callback_.Run();
}
void GCMDriverTest::SendCompleted(const std::string& message_id,
GCMClient::Result result) {
send_message_id_ = message_id;
send_result_ = result;
if (!async_operation_completed_callback_.is_null())
async_operation_completed_callback_.Run();
}
void GCMDriverTest::GetEncryptionInfoCompleted(const std::string& p256dh,
const std::string& auth_secret) {
p256dh_ = p256dh;
auth_secret_ = auth_secret;
if (!async_operation_completed_callback_.is_null())
async_operation_completed_callback_.Run();
}
void GCMDriverTest::UnregisterCompleted(GCMClient::Result result) {
unregistration_result_ = result;
if (!async_operation_completed_callback_.is_null())
async_operation_completed_callback_.Run();
}
TEST_F(GCMDriverTest, Create) {
// Create GCMDriver first. By default GCM is set to delay start.
CreateDriver();
EXPECT_FALSE(driver()->IsStarted());
// Adding an app handler will not start GCM.
AddAppHandlers();
PumpIOLoop();
PumpUILoop();
EXPECT_FALSE(driver()->IsStarted());
EXPECT_FALSE(driver()->IsConnected());
EXPECT_FALSE(gcm_connection_observer()->connected());
// The GCM registration will kick off the GCM.
Register(kTestAppID1, ToSenderList("sender"), GCMDriverTest::WAIT);
EXPECT_TRUE(driver()->IsStarted());
EXPECT_TRUE(driver()->IsConnected());
EXPECT_TRUE(gcm_connection_observer()->connected());
}
TEST_F(GCMDriverTest, Shutdown) {
CreateDriver();
EXPECT_FALSE(HasAppHandlers());
AddAppHandlers();
EXPECT_TRUE(HasAppHandlers());
ShutdownDriver();
EXPECT_FALSE(HasAppHandlers());
EXPECT_FALSE(driver()->IsConnected());
EXPECT_FALSE(gcm_connection_observer()->connected());
}
TEST_F(GCMDriverTest, DisableAndReenableGCM) {
CreateDriver();
AddAppHandlers();
PumpIOLoop();
PumpUILoop();
EXPECT_FALSE(driver()->IsStarted());
// The GCM registration will kick off the GCM.
Register(kTestAppID1, ToSenderList("sender"), GCMDriverTest::WAIT);
EXPECT_TRUE(driver()->IsStarted());
// Disables the GCM. GCM will be stopped.
driver()->Disable();
PumpIOLoop();
PumpUILoop();
EXPECT_FALSE(driver()->IsStarted());
// Enables the GCM. GCM will be started.
driver()->Enable();
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(driver()->IsStarted());
}
TEST_F(GCMDriverTest, StartOrStopGCMOnDemand) {
CreateDriver();
PumpIOLoop();
PumpUILoop();
EXPECT_FALSE(driver()->IsStarted());
// Adding an app handler will not start GCM.
driver()->AddAppHandler(kTestAppID1, gcm_app_handler());
PumpIOLoop();
PumpUILoop();
EXPECT_FALSE(driver()->IsStarted());
// The GCM registration will kick off the GCM.
Register(kTestAppID1, ToSenderList("sender"), GCMDriverTest::WAIT);
EXPECT_TRUE(driver()->IsStarted());
// Add another app handler.
driver()->AddAppHandler(kTestAppID2, gcm_app_handler());
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(driver()->IsStarted());
// GCMClient remains active after one app handler is gone.
driver()->RemoveAppHandler(kTestAppID1);
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(driver()->IsStarted());
// GCMClient should be stopped after the last app handler is gone.
driver()->RemoveAppHandler(kTestAppID2);
PumpIOLoop();
PumpUILoop();
EXPECT_FALSE(driver()->IsStarted());
// GCMClient is restarted after an app handler has been added.
driver()->AddAppHandler(kTestAppID2, gcm_app_handler());
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(driver()->IsStarted());
}
TEST_F(GCMDriverTest, RegisterFailed) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
CreateDriver();
// Registration fails when the no app handler is added.
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_TRUE(registration_id().empty());
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, registration_result());
ClearResults();
// Registration fails when GCM is disabled.
AddAppHandlers();
driver()->Disable();
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_TRUE(registration_id().empty());
EXPECT_EQ(GCMClient::GCM_DISABLED, registration_result());
}
TEST_F(GCMDriverTest, UnregisterFailed) {
CreateDriver();
// Unregistration fails when the no app handler is added.
Unregister(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, unregistration_result());
ClearResults();
// Unregistration fails when GCM is disabled.
AddAppHandlers();
driver()->Disable();
Unregister(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::GCM_DISABLED, unregistration_result());
}
TEST_F(GCMDriverTest, SendFailed) {
OutgoingMessage message;
message.id = "1";
message.data["key1"] = "value1";
CreateDriver();
// Sending fails when the no app handler is added.
Send(kTestAppID1, kUserID1, message, GCMDriverTest::WAIT);
EXPECT_TRUE(send_message_id().empty());
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, send_result());
ClearResults();
// Sending fails when GCM is disabled.
AddAppHandlers();
driver()->Disable();
Send(kTestAppID1, kUserID1, message, GCMDriverTest::WAIT);
EXPECT_TRUE(send_message_id().empty());
EXPECT_EQ(GCMClient::GCM_DISABLED, send_result());
}
TEST_F(GCMDriverTest, GCMClientNotReadyBeforeRegistration) {
CreateDriver();
PumpIOLoop();
PumpUILoop();
// Make GCMClient not ready until PerformDelayedStart is called.
GetGCMClient()->set_start_mode_overridding(
FakeGCMClient::FORCE_TO_ALWAYS_DELAY_START_GCM);
AddAppHandlers();
// The registration is on hold until GCMClient is ready.
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestAppID1,
sender_ids,
GCMDriverTest::DO_NOT_WAIT);
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(registration_id().empty());
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, registration_result());
// Register operation will be invoked after GCMClient becomes ready.
GetGCMClient()->PerformDelayedStart();
WaitForAsyncOperation();
EXPECT_FALSE(registration_id().empty());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverTest, GCMClientNotReadyBeforeSending) {
CreateDriver();
PumpIOLoop();
PumpUILoop();
// Make GCMClient not ready until PerformDelayedStart is called.
GetGCMClient()->set_start_mode_overridding(
FakeGCMClient::FORCE_TO_ALWAYS_DELAY_START_GCM);
AddAppHandlers();
// The sending is on hold until GCMClient is ready.
OutgoingMessage message;
message.id = "1";
message.data["key1"] = "value1";
message.data["key2"] = "value2";
Send(kTestAppID1, kUserID1, message, GCMDriverTest::DO_NOT_WAIT);
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(send_message_id().empty());
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, send_result());
// Send operation will be invoked after GCMClient becomes ready.
GetGCMClient()->PerformDelayedStart();
WaitForAsyncOperation();
EXPECT_EQ(message.id, send_message_id());
EXPECT_EQ(GCMClient::SUCCESS, send_result());
}
// Tests a single instance of GCMDriver.
class GCMDriverFunctionalTest : public GCMDriverTest {
public:
GCMDriverFunctionalTest();
~GCMDriverFunctionalTest() override;
// GCMDriverTest:
void SetUp() override;
private:
DISALLOW_COPY_AND_ASSIGN(GCMDriverFunctionalTest);
};
GCMDriverFunctionalTest::GCMDriverFunctionalTest() {
}
GCMDriverFunctionalTest::~GCMDriverFunctionalTest() {
}
void GCMDriverFunctionalTest::SetUp() {
GCMDriverTest::SetUp();
CreateDriver();
AddAppHandlers();
PumpIOLoop();
PumpUILoop();
}
TEST_F(GCMDriverFunctionalTest, Register) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
const std::string expected_registration_id =
FakeGCMClient::GenerateGCMRegistrationID(sender_ids);
EXPECT_EQ(expected_registration_id, registration_id());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverFunctionalTest, RegisterError) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1@error");
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_TRUE(registration_id().empty());
EXPECT_NE(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverFunctionalTest, RegisterAgainWithSameSenderIDs) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
sender_ids.push_back("sender2");
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
const std::string expected_registration_id =
FakeGCMClient::GenerateGCMRegistrationID(sender_ids);
EXPECT_EQ(expected_registration_id, registration_id());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
// Clears the results the would be set by the Register callback in preparation
// to call register 2nd time.
ClearResults();
// Calling register 2nd time with the same set of sender IDs but different
// ordering will get back the same registration ID.
std::vector<std::string> another_sender_ids;
another_sender_ids.push_back("sender2");
another_sender_ids.push_back("sender1");
Register(kTestAppID1, another_sender_ids, GCMDriverTest::WAIT);
EXPECT_EQ(expected_registration_id, registration_id());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverFunctionalTest, RegisterAgainWithDifferentSenderIDs) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
const std::string expected_registration_id =
FakeGCMClient::GenerateGCMRegistrationID(sender_ids);
EXPECT_EQ(expected_registration_id, registration_id());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
// Make sender IDs different.
sender_ids.push_back("sender2");
const std::string expected_registration_id2 =
FakeGCMClient::GenerateGCMRegistrationID(sender_ids);
// Calling register 2nd time with the different sender IDs will get back a new
// registration ID.
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_EQ(expected_registration_id2, registration_id());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverFunctionalTest, UnregisterExplicitly) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_FALSE(registration_id().empty());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
Unregister(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::SUCCESS, unregistration_result());
}
TEST_F(GCMDriverFunctionalTest, UnregisterRemovesEncryptionInfo) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_FALSE(registration_id().empty());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
GetEncryptionInfo(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_FALSE(p256dh().empty());
EXPECT_FALSE(auth_secret().empty());
const std::string app_p256dh = p256dh();
const std::string app_auth_secret = auth_secret();
GetEncryptionInfo(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(app_p256dh, p256dh());
EXPECT_EQ(app_auth_secret, auth_secret());
Unregister(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::SUCCESS, unregistration_result());
GetEncryptionInfo(kTestAppID1, GCMDriverTest::WAIT);
// The GCMKeyStore eagerly creates new keying material for registrations that
// don't have any associated with them, so the most appropriate check to do is
// to verify that the returned material is different from before.
EXPECT_NE(app_p256dh, p256dh());
EXPECT_NE(app_auth_secret, auth_secret());
}
TEST_F(GCMDriverFunctionalTest, UnregisterWhenAsyncOperationPending) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
// First start registration without waiting for it to complete.
Register(kTestAppID1, sender_ids, GCMDriverTest::DO_NOT_WAIT);
// Test that unregistration fails with async operation pending when there is a
// registration already in progress.
Unregister(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
unregistration_result());
// Complete the unregistration.
WaitForAsyncOperation();
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
// Start unregistration without waiting for it to complete. This time no async
// operation is pending.
Unregister(kTestAppID1, GCMDriverTest::DO_NOT_WAIT);
// Test that unregistration fails with async operation pending when there is
// an unregistration already in progress.
Unregister(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
unregistration_result());
ClearResults();
// Complete unregistration.
WaitForAsyncOperation();
EXPECT_EQ(GCMClient::SUCCESS, unregistration_result());
}
TEST_F(GCMDriverFunctionalTest, RegisterWhenAsyncOperationPending) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
// First start registration without waiting for it to complete.
Register(kTestAppID1, sender_ids, GCMDriverTest::DO_NOT_WAIT);
// Test that registration fails with async operation pending when there is a
// registration already in progress.
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::ASYNC_OPERATION_PENDING,
registration_result());
ClearResults();
// Complete the registration.
WaitForAsyncOperation();
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverFunctionalTest, RegisterAfterUnfinishedUnregister) {
// Register and wait for it to complete.
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
EXPECT_EQ(FakeGCMClient::GenerateGCMRegistrationID(sender_ids),
registration_id());
// Clears the results the would be set by the Register callback in preparation
// to call register 2nd time.
ClearResults();
// Start unregistration without waiting for it to complete.
Unregister(kTestAppID1, GCMDriverTest::DO_NOT_WAIT);
// Register immeidately after unregistration is not completed.
sender_ids.push_back("sender2");
Register(kTestAppID1, sender_ids, GCMDriverTest::WAIT);
// We need one more waiting since the waiting in Register is indeed for
// uncompleted Unregister.
WaitForAsyncOperation();
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
EXPECT_EQ(FakeGCMClient::GenerateGCMRegistrationID(sender_ids),
registration_id());
}
TEST_F(GCMDriverFunctionalTest, Send) {
OutgoingMessage message;
message.id = "1@ack";
message.data["key1"] = "value1";
message.data["key2"] = "value2";
Send(kTestAppID1, kUserID1, message, GCMDriverTest::WAIT);
EXPECT_EQ(message.id, send_message_id());
EXPECT_EQ(GCMClient::SUCCESS, send_result());
gcm_app_handler()->WaitForNotification();
EXPECT_EQ(message.id, gcm_app_handler()->acked_message_id());
EXPECT_EQ(kTestAppID1, gcm_app_handler()->app_id());
}
TEST_F(GCMDriverFunctionalTest, SendError) {
OutgoingMessage message;
// Embedding error in id will tell the mock to simulate the send error.
message.id = "1@error";
message.data["key1"] = "value1";
message.data["key2"] = "value2";
Send(kTestAppID1, kUserID1, message, GCMDriverTest::WAIT);
EXPECT_EQ(message.id, send_message_id());
EXPECT_EQ(GCMClient::SUCCESS, send_result());
// Wait for the send error.
gcm_app_handler()->WaitForNotification();
EXPECT_EQ(FakeGCMAppHandler::SEND_ERROR_EVENT,
gcm_app_handler()->received_event());
EXPECT_EQ(kTestAppID1, gcm_app_handler()->app_id());
EXPECT_EQ(message.id,
gcm_app_handler()->send_error_details().message_id);
EXPECT_NE(GCMClient::SUCCESS,
gcm_app_handler()->send_error_details().result);
EXPECT_EQ(message.data,
gcm_app_handler()->send_error_details().additional_data);
}
TEST_F(GCMDriverFunctionalTest, MessageReceived) {
// GCM registration has to be performed otherwise GCM will not be started.
Register(kTestAppID1, ToSenderList("sender"), GCMDriverTest::WAIT);
IncomingMessage message;
message.data["key1"] = "value1";
message.data["key2"] = "value2";
message.sender_id = "sender";
GetGCMClient()->ReceiveMessage(kTestAppID1, message);
gcm_app_handler()->WaitForNotification();
EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
gcm_app_handler()->received_event());
EXPECT_EQ(kTestAppID1, gcm_app_handler()->app_id());
EXPECT_EQ(message.data, gcm_app_handler()->message().data);
EXPECT_TRUE(gcm_app_handler()->message().collapse_key.empty());
EXPECT_EQ(message.sender_id, gcm_app_handler()->message().sender_id);
}
TEST_F(GCMDriverFunctionalTest, MessageWithCollapseKeyReceived) {
// GCM registration has to be performed otherwise GCM will not be started.
Register(kTestAppID1, ToSenderList("sender"), GCMDriverTest::WAIT);
IncomingMessage message;
message.data["key1"] = "value1";
message.collapse_key = "collapse_key_value";
message.sender_id = "sender";
GetGCMClient()->ReceiveMessage(kTestAppID1, message);
gcm_app_handler()->WaitForNotification();
EXPECT_EQ(FakeGCMAppHandler::MESSAGE_EVENT,
gcm_app_handler()->received_event());
EXPECT_EQ(kTestAppID1, gcm_app_handler()->app_id());
EXPECT_EQ(message.data, gcm_app_handler()->message().data);
EXPECT_EQ(message.collapse_key,
gcm_app_handler()->message().collapse_key);
}
TEST_F(GCMDriverFunctionalTest, EncryptedMessageReceivedError) {
// GCM registration has to be performed otherwise GCM will not be started.
Register(kTestAppID1, ToSenderList("sender"), GCMDriverTest::WAIT);
IncomingMessage message;
// All required information to trigger the encryption path, but with an
// invalid Crypto-Key header value to trigger an error.
message.data["encryption"] = "salt=ysyxqlYTgE0WvcZrmHbUbg";
message.data["crypto-key"] = "hey=thereisnopublickey";
message.sender_id = "sender";
message.raw_data = "foobar";
GetGCMClient()->SetRecording(true);
GetGCMClient()->ReceiveMessage(kTestAppID1, message);
PumpIOLoop();
PumpUILoop();
PumpIOLoop();
GCMClient::GCMStatistics statistics = GetGCMClient()->GetStatistics();
EXPECT_TRUE(statistics.is_recording);
EXPECT_EQ(
1u, statistics.recorded_activities.decryption_failure_activities.size());
}
TEST_F(GCMDriverFunctionalTest, MessagesDeleted) {
// GCM registration has to be performed otherwise GCM will not be started.
Register(kTestAppID1, ToSenderList("sender"), GCMDriverTest::WAIT);
GetGCMClient()->DeleteMessages(kTestAppID1);
gcm_app_handler()->WaitForNotification();
EXPECT_EQ(FakeGCMAppHandler::MESSAGES_DELETED_EVENT,
gcm_app_handler()->received_event());
EXPECT_EQ(kTestAppID1, gcm_app_handler()->app_id());
}
TEST_F(GCMDriverFunctionalTest, LastTokenFetchTime) {
// GCM registration has to be performed otherwise GCM will not be started.
Register(kTestAppID1, ToSenderList("sender"), GCMDriverTest::WAIT);
EXPECT_EQ(base::Time(), driver()->GetLastTokenFetchTime());
base::Time fetch_time = base::Time::Now();
driver()->SetLastTokenFetchTime(fetch_time);
EXPECT_EQ(fetch_time, driver()->GetLastTokenFetchTime());
}
// Tests a single instance of GCMDriver.
class GCMChannelStatusSyncerTest : public GCMDriverTest {
public:
GCMChannelStatusSyncerTest();
~GCMChannelStatusSyncerTest() override;
// testing::Test:
void SetUp() override;
void CompleteGCMChannelStatusRequest(bool enabled, int poll_interval_seconds);
bool CompareDelaySeconds(int64_t expected_delay_seconds,
int64_t actual_delay_seconds);
GCMChannelStatusSyncer* syncer() {
return driver()->gcm_channel_status_syncer_for_testing();
}
private:
DISALLOW_COPY_AND_ASSIGN(GCMChannelStatusSyncerTest);
};
GCMChannelStatusSyncerTest::GCMChannelStatusSyncerTest() {
}
GCMChannelStatusSyncerTest::~GCMChannelStatusSyncerTest() {
}
void GCMChannelStatusSyncerTest::SetUp() {
GCMDriverTest::SetUp();
}
void GCMChannelStatusSyncerTest::CompleteGCMChannelStatusRequest(
bool enabled, int poll_interval_seconds) {
sync_pb::ExperimentStatusResponse response_proto;
sync_pb::ExperimentsSpecifics* experiment_specifics =
response_proto.add_experiment();
experiment_specifics->mutable_gcm_channel()->set_enabled(enabled);
if (poll_interval_seconds)
response_proto.set_poll_interval_seconds(poll_interval_seconds);
syncer()->request_for_testing()->ParseResponseProto(response_proto);
}
bool GCMChannelStatusSyncerTest::CompareDelaySeconds(
int64_t expected_delay_seconds,
int64_t actual_delay_seconds) {
// Most of time, the actual delay should not be smaller than the expected
// delay.
if (actual_delay_seconds >= expected_delay_seconds)
return true;
// It is also OK that the actual delay is a bit smaller than the expected
// delay in case that the test runs slowly.
return expected_delay_seconds - actual_delay_seconds < 30;
}
TEST_F(GCMChannelStatusSyncerTest, DisableAndEnable) {
// Create GCMDriver first. By default, GCM is enabled.
CreateDriver();
EXPECT_TRUE(driver()->gcm_enabled());
EXPECT_TRUE(syncer()->gcm_enabled());
// Remove delay such that the request could be executed immediately.
syncer()->set_delay_removed_for_testing(true);
// GCM is still enabled at this point.
AddAppHandlers();
EXPECT_TRUE(driver()->gcm_enabled());
EXPECT_TRUE(syncer()->gcm_enabled());
// Wait until the GCM channel status request gets triggered.
PumpUILoop();
// Complete the request that disables the GCM.
CompleteGCMChannelStatusRequest(false, 0);
EXPECT_FALSE(driver()->gcm_enabled());
EXPECT_FALSE(syncer()->gcm_enabled());
EXPECT_FALSE(driver()->IsStarted());
// Wait until next GCM channel status request gets triggered.
PumpUILoop();
// Complete the request that enables the GCM.
CompleteGCMChannelStatusRequest(true, 0);
EXPECT_TRUE(driver()->gcm_enabled());
EXPECT_TRUE(syncer()->gcm_enabled());
}
TEST_F(GCMChannelStatusSyncerTest, DisableRestartAndEnable) {
// Create GCMDriver first. By default, GCM is enabled.
CreateDriver();
EXPECT_TRUE(driver()->gcm_enabled());
EXPECT_TRUE(syncer()->gcm_enabled());
// Remove delay such that the request could be executed immediately.
syncer()->set_delay_removed_for_testing(true);
// GCM is still enabled at this point.
AddAppHandlers();
EXPECT_TRUE(driver()->gcm_enabled());
EXPECT_TRUE(syncer()->gcm_enabled());
// Wait until the GCM channel status request gets triggered.
PumpUILoop();
// Complete the request that disables the GCM.
CompleteGCMChannelStatusRequest(false, 0);
EXPECT_FALSE(driver()->gcm_enabled());
EXPECT_FALSE(syncer()->gcm_enabled());
// Simulate browser start by recreating GCMDriver.
ShutdownDriver();
CreateDriver();
// Remove delay such that the request could be executed immediately.
syncer()->set_delay_removed_for_testing(true);
// GCM is still disabled.
EXPECT_FALSE(driver()->gcm_enabled());
EXPECT_FALSE(syncer()->gcm_enabled());
AddAppHandlers();
EXPECT_FALSE(driver()->gcm_enabled());
EXPECT_FALSE(syncer()->gcm_enabled());
// Wait until the GCM channel status request gets triggered.
PumpUILoop();
// Complete the request that re-enables the GCM.
CompleteGCMChannelStatusRequest(true, 0);
EXPECT_TRUE(driver()->gcm_enabled());
EXPECT_TRUE(syncer()->gcm_enabled());
}
TEST_F(GCMChannelStatusSyncerTest, FirstTimePolling) {
// Start GCM.
CreateDriver();
AddAppHandlers();
// The 1st request should be triggered shortly without jittering.
EXPECT_EQ(GCMChannelStatusSyncer::first_time_delay_seconds(),
syncer()->current_request_delay_interval().InSeconds());
}
TEST_F(GCMChannelStatusSyncerTest, SubsequentPollingWithDefaultInterval) {
// Create GCMDriver first. GCM is not started.
CreateDriver();
// Remove delay such that the request could be executed immediately.
syncer()->set_delay_removed_for_testing(true);
// Now GCM is started.
AddAppHandlers();
// Wait until the GCM channel status request gets triggered.
PumpUILoop();
// Keep delay such that we can find out the computed delay time.
syncer()->set_delay_removed_for_testing(false);
// Complete the request. The default interval is intact.
CompleteGCMChannelStatusRequest(true, 0);
// The next request should be scheduled at the expected default interval.
int64_t actual_delay_seconds =
syncer()->current_request_delay_interval().InSeconds();
int64_t expected_delay_seconds =
GCMChannelStatusRequest::default_poll_interval_seconds();
EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds))
<< "expected delay: " << expected_delay_seconds
<< " actual delay: " << actual_delay_seconds;
// Simulate browser start by recreating GCMDriver.
ShutdownDriver();
CreateDriver();
AddAppHandlers();
// After start-up, the request should still be scheduled at the expected
// default interval.
actual_delay_seconds =
syncer()->current_request_delay_interval().InSeconds();
EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds))
<< "expected delay: " << expected_delay_seconds
<< " actual delay: " << actual_delay_seconds;
}
TEST_F(GCMChannelStatusSyncerTest, SubsequentPollingWithUpdatedInterval) {
// Create GCMDriver first. GCM is not started.
CreateDriver();
// Remove delay such that the request could be executed immediately.
syncer()->set_delay_removed_for_testing(true);
// Now GCM is started.
AddAppHandlers();
// Wait until the GCM channel status request gets triggered.
PumpUILoop();
// Keep delay such that we can find out the computed delay time.
syncer()->set_delay_removed_for_testing(false);
// Complete the request. The interval is being changed.
int new_poll_interval_seconds =
GCMChannelStatusRequest::default_poll_interval_seconds() * 2;
CompleteGCMChannelStatusRequest(true, new_poll_interval_seconds);
// The next request should be scheduled at the expected updated interval.
int64_t actual_delay_seconds =
syncer()->current_request_delay_interval().InSeconds();
int64_t expected_delay_seconds = new_poll_interval_seconds;
EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds))
<< "expected delay: " << expected_delay_seconds
<< " actual delay: " << actual_delay_seconds;
// Simulate browser start by recreating GCMDriver.
ShutdownDriver();
CreateDriver();
AddAppHandlers();
// After start-up, the request should still be scheduled at the expected
// updated interval.
actual_delay_seconds =
syncer()->current_request_delay_interval().InSeconds();
EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds))
<< "expected delay: " << expected_delay_seconds
<< " actual delay: " << actual_delay_seconds;
}
class GCMDriverInstanceIDTest : public GCMDriverTest {
public:
GCMDriverInstanceIDTest();
~GCMDriverInstanceIDTest() override;
void GetReady();
void GetInstanceID(const std::string& app_id, WaitToFinish wait_to_finish);
void GetInstanceIDDataCompleted(const std::string& instance_id,
const std::string& extra_data);
void GetToken(const std::string& app_id,
const std::string& authorized_entity,
const std::string& scope,
WaitToFinish wait_to_finish);
void DeleteToken(const std::string& app_id,
const std::string& authorized_entity,
const std::string& scope,
WaitToFinish wait_to_finish);
void AddInstanceIDData(const std::string& app_id,
const std::string& instance_id,
const std::string& extra_data);
void RemoveInstanceIDData(const std::string& app_id);
std::string instance_id() const { return instance_id_; }
std::string extra_data() const { return extra_data_; }
private:
std::string instance_id_;
std::string extra_data_;
DISALLOW_COPY_AND_ASSIGN(GCMDriverInstanceIDTest);
};
GCMDriverInstanceIDTest::GCMDriverInstanceIDTest() {
}
GCMDriverInstanceIDTest::~GCMDriverInstanceIDTest() {
}
void GCMDriverInstanceIDTest::GetReady() {
CreateDriver();
AddAppHandlers();
PumpIOLoop();
PumpUILoop();
}
void GCMDriverInstanceIDTest::GetInstanceID(const std::string& app_id,
WaitToFinish wait_to_finish) {
base::RunLoop run_loop;
set_async_operation_completed_callback(run_loop.QuitClosure());
driver()->GetInstanceIDHandlerInternal()->GetInstanceIDData(
app_id, base::Bind(&GCMDriverInstanceIDTest::GetInstanceIDDataCompleted,
base::Unretained(this)));
if (wait_to_finish == WAIT)
run_loop.Run();
}
void GCMDriverInstanceIDTest::GetInstanceIDDataCompleted(
const std::string& instance_id, const std::string& extra_data) {
instance_id_ = instance_id;
extra_data_ = extra_data;
if (!async_operation_completed_callback().is_null())
async_operation_completed_callback().Run();
}
void GCMDriverInstanceIDTest::GetToken(const std::string& app_id,
const std::string& authorized_entity,
const std::string& scope,
WaitToFinish wait_to_finish) {
base::RunLoop run_loop;
set_async_operation_completed_callback(run_loop.QuitClosure());
std::map<std::string, std::string> options;
driver()->GetInstanceIDHandlerInternal()->GetToken(
app_id, authorized_entity, scope, options,
base::Bind(&GCMDriverTest::RegisterCompleted, base::Unretained(this)));
if (wait_to_finish == WAIT)
run_loop.Run();
}
void GCMDriverInstanceIDTest::DeleteToken(const std::string& app_id,
const std::string& authorized_entity,
const std::string& scope,
WaitToFinish wait_to_finish) {
base::RunLoop run_loop;
set_async_operation_completed_callback(run_loop.QuitClosure());
driver()->GetInstanceIDHandlerInternal()->DeleteToken(
app_id, authorized_entity, scope,
base::Bind(&GCMDriverTest::UnregisterCompleted, base::Unretained(this)));
if (wait_to_finish == WAIT)
run_loop.Run();
}
void GCMDriverInstanceIDTest::AddInstanceIDData(const std::string& app_id,
const std::string& instance_id,
const std::string& extra_data) {
driver()->GetInstanceIDHandlerInternal()->AddInstanceIDData(
app_id, instance_id, extra_data);
}
void GCMDriverInstanceIDTest::RemoveInstanceIDData(const std::string& app_id) {
driver()->GetInstanceIDHandlerInternal()->RemoveInstanceIDData(app_id);
}
TEST_F(GCMDriverInstanceIDTest, InstanceIDData) {
GetReady();
AddInstanceIDData(kTestAppID1, kInstanceID1, "Foo");
GetInstanceID(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_EQ(kInstanceID1, instance_id());
EXPECT_EQ("Foo", extra_data());
RemoveInstanceIDData(kTestAppID1);
GetInstanceID(kTestAppID1, GCMDriverTest::WAIT);
EXPECT_TRUE(instance_id().empty());
EXPECT_TRUE(extra_data().empty());
}
TEST_F(GCMDriverInstanceIDTest, GCMClientNotReadyBeforeInstanceIDData) {
CreateDriver();
PumpIOLoop();
PumpUILoop();
// Make GCMClient not ready until PerformDelayedStart is called.
GetGCMClient()->set_start_mode_overridding(
FakeGCMClient::FORCE_TO_ALWAYS_DELAY_START_GCM);
AddAppHandlers();
// All operations are on hold until GCMClient is ready.
AddInstanceIDData(kTestAppID1, kInstanceID1, "Foo");
AddInstanceIDData(kTestAppID2, kInstanceID2, "Bar");
RemoveInstanceIDData(kTestAppID1);
GetInstanceID(kTestAppID2, GCMDriverTest::DO_NOT_WAIT);
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(instance_id().empty());
EXPECT_TRUE(extra_data().empty());
// All operations will be performed after GCMClient becomes ready.
GetGCMClient()->PerformDelayedStart();
WaitForAsyncOperation();
EXPECT_EQ(kInstanceID2, instance_id());
EXPECT_EQ("Bar", extra_data());
}
TEST_F(GCMDriverInstanceIDTest, GetToken) {
GetReady();
const std::string expected_token =
FakeGCMClient::GenerateInstanceIDToken(kUserID1, kScope);
GetToken(kTestAppID1, kUserID1, kScope, GCMDriverTest::WAIT);
EXPECT_EQ(expected_token, registration_id());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverInstanceIDTest, GetTokenError) {
GetReady();
std::string error_entity = "sender@error";
GetToken(kTestAppID1, error_entity, kScope, GCMDriverTest::WAIT);
EXPECT_TRUE(registration_id().empty());
EXPECT_NE(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverInstanceIDTest, GCMClientNotReadyBeforeGetToken) {
CreateDriver();
PumpIOLoop();
PumpUILoop();
// Make GCMClient not ready until PerformDelayedStart is called.
GetGCMClient()->set_start_mode_overridding(
FakeGCMClient::FORCE_TO_ALWAYS_DELAY_START_GCM);
AddAppHandlers();
// GetToken operation is on hold until GCMClient is ready.
GetToken(kTestAppID1, kUserID1, kScope, GCMDriverTest::DO_NOT_WAIT);
PumpIOLoop();
PumpUILoop();
EXPECT_TRUE(registration_id().empty());
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, registration_result());
// GetToken operation will be invoked after GCMClient becomes ready.
GetGCMClient()->PerformDelayedStart();
WaitForAsyncOperation();
EXPECT_FALSE(registration_id().empty());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
}
TEST_F(GCMDriverInstanceIDTest, DeleteToken) {
GetReady();
const std::string expected_token =
FakeGCMClient::GenerateInstanceIDToken(kUserID1, kScope);
GetToken(kTestAppID1, kUserID1, kScope, GCMDriverTest::WAIT);
EXPECT_EQ(expected_token, registration_id());
EXPECT_EQ(GCMClient::SUCCESS, registration_result());
DeleteToken(kTestAppID1, kUserID1, kScope, GCMDriverTest::WAIT);
EXPECT_EQ(GCMClient::SUCCESS, unregistration_result());
}
TEST_F(GCMDriverInstanceIDTest, GCMClientNotReadyBeforeDeleteToken) {
CreateDriver();
PumpIOLoop();
PumpUILoop();
// Make GCMClient not ready until PerformDelayedStart is called.
GetGCMClient()->set_start_mode_overridding(
FakeGCMClient::FORCE_TO_ALWAYS_DELAY_START_GCM);
AddAppHandlers();
// DeleteToken operation is on hold until GCMClient is ready.
DeleteToken(kTestAppID1, kUserID1, kScope, GCMDriverTest::DO_NOT_WAIT);
PumpIOLoop();
PumpUILoop();
EXPECT_EQ(GCMClient::UNKNOWN_ERROR, unregistration_result());
// DeleteToken operation will be invoked after GCMClient becomes ready.
GetGCMClient()->PerformDelayedStart();
WaitForAsyncOperation();
EXPECT_EQ(GCMClient::SUCCESS, unregistration_result());
}
} // namespace gcm