Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_gcm_app_handler_unittest.cc
blob59737ca44214de995f700e2dd4a65c885fc3e936
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/memory/scoped_ptr.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/path_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/gcm_profile_service.h"
30 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/test/base/testing_profile.h"
33 #include "components/gcm_driver/fake_gcm_app_handler.h"
34 #include "components/gcm_driver/fake_gcm_client.h"
35 #include "components/gcm_driver/fake_gcm_client_factory.h"
36 #include "components/gcm_driver/gcm_client_factory.h"
37 #include "components/gcm_driver/gcm_driver.h"
38 #include "components/keyed_service/core/keyed_service.h"
39 #include "content/public/browser/browser_context.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "content/public/test/test_browser_thread_bundle.h"
42 #include "content/public/test/test_utils.h"
43 #include "extensions/browser/extension_system.h"
44 #include "extensions/browser/uninstall_reason.h"
45 #include "extensions/common/extension.h"
46 #include "extensions/common/manifest.h"
47 #include "extensions/common/manifest_constants.h"
48 #include "extensions/common/permissions/api_permission.h"
49 #include "extensions/common/permissions/permissions_data.h"
50 #include "testing/gtest/include/gtest/gtest.h"
52 #if defined(OS_CHROMEOS)
53 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
54 #include "chrome/browser/chromeos/settings/cros_settings.h"
55 #include "chrome/browser/chromeos/settings/device_settings_service.h"
56 #include "chromeos/dbus/dbus_thread_manager.h"
57 #endif
59 namespace extensions {
61 namespace {
63 const char kTestExtensionName[] = "FooBar";
65 } // namespace
67 // Helper class for asynchronous waiting.
68 class Waiter {
69 public:
70 Waiter() {}
71 ~Waiter() {}
73 // Waits until the asynchronous operation finishes.
74 void WaitUntilCompleted() {
75 run_loop_.reset(new base::RunLoop);
76 run_loop_->Run();
79 // Signals that the asynchronous operation finishes.
80 void SignalCompleted() {
81 if (run_loop_ && run_loop_->running())
82 run_loop_->Quit();
85 // Runs until UI loop becomes idle.
86 void PumpUILoop() {
87 base::MessageLoop::current()->RunUntilIdle();
90 // Runs until IO loop becomes idle.
91 void PumpIOLoop() {
92 content::BrowserThread::PostTask(
93 content::BrowserThread::IO,
94 FROM_HERE,
95 base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this)));
97 WaitUntilCompleted();
100 private:
101 void PumpIOLoopCompleted() {
102 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
104 SignalCompleted();
107 void OnIOLoopPump() {
108 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
110 content::BrowserThread::PostTask(
111 content::BrowserThread::IO,
112 FROM_HERE,
113 base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
116 void OnIOLoopPumpCompleted() {
117 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
119 content::BrowserThread::PostTask(
120 content::BrowserThread::UI,
121 FROM_HERE,
122 base::Bind(&Waiter::PumpIOLoopCompleted, base::Unretained(this)));
125 scoped_ptr<base::RunLoop> run_loop_;
127 DISALLOW_COPY_AND_ASSIGN(Waiter);
130 class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler {
131 public:
132 FakeExtensionGCMAppHandler(Profile* profile, Waiter* waiter)
133 : ExtensionGCMAppHandler(profile),
134 waiter_(waiter),
135 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR),
136 delete_id_result_(instance_id::InstanceID::UNKNOWN_ERROR),
137 app_handler_count_drop_to_zero_(false) {
140 ~FakeExtensionGCMAppHandler() override {}
142 void OnMessage(const std::string& app_id,
143 const gcm::IncomingMessage& message) override {}
145 void OnMessagesDeleted(const std::string& app_id) override {}
147 void OnSendError(
148 const std::string& app_id,
149 const gcm::GCMClient::SendErrorDetails& send_error_details) override {}
151 void OnUnregisterCompleted(const std::string& app_id,
152 gcm::GCMClient::Result result) override {
153 ExtensionGCMAppHandler::OnUnregisterCompleted(app_id, result);
154 unregistration_result_ = result;
155 waiter_->SignalCompleted();
158 void OnDeleteIDCompleted(const std::string& app_id,
159 instance_id::InstanceID::Result result) override {
160 delete_id_result_ = result;
161 ExtensionGCMAppHandler::OnDeleteIDCompleted(app_id, result);
164 void RemoveAppHandler(const std::string& app_id) override {
165 ExtensionGCMAppHandler::RemoveAppHandler(app_id);
166 if (!GetGCMDriver()->app_handlers().size())
167 app_handler_count_drop_to_zero_ = true;
170 gcm::GCMClient::Result unregistration_result() const {
171 return unregistration_result_;
173 instance_id::InstanceID::Result delete_id_result() const {
174 return delete_id_result_;
176 bool app_handler_count_drop_to_zero() const {
177 return app_handler_count_drop_to_zero_;
180 private:
181 Waiter* waiter_;
182 gcm::GCMClient::Result unregistration_result_;
183 instance_id::InstanceID::Result delete_id_result_;
184 bool app_handler_count_drop_to_zero_;
186 DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler);
189 class ExtensionGCMAppHandlerTest : public testing::Test {
190 public:
191 static scoped_ptr<KeyedService> BuildGCMProfileService(
192 content::BrowserContext* context) {
193 return make_scoped_ptr(new gcm::GCMProfileService(
194 Profile::FromBrowserContext(context),
195 scoped_ptr<gcm::GCMClientFactory>(new gcm::FakeGCMClientFactory(
196 content::BrowserThread::GetMessageLoopProxyForThread(
197 content::BrowserThread::UI),
198 content::BrowserThread::GetMessageLoopProxyForThread(
199 content::BrowserThread::IO)))));
202 ExtensionGCMAppHandlerTest()
203 : extension_service_(NULL),
204 registration_result_(gcm::GCMClient::UNKNOWN_ERROR),
205 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) {
208 ~ExtensionGCMAppHandlerTest() override {}
210 // Overridden from test::Test:
211 void SetUp() override {
212 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
214 // Make BrowserThread work in unittest.
215 thread_bundle_.reset(new content::TestBrowserThreadBundle(
216 content::TestBrowserThreadBundle::REAL_IO_THREAD));
218 // Allow extension update to unpack crx in process.
219 in_process_utility_thread_helper_.reset(
220 new content::InProcessUtilityThreadHelper);
222 // This is needed to create extension service under CrOS.
223 #if defined(OS_CHROMEOS)
224 test_user_manager_.reset(new chromeos::ScopedTestUserManager());
225 // Creating a DBus thread manager setter has the side effect of
226 // creating a DBusThreadManager, which is needed for testing.
227 // We don't actually need the setter so we ignore the return value.
228 chromeos::DBusThreadManager::GetSetterForTesting();
229 #endif
231 // Create a new profile.
232 TestingProfile::Builder builder;
233 profile_ = builder.Build();
235 // Create extension service in order to uninstall the extension.
236 TestExtensionSystem* extension_system(
237 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile())));
238 base::FilePath extensions_install_dir =
239 temp_dir_.path().Append(FILE_PATH_LITERAL("Extensions"));
240 extension_system->CreateExtensionService(
241 base::CommandLine::ForCurrentProcess(), extensions_install_dir, false);
242 extension_service_ = extension_system->Get(profile())->extension_service();
243 extension_service_->set_extensions_enabled(true);
244 extension_service_->set_show_extensions_prompts(false);
245 extension_service_->set_install_updates_when_idle_for_test(false);
247 // Create GCMProfileService that talks with fake GCMClient.
248 gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
249 profile(), &ExtensionGCMAppHandlerTest::BuildGCMProfileService);
251 // Create a fake version of ExtensionGCMAppHandler.
252 gcm_app_handler_.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_));
255 void TearDown() override {
256 #if defined(OS_CHROMEOS)
257 test_user_manager_.reset();
258 #endif
260 waiter_.PumpUILoop();
263 // Returns a barebones test extension.
264 scoped_refptr<Extension> CreateExtension() {
265 base::DictionaryValue manifest;
266 manifest.SetString(manifest_keys::kVersion, "1.0.0.0");
267 manifest.SetString(manifest_keys::kName, kTestExtensionName);
268 base::ListValue* permission_list = new base::ListValue;
269 permission_list->Append(new base::StringValue("gcm"));
270 manifest.Set(manifest_keys::kPermissions, permission_list);
272 std::string error;
273 scoped_refptr<Extension> extension = Extension::Create(
274 temp_dir_.path(),
275 Manifest::UNPACKED,
276 manifest,
277 Extension::NO_FLAGS,
278 "ldnnhddmnhbkjipkidpdiheffobcpfmf",
279 &error);
280 EXPECT_TRUE(extension.get()) << error;
281 EXPECT_TRUE(
282 extension->permissions_data()->HasAPIPermission(APIPermission::kGcm));
284 return extension;
287 void LoadExtension(const Extension* extension) {
288 extension_service_->AddExtension(extension);
291 static bool IsCrxInstallerDone(extensions::CrxInstaller** installer,
292 const content::NotificationSource& source,
293 const content::NotificationDetails& details) {
294 return content::Source<extensions::CrxInstaller>(source).ptr() ==
295 *installer;
298 void UpdateExtension(const Extension* extension,
299 const std::string& update_crx) {
300 base::FilePath data_dir;
301 if (!PathService::Get(chrome::DIR_TEST_DATA, &data_dir)) {
302 ADD_FAILURE();
303 return;
305 data_dir = data_dir.AppendASCII("extensions");
306 data_dir = data_dir.AppendASCII(update_crx);
308 base::FilePath path = temp_dir_.path();
309 path = path.Append(data_dir.BaseName());
310 ASSERT_TRUE(base::CopyFile(data_dir, path));
312 extensions::CrxInstaller* installer = NULL;
313 content::WindowedNotificationObserver observer(
314 extensions::NOTIFICATION_CRX_INSTALLER_DONE,
315 base::Bind(&IsCrxInstallerDone, &installer));
316 extension_service_->UpdateExtension(
317 extensions::CRXFileInfo(extension->id(), path), true, &installer);
319 if (installer)
320 observer.Wait();
323 void DisableExtension(const Extension* extension) {
324 extension_service_->DisableExtension(
325 extension->id(), Extension::DISABLE_USER_ACTION);
328 void EnableExtension(const Extension* extension) {
329 extension_service_->EnableExtension(extension->id());
332 void UninstallExtension(const Extension* extension) {
333 extension_service_->UninstallExtension(
334 extension->id(),
335 extensions::UNINSTALL_REASON_FOR_TESTING,
336 base::Bind(&base::DoNothing),
337 NULL);
340 void Register(const std::string& app_id,
341 const std::vector<std::string>& sender_ids) {
342 GetGCMDriver()->Register(
343 app_id,
344 sender_ids,
345 base::Bind(&ExtensionGCMAppHandlerTest::RegisterCompleted,
346 base::Unretained(this)));
349 void RegisterCompleted(const std::string& registration_id,
350 gcm::GCMClient::Result result) {
351 registration_result_ = result;
352 waiter_.SignalCompleted();
355 gcm::GCMDriver* GetGCMDriver() const {
356 return gcm::GCMProfileServiceFactory::GetForProfile(profile())->driver();
359 bool HasAppHandlers(const std::string& app_id) const {
360 return GetGCMDriver()->app_handlers().count(app_id);
363 Profile* profile() const { return profile_.get(); }
364 Waiter* waiter() { return &waiter_; }
365 FakeExtensionGCMAppHandler* gcm_app_handler() const {
366 return gcm_app_handler_.get();
368 gcm::GCMClient::Result registration_result() const {
369 return registration_result_;
371 gcm::GCMClient::Result unregistration_result() const {
372 return unregistration_result_;
375 private:
376 scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
377 scoped_ptr<content::InProcessUtilityThreadHelper>
378 in_process_utility_thread_helper_;
379 scoped_ptr<TestingProfile> profile_;
380 ExtensionService* extension_service_; // Not owned.
381 base::ScopedTempDir temp_dir_;
383 // This is needed to create extension service under CrOS.
384 #if defined(OS_CHROMEOS)
385 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
386 chromeos::ScopedTestCrosSettings test_cros_settings_;
387 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
388 #endif
390 Waiter waiter_;
391 scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_;
392 gcm::GCMClient::Result registration_result_;
393 gcm::GCMClient::Result unregistration_result_;
395 DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest);
398 TEST_F(ExtensionGCMAppHandlerTest, AddAndRemoveAppHandler) {
399 scoped_refptr<Extension> extension(CreateExtension());
401 // App handler is added when extension is loaded.
402 LoadExtension(extension.get());
403 waiter()->PumpUILoop();
404 EXPECT_TRUE(HasAppHandlers(extension->id()));
406 // App handler is removed when extension is unloaded.
407 DisableExtension(extension.get());
408 waiter()->PumpUILoop();
409 EXPECT_FALSE(HasAppHandlers(extension->id()));
411 // App handler is added when extension is reloaded.
412 EnableExtension(extension.get());
413 waiter()->PumpUILoop();
414 EXPECT_TRUE(HasAppHandlers(extension->id()));
416 // App handler is removed when extension is uninstalled.
417 UninstallExtension(extension.get());
418 waiter()->WaitUntilCompleted();
419 EXPECT_FALSE(HasAppHandlers(extension->id()));
422 TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
423 scoped_refptr<Extension> extension(CreateExtension());
424 LoadExtension(extension.get());
426 // Kick off registration.
427 std::vector<std::string> sender_ids;
428 sender_ids.push_back("sender1");
429 Register(extension->id(), sender_ids);
430 waiter()->WaitUntilCompleted();
431 EXPECT_EQ(gcm::GCMClient::SUCCESS, registration_result());
433 // Both token deletion and unregistration should be triggered when the
434 // extension is uninstalled.
435 UninstallExtension(extension.get());
436 waiter()->WaitUntilCompleted();
437 EXPECT_EQ(instance_id::InstanceID::SUCCESS,
438 gcm_app_handler()->delete_id_result());
439 EXPECT_EQ(gcm::GCMClient::SUCCESS,
440 gcm_app_handler()->unregistration_result());
443 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionKept) {
444 scoped_refptr<Extension> extension(CreateExtension());
446 // App handler is added when the extension is loaded.
447 LoadExtension(extension.get());
448 waiter()->PumpUILoop();
449 EXPECT_TRUE(HasAppHandlers(extension->id()));
451 // App handler count should not drop to zero when the extension is updated.
452 UpdateExtension(extension.get(), "gcm2.crx");
453 waiter()->PumpUILoop();
454 EXPECT_FALSE(gcm_app_handler()->app_handler_count_drop_to_zero());
455 EXPECT_TRUE(HasAppHandlers(extension->id()));
458 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionRemoved) {
459 scoped_refptr<Extension> extension(CreateExtension());
461 // App handler is added when the extension is loaded.
462 LoadExtension(extension.get());
463 waiter()->PumpUILoop();
464 EXPECT_TRUE(HasAppHandlers(extension->id()));
466 // App handler is removed when the extension is updated to the version that
467 // has GCM permission removed.
468 UpdateExtension(extension.get(), "good2.crx");
469 waiter()->PumpUILoop();
470 EXPECT_TRUE(gcm_app_handler()->app_handler_count_drop_to_zero());
471 EXPECT_FALSE(HasAppHandlers(extension->id()));
474 } // namespace extensions