extension_gcm_app_handler_unittest.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/extensions/extension_gcm_app_handler.h" 6 7#include <vector> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/command_line.h" 12#include "base/files/file_path.h" 13#include "base/location.h" 14#include "base/logging.h" 15#include "base/memory/ref_counted.h" 16#include "base/message_loop/message_loop.h" 17#include "base/run_loop.h" 18#include "base/values.h" 19#include "chrome/browser/extensions/extension_service.h" 20#include "chrome/browser/extensions/test_extension_service.h" 21#include "chrome/browser/extensions/test_extension_system.h" 22#include "chrome/browser/profiles/profile.h" 23#include "chrome/browser/services/gcm/fake_gcm_client_factory.h" 24#include "chrome/browser/services/gcm/fake_signin_manager.h" 25#include "chrome/browser/services/gcm/gcm_client_factory.h" 26#include "chrome/browser/services/gcm/gcm_client_mock.h" 27#include "chrome/browser/services/gcm/gcm_profile_service.h" 28#include "chrome/browser/services/gcm/gcm_profile_service_factory.h" 29#include "chrome/browser/signin/signin_manager_factory.h" 30#include "chrome/common/pref_names.h" 31#include "chrome/test/base/testing_profile.h" 32#include "components/keyed_service/core/keyed_service.h" 33#include "content/public/browser/browser_context.h" 34#include "content/public/browser/browser_thread.h" 35#include "content/public/test/test_browser_thread_bundle.h" 36#include "extensions/browser/extension_system.h" 37#include "extensions/common/extension.h" 38#include "extensions/common/manifest.h" 39#include "extensions/common/manifest_constants.h" 40#include "extensions/common/permissions/api_permission.h" 41#include "testing/gtest/include/gtest/gtest.h" 42 43#if !defined(OS_ANDROID) 44#include "chrome/browser/extensions/api/gcm/gcm_api.h" 45#endif 46 47#if defined(OS_CHROMEOS) 48#include "chrome/browser/chromeos/login/user_manager.h" 49#include "chrome/browser/chromeos/settings/cros_settings.h" 50#include "chrome/browser/chromeos/settings/device_settings_service.h" 51#endif 52 53namespace extensions { 54 55namespace { 56 57const char kTestExtensionName[] = "FooBar"; 58const char kTestingUsername[] = "user1@example.com"; 59 60} // namespace 61 62// Helper class for asynchronous waiting. 63class Waiter { 64 public: 65 Waiter() {} 66 ~Waiter() {} 67 68 // Waits until the asynchronous operation finishes. 69 void WaitUntilCompleted() { 70 run_loop_.reset(new base::RunLoop); 71 run_loop_->Run(); 72 } 73 74 // Signals that the asynchronous operation finishes. 75 void SignalCompleted() { 76 if (run_loop_ && run_loop_->running()) 77 run_loop_->Quit(); 78 } 79 80 // Runs until UI loop becomes idle. 81 void PumpUILoop() { 82 base::MessageLoop::current()->RunUntilIdle(); 83 } 84 85 // Runs until IO loop becomes idle. 86 void PumpIOLoop() { 87 content::BrowserThread::PostTask( 88 content::BrowserThread::IO, 89 FROM_HERE, 90 base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this))); 91 92 WaitUntilCompleted(); 93 } 94 95 private: 96 void PumpIOLoopCompleted() { 97 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 98 99 SignalCompleted(); 100 } 101 102 void OnIOLoopPump() { 103 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 104 105 content::BrowserThread::PostTask( 106 content::BrowserThread::IO, 107 FROM_HERE, 108 base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this))); 109 } 110 111 void OnIOLoopPumpCompleted() { 112 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 113 114 content::BrowserThread::PostTask( 115 content::BrowserThread::UI, 116 FROM_HERE, 117 base::Bind(&Waiter::PumpIOLoopCompleted, base::Unretained(this))); 118 } 119 120 scoped_ptr<base::RunLoop> run_loop_; 121 122 DISALLOW_COPY_AND_ASSIGN(Waiter); 123}; 124 125class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler { 126 public: 127 FakeExtensionGCMAppHandler(Profile* profile, Waiter* waiter) 128 : ExtensionGCMAppHandler(profile), 129 waiter_(waiter), 130 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) { 131 } 132 133 virtual ~FakeExtensionGCMAppHandler() { 134 } 135 136 virtual void OnMessage( 137 const std::string& app_id, 138 const gcm::GCMClient::IncomingMessage& message) OVERRIDE { 139 } 140 141 virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE { 142 } 143 144 virtual void OnSendError( 145 const std::string& app_id, 146 const gcm::GCMClient::SendErrorDetails& send_error_details) OVERRIDE { 147 } 148 149 virtual void OnUnregisterCompleted(const std::string& app_id, 150 gcm::GCMClient::Result result) OVERRIDE { 151 unregistration_result_ = result; 152 waiter_->SignalCompleted(); 153 } 154 155 gcm::GCMClient::Result unregistration_result() const { 156 return unregistration_result_; 157 } 158 159 private: 160 Waiter* waiter_; 161 gcm::GCMClient::Result unregistration_result_; 162 163 DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler); 164}; 165 166class ExtensionGCMAppHandlerTest : public testing::Test { 167 public: 168 static KeyedService* BuildGCMProfileService( 169 content::BrowserContext* context) { 170 return new gcm::GCMProfileService(static_cast<Profile*>(context)); 171 } 172 173 ExtensionGCMAppHandlerTest() 174 : extension_service_(NULL), 175 registration_result_(gcm::GCMClient::UNKNOWN_ERROR), 176 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) { 177 } 178 179 virtual ~ExtensionGCMAppHandlerTest() { 180 } 181 182 // Overridden from test::Test: 183 virtual void SetUp() OVERRIDE { 184 // Make BrowserThread work in unittest. 185 thread_bundle_.reset(new content::TestBrowserThreadBundle( 186 content::TestBrowserThreadBundle::REAL_IO_THREAD)); 187 188 // This is needed to create extension service under CrOS. 189#if defined(OS_CHROMEOS) 190 test_user_manager_.reset(new chromeos::ScopedTestUserManager()); 191#endif 192 193 // Create a new profile. 194 TestingProfile::Builder builder; 195 builder.AddTestingFactory(SigninManagerFactory::GetInstance(), 196 gcm::FakeSigninManager::Build); 197 profile_ = builder.Build(); 198 signin_manager_ = static_cast<gcm::FakeSigninManager*>( 199 SigninManagerFactory::GetInstance()->GetForProfile(profile_.get())); 200 201 // Create extension service in order to uninstall the extension. 202 TestExtensionSystem* extension_system( 203 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()))); 204 extension_system->CreateExtensionService( 205 CommandLine::ForCurrentProcess(), base::FilePath(), false); 206 extension_service_ = extension_system->Get(profile())->extension_service(); 207 208 // Enable GCM such that tests could be run on all channels. 209 profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true); 210 211 // Create GCMProfileService that talks with fake GCMClient. 212 gcm::GCMProfileService* gcm_profile_service = 213 static_cast<gcm::GCMProfileService*>( 214 gcm::GCMProfileServiceFactory::GetInstance()-> 215 SetTestingFactoryAndUse( 216 profile(), 217 &ExtensionGCMAppHandlerTest::BuildGCMProfileService)); 218 scoped_ptr<gcm::GCMClientFactory> gcm_client_factory( 219 new gcm::FakeGCMClientFactory(gcm::GCMClientMock::NO_DELAY_LOADING)); 220 gcm_profile_service->Initialize(gcm_client_factory.Pass()); 221 222 // Create a fake version of ExtensionGCMAppHandler. 223 gcm_app_handler_.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_)); 224 } 225 226 virtual void TearDown() OVERRIDE { 227#if defined(OS_CHROMEOS) 228 test_user_manager_.reset(); 229#endif 230 231 waiter_.PumpUILoop(); 232 } 233 234 // Returns a barebones test extension. 235 scoped_refptr<Extension> CreateExtension() { 236#if defined(OS_WIN) 237 base::FilePath path(FILE_PATH_LITERAL("c:\\foo")); 238#elif defined(OS_POSIX) 239 base::FilePath path(FILE_PATH_LITERAL("/foo")); 240#endif 241 242 base::DictionaryValue manifest; 243 manifest.SetString(manifest_keys::kVersion, "1.0.0.0"); 244 manifest.SetString(manifest_keys::kName, kTestExtensionName); 245 base::ListValue* permission_list = new base::ListValue; 246 permission_list->Append(base::Value::CreateStringValue("gcm")); 247 manifest.Set(manifest_keys::kPermissions, permission_list); 248 249 std::string error; 250 scoped_refptr<Extension> extension = Extension::Create( 251 path.AppendASCII(kTestExtensionName), 252 Manifest::INVALID_LOCATION, 253 manifest, 254 Extension::NO_FLAGS, 255 &error); 256 EXPECT_TRUE(extension.get()) << error; 257 EXPECT_TRUE(extension->HasAPIPermission(APIPermission::kGcm)); 258 259 return extension; 260 } 261 262 void LoadExtension(const Extension* extension) { 263 extension_service_->AddExtension(extension); 264 } 265 266 void DisableExtension(const Extension* extension) { 267 extension_service_->DisableExtension( 268 extension->id(), Extension::DISABLE_USER_ACTION); 269 } 270 271 void EnableExtension(const Extension* extension) { 272 extension_service_->EnableExtension(extension->id()); 273 } 274 275 void UninstallExtension(const Extension* extension) { 276 extension_service_->UninstallExtension(extension->id(), false, NULL); 277 } 278 279 void SignIn(const std::string& username) { 280 signin_manager_->SignIn(username); 281 waiter_.PumpIOLoop(); 282 } 283 284 void SignOut() { 285 signin_manager_->SignOut(); 286 waiter_.PumpIOLoop(); 287 } 288 289 void Register(const std::string& app_id, 290 const std::vector<std::string>& sender_ids) { 291 GetGCMProfileService()->Register( 292 app_id, 293 sender_ids, 294 base::Bind(&ExtensionGCMAppHandlerTest::RegisterCompleted, 295 base::Unretained(this))); 296 } 297 298 void RegisterCompleted(const std::string& registration_id, 299 gcm::GCMClient::Result result) { 300 registration_result_ = result; 301 waiter_.SignalCompleted(); 302 } 303 304 gcm::GCMProfileService* GetGCMProfileService() const { 305 return gcm::GCMProfileServiceFactory::GetForProfile(profile()); 306 } 307 308 bool HasAppHandlers(const std::string& app_id) const { 309 return GetGCMProfileService()->app_handlers_.count(app_id); 310 } 311 312 Profile* profile() const { return profile_.get(); } 313 Waiter* waiter() { return &waiter_; } 314 FakeExtensionGCMAppHandler* gcm_app_handler() const { 315 return gcm_app_handler_.get(); 316 } 317 gcm::GCMClient::Result registration_result() const { 318 return registration_result_; 319 } 320 gcm::GCMClient::Result unregistration_result() const { 321 return unregistration_result_; 322 } 323 324 private: 325 scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_; 326 scoped_ptr<TestingProfile> profile_; 327 ExtensionService* extension_service_; // Not owned. 328 gcm::FakeSigninManager* signin_manager_; // Not owned. 329 330 // This is needed to create extension service under CrOS. 331#if defined(OS_CHROMEOS) 332 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 333 chromeos::ScopedTestCrosSettings test_cros_settings_; 334 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_; 335#endif 336 337 Waiter waiter_; 338 scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_; 339 gcm::GCMClient::Result registration_result_; 340 gcm::GCMClient::Result unregistration_result_; 341 342 DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest); 343}; 344 345TEST_F(ExtensionGCMAppHandlerTest, AddAndRemoveAppHandler) { 346 scoped_refptr<Extension> extension(CreateExtension()); 347 348 // App handler is added when extension is loaded. 349 LoadExtension(extension); 350 waiter()->PumpUILoop(); 351 EXPECT_TRUE(HasAppHandlers(extension->id())); 352 353 // App handler is removed when extension is unloaded. 354 DisableExtension(extension); 355 waiter()->PumpUILoop(); 356 EXPECT_FALSE(HasAppHandlers(extension->id())); 357 358 // App handler is added when extension is reloaded. 359 EnableExtension(extension); 360 waiter()->PumpUILoop(); 361 EXPECT_TRUE(HasAppHandlers(extension->id())); 362 363 // App handler is removed when extension is uninstalled. 364 UninstallExtension(extension); 365 waiter()->PumpUILoop(); 366 EXPECT_FALSE(HasAppHandlers(extension->id())); 367} 368 369TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) { 370 scoped_refptr<Extension> extension(CreateExtension()); 371 LoadExtension(extension); 372 373 // Sign-in is needed for registration. 374 SignIn(kTestingUsername); 375 376 // Kick off registration. 377 std::vector<std::string> sender_ids; 378 sender_ids.push_back("sender1"); 379 Register(extension->id(), sender_ids); 380 waiter()->WaitUntilCompleted(); 381 EXPECT_EQ(gcm::GCMClient::SUCCESS, registration_result()); 382 383 // Unregistration should be triggered when the extension is uninstalled. 384 UninstallExtension(extension); 385 waiter()->WaitUntilCompleted(); 386 EXPECT_EQ(gcm::GCMClient::SUCCESS, 387 gcm_app_handler()->unregistration_result()); 388} 389 390} // namespace extensions 391