Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / extensions / extension_gcm_app_handler_unittest.cc
blobd5d8f898e15d1f0de3181021925e237e2d0ec284
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/run_loop.h"
21 #include "base/values.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/extensions/api/gcm/gcm_api.h"
24 #include "chrome/browser/extensions/extension_service.h"
25 #include "chrome/browser/extensions/test_extension_service.h"
26 #include "chrome/browser/extensions/test_extension_system.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/services/gcm/gcm_profile_service.h"
29 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
30 #include "chrome/common/chrome_paths.h"
31 #include "chrome/test/base/testing_profile.h"
32 #include "components/gcm_driver/fake_gcm_app_handler.h"
33 #include "components/gcm_driver/fake_gcm_client.h"
34 #include "components/gcm_driver/fake_gcm_client_factory.h"
35 #include "components/gcm_driver/gcm_client_factory.h"
36 #include "components/gcm_driver/gcm_driver.h"
37 #include "components/keyed_service/core/keyed_service.h"
38 #include "content/public/browser/browser_context.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/test/test_browser_thread_bundle.h"
41 #include "content/public/test/test_utils.h"
42 #include "extensions/browser/extension_system.h"
43 #include "extensions/browser/uninstall_reason.h"
44 #include "extensions/common/extension.h"
45 #include "extensions/common/manifest.h"
46 #include "extensions/common/manifest_constants.h"
47 #include "extensions/common/permissions/api_permission.h"
48 #include "extensions/common/permissions/permissions_data.h"
49 #include "testing/gtest/include/gtest/gtest.h"
51 #if defined(OS_CHROMEOS)
52 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
53 #include "chrome/browser/chromeos/settings/cros_settings.h"
54 #include "chrome/browser/chromeos/settings/device_settings_service.h"
55 #include "chromeos/dbus/dbus_thread_manager.h"
56 #endif
58 namespace extensions {
60 namespace {
62 const char kTestExtensionName[] = "FooBar";
64 } // namespace
66 // Helper class for asynchronous waiting.
67 class Waiter {
68 public:
69 Waiter() {}
70 ~Waiter() {}
72 // Waits until the asynchronous operation finishes.
73 void WaitUntilCompleted() {
74 run_loop_.reset(new base::RunLoop);
75 run_loop_->Run();
78 // Signals that the asynchronous operation finishes.
79 void SignalCompleted() {
80 if (run_loop_ && run_loop_->running())
81 run_loop_->Quit();
84 // Runs until UI loop becomes idle.
85 void PumpUILoop() {
86 base::MessageLoop::current()->RunUntilIdle();
89 // Runs until IO loop becomes idle.
90 void PumpIOLoop() {
91 content::BrowserThread::PostTask(
92 content::BrowserThread::IO,
93 FROM_HERE,
94 base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this)));
96 WaitUntilCompleted();
99 private:
100 void PumpIOLoopCompleted() {
101 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
103 SignalCompleted();
106 void OnIOLoopPump() {
107 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
109 content::BrowserThread::PostTask(
110 content::BrowserThread::IO,
111 FROM_HERE,
112 base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
115 void OnIOLoopPumpCompleted() {
116 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
118 content::BrowserThread::PostTask(
119 content::BrowserThread::UI,
120 FROM_HERE,
121 base::Bind(&Waiter::PumpIOLoopCompleted, base::Unretained(this)));
124 scoped_ptr<base::RunLoop> run_loop_;
126 DISALLOW_COPY_AND_ASSIGN(Waiter);
129 class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler {
130 public:
131 FakeExtensionGCMAppHandler(Profile* profile, Waiter* waiter)
132 : ExtensionGCMAppHandler(profile),
133 waiter_(waiter),
134 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR),
135 app_handler_count_drop_to_zero_(false) {
138 ~FakeExtensionGCMAppHandler() override {}
140 void OnMessage(const std::string& app_id,
141 const gcm::GCMClient::IncomingMessage& message) override {}
143 void OnMessagesDeleted(const std::string& app_id) override {}
145 void OnSendError(
146 const std::string& app_id,
147 const gcm::GCMClient::SendErrorDetails& send_error_details) override {}
149 void OnUnregisterCompleted(const std::string& app_id,
150 gcm::GCMClient::Result result) override {
151 ExtensionGCMAppHandler::OnUnregisterCompleted(app_id, result);
152 unregistration_result_ = result;
153 waiter_->SignalCompleted();
156 void RemoveAppHandler(const std::string& app_id) override {
157 ExtensionGCMAppHandler::RemoveAppHandler(app_id);
158 if (!GetGCMDriver()->app_handlers().size())
159 app_handler_count_drop_to_zero_ = true;
162 gcm::GCMClient::Result unregistration_result() const {
163 return unregistration_result_;
165 bool app_handler_count_drop_to_zero() const {
166 return app_handler_count_drop_to_zero_;
169 private:
170 Waiter* waiter_;
171 gcm::GCMClient::Result unregistration_result_;
172 bool app_handler_count_drop_to_zero_;
174 DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler);
177 class ExtensionGCMAppHandlerTest : public testing::Test {
178 public:
179 static KeyedService* BuildGCMProfileService(
180 content::BrowserContext* context) {
181 return new gcm::GCMProfileService(
182 Profile::FromBrowserContext(context),
183 scoped_ptr<gcm::GCMClientFactory>(new gcm::FakeGCMClientFactory(
184 content::BrowserThread::GetMessageLoopProxyForThread(
185 content::BrowserThread::UI),
186 content::BrowserThread::GetMessageLoopProxyForThread(
187 content::BrowserThread::IO))));
190 ExtensionGCMAppHandlerTest()
191 : extension_service_(NULL),
192 registration_result_(gcm::GCMClient::UNKNOWN_ERROR),
193 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) {
196 ~ExtensionGCMAppHandlerTest() override {}
198 // Overridden from test::Test:
199 void SetUp() override {
200 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
202 // Make BrowserThread work in unittest.
203 thread_bundle_.reset(new content::TestBrowserThreadBundle(
204 content::TestBrowserThreadBundle::REAL_IO_THREAD));
206 // Allow extension update to unpack crx in process.
207 in_process_utility_thread_helper_.reset(
208 new content::InProcessUtilityThreadHelper);
210 // This is needed to create extension service under CrOS.
211 #if defined(OS_CHROMEOS)
212 test_user_manager_.reset(new chromeos::ScopedTestUserManager());
213 // Creating a DBus thread manager setter has the side effect of
214 // creating a DBusThreadManager, which is needed for testing.
215 // We don't actually need the setter so we ignore the return value.
216 chromeos::DBusThreadManager::GetSetterForTesting();
217 #endif
219 // Create a new profile.
220 TestingProfile::Builder builder;
221 profile_ = builder.Build();
223 // Create extension service in order to uninstall the extension.
224 TestExtensionSystem* extension_system(
225 static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile())));
226 base::FilePath extensions_install_dir =
227 temp_dir_.path().Append(FILE_PATH_LITERAL("Extensions"));
228 extension_system->CreateExtensionService(
229 base::CommandLine::ForCurrentProcess(), extensions_install_dir, false);
230 extension_service_ = extension_system->Get(profile())->extension_service();
231 extension_service_->set_extensions_enabled(true);
232 extension_service_->set_show_extensions_prompts(false);
233 extension_service_->set_install_updates_when_idle_for_test(false);
235 // Create GCMProfileService that talks with fake GCMClient.
236 gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
237 profile(), &ExtensionGCMAppHandlerTest::BuildGCMProfileService);
239 // Create a fake version of ExtensionGCMAppHandler.
240 gcm_app_handler_.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_));
243 void TearDown() override {
244 #if defined(OS_CHROMEOS)
245 test_user_manager_.reset();
246 #endif
248 waiter_.PumpUILoop();
251 // Returns a barebones test extension.
252 scoped_refptr<Extension> CreateExtension() {
253 base::DictionaryValue manifest;
254 manifest.SetString(manifest_keys::kVersion, "1.0.0.0");
255 manifest.SetString(manifest_keys::kName, kTestExtensionName);
256 base::ListValue* permission_list = new base::ListValue;
257 permission_list->Append(new base::StringValue("gcm"));
258 manifest.Set(manifest_keys::kPermissions, permission_list);
260 std::string error;
261 scoped_refptr<Extension> extension = Extension::Create(
262 temp_dir_.path(),
263 Manifest::UNPACKED,
264 manifest,
265 Extension::NO_FLAGS,
266 "ldnnhddmnhbkjipkidpdiheffobcpfmf",
267 &error);
268 EXPECT_TRUE(extension.get()) << error;
269 EXPECT_TRUE(
270 extension->permissions_data()->HasAPIPermission(APIPermission::kGcm));
272 return extension;
275 void LoadExtension(const Extension* extension) {
276 extension_service_->AddExtension(extension);
279 static bool IsCrxInstallerDone(extensions::CrxInstaller** installer,
280 const content::NotificationSource& source,
281 const content::NotificationDetails& details) {
282 return content::Source<extensions::CrxInstaller>(source).ptr() ==
283 *installer;
286 void UpdateExtension(const Extension* extension,
287 const std::string& update_crx) {
288 base::FilePath data_dir;
289 if (!PathService::Get(chrome::DIR_TEST_DATA, &data_dir)) {
290 ADD_FAILURE();
291 return;
293 data_dir = data_dir.AppendASCII("extensions");
294 data_dir = data_dir.AppendASCII(update_crx);
296 base::FilePath path = temp_dir_.path();
297 path = path.Append(data_dir.BaseName());
298 ASSERT_TRUE(base::CopyFile(data_dir, path));
300 extensions::CrxInstaller* installer = NULL;
301 content::WindowedNotificationObserver observer(
302 extensions::NOTIFICATION_CRX_INSTALLER_DONE,
303 base::Bind(&IsCrxInstallerDone, &installer));
304 extension_service_->UpdateExtension(
305 extensions::CRXFileInfo(extension->id(), path), true, &installer);
307 if (installer)
308 observer.Wait();
311 void DisableExtension(const Extension* extension) {
312 extension_service_->DisableExtension(
313 extension->id(), Extension::DISABLE_USER_ACTION);
316 void EnableExtension(const Extension* extension) {
317 extension_service_->EnableExtension(extension->id());
320 void UninstallExtension(const Extension* extension) {
321 extension_service_->UninstallExtension(
322 extension->id(),
323 extensions::UNINSTALL_REASON_FOR_TESTING,
324 base::Bind(&base::DoNothing),
325 NULL);
328 void Register(const std::string& app_id,
329 const std::vector<std::string>& sender_ids) {
330 GetGCMDriver()->Register(
331 app_id,
332 sender_ids,
333 base::Bind(&ExtensionGCMAppHandlerTest::RegisterCompleted,
334 base::Unretained(this)));
337 void RegisterCompleted(const std::string& registration_id,
338 gcm::GCMClient::Result result) {
339 registration_result_ = result;
340 waiter_.SignalCompleted();
343 gcm::GCMDriver* GetGCMDriver() const {
344 return gcm::GCMProfileServiceFactory::GetForProfile(profile())->driver();
347 bool HasAppHandlers(const std::string& app_id) const {
348 return GetGCMDriver()->app_handlers().count(app_id);
351 Profile* profile() const { return profile_.get(); }
352 Waiter* waiter() { return &waiter_; }
353 FakeExtensionGCMAppHandler* gcm_app_handler() const {
354 return gcm_app_handler_.get();
356 gcm::GCMClient::Result registration_result() const {
357 return registration_result_;
359 gcm::GCMClient::Result unregistration_result() const {
360 return unregistration_result_;
363 private:
364 scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
365 scoped_ptr<content::InProcessUtilityThreadHelper>
366 in_process_utility_thread_helper_;
367 scoped_ptr<TestingProfile> profile_;
368 ExtensionService* extension_service_; // Not owned.
369 base::ScopedTempDir temp_dir_;
371 // This is needed to create extension service under CrOS.
372 #if defined(OS_CHROMEOS)
373 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
374 chromeos::ScopedTestCrosSettings test_cros_settings_;
375 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
376 #endif
378 Waiter waiter_;
379 scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_;
380 gcm::GCMClient::Result registration_result_;
381 gcm::GCMClient::Result unregistration_result_;
383 DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest);
386 TEST_F(ExtensionGCMAppHandlerTest, AddAndRemoveAppHandler) {
387 scoped_refptr<Extension> extension(CreateExtension());
389 // App handler is added when extension is loaded.
390 LoadExtension(extension.get());
391 waiter()->PumpUILoop();
392 EXPECT_TRUE(HasAppHandlers(extension->id()));
394 // App handler is removed when extension is unloaded.
395 DisableExtension(extension.get());
396 waiter()->PumpUILoop();
397 EXPECT_FALSE(HasAppHandlers(extension->id()));
399 // App handler is added when extension is reloaded.
400 EnableExtension(extension.get());
401 waiter()->PumpUILoop();
402 EXPECT_TRUE(HasAppHandlers(extension->id()));
404 // App handler is removed when extension is uninstalled.
405 UninstallExtension(extension.get());
406 waiter()->WaitUntilCompleted();
407 EXPECT_FALSE(HasAppHandlers(extension->id()));
410 TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
411 scoped_refptr<Extension> extension(CreateExtension());
412 LoadExtension(extension.get());
414 // Kick off registration.
415 std::vector<std::string> sender_ids;
416 sender_ids.push_back("sender1");
417 Register(extension->id(), sender_ids);
418 waiter()->WaitUntilCompleted();
419 EXPECT_EQ(gcm::GCMClient::SUCCESS, registration_result());
421 // Unregistration should be triggered when the extension is uninstalled.
422 UninstallExtension(extension.get());
423 waiter()->WaitUntilCompleted();
424 EXPECT_EQ(gcm::GCMClient::SUCCESS,
425 gcm_app_handler()->unregistration_result());
428 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionKept) {
429 scoped_refptr<Extension> extension(CreateExtension());
431 // App handler is added when the extension is loaded.
432 LoadExtension(extension.get());
433 waiter()->PumpUILoop();
434 EXPECT_TRUE(HasAppHandlers(extension->id()));
436 // App handler count should not drop to zero when the extension is updated.
437 UpdateExtension(extension.get(), "gcm2.crx");
438 waiter()->PumpUILoop();
439 EXPECT_FALSE(gcm_app_handler()->app_handler_count_drop_to_zero());
440 EXPECT_TRUE(HasAppHandlers(extension->id()));
443 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionRemoved) {
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 is removed when the extension is updated to the version that
452 // has GCM permission removed.
453 UpdateExtension(extension.get(), "good2.crx");
454 waiter()->PumpUILoop();
455 EXPECT_TRUE(gcm_app_handler()->app_handler_count_drop_to_zero());
456 EXPECT_FALSE(HasAppHandlers(extension->id()));
459 } // namespace extensions