Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / extensions / extension_gcm_app_handler_unittest.cc
blobb9a20bbc98cdaf02940d7baeb7dc49e38c597ea4
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.
5 #include "chrome/browser/extensions/extension_gcm_app_handler.h"
7 #include <vector>
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/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/path_service.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/run_loop.h"
22 #include "base/values.h"
23 #include "chrome/browser/chrome_notification_types.h"
24 #include "chrome/browser/extensions/api/gcm/gcm_api.h"
25 #include "chrome/browser/extensions/extension_service.h"
26 #include "chrome/browser/extensions/test_extension_service.h"
27 #include "chrome/browser/extensions/test_extension_system.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/services/gcm/fake_signin_manager.h"
30 #include "chrome/browser/services/gcm/gcm_profile_service.h"
31 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
32 #include "chrome/browser/signin/signin_manager_factory.h"
33 #include "chrome/common/chrome_paths.h"
34 #include "chrome/common/pref_names.h"
35 #include "chrome/test/base/testing_profile.h"
36 #include "components/gcm_driver/fake_gcm_app_handler.h"
37 #include "components/gcm_driver/fake_gcm_client.h"
38 #include "components/gcm_driver/fake_gcm_client_factory.h"
39 #include "components/gcm_driver/gcm_client_factory.h"
40 #include "components/gcm_driver/gcm_driver.h"
41 #include "components/keyed_service/core/keyed_service.h"
42 #include "content/public/browser/browser_context.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "content/public/test/test_browser_thread_bundle.h"
45 #include "content/public/test/test_utils.h"
46 #include "extensions/browser/extension_system.h"
47 #include "extensions/browser/uninstall_reason.h"
48 #include "extensions/common/extension.h"
49 #include "extensions/common/manifest.h"
50 #include "extensions/common/manifest_constants.h"
51 #include "extensions/common/permissions/api_permission.h"
52 #include "extensions/common/permissions/permissions_data.h"
53 #include "testing/gtest/include/gtest/gtest.h"
55 #if defined(OS_CHROMEOS)
56 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
57 #include "chrome/browser/chromeos/settings/cros_settings.h"
58 #include "chrome/browser/chromeos/settings/device_settings_service.h"
59 #include "chromeos/dbus/dbus_thread_manager.h"
60 #endif
62 namespace extensions {
64 namespace {
66 const char kTestExtensionName[] = "FooBar";
67 const char kTestingUsername[] = "user1@example.com";
69 } // namespace
71 // Helper class for asynchronous waiting.
72 class Waiter {
73 public:
74 Waiter() {}
75 ~Waiter() {}
77 // Waits until the asynchronous operation finishes.
78 void WaitUntilCompleted() {
79 run_loop_.reset(new base::RunLoop);
80 run_loop_->Run();
83 // Signals that the asynchronous operation finishes.
84 void SignalCompleted() {
85 if (run_loop_ && run_loop_->running())
86 run_loop_->Quit();
89 // Runs until UI loop becomes idle.
90 void PumpUILoop() {
91 base::MessageLoop::current()->RunUntilIdle();
94 // Runs until IO loop becomes idle.
95 void PumpIOLoop() {
96 content::BrowserThread::PostTask(
97 content::BrowserThread::IO,
98 FROM_HERE,
99 base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this)));
101 WaitUntilCompleted();
104 private:
105 void PumpIOLoopCompleted() {
106 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
108 SignalCompleted();
111 void OnIOLoopPump() {
112 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
114 content::BrowserThread::PostTask(
115 content::BrowserThread::IO,
116 FROM_HERE,
117 base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
120 void OnIOLoopPumpCompleted() {
121 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
123 content::BrowserThread::PostTask(
124 content::BrowserThread::UI,
125 FROM_HERE,
126 base::Bind(&Waiter::PumpIOLoopCompleted, base::Unretained(this)));
129 scoped_ptr<base::RunLoop> run_loop_;
131 DISALLOW_COPY_AND_ASSIGN(Waiter);
134 class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler {
135 public:
136 FakeExtensionGCMAppHandler(Profile* profile, Waiter* waiter)
137 : ExtensionGCMAppHandler(profile),
138 waiter_(waiter),
139 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR),
140 app_handler_count_drop_to_zero_(false) {
143 virtual ~FakeExtensionGCMAppHandler() {
146 virtual void OnMessage(
147 const std::string& app_id,
148 const gcm::GCMClient::IncomingMessage& message) override {
151 virtual void OnMessagesDeleted(const std::string& app_id) override {
154 virtual void OnSendError(
155 const std::string& app_id,
156 const gcm::GCMClient::SendErrorDetails& send_error_details) override {
159 virtual void OnUnregisterCompleted(const std::string& app_id,
160 gcm::GCMClient::Result result) override {
161 unregistration_result_ = result;
162 waiter_->SignalCompleted();
165 virtual void RemoveAppHandler(const std::string& app_id) override{
166 ExtensionGCMAppHandler::RemoveAppHandler(app_id);
167 if (!GetGCMDriver()->app_handlers().size())
168 app_handler_count_drop_to_zero_ = true;
171 gcm::GCMClient::Result unregistration_result() const {
172 return unregistration_result_;
174 bool app_handler_count_drop_to_zero() const {
175 return app_handler_count_drop_to_zero_;
178 private:
179 Waiter* waiter_;
180 gcm::GCMClient::Result unregistration_result_;
181 bool app_handler_count_drop_to_zero_;
183 DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler);
186 class ExtensionGCMAppHandlerTest : public testing::Test {
187 public:
188 static KeyedService* BuildGCMProfileService(
189 content::BrowserContext* context) {
190 return new gcm::GCMProfileService(
191 Profile::FromBrowserContext(context),
192 scoped_ptr<gcm::GCMClientFactory>(new gcm::FakeGCMClientFactory(
193 gcm::FakeGCMClient::NO_DELAY_START,
194 content::BrowserThread::GetMessageLoopProxyForThread(
195 content::BrowserThread::UI),
196 content::BrowserThread::GetMessageLoopProxyForThread(
197 content::BrowserThread::IO))));
200 ExtensionGCMAppHandlerTest()
201 : extension_service_(NULL),
202 registration_result_(gcm::GCMClient::UNKNOWN_ERROR),
203 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) {
206 virtual ~ExtensionGCMAppHandlerTest() {
209 // Overridden from test::Test:
210 virtual void SetUp() override {
211 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
213 // Make BrowserThread work in unittest.
214 thread_bundle_.reset(new content::TestBrowserThreadBundle(
215 content::TestBrowserThreadBundle::REAL_IO_THREAD));
217 // Allow extension update to unpack crx in process.
218 in_process_utility_thread_helper_.reset(
219 new content::InProcessUtilityThreadHelper);
221 // This is needed to create extension service under CrOS.
222 #if defined(OS_CHROMEOS)
223 test_user_manager_.reset(new chromeos::ScopedTestUserManager());
224 // Creating a DBus thread manager setter has the side effect of
225 // creating a DBusThreadManager, which is needed for testing.
226 // We don't actually need the setter so we ignore the return value.
227 chromeos::DBusThreadManager::GetSetterForTesting();
228 #endif
230 // Create a new profile.
231 TestingProfile::Builder builder;
232 builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
233 gcm::FakeSigninManager::Build);
234 profile_ = builder.Build();
235 signin_manager_ = static_cast<gcm::FakeSigninManager*>(
236 SigninManagerFactory::GetInstance()->GetForProfile(profile_.get()));
238 // Create extension service in order to uninstall the extension.
239 TestExtensionSystem* extension_system(
240 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile())));
241 base::FilePath extensions_install_dir =
242 temp_dir_.path().Append(FILE_PATH_LITERAL("Extensions"));
243 extension_system->CreateExtensionService(
244 CommandLine::ForCurrentProcess(), extensions_install_dir, false);
245 extension_service_ = extension_system->Get(profile())->extension_service();
246 extension_service_->set_extensions_enabled(true);
247 extension_service_->set_show_extensions_prompts(false);
248 extension_service_->set_install_updates_when_idle_for_test(false);
250 // Enable GCM such that tests could be run on all channels.
251 profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
253 // Create GCMProfileService that talks with fake GCMClient.
254 gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
255 profile(), &ExtensionGCMAppHandlerTest::BuildGCMProfileService);
257 // Create a fake version of ExtensionGCMAppHandler.
258 gcm_app_handler_.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_));
261 virtual void TearDown() override {
262 #if defined(OS_CHROMEOS)
263 test_user_manager_.reset();
264 #endif
266 waiter_.PumpUILoop();
269 // Returns a barebones test extension.
270 scoped_refptr<Extension> CreateExtension() {
271 base::DictionaryValue manifest;
272 manifest.SetString(manifest_keys::kVersion, "1.0.0.0");
273 manifest.SetString(manifest_keys::kName, kTestExtensionName);
274 base::ListValue* permission_list = new base::ListValue;
275 permission_list->Append(new base::StringValue("gcm"));
276 manifest.Set(manifest_keys::kPermissions, permission_list);
278 std::string error;
279 scoped_refptr<Extension> extension = Extension::Create(
280 temp_dir_.path(),
281 Manifest::UNPACKED,
282 manifest,
283 Extension::NO_FLAGS,
284 "ldnnhddmnhbkjipkidpdiheffobcpfmf",
285 &error);
286 EXPECT_TRUE(extension.get()) << error;
287 EXPECT_TRUE(
288 extension->permissions_data()->HasAPIPermission(APIPermission::kGcm));
290 return extension;
293 void LoadExtension(const Extension* extension) {
294 extension_service_->AddExtension(extension);
297 static bool IsCrxInstallerDone(extensions::CrxInstaller** installer,
298 const content::NotificationSource& source,
299 const content::NotificationDetails& details) {
300 return content::Source<extensions::CrxInstaller>(source).ptr() ==
301 *installer;
304 void UpdateExtension(const Extension* extension,
305 const std::string& update_crx) {
306 base::FilePath data_dir;
307 if (!PathService::Get(chrome::DIR_TEST_DATA, &data_dir)) {
308 ADD_FAILURE();
309 return;
311 data_dir = data_dir.AppendASCII("extensions");
312 data_dir = data_dir.AppendASCII(update_crx);
314 base::FilePath path = temp_dir_.path();
315 path = path.Append(data_dir.BaseName());
316 ASSERT_TRUE(base::CopyFile(data_dir, path));
318 extensions::CrxInstaller* installer = NULL;
319 content::WindowedNotificationObserver observer(
320 extensions::NOTIFICATION_CRX_INSTALLER_DONE,
321 base::Bind(&IsCrxInstallerDone, &installer));
322 extension_service_->UpdateExtension(
323 extension->id(), path, true, &installer);
325 if (installer)
326 observer.Wait();
329 void DisableExtension(const Extension* extension) {
330 extension_service_->DisableExtension(
331 extension->id(), Extension::DISABLE_USER_ACTION);
334 void EnableExtension(const Extension* extension) {
335 extension_service_->EnableExtension(extension->id());
338 void UninstallExtension(const Extension* extension) {
339 extension_service_->UninstallExtension(
340 extension->id(),
341 extensions::UNINSTALL_REASON_FOR_TESTING,
342 base::Bind(&base::DoNothing),
343 NULL);
346 void SignIn(const std::string& username) {
347 signin_manager_->SignIn(username);
348 waiter_.PumpIOLoop();
351 void SignOut() {
352 signin_manager_->SignOut(signin_metrics::SIGNOUT_TEST);
353 waiter_.PumpIOLoop();
356 void Register(const std::string& app_id,
357 const std::vector<std::string>& sender_ids) {
358 GetGCMDriver()->Register(
359 app_id,
360 sender_ids,
361 base::Bind(&ExtensionGCMAppHandlerTest::RegisterCompleted,
362 base::Unretained(this)));
365 void RegisterCompleted(const std::string& registration_id,
366 gcm::GCMClient::Result result) {
367 registration_result_ = result;
368 waiter_.SignalCompleted();
371 gcm::GCMDriver* GetGCMDriver() const {
372 return gcm::GCMProfileServiceFactory::GetForProfile(profile())->driver();
375 bool HasAppHandlers(const std::string& app_id) const {
376 return GetGCMDriver()->app_handlers().count(app_id);
379 Profile* profile() const { return profile_.get(); }
380 Waiter* waiter() { return &waiter_; }
381 FakeExtensionGCMAppHandler* gcm_app_handler() const {
382 return gcm_app_handler_.get();
384 gcm::GCMClient::Result registration_result() const {
385 return registration_result_;
387 gcm::GCMClient::Result unregistration_result() const {
388 return unregistration_result_;
391 private:
392 scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
393 scoped_ptr<content::InProcessUtilityThreadHelper>
394 in_process_utility_thread_helper_;
395 scoped_ptr<TestingProfile> profile_;
396 ExtensionService* extension_service_; // Not owned.
397 gcm::FakeSigninManager* signin_manager_; // Not owned.
398 base::ScopedTempDir temp_dir_;
400 // This is needed to create extension service under CrOS.
401 #if defined(OS_CHROMEOS)
402 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
403 chromeos::ScopedTestCrosSettings test_cros_settings_;
404 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
405 #endif
407 Waiter waiter_;
408 scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_;
409 gcm::GCMClient::Result registration_result_;
410 gcm::GCMClient::Result unregistration_result_;
412 DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest);
415 TEST_F(ExtensionGCMAppHandlerTest, AddAndRemoveAppHandler) {
416 scoped_refptr<Extension> extension(CreateExtension());
418 // App handler is added when extension is loaded.
419 LoadExtension(extension.get());
420 waiter()->PumpUILoop();
421 EXPECT_TRUE(HasAppHandlers(extension->id()));
423 // App handler is removed when extension is unloaded.
424 DisableExtension(extension.get());
425 waiter()->PumpUILoop();
426 EXPECT_FALSE(HasAppHandlers(extension->id()));
428 // App handler is added when extension is reloaded.
429 EnableExtension(extension.get());
430 waiter()->PumpUILoop();
431 EXPECT_TRUE(HasAppHandlers(extension->id()));
433 // App handler is removed when extension is uninstalled.
434 UninstallExtension(extension.get());
435 waiter()->PumpUILoop();
436 EXPECT_FALSE(HasAppHandlers(extension->id()));
439 TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
440 scoped_refptr<Extension> extension(CreateExtension());
441 LoadExtension(extension.get());
443 // Sign-in is needed for registration.
444 SignIn(kTestingUsername);
446 // Kick off registration.
447 std::vector<std::string> sender_ids;
448 sender_ids.push_back("sender1");
449 Register(extension->id(), sender_ids);
450 waiter()->WaitUntilCompleted();
451 EXPECT_EQ(gcm::GCMClient::SUCCESS, registration_result());
453 // Add another app handler in order to prevent the GCM service from being
454 // stopped when the extension is uninstalled. This is needed because otherwise
455 // we are not able to receive the unregistration result.
456 GetGCMDriver()->AddAppHandler("Foo", gcm_app_handler());
458 // Unregistration should be triggered when the extension is uninstalled.
459 UninstallExtension(extension.get());
460 waiter()->WaitUntilCompleted();
461 EXPECT_EQ(gcm::GCMClient::SUCCESS,
462 gcm_app_handler()->unregistration_result());
464 // Clean up.
465 GetGCMDriver()->RemoveAppHandler("Foo");
468 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionKept) {
469 scoped_refptr<Extension> extension(CreateExtension());
471 // App handler is added when the extension is loaded.
472 LoadExtension(extension.get());
473 waiter()->PumpUILoop();
474 EXPECT_TRUE(HasAppHandlers(extension->id()));
476 // App handler count should not drop to zero when the extension is updated.
477 UpdateExtension(extension.get(), "gcm2.crx");
478 waiter()->PumpUILoop();
479 EXPECT_FALSE(gcm_app_handler()->app_handler_count_drop_to_zero());
480 EXPECT_TRUE(HasAppHandlers(extension->id()));
483 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionRemoved) {
484 scoped_refptr<Extension> extension(CreateExtension());
486 // App handler is added when the extension is loaded.
487 LoadExtension(extension.get());
488 waiter()->PumpUILoop();
489 EXPECT_TRUE(HasAppHandlers(extension->id()));
491 // App handler is removed when the extension is updated to the version that
492 // has GCM permission removed.
493 UpdateExtension(extension.get(), "good2.crx");
494 waiter()->PumpUILoop();
495 EXPECT_TRUE(gcm_app_handler()->app_handler_count_drop_to_zero());
496 EXPECT_FALSE(HasAppHandlers(extension->id()));
499 } // namespace extensions