blob: 77992bef8521240c5a78e961b1b555c9fce73550 [file] [log] [blame]
// Copyright (c) 2013 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 <algorithm>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/prefs/pref_service.h"
#include "base/run_loop.h"
#include "chrome/browser/extensions/state_store.h"
#include "chrome/browser/extensions/test_extension_service.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/services/gcm/gcm_client_mock.h"
#include "chrome/browser/services/gcm/gcm_event_router.h"
#include "chrome/browser/services/gcm/gcm_profile_service.h"
#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
#include "chrome/browser/signin/fake_signin_manager.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/webdata/encryptor/encryptor.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
#endif
using namespace extensions;
namespace gcm {
const char kTestExtensionName[] = "FooBar";
const char kTestingUsername[] = "user@example.com";
const char kTestingAppId[] = "test1";
const char kTestingSha1Cert[] = "testing_cert1";
const char kUserId[] = "user2";
class GCMProfileServiceTest;
class GCMEventRouterMock : public GCMEventRouter {
public:
enum Event {
NO_EVENT,
MESSAGE_EVENT,
MESSAGES_DELETED_EVENT,
SEND_ERROR_EVENT
};
explicit GCMEventRouterMock(GCMProfileServiceTest* test);
virtual ~GCMEventRouterMock();
virtual void OnMessage(const std::string& app_id,
const GCMClient::IncomingMessage& message) OVERRIDE;
virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
virtual void OnSendError(const std::string& app_id,
const std::string& message_id,
GCMClient::Result result) OVERRIDE;
Event received_event() const { return received_event_; }
const std::string& app_id() const { return app_id_; }
const GCMClient::IncomingMessage incoming_message() const {
return incoming_message_;
}
const std::string& send_message_id() const { return send_error_message_id_; }
GCMClient::Result send_result() const { return send_error_result_; }
private:
GCMProfileServiceTest* test_;
Event received_event_;
std::string app_id_;
GCMClient::IncomingMessage incoming_message_;
std::string send_error_message_id_;
GCMClient::Result send_error_result_;
};
class GCMProfileServiceTest : public testing::Test,
public GCMProfileService::TestingDelegate {
public:
static BrowserContextKeyedService* BuildGCMProfileService(
content::BrowserContext* profile) {
return new GCMProfileService(
static_cast<Profile*>(profile), gps_testing_delegate_);
}
GCMProfileServiceTest() : extension_service_(NULL) {
}
virtual ~GCMProfileServiceTest() {
}
// Overridden from test::Test:
virtual void SetUp() OVERRIDE {
profile_.reset(new TestingProfile);
// Make BrowserThread work in unittest.
thread_bundle_.reset(new content::TestBrowserThreadBundle(
content::TestBrowserThreadBundle::REAL_IO_THREAD));
// This is needed to create extension service under CrOS.
#if defined(OS_CHROMEOS)
test_user_manager_.reset(new chromeos::ScopedTestUserManager());
#endif
// Create extension service in order to uninstall the extension.
extensions::TestExtensionSystem* extension_system(
static_cast<extensions::TestExtensionSystem*>(
extensions::ExtensionSystem::Get(profile())));
extension_system->CreateExtensionService(
CommandLine::ForCurrentProcess(), base::FilePath(), false);
extension_service_ = extension_system->Get(profile())->extension_service();
// Mock a signed-in user.
SigninManagerBase* signin_manager =
SigninManagerFactory::GetForProfile(profile());
signin_manager->SetAuthenticatedUsername(kTestingUsername);
// Encryptor ends up needing access to the keychain on OS X. So use the mock
// keychain to prevent prompts.
#if defined(OS_MACOSX)
Encryptor::UseMockKeychain(true);
#endif
// Mock a GCMClient.
gcm_client_mock_.reset(new GCMClientMock());
GCMClient::SetForTesting(gcm_client_mock_.get());
// Mock a GCMEventRouter.
gcm_event_router_mock_.reset(new GCMEventRouterMock(this));
// Set |gps_testing_delegate_| for it to be picked up by
// BuildGCMProfileService and pass it to GCMProfileService.
gps_testing_delegate_ = this;
// This will create GCMProfileService that causes check-in to be initiated.
GCMProfileService::enable_gcm_for_testing_ = true;
GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), &GCMProfileServiceTest::BuildGCMProfileService);
// Wait till the asynchronous check-in is done.
WaitForCompleted();
}
virtual void TearDown() OVERRIDE {
GCMClient::SetForTesting(NULL);
#if defined(OS_CHROMEOS)
test_user_manager_.reset();
#endif
extension_service_ = NULL;
profile_.reset();
base::RunLoop().RunUntilIdle();
}
// Overridden from GCMProfileService::TestingDelegate:
virtual GCMEventRouter* GetEventRouter() const OVERRIDE {
return gcm_event_router_mock_.get();
}
virtual void CheckInFinished(const GCMClient::CheckInInfo& checkin_info,
GCMClient::Result result) OVERRIDE {
checkin_info_ = checkin_info;
SignalCompleted();
}
virtual void LoadingFromPersistentStoreFinished() OVERRIDE {
SignalCompleted();
}
// Waits until the asynchrnous operation finishes.
void WaitForCompleted() {
run_loop_.reset(new base::RunLoop);
run_loop_->Run();
}
// Signals that the asynchrnous operation finishes.
void SignalCompleted() {
if (run_loop_ && run_loop_->running())
run_loop_->Quit();
}
// Returns a barebones test extension.
scoped_refptr<Extension> CreateExtension() {
#if defined(OS_WIN)
base::FilePath path(FILE_PATH_LITERAL("c:\\foo"));
#elif defined(OS_POSIX)
base::FilePath path(FILE_PATH_LITERAL("/foo"));
#endif
DictionaryValue manifest;
manifest.SetString(manifest_keys::kVersion, "1.0.0.0");
manifest.SetString(manifest_keys::kName, kTestExtensionName);
ListValue* permission_list = new ListValue;
permission_list->Append(Value::CreateStringValue("gcm"));
manifest.Set(manifest_keys::kPermissions, permission_list);
std::string error;
scoped_refptr<Extension> extension =
Extension::Create(path.AppendASCII(kTestExtensionName),
Manifest::INVALID_LOCATION,
manifest,
Extension::NO_FLAGS,
&error);
EXPECT_TRUE(extension.get()) << error;
extension_service_->AddExtension(extension.get());
return extension;
}
Profile* profile() const { return profile_.get(); }
GCMProfileService* GetGCMProfileService() const {
return GCMProfileServiceFactory::GetForProfile(profile());
}
protected:
scoped_ptr<TestingProfile> profile_;
scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
ExtensionService* extension_service_; // Not owned.
scoped_ptr<GCMClientMock> gcm_client_mock_;
scoped_ptr<base::RunLoop> run_loop_;
scoped_ptr<GCMEventRouterMock> gcm_event_router_mock_;
GCMClient::CheckInInfo checkin_info_;
#if defined(OS_CHROMEOS)
chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
chromeos::ScopedTestCrosSettings test_cros_settings_;
scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
#endif
private:
static GCMProfileService::TestingDelegate* gps_testing_delegate_;
DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceTest);
};
GCMProfileService::TestingDelegate*
GCMProfileServiceTest::gps_testing_delegate_ = NULL;
GCMEventRouterMock::GCMEventRouterMock(GCMProfileServiceTest* test)
: test_(test),
received_event_(NO_EVENT),
send_error_result_(GCMClient::SUCCESS) {
}
GCMEventRouterMock::~GCMEventRouterMock() {
}
void GCMEventRouterMock::OnMessage(const std::string& app_id,
const GCMClient::IncomingMessage& message) {
received_event_ = MESSAGE_EVENT;
app_id_ = app_id;
incoming_message_ = message;
test_->SignalCompleted();
}
void GCMEventRouterMock::OnMessagesDeleted(const std::string& app_id) {
received_event_ = MESSAGES_DELETED_EVENT;
app_id_ = app_id;
test_->SignalCompleted();
}
void GCMEventRouterMock::OnSendError(const std::string& app_id,
const std::string& message_id,
GCMClient::Result result) {
received_event_ = SEND_ERROR_EVENT;
app_id_ = app_id;
send_error_message_id_ = message_id;
send_error_result_ = result;
test_->SignalCompleted();
}
TEST_F(GCMProfileServiceTest, CheckIn) {
EXPECT_TRUE(checkin_info_.IsValid());
GCMClient::CheckInInfo expected_checkin_info =
gcm_client_mock_->GetCheckInInfoFromUsername(kTestingUsername);
EXPECT_EQ(expected_checkin_info.android_id, checkin_info_.android_id);
EXPECT_EQ(expected_checkin_info.secret, checkin_info_.secret);
}
TEST_F(GCMProfileServiceTest, CheckInFromPrefsStore) {
// The first check-in should be successful.
EXPECT_TRUE(checkin_info_.IsValid());
GCMClient::CheckInInfo saved_checkin_info = checkin_info_;
checkin_info_.Reset();
gcm_client_mock_->set_checkin_failure_enabled(true);
// Recreate GCMProfileService to test reading the check-in info from the
// prefs store.
GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), &GCMProfileServiceTest::BuildGCMProfileService);
EXPECT_EQ(saved_checkin_info.android_id, checkin_info_.android_id);
EXPECT_EQ(saved_checkin_info.secret, checkin_info_.secret);
}
TEST_F(GCMProfileServiceTest, CheckOut) {
EXPECT_TRUE(profile()->GetPrefs()->HasPrefPath(prefs::kGCMUserAccountID));
EXPECT_TRUE(profile()->GetPrefs()->HasPrefPath(prefs::kGCMUserToken));
GetGCMProfileService()->RemoveUser();
EXPECT_FALSE(profile()->GetPrefs()->HasPrefPath(prefs::kGCMUserAccountID));
EXPECT_FALSE(profile()->GetPrefs()->HasPrefPath(prefs::kGCMUserToken));
}
class GCMProfileServiceRegisterTest : public GCMProfileServiceTest {
public:
GCMProfileServiceRegisterTest()
: result_(GCMClient::SUCCESS),
has_persisted_registration_info_(false) {
}
virtual ~GCMProfileServiceRegisterTest() {
}
void Register(const std::string& app_id,
const std::vector<std::string>& sender_ids) {
GetGCMProfileService()->Register(
app_id,
sender_ids,
kTestingSha1Cert,
base::Bind(&GCMProfileServiceRegisterTest::RegisterCompleted,
base::Unretained(this)));
}
void RegisterCompleted(const std::string& registration_id,
GCMClient::Result result) {
registration_id_ = registration_id;
result_ = result;
SignalCompleted();
}
bool HasPersistedRegistrationInfo(const std::string& app_id) {
StateStore* storage = ExtensionSystem::Get(profile())->state_store();
if (!storage)
return false;
has_persisted_registration_info_ = false;
storage->GetExtensionValue(
app_id,
GCMProfileService::GetPersistentRegisterKeyForTesting(),
base::Bind(
&GCMProfileServiceRegisterTest::ReadRegistrationInfoFinished,
base::Unretained(this)));
WaitForCompleted();
return has_persisted_registration_info_;
}
void ReadRegistrationInfoFinished(scoped_ptr<base::Value> value) {
has_persisted_registration_info_ = value.get() != NULL;
SignalCompleted();
}
protected:
std::string registration_id_;
GCMClient::Result result_;
bool has_persisted_registration_info_;
private:
DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceRegisterTest);
};
TEST_F(GCMProfileServiceRegisterTest, Register) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestingAppId, sender_ids);
std::string expected_registration_id =
gcm_client_mock_->GetRegistrationIdFromSenderIds(sender_ids);
WaitForCompleted();
EXPECT_FALSE(registration_id_.empty());
EXPECT_EQ(expected_registration_id, registration_id_);
EXPECT_EQ(GCMClient::SUCCESS, result_);
}
TEST_F(GCMProfileServiceRegisterTest, DoubleRegister) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestingAppId, sender_ids);
std::string expected_registration_id =
gcm_client_mock_->GetRegistrationIdFromSenderIds(sender_ids);
// Calling regsiter 2nd time without waiting 1st one to finish will fail
// immediately.
sender_ids.push_back("sender2");
Register(kTestingAppId, sender_ids);
EXPECT_TRUE(registration_id_.empty());
EXPECT_NE(GCMClient::SUCCESS, result_);
// The 1st register is still doing fine.
WaitForCompleted();
EXPECT_FALSE(registration_id_.empty());
EXPECT_EQ(expected_registration_id, registration_id_);
EXPECT_EQ(GCMClient::SUCCESS, result_);
}
TEST_F(GCMProfileServiceRegisterTest, RegisterError) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1@error");
Register(kTestingAppId, sender_ids);
WaitForCompleted();
EXPECT_TRUE(registration_id_.empty());
EXPECT_NE(GCMClient::SUCCESS, result_);
}
TEST_F(GCMProfileServiceRegisterTest, RegisterAgainWithSameSenderIDs) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
sender_ids.push_back("sender2");
Register(kTestingAppId, sender_ids);
std::string expected_registration_id =
gcm_client_mock_->GetRegistrationIdFromSenderIds(sender_ids);
WaitForCompleted();
EXPECT_EQ(expected_registration_id, registration_id_);
EXPECT_EQ(GCMClient::SUCCESS, result_);
// Clears the results the would be set by the Register callback in preparation
// to call register 2nd time.
registration_id_.clear();
result_ = GCMClient::UNKNOWN_ERROR;
// Calling register 2nd time with the same set of sender IDs but different
// ordering will get back the same registration ID. There is no need to wait
// since register simply returns the cached registration ID.
std::vector<std::string> another_sender_ids;
another_sender_ids.push_back("sender2");
another_sender_ids.push_back("sender1");
Register(kTestingAppId, another_sender_ids);
EXPECT_EQ(expected_registration_id, registration_id_);
EXPECT_EQ(GCMClient::SUCCESS, result_);
}
TEST_F(GCMProfileServiceRegisterTest, RegisterAgainWithDifferentSenderIDs) {
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(kTestingAppId, sender_ids);
std::string expected_registration_id =
gcm_client_mock_->GetRegistrationIdFromSenderIds(sender_ids);
WaitForCompleted();
EXPECT_EQ(expected_registration_id, registration_id_);
EXPECT_EQ(GCMClient::SUCCESS, result_);
// Make sender IDs different.
sender_ids.push_back("sender2");
std::string expected_registration_id2 =
gcm_client_mock_->GetRegistrationIdFromSenderIds(sender_ids);
// Calling register 2nd time with the different sender IDs will get back a new
// registration ID.
Register(kTestingAppId, sender_ids);
WaitForCompleted();
EXPECT_EQ(expected_registration_id2, registration_id_);
EXPECT_EQ(GCMClient::SUCCESS, result_);
}
// http://crbug.com/326321
#if defined(OS_WIN)
#define MAYBE_RegisterFromStateStore DISABLED_RegisterFromStateStore
#else
#define MAYBE_RegisterFromStateStore RegisterFromStateStore
#endif
TEST_F(GCMProfileServiceRegisterTest, MAYBE_RegisterFromStateStore) {
scoped_refptr<Extension> extension(CreateExtension());
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(extension->id(), sender_ids);
WaitForCompleted();
EXPECT_FALSE(registration_id_.empty());
EXPECT_EQ(GCMClient::SUCCESS, result_);
std::string old_registration_id = registration_id_;
// Clears the results the would be set by the Register callback in preparation
// to call register 2nd time.
registration_id_.clear();
result_ = GCMClient::UNKNOWN_ERROR;
// Simulate start-up by recreating GCMProfileService.
GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), &GCMProfileServiceTest::BuildGCMProfileService);
// Simulate start-up by reloading extension.
extension_service_->UnloadExtension(extension->id(),
UnloadedExtensionInfo::REASON_TERMINATE);
extension_service_->AddExtension(extension.get());
// TODO(jianli): The waiting would be removed once we support delaying running
// register operation until the persistent loading completes.
WaitForCompleted();
// This should read the registration info from the extension's state store.
// There is no need to wait since register returns the registration ID being
// read.
Register(extension->id(), sender_ids);
EXPECT_EQ(old_registration_id, registration_id_);
EXPECT_EQ(GCMClient::SUCCESS, result_);
}
TEST_F(GCMProfileServiceRegisterTest, Unregister) {
scoped_refptr<Extension> extension(CreateExtension());
std::vector<std::string> sender_ids;
sender_ids.push_back("sender1");
Register(extension->id(), sender_ids);
WaitForCompleted();
EXPECT_FALSE(registration_id_.empty());
EXPECT_EQ(GCMClient::SUCCESS, result_);
// The registration info should be cached.
EXPECT_FALSE(GetGCMProfileService()->registration_info_map_.empty());
// The registration info should be persisted.
EXPECT_TRUE(HasPersistedRegistrationInfo(extension->id()));
// Uninstall the extension.
extension_service_->UninstallExtension(extension->id(), false, NULL);
base::MessageLoop::current()->RunUntilIdle();
// The cached registration info should be removed.
EXPECT_TRUE(GetGCMProfileService()->registration_info_map_.empty());
// The persisted registration info should be removed.
EXPECT_FALSE(HasPersistedRegistrationInfo(extension->id()));
}
class GCMProfileServiceSendTest : public GCMProfileServiceTest {
public:
GCMProfileServiceSendTest() : result_(GCMClient::SUCCESS) {
}
virtual ~GCMProfileServiceSendTest() {
}
void Send(const std::string& receiver_id,
const GCMClient::OutgoingMessage& message) {
GetGCMProfileService()->Send(
kTestingAppId,
receiver_id,
message,
base::Bind(&GCMProfileServiceSendTest::SendCompleted,
base::Unretained(this)));
}
void SendCompleted(const std::string& message_id, GCMClient::Result result) {
message_id_ = message_id;
result_ = result;
SignalCompleted();
}
protected:
std::string message_id_;
GCMClient::Result result_;
private:
DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceSendTest);
};
TEST_F(GCMProfileServiceSendTest, Send) {
GCMClient::OutgoingMessage message;
message.id = "1";
message.data["key1"] = "value1";
message.data["key2"] = "value2";
Send(kUserId, message);
// Wait for the send callback is called.
WaitForCompleted();
EXPECT_EQ(message_id_, message.id);
EXPECT_EQ(GCMClient::SUCCESS, result_);
}
TEST_F(GCMProfileServiceSendTest, SendError) {
GCMClient::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(kUserId, message);
// Wait for the send callback is called.
WaitForCompleted();
EXPECT_EQ(message_id_, message.id);
EXPECT_EQ(GCMClient::SUCCESS, result_);
// Wait for the send error.
WaitForCompleted();
EXPECT_EQ(GCMEventRouterMock::SEND_ERROR_EVENT,
gcm_event_router_mock_->received_event());
EXPECT_EQ(kTestingAppId, gcm_event_router_mock_->app_id());
EXPECT_EQ(message_id_, gcm_event_router_mock_->send_message_id());
EXPECT_NE(GCMClient::SUCCESS, gcm_event_router_mock_->send_result());
}
TEST_F(GCMProfileServiceTest, MessageReceived) {
GCMClient::IncomingMessage message;
message.data["key1"] = "value1";
message.data["key2"] = "value2";
gcm_client_mock_->ReceiveMessage(kTestingUsername, kTestingAppId, message);
WaitForCompleted();
EXPECT_EQ(GCMEventRouterMock::MESSAGE_EVENT,
gcm_event_router_mock_->received_event());
EXPECT_EQ(kTestingAppId, gcm_event_router_mock_->app_id());
ASSERT_EQ(message.data.size(),
gcm_event_router_mock_->incoming_message().data.size());
EXPECT_EQ(
message.data.find("key1")->second,
gcm_event_router_mock_->incoming_message().data.find("key1")->second);
EXPECT_EQ(
message.data.find("key2")->second,
gcm_event_router_mock_->incoming_message().data.find("key2")->second);
}
TEST_F(GCMProfileServiceTest, MessagesDeleted) {
gcm_client_mock_->DeleteMessages(kTestingUsername, kTestingAppId);
WaitForCompleted();
EXPECT_EQ(GCMEventRouterMock::MESSAGES_DELETED_EVENT,
gcm_event_router_mock_->received_event());
EXPECT_EQ(kTestingAppId, gcm_event_router_mock_->app_id());
}
} // namespace gcm