Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_gcm_app_handler_unittest.cc
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/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"
54
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
61
62 namespace extensions {
63
64 namespace {
65
66 const char kTestExtensionName[] = "FooBar";
67 const char kTestingUsername[] = "user1@example.com";
68
69 }  // namespace
70
71 // Helper class for asynchronous waiting.
72 class Waiter {
73  public:
74   Waiter() {}
75   ~Waiter() {}
76
77   // Waits until the asynchronous operation finishes.
78   void WaitUntilCompleted() {
79     run_loop_.reset(new base::RunLoop);
80     run_loop_->Run();
81   }
82
83   // Signals that the asynchronous operation finishes.
84   void SignalCompleted() {
85     if (run_loop_ && run_loop_->running())
86       run_loop_->Quit();
87   }
88
89   // Runs until UI loop becomes idle.
90   void PumpUILoop() {
91     base::MessageLoop::current()->RunUntilIdle();
92   }
93
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)));
100
101     WaitUntilCompleted();
102   }
103
104  private:
105   void PumpIOLoopCompleted() {
106     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
107
108     SignalCompleted();
109   }
110
111   void OnIOLoopPump() {
112     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
113
114     content::BrowserThread::PostTask(
115         content::BrowserThread::IO,
116         FROM_HERE,
117         base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
118   }
119
120   void OnIOLoopPumpCompleted() {
121     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
122
123     content::BrowserThread::PostTask(
124         content::BrowserThread::UI,
125         FROM_HERE,
126         base::Bind(&Waiter::PumpIOLoopCompleted, base::Unretained(this)));
127   }
128
129   scoped_ptr<base::RunLoop> run_loop_;
130
131   DISALLOW_COPY_AND_ASSIGN(Waiter);
132 };
133
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) {
141   }
142
143   virtual ~FakeExtensionGCMAppHandler() {
144   }
145
146   virtual void OnMessage(
147       const std::string& app_id,
148       const gcm::GCMClient::IncomingMessage& message) OVERRIDE {
149   }
150
151   virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE {
152   }
153
154   virtual void OnSendError(
155       const std::string& app_id,
156       const gcm::GCMClient::SendErrorDetails& send_error_details) OVERRIDE {
157   }
158
159   virtual void OnUnregisterCompleted(const std::string& app_id,
160                                      gcm::GCMClient::Result result) OVERRIDE {
161     unregistration_result_ = result;
162     waiter_->SignalCompleted();
163   }
164
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;
169   }
170
171   gcm::GCMClient::Result unregistration_result() const {
172     return unregistration_result_;
173   }
174   bool app_handler_count_drop_to_zero() const {
175     return app_handler_count_drop_to_zero_;
176   }
177
178  private:
179   Waiter* waiter_;
180   gcm::GCMClient::Result unregistration_result_;
181   bool app_handler_count_drop_to_zero_;
182
183   DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler);
184 };
185
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))));
198   }
199
200   ExtensionGCMAppHandlerTest()
201       : extension_service_(NULL),
202         registration_result_(gcm::GCMClient::UNKNOWN_ERROR),
203         unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) {
204   }
205
206   virtual ~ExtensionGCMAppHandlerTest() {
207   }
208
209   // Overridden from test::Test:
210   virtual void SetUp() OVERRIDE {
211     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
212
213     // Make BrowserThread work in unittest.
214     thread_bundle_.reset(new content::TestBrowserThreadBundle(
215         content::TestBrowserThreadBundle::REAL_IO_THREAD));
216
217     // Allow extension update to unpack crx in process.
218     in_process_utility_thread_helper_.reset(
219         new content::InProcessUtilityThreadHelper);
220
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
229
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()));
237
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);
249
250     // Enable GCM such that tests could be run on all channels.
251     profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
252
253     // Create GCMProfileService that talks with fake GCMClient.
254     gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
255         profile(), &ExtensionGCMAppHandlerTest::BuildGCMProfileService);
256
257     // Create a fake version of ExtensionGCMAppHandler.
258     gcm_app_handler_.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_));
259   }
260
261   virtual void TearDown() OVERRIDE {
262 #if defined(OS_CHROMEOS)
263     test_user_manager_.reset();
264 #endif
265
266     waiter_.PumpUILoop();
267   }
268
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);
277
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));
289
290     return extension;
291   }
292
293   void LoadExtension(const Extension* extension) {
294     extension_service_->AddExtension(extension);
295   }
296
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;
302   }
303
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;
310     }
311     data_dir = data_dir.AppendASCII("extensions");
312     data_dir = data_dir.AppendASCII(update_crx);
313
314     base::FilePath path = temp_dir_.path();
315     path = path.Append(data_dir.BaseName());
316     ASSERT_TRUE(base::CopyFile(data_dir, path));
317
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);
324
325     if (installer)
326       observer.Wait();
327   }
328
329   void DisableExtension(const Extension* extension) {
330     extension_service_->DisableExtension(
331         extension->id(), Extension::DISABLE_USER_ACTION);
332   }
333
334   void EnableExtension(const Extension* extension) {
335     extension_service_->EnableExtension(extension->id());
336   }
337
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);
344   }
345
346   void SignIn(const std::string& username) {
347     signin_manager_->SignIn(username);
348     waiter_.PumpIOLoop();
349   }
350
351   void SignOut() {
352     signin_manager_->SignOut(signin_metrics::SIGNOUT_TEST);
353     waiter_.PumpIOLoop();
354   }
355
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)));
363   }
364
365   void RegisterCompleted(const std::string& registration_id,
366                          gcm::GCMClient::Result result) {
367     registration_result_ = result;
368     waiter_.SignalCompleted();
369   }
370
371   gcm::GCMDriver* GetGCMDriver() const {
372     return gcm::GCMProfileServiceFactory::GetForProfile(profile())->driver();
373   }
374
375   bool HasAppHandlers(const std::string& app_id) const {
376     return GetGCMDriver()->app_handlers().count(app_id);
377   }
378
379   Profile* profile() const { return profile_.get(); }
380   Waiter* waiter() { return &waiter_; }
381   FakeExtensionGCMAppHandler* gcm_app_handler() const {
382     return gcm_app_handler_.get();
383   }
384   gcm::GCMClient::Result registration_result() const {
385     return registration_result_;
386   }
387   gcm::GCMClient::Result unregistration_result() const {
388     return unregistration_result_;
389   }
390
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_;
399
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
406
407   Waiter waiter_;
408   scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_;
409   gcm::GCMClient::Result registration_result_;
410   gcm::GCMClient::Result unregistration_result_;
411
412   DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest);
413 };
414
415 TEST_F(ExtensionGCMAppHandlerTest, AddAndRemoveAppHandler) {
416   scoped_refptr<Extension> extension(CreateExtension());
417
418   // App handler is added when extension is loaded.
419   LoadExtension(extension.get());
420   waiter()->PumpUILoop();
421   EXPECT_TRUE(HasAppHandlers(extension->id()));
422
423   // App handler is removed when extension is unloaded.
424   DisableExtension(extension.get());
425   waiter()->PumpUILoop();
426   EXPECT_FALSE(HasAppHandlers(extension->id()));
427
428   // App handler is added when extension is reloaded.
429   EnableExtension(extension.get());
430   waiter()->PumpUILoop();
431   EXPECT_TRUE(HasAppHandlers(extension->id()));
432
433   // App handler is removed when extension is uninstalled.
434   UninstallExtension(extension.get());
435   waiter()->PumpUILoop();
436   EXPECT_FALSE(HasAppHandlers(extension->id()));
437 }
438
439 TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
440   scoped_refptr<Extension> extension(CreateExtension());
441   LoadExtension(extension.get());
442
443   // Sign-in is needed for registration.
444   SignIn(kTestingUsername);
445
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());
452
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());
457
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());
463
464   // Clean up.
465   GetGCMDriver()->RemoveAppHandler("Foo");
466 }
467
468 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionKept) {
469   scoped_refptr<Extension> extension(CreateExtension());
470
471   // App handler is added when the extension is loaded.
472   LoadExtension(extension.get());
473   waiter()->PumpUILoop();
474   EXPECT_TRUE(HasAppHandlers(extension->id()));
475
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()));
481 }
482
483 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionRemoved) {
484   scoped_refptr<Extension> extension(CreateExtension());
485
486   // App handler is added when the extension is loaded.
487   LoadExtension(extension.get());
488   waiter()->PumpUILoop();
489   EXPECT_TRUE(HasAppHandlers(extension->id()));
490
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()));
497 }
498
499 }  // namespace extensions