[GCM] Move extension specific tests out of gcm_profile_service_unit_test

These tests are moved to extension_gcm_profile_service_unittest.
Also did some cleanup.

BUG=356421
TEST=tests updated and new tests added

Review URL: https://codereview.chromium.org/213313006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@261568 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/extension_gcm_app_handler.h b/chrome/browser/extensions/extension_gcm_app_handler.h
index af22e210..c1fac2d 100644
--- a/chrome/browser/extensions/extension_gcm_app_handler.h
+++ b/chrome/browser/extensions/extension_gcm_app_handler.h
@@ -47,6 +47,10 @@
       const std::string& app_id,
       const gcm::GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
 
+ protected:
+  virtual void OnUnregisterCompleted(const std::string& app_id,
+                                     gcm::GCMClient::Result result);
+
  private:
   friend class BrowserContextKeyedAPIFactory<ExtensionGCMAppHandler>;
 
@@ -56,8 +60,6 @@
                        const content::NotificationDetails& details) OVERRIDE;
 
   gcm::GCMProfileService* GetGCMProfileService() const;
-  void OnUnregisterCompleted(const std::string& app_id,
-                             gcm::GCMClient::Result result);
 
   // BrowserContextKeyedAPI implementation.
   static const char* service_name() { return "ExtensionGCMAppHandler"; }
diff --git a/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc b/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc
new file mode 100644
index 0000000..1adc3dc2
--- /dev/null
+++ b/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc
@@ -0,0 +1,303 @@
+// 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 "base/bind.h"
+#include "base/command_line.h"
+#include "base/prefs/pref_service.h"
+#include "chrome/browser/extensions/extension_gcm_app_handler.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_profile_service.h"
+#include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
+#include "chrome/browser/services/gcm/gcm_profile_service_test_helper.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/browser/browser_thread.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"
+#else
+#include "components/signin/core/browser/signin_manager.h"
+#endif
+
+using namespace gcm;
+
+namespace extensions {
+
+namespace {
+
+const char kTestExtensionName[] = "FooBar";
+const char kTestingUsername[] = "[email protected]";
+
+}  // namespace
+
+class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler {
+ public:
+  FakeExtensionGCMAppHandler(Profile* profile, Waiter* waiter)
+      : ExtensionGCMAppHandler(profile),
+        waiter_(waiter),
+        unregistration_result_(GCMClient::UNKNOWN_ERROR) {
+  }
+
+  virtual ~FakeExtensionGCMAppHandler() {
+  }
+
+  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 GCMClient::SendErrorDetails& send_error_details) OVERRIDE {
+  }
+
+  virtual void OnUnregisterCompleted(const std::string& app_id,
+                                     GCMClient::Result result) OVERRIDE {
+    unregistration_result_ = result;
+    waiter_->SignalCompleted();
+  }
+
+  GCMClient::Result unregistration_result() const {
+    return unregistration_result_;
+  }
+
+ private:
+  Waiter* waiter_;
+  GCMClient::Result unregistration_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler);
+};
+
+class ExtensionGCMAppHandlerTest : public testing::Test {
+ public:
+  static KeyedService* BuildGCMProfileService(
+      content::BrowserContext* context) {
+    return new GCMProfileService(static_cast<Profile*>(context));
+  }
+
+  ExtensionGCMAppHandlerTest()
+      : extension_service_(NULL),
+        registration_result_(GCMClient::UNKNOWN_ERROR),
+        unregistration_result_(GCMClient::UNKNOWN_ERROR) {
+  }
+
+  virtual ~ExtensionGCMAppHandlerTest() {
+  }
+
+  // Overridden from test::Test:
+  virtual void SetUp() OVERRIDE {
+    // 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 a new profile.
+    TestingProfile::Builder builder;
+    builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
+                              gcm::FakeSigninManager::Build);
+    profile_ = builder.Build();
+    signin_manager_ = static_cast<gcm::FakeSigninManager*>(
+        SigninManagerFactory::GetInstance()->GetForProfile(profile_.get()));
+
+    // Create extension service in order to uninstall the extension.
+    TestExtensionSystem* extension_system(
+        static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile())));
+    extension_system->CreateExtensionService(
+        CommandLine::ForCurrentProcess(), base::FilePath(), false);
+    extension_service_ = extension_system->Get(profile())->extension_service();
+
+    // Enable GCM such that tests could be run on all channels.
+    profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
+
+    // Create GCMProfileService that talks with fake GCMClient.
+    GCMProfileService* gcm_profile_service = static_cast<GCMProfileService*>(
+        GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+            profile(),
+            &ExtensionGCMAppHandlerTest::BuildGCMProfileService));
+    scoped_ptr<GCMClientFactory> gcm_client_factory(
+        new FakeGCMClientFactory(GCMClientMock::NO_DELAY_LOADING));
+    gcm_profile_service->Initialize(gcm_client_factory.Pass());
+
+    // Create a fake version of ExtensionGCMAppHandler.
+    gcm_app_handler_.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_));
+  }
+
+  virtual void TearDown() OVERRIDE {
+#if defined(OS_CHROMEOS)
+    test_user_manager_.reset();
+#endif
+
+    waiter_.PumpUILoop();
+  }
+
+  // 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
+
+    base::DictionaryValue manifest;
+    manifest.SetString(manifest_keys::kVersion, "1.0.0.0");
+    manifest.SetString(manifest_keys::kName, kTestExtensionName);
+    base::ListValue* permission_list = new base::ListValue;
+    permission_list->Append(base::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;
+    EXPECT_TRUE(extension->HasAPIPermission(APIPermission::kGcm));
+
+    return extension;
+  }
+
+  void LoadExtension(const Extension* extension) {
+    extension_service_->AddExtension(extension);
+  }
+
+  void DisableExtension(const Extension* extension) {
+    extension_service_->DisableExtension(
+        extension->id(), Extension::DISABLE_USER_ACTION);
+  }
+
+  void EnableExtension(const Extension* extension) {
+    extension_service_->EnableExtension(extension->id());
+  }
+
+  void UninstallExtension(const Extension* extension) {
+    extension_service_->UninstallExtension(extension->id(), false, NULL);
+  }
+
+  void SignIn(const std::string& username) {
+    signin_manager_->SignIn(username);
+    waiter_.PumpIOLoop();
+  }
+
+  void SignOut() {
+    signin_manager_->SignOut();
+    waiter_.PumpIOLoop();
+  }
+
+  void Register(const std::string& app_id,
+                const std::vector<std::string>& sender_ids) {
+    GetGCMProfileService()->Register(
+        app_id,
+        sender_ids,
+        base::Bind(&ExtensionGCMAppHandlerTest::RegisterCompleted,
+                   base::Unretained(this)));
+  }
+
+  void RegisterCompleted(const std::string& registration_id,
+                         GCMClient::Result result) {
+    registration_result_ = result;
+    waiter_.SignalCompleted();
+  }
+
+  GCMProfileService* GetGCMProfileService() const {
+    return GCMProfileServiceFactory::GetForProfile(profile());
+  }
+
+  bool HasAppHandlers(const std::string& app_id) const {
+    return GetGCMProfileService()->app_handlers_.count(app_id);
+  }
+
+  Profile* profile() const { return profile_.get(); }
+  Waiter* waiter() { return &waiter_; }
+  FakeExtensionGCMAppHandler* gcm_app_handler() const {
+    return gcm_app_handler_.get();
+  }
+  GCMClient::Result registration_result() const { return registration_result_; }
+  GCMClient::Result unregistration_result() const {
+    return unregistration_result_;
+  }
+
+ private:
+  scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
+  scoped_ptr<TestingProfile> profile_;
+  ExtensionService* extension_service_;  // Not owned.
+  gcm::FakeSigninManager* signin_manager_;  // Not owned.
+
+  // This is needed to create extension service under CrOS.
+#if defined(OS_CHROMEOS)
+  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
+  chromeos::ScopedTestCrosSettings test_cros_settings_;
+  scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
+#endif
+
+  Waiter waiter_;
+  scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_;
+  GCMClient::Result registration_result_;
+  GCMClient::Result unregistration_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest);
+};
+
+TEST_F(ExtensionGCMAppHandlerTest, AddAndRemoveAppHandler) {
+  scoped_refptr<Extension> extension(CreateExtension());
+
+  // App handler is added when extension is loaded.
+  LoadExtension(extension);
+  waiter()->PumpUILoop();
+  EXPECT_TRUE(HasAppHandlers(extension->id()));
+
+  // App handler is removed when extension is unloaded.
+  DisableExtension(extension);
+  waiter()->PumpUILoop();
+  EXPECT_FALSE(HasAppHandlers(extension->id()));
+
+  // App handler is added when extension is reloaded.
+  EnableExtension(extension);
+  waiter()->PumpUILoop();
+  EXPECT_TRUE(HasAppHandlers(extension->id()));
+
+  // App handler is removed when extension is uninstalled.
+  UninstallExtension(extension);
+  waiter()->PumpUILoop();
+  EXPECT_FALSE(HasAppHandlers(extension->id()));
+}
+
+TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
+  scoped_refptr<Extension> extension(CreateExtension());
+  LoadExtension(extension);
+
+  // Sign-in is needed for registration.
+  SignIn(kTestingUsername);
+
+  // Kick off registration.
+  std::vector<std::string> sender_ids;
+  sender_ids.push_back("sender1");
+  Register(extension->id(), sender_ids);
+  waiter()->WaitUntilCompleted();
+  EXPECT_EQ(GCMClient::SUCCESS, registration_result());
+
+  // Unregistration should be triggered when the extension is uninstalled.
+  UninstallExtension(extension);
+  waiter()->WaitUntilCompleted();
+  EXPECT_EQ(GCMClient::SUCCESS, gcm_app_handler()->unregistration_result());
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/services/gcm/gcm_client_mock.cc b/chrome/browser/services/gcm/gcm_client_mock.cc
index 3292f3fa..3b58cda 100644
--- a/chrome/browser/services/gcm/gcm_client_mock.cc
+++ b/chrome/browser/services/gcm/gcm_client_mock.cc
@@ -13,12 +13,10 @@
 
 namespace gcm {
 
-GCMClientMock::GCMClientMock(LoadingDelay loading_delay,
-                             ErrorSimulation error_simulation)
+GCMClientMock::GCMClientMock(LoadingDelay loading_delay)
     : delegate_(NULL),
       status_(UNINITIALIZED),
       loading_delay_(loading_delay),
-      error_simulation_(error_simulation),
       weak_ptr_factory_(this) {
 }
 
@@ -67,10 +65,7 @@
                              const std::vector<std::string>& sender_ids) {
   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
 
-  std::string registration_id;
-  if (error_simulation_ == ALWAYS_SUCCEED)
-    registration_id = GetRegistrationIdFromSenderIds(sender_ids);
-
+  std::string registration_id = GetRegistrationIdFromSenderIds(sender_ids);
   base::MessageLoop::current()->PostTask(
       FROM_HERE,
       base::Bind(&GCMClientMock::RegisterFinished,
diff --git a/chrome/browser/services/gcm/gcm_client_mock.h b/chrome/browser/services/gcm/gcm_client_mock.h
index 2a1004a..2cdc104 100644
--- a/chrome/browser/services/gcm/gcm_client_mock.h
+++ b/chrome/browser/services/gcm/gcm_client_mock.h
@@ -25,14 +25,8 @@
     DELAY_LOADING,
   };
 
-  enum ErrorSimulation {
-    ALWAYS_SUCCEED,
-    FORCE_ERROR
-  };
-
   // |loading_delay| denotes if the check-in should be delayed.
-  // |error_simulation| denotes if we should simulate server error.
-  GCMClientMock(LoadingDelay loading_delay, ErrorSimulation error_simulation);
+  explicit GCMClientMock(LoadingDelay loading_delay);
   virtual ~GCMClientMock();
 
   // Overridden from GCMClient:
@@ -88,7 +82,6 @@
   Delegate* delegate_;
   Status status_;
   LoadingDelay loading_delay_;
-  ErrorSimulation error_simulation_;
   base::WeakPtrFactory<GCMClientMock> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GCMClientMock);
diff --git a/chrome/browser/services/gcm/gcm_profile_service.h b/chrome/browser/services/gcm/gcm_profile_service.h
index 39b72663..3817b0e 100644
--- a/chrome/browser/services/gcm/gcm_profile_service.h
+++ b/chrome/browser/services/gcm/gcm_profile_service.h
@@ -27,6 +27,10 @@
 class Value;
 }
 
+namespace extensions {
+class ExtensionGCMAppHandlerTest;
+}
+
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -131,6 +135,7 @@
 
  private:
   friend class GCMProfileServiceTestConsumer;
+  friend class extensions::ExtensionGCMAppHandlerTest;
 
   class DelayedTaskController;
   class IOWorker;
diff --git a/chrome/browser/services/gcm/gcm_profile_service_test_helper.cc b/chrome/browser/services/gcm/gcm_profile_service_test_helper.cc
new file mode 100644
index 0000000..2379482
--- /dev/null
+++ b/chrome/browser/services/gcm/gcm_profile_service_test_helper.cc
@@ -0,0 +1,119 @@
+// 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 "chrome/browser/services/gcm/gcm_profile_service_test_helper.h"
+
+#include "base/bind.h"
+#include "base/prefs/pref_service.h"
+#include "base/run_loop.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/chrome_signin_client_factory.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace gcm {
+
+Waiter::Waiter() {
+}
+
+Waiter::~Waiter() {
+}
+
+void Waiter::WaitUntilCompleted() {
+  run_loop_.reset(new base::RunLoop);
+  run_loop_->Run();
+}
+
+void Waiter::SignalCompleted() {
+  if (run_loop_ && run_loop_->running())
+    run_loop_->Quit();
+}
+
+void Waiter::PumpUILoop() {
+  base::MessageLoop::current()->RunUntilIdle();
+}
+
+void Waiter::PumpIOLoop() {
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this)));
+
+  WaitUntilCompleted();
+}
+
+void Waiter::PumpIOLoopCompleted() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+  SignalCompleted();
+}
+
+void Waiter::OnIOLoopPump() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::IO,
+      FROM_HERE,
+      base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
+}
+
+void Waiter::OnIOLoopPumpCompleted() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI,
+      FROM_HERE,
+      base::Bind(&Waiter::PumpIOLoopCompleted,
+                 base::Unretained(this)));
+}
+
+// static
+KeyedService* FakeSigninManager::Build(content::BrowserContext* context) {
+  return new FakeSigninManager(static_cast<Profile*>(context));
+}
+
+FakeSigninManager::FakeSigninManager(Profile* profile)
+#if defined(OS_CHROMEOS)
+    : SigninManagerBase(
+          ChromeSigninClientFactory::GetInstance()->GetForProfile(profile)),
+#else
+    : SigninManager(
+          ChromeSigninClientFactory::GetInstance()->GetForProfile(profile),
+          ProfileOAuth2TokenServiceFactory::GetForProfile(profile)),
+#endif
+      profile_(profile) {
+  Initialize(NULL);
+}
+
+FakeSigninManager::~FakeSigninManager() {
+}
+
+void FakeSigninManager::SignIn(const std::string& username) {
+  SetAuthenticatedUsername(username);
+  FOR_EACH_OBSERVER(Observer,
+                    observer_list_,
+                    GoogleSigninSucceeded(username, std::string()));
+}
+
+void FakeSigninManager::SignOut() {
+  std::string username = GetAuthenticatedUsername();
+  clear_authenticated_username();
+  profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
+  FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSignedOut(username));
+}
+
+FakeGCMClientFactory::FakeGCMClientFactory(
+    GCMClientMock::LoadingDelay gcm_client_loading_delay)
+    : gcm_client_loading_delay_(gcm_client_loading_delay) {
+}
+
+FakeGCMClientFactory::~FakeGCMClientFactory() {
+}
+
+scoped_ptr<GCMClient> FakeGCMClientFactory::BuildInstance() {
+  return scoped_ptr<GCMClient>(new GCMClientMock(gcm_client_loading_delay_));
+}
+
+}  // namespace gcm
diff --git a/chrome/browser/services/gcm/gcm_profile_service_test_helper.h b/chrome/browser/services/gcm/gcm_profile_service_test_helper.h
new file mode 100644
index 0000000..66eea37
--- /dev/null
+++ b/chrome/browser/services/gcm/gcm_profile_service_test_helper.h
@@ -0,0 +1,94 @@
+// 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.
+
+#ifndef CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_TEST_HELPER_H_
+#define CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_TEST_HELPER_H_
+
+#include <string>
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/services/gcm/gcm_client_factory.h"
+#include "chrome/browser/services/gcm/gcm_client_mock.h"
+#include "components/signin/core/browser/signin_manager.h"
+
+class KeyedService;
+class Profile;
+namespace base {
+class RunLoop;
+}
+namespace content {
+class BrowserContext;
+}
+
+namespace gcm {
+
+// Helper class for asynchronous waiting.
+class Waiter {
+ public:
+  Waiter();
+  ~Waiter();
+
+  // Waits until the asynchrnous operation finishes.
+  void WaitUntilCompleted();
+
+  // Signals that the asynchronous operation finishes.
+  void SignalCompleted();
+
+  // Runs until UI loop becomes idle.
+  void PumpUILoop();
+
+  // Runs until IO loop becomes idle.
+  void PumpIOLoop();
+
+ private:
+  void PumpIOLoopCompleted();
+  void OnIOLoopPump();
+  void OnIOLoopPumpCompleted();
+
+  scoped_ptr<base::RunLoop> run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(Waiter);
+};
+
+#if defined(OS_CHROMEOS)
+class FakeSigninManager : public SigninManagerBase {
+#else
+class FakeSigninManager : public SigninManager {
+#endif
+ public:
+  static KeyedService* Build(content::BrowserContext* context);
+
+  explicit FakeSigninManager(Profile* profile);
+  virtual ~FakeSigninManager();
+
+  void SignIn(const std::string& username);
+#if defined(OS_CHROMEOS)
+  void SignOut();
+#else
+  virtual void SignOut() OVERRIDE;
+#endif
+
+ private:
+  Profile* profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeSigninManager);
+};
+
+class FakeGCMClientFactory : public GCMClientFactory {
+ public:
+  explicit FakeGCMClientFactory(
+      GCMClientMock::LoadingDelay gcm_client_loading_delay);
+  virtual ~FakeGCMClientFactory();
+
+  virtual scoped_ptr<GCMClient> BuildInstance() OVERRIDE;
+
+ private:
+  GCMClientMock::LoadingDelay gcm_client_loading_delay_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeGCMClientFactory);
+};
+
+}  // namespace gcm
+
+#endif  // CHROME_BROWSER_SERVICES_GCM_GCM_PROFILE_SERVICE_TEST_HELPER_H_
diff --git a/chrome/browser/services/gcm/gcm_profile_service_unittest.cc b/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
index db5719a..9be82fe 100644
--- a/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
+++ b/chrome/browser/services/gcm/gcm_profile_service_unittest.cc
@@ -2,56 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <algorithm>
-#include <map>
-
 #include "base/bind.h"
-#include "base/command_line.h"
-#include "base/message_loop/message_loop.h"
 #include "base/prefs/pref_service.h"
-#include "base/run_loop.h"
-#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/extensions/extension_gcm_app_handler.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_app_handler.h"
 #include "chrome/browser/services/gcm/gcm_client_factory.h"
 #include "chrome/browser/services/gcm/gcm_client_mock.h"
 #include "chrome/browser/services/gcm/gcm_profile_service.h"
 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
-#include "chrome/browser/signin/chrome_signin_client.h"
-#include "chrome/browser/signin/chrome_signin_client_factory.h"
+#include "chrome/browser/services/gcm/gcm_profile_service_test_helper.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/os_crypt/os_crypt.h"
-#include "components/signin/core/browser/signin_manager_base.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/extension_prefs.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"
-#else
-#include "components/signin/core/browser/signin_manager.h"
-#endif
-
-using namespace extensions;
-
 namespace gcm {
 
 namespace {
 
-const char kTestExtensionName[] = "FooBar";
 const char kTestingUsername[] = "[email protected]";
 const char kTestingUsername2[] = "[email protected]";
 const char kTestingUsername3[] = "[email protected]";
@@ -62,108 +33,13 @@
 
 std::vector<std::string> ToSenderList(const std::string& sender_ids) {
   std::vector<std::string> senders;
-  base::StringTokenizer tokenizer(sender_ids, ",");
-  while (tokenizer.GetNext())
-    senders.push_back(tokenizer.token());
+  Tokenize(sender_ids, ",", &senders);
   return senders;
 }
 
-// Helper class for asynchrnous waiting.
-class Waiter {
- public:
-  Waiter() {}
-  virtual ~Waiter() {}
-
-  // Waits until the asynchrnous operation finishes.
-  void WaitUntilCompleted() {
-    run_loop_.reset(new base::RunLoop);
-    run_loop_->Run();
-  }
-
-  // Signals that the asynchronous operation finishes.
-  void SignalCompleted() {
-    if (run_loop_ && run_loop_->running())
-      run_loop_->Quit();
-  }
-
-  // Runs until UI loop becomes idle.
-  void PumpUILoop() {
-    base::MessageLoop::current()->RunUntilIdle();
-  }
-
-  // Runs until IO loop becomes idle.
-  void PumpIOLoop() {
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO,
-        FROM_HERE,
-        base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this)));
-
-    WaitUntilCompleted();
-  }
-
- private:
-  void PumpIOLoopCompleted() {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
-
-    SignalCompleted();
-  }
-
-  void OnIOLoopPump() {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
-    content::BrowserThread::PostTask(
-        content::BrowserThread::IO,
-        FROM_HERE,
-        base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
-  }
-
-  void OnIOLoopPumpCompleted() {
-    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
-
-    content::BrowserThread::PostTask(
-        content::BrowserThread::UI,
-        FROM_HERE,
-        base::Bind(&Waiter::PumpIOLoopCompleted,
-                   base::Unretained(this)));
-  }
-
-  scoped_ptr<base::RunLoop> run_loop_;
-};
-
-class FakeSigninManager : public SigninManagerBase {
- public:
-  explicit FakeSigninManager(Profile* profile)
-      : SigninManagerBase(
-            ChromeSigninClientFactory::GetInstance()->GetForProfile(profile)),
-        profile_(profile) {
-    Initialize(NULL);
-  }
-
-  virtual ~FakeSigninManager() {
-  }
-
-  void SignIn(const std::string& username) {
-    SetAuthenticatedUsername(username);
-    FOR_EACH_OBSERVER(Observer,
-                      observer_list_,
-                      GoogleSigninSucceeded(username, std::string()));
-  }
-
-  void SignOut() {
-    std::string username = GetAuthenticatedUsername();
-    clear_authenticated_username();
-    profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
-    FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSignedOut(username));
-  }
-
- private:
-  Profile* profile_;
-};
-
 }  // namespace
 
-// TODO(jianli): Move extension specific tests to a separate file.
-class FakeGCMAppHandler : public ExtensionGCMAppHandler {
+class FakeGCMAppHandler : public GCMAppHandler {
  public:
   enum Event {
     NO_EVENT,
@@ -172,15 +48,17 @@
     SEND_ERROR_EVENT
   };
 
-  FakeGCMAppHandler(Profile* profile, Waiter* waiter)
-      : ExtensionGCMAppHandler(profile),
-        waiter_(waiter),
+  explicit FakeGCMAppHandler(Waiter* waiter)
+      : waiter_(waiter),
         received_event_(NO_EVENT) {
   }
 
   virtual ~FakeGCMAppHandler() {
   }
 
+  virtual void ShutdownHandler() OVERRIDE {
+  }
+
   virtual void OnMessage(const std::string& app_id,
                          const GCMClient::IncomingMessage& message) OVERRIDE {
     clear_results();
@@ -238,42 +116,8 @@
   GCMClient::SendErrorDetails send_error_details_;
 };
 
-class FakeGCMClientFactory : public GCMClientFactory {
- public:
-  FakeGCMClientFactory(
-      GCMClientMock::LoadingDelay gcm_client_loading_delay,
-      GCMClientMock::ErrorSimulation gcm_client_error_simulation)
-      : gcm_client_loading_delay_(gcm_client_loading_delay),
-        gcm_client_error_simulation_(gcm_client_error_simulation),
-        gcm_client_(NULL) {
-  }
-
-  virtual ~FakeGCMClientFactory() {
-  }
-
-  virtual scoped_ptr<GCMClient> BuildInstance() OVERRIDE {
-    gcm_client_ = new GCMClientMock(gcm_client_loading_delay_,
-                                    gcm_client_error_simulation_);
-    return scoped_ptr<GCMClient>(gcm_client_);
-  }
-
-  GCMClientMock* gcm_client() const { return gcm_client_; }
-
- private:
-  GCMClientMock::LoadingDelay gcm_client_loading_delay_;
-  GCMClientMock::ErrorSimulation gcm_client_error_simulation_;
-  GCMClientMock* gcm_client_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeGCMClientFactory);
-};
-
 class GCMProfileServiceTestConsumer {
  public:
-  static KeyedService* BuildFakeSigninManager(
-      content::BrowserContext* context) {
-    return new FakeSigninManager(static_cast<Profile*>(context));
-  }
-
   static KeyedService* BuildGCMProfileService(
       content::BrowserContext* context) {
     return new GCMProfileService(static_cast<Profile*>(context));
@@ -281,37 +125,18 @@
 
   explicit GCMProfileServiceTestConsumer(Waiter* waiter)
       : waiter_(waiter),
-        extension_service_(NULL),
         signin_manager_(NULL),
         gcm_client_loading_delay_(GCMClientMock::NO_DELAY_LOADING),
-        gcm_client_error_simulation_(GCMClientMock::ALWAYS_SUCCEED),
-        registration_result_(GCMClient::SUCCESS),
-        has_persisted_registration_info_(false),
-        send_result_(GCMClient::SUCCESS) {
+        registration_result_(GCMClient::UNKNOWN_ERROR),
+        unregistration_result_(GCMClient::UNKNOWN_ERROR),
+        send_result_(GCMClient::UNKNOWN_ERROR) {
     // Create a new profile.
     TestingProfile::Builder builder;
-    builder.AddTestingFactory(
-        SigninManagerFactory::GetInstance(),
-        GCMProfileServiceTestConsumer::BuildFakeSigninManager);
+    builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
+                              FakeSigninManager::Build);
     profile_ = builder.Build();
-
-    SigninManagerBase* signin_manager =
-        SigninManagerFactory::GetInstance()->GetForProfile(profile_.get());
-    signin_manager_ = static_cast<FakeSigninManager*>(signin_manager);
-
-    // 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();
-
-    // EventRouter is needed for GcmJsEventRouter.
-    if (!extension_system->event_router()) {
-      extension_system->SetEventRouter(scoped_ptr<EventRouter>(
-          new EventRouter(profile(), ExtensionPrefs::Get(profile()))));
-    }
+    signin_manager_ = static_cast<FakeSigninManager*>(
+        SigninManagerFactory::GetInstance()->GetForProfile(profile_.get()));
 
     // Enable GCM such that tests could be run on all channels.
     profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
@@ -322,55 +147,15 @@
     GetGCMProfileService()->RemoveAppHandler(kTestingAppId2);
   }
 
-  // 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
-
-    base::DictionaryValue manifest;
-    manifest.SetString(manifest_keys::kVersion, "1.0.0.0");
-    manifest.SetString(manifest_keys::kName, kTestExtensionName);
-    base::ListValue* permission_list = new base::ListValue;
-    permission_list->Append(base::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;
-    EXPECT_TRUE(extension->HasAPIPermission(APIPermission::kGcm));
-
-    extension_service_->AddExtension(extension.get());
-    return extension;
-  }
-
-  void UninstallExtension(const extensions::Extension* extension) {
-    extension_service_->UninstallExtension(extension->id(), false, NULL);
-  }
-
-  void ReloadExtension(const extensions::Extension* extension) {
-    extension_service_->UnloadExtension(
-        extension->id(), UnloadedExtensionInfo::REASON_TERMINATE);
-    extension_service_->AddExtension(extension);
-  }
-
   void CreateGCMProfileServiceInstance() {
     GCMProfileService* gcm_profile_service = static_cast<GCMProfileService*>(
         GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
             profile(), &GCMProfileServiceTestConsumer::BuildGCMProfileService));
     scoped_ptr<GCMClientFactory> gcm_client_factory(
-        new FakeGCMClientFactory(gcm_client_loading_delay_,
-                                 gcm_client_error_simulation_));
+        new FakeGCMClientFactory(gcm_client_loading_delay_));
     gcm_profile_service->Initialize(gcm_client_factory.Pass());
 
-    gcm_app_handler_.reset(new FakeGCMAppHandler(profile(), waiter_));
+    gcm_app_handler_.reset(new FakeGCMAppHandler(waiter_));
     gcm_profile_service->AddAppHandler(kTestingAppId, gcm_app_handler());
     gcm_profile_service->AddAppHandler(kTestingAppId2, gcm_app_handler());
 
@@ -463,9 +248,6 @@
   void set_gcm_client_loading_delay(GCMClientMock::LoadingDelay delay) {
     gcm_client_loading_delay_ = delay;
   }
-  void set_gcm_client_error_simulation(GCMClientMock::ErrorSimulation error) {
-    gcm_client_error_simulation_ = error;
-  }
 
   const std::string& registration_id() const { return registration_id_; }
   GCMClient::Result registration_result() const { return registration_result_; }
@@ -490,16 +272,13 @@
  private:
   Waiter* waiter_;  // Not owned.
   scoped_ptr<TestingProfile> profile_;
-  ExtensionService* extension_service_;  // Not owned.
   FakeSigninManager* signin_manager_;  // Not owned.
   scoped_ptr<FakeGCMAppHandler> gcm_app_handler_;
 
   GCMClientMock::LoadingDelay gcm_client_loading_delay_;
-  GCMClientMock::ErrorSimulation gcm_client_error_simulation_;
 
   std::string registration_id_;
   GCMClient::Result registration_result_;
-  bool has_persisted_registration_info_;
 
   GCMClient::Result unregistration_result_;
 
@@ -523,28 +302,13 @@
     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
-
-    // OSCrypt ends up needing access to the keychain on OS X. So use the mock
-    // keychain to prevent prompts.
-#if defined(OS_MACOSX)
-    OSCrypt::UseMockKeychain(true);
-#endif
-
     // Create a main profile consumer.
     consumer_.reset(new GCMProfileServiceTestConsumer(&waiter_));
   }
 
   virtual void TearDown() OVERRIDE {
-#if defined(OS_CHROMEOS)
-    test_user_manager_.reset();
-#endif
-
     consumer_.reset();
-    base::RunLoop().RunUntilIdle();
+    PumpUILoop();
   }
 
   void WaitUntilCompleted() {
@@ -571,12 +335,6 @@
 
  private:
   scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
-#if defined(OS_CHROMEOS)
-  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
-  chromeos::ScopedTestCrosSettings test_cros_settings_;
-  scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
-#endif
-
   scoped_ptr<GCMProfileServiceTestConsumer> consumer_;
 
   DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceTest);
@@ -895,40 +653,24 @@
 }
 
 TEST_F(GCMProfileServiceSingleProfileTest,
-       GCMClientReadyAfterReadingRegistration) {
-  scoped_refptr<Extension> extension(consumer()->CreateExtension());
-
-  std::vector<std::string> sender_ids;
-  sender_ids.push_back("sender1");
-  consumer()->Register(extension->id(), sender_ids);
-
-  WaitUntilCompleted();
-  EXPECT_FALSE(consumer()->registration_id().empty());
-  EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-  std::string old_registration_id = consumer()->registration_id();
-
-  // Clears the results that would be set by the Register callback in
-  // preparation to call register 2nd time.
-  consumer()->clear_registration_result();
-
-  // Simulate start-up by recreating GCMProfileService.
+       GCMClientNotReadyBeforeRegistration) {
+  // Make GCMClient not ready initially.
   consumer()->set_gcm_client_loading_delay(GCMClientMock::DELAY_LOADING);
   consumer()->CreateGCMProfileServiceInstance();
 
-  // Simulate start-up by reloading extension.
-  consumer()->ReloadExtension(extension);
-
-  // Read the registration info from the extension's state store.
-  // This would hold up because GCMClient is in loading state.
-  consumer()->Register(extension->id(), sender_ids);
-  base::RunLoop().RunUntilIdle();
+  // The registration is on hold until GCMClient is ready.
+  std::vector<std::string> sender_ids;
+  sender_ids.push_back("sender1");
+  consumer()->Register(kTestingAppId, sender_ids);
+  PumpIOLoop();
   EXPECT_TRUE(consumer()->registration_id().empty());
   EXPECT_EQ(GCMClient::UNKNOWN_ERROR, consumer()->registration_result());
 
   // Register operation will be invoked after GCMClient becomes ready.
   consumer()->GetGCMClient()->PerformDelayedLoading();
-  WaitUntilCompleted();
-  EXPECT_EQ(old_registration_id, consumer()->registration_id());
+  PumpIOLoop();  // The 1st pump is to wait till the delayed loading is done.
+  PumpIOLoop();  // The 2nd pump is to wait till the registration is done.
+  EXPECT_FALSE(consumer()->registration_id().empty());
   EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
 }
 
@@ -944,22 +686,6 @@
   EXPECT_EQ(GCMClient::NOT_SIGNED_IN, consumer()->registration_result());
 }
 
-TEST_F(GCMProfileServiceSingleProfileTest, UnregisterImplicitly) {
-  scoped_refptr<Extension> extension(consumer()->CreateExtension());
-
-  std::vector<std::string> sender_ids;
-  sender_ids.push_back("sender1");
-  consumer()->Register(extension->id(), sender_ids);
-
-  WaitUntilCompleted();
-  EXPECT_FALSE(consumer()->registration_id().empty());
-  EXPECT_EQ(GCMClient::SUCCESS, consumer()->registration_result());
-
-  // Uninstall the extension.
-  consumer()->UninstallExtension(extension);
-  base::MessageLoop::current()->RunUntilIdle();
-}
-
 TEST_F(GCMProfileServiceSingleProfileTest, UnregisterExplicitly) {
   std::vector<std::string> sender_ids;
   sender_ids.push_back("sender1");
@@ -1059,6 +785,29 @@
   EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
 }
 
+TEST_F(GCMProfileServiceSingleProfileTest, GCMClientNotReadyBeforeSending) {
+  // Make GCMClient not ready initially.
+  consumer()->set_gcm_client_loading_delay(GCMClientMock::DELAY_LOADING);
+  consumer()->CreateGCMProfileServiceInstance();
+
+  // The sending is on hold until GCMClient is ready.
+  GCMClient::OutgoingMessage message;
+  message.id = "1";
+  message.data["key1"] = "value1";
+  message.data["key2"] = "value2";
+  consumer()->Send(kTestingAppId, kUserId, message);
+  PumpIOLoop();
+  EXPECT_TRUE(consumer()->send_message_id().empty());
+  EXPECT_EQ(GCMClient::UNKNOWN_ERROR, consumer()->send_result());
+
+  // Register operation will be invoked after GCMClient becomes ready.
+  consumer()->GetGCMClient()->PerformDelayedLoading();
+  PumpIOLoop();
+  PumpIOLoop();
+  EXPECT_EQ(consumer()->send_message_id(), message.id);
+  EXPECT_EQ(GCMClient::SUCCESS, consumer()->send_result());
+}
+
 TEST_F(GCMProfileServiceSingleProfileTest, SendAfterSignOut) {
   // This will trigger check-out.
   consumer()->SignOut();
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 91e5bd3..539dc98 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -939,6 +939,7 @@
         'browser/extensions/extension_function_test_utils.cc',
         'browser/extensions/extension_function_test_utils.h',
         'browser/extensions/extension_garbage_collector_unittest.cc',
+        'browser/extensions/extension_gcm_app_handler_unittest.cc',
         'browser/extensions/extension_icon_image_unittest.cc',
         'browser/extensions/extension_icon_manager_unittest.cc',
         'browser/extensions/extension_message_bubble_controller_unittest.cc',
@@ -1263,6 +1264,8 @@
         'browser/search_engines/template_url_unittest.cc',
         'browser/services/gcm/gcm_client_mock.cc',
         'browser/services/gcm/gcm_client_mock.h',
+        'browser/services/gcm/gcm_profile_service_test_helper.cc',
+        'browser/services/gcm/gcm_profile_service_test_helper.h',
         'browser/services/gcm/gcm_profile_service_unittest.cc',
         'browser/sessions/persistent_tab_restore_service_unittest.cc',
         'browser/sessions/restore_on_startup_policy_handler_unittest.cc',