Upstream version 9.37.195.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/file_util.h"
13 #include "base/files/file_path.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/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/fake_signin_manager.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/browser/signin/signin_manager_factory.h"
32 #include "chrome/common/chrome_paths.h"
33 #include "chrome/common/pref_names.h"
34 #include "chrome/test/base/testing_profile.h"
35 #include "components/gcm_driver/fake_gcm_app_handler.h"
36 #include "components/gcm_driver/fake_gcm_client.h"
37 #include "components/gcm_driver/fake_gcm_client_factory.h"
38 #include "components/gcm_driver/gcm_client_factory.h"
39 #include "components/gcm_driver/gcm_driver.h"
40 #include "components/keyed_service/core/keyed_service.h"
41 #include "content/public/browser/browser_context.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/test/test_browser_thread_bundle.h"
44 #include "content/public/test/test_utils.h"
45 #include "extensions/browser/extension_system.h"
46 #include "extensions/common/extension.h"
47 #include "extensions/common/manifest.h"
48 #include "extensions/common/manifest_constants.h"
49 #include "extensions/common/permissions/api_permission.h"
50 #include "extensions/common/permissions/permissions_data.h"
51 #include "testing/gtest/include/gtest/gtest.h"
52
53 #if !defined(OS_ANDROID)
54 #include "chrome/browser/extensions/api/gcm/gcm_api.h"
55 #endif
56
57 #if defined(OS_CHROMEOS)
58 #include "chrome/browser/chromeos/login/users/user_manager.h"
59 #include "chrome/browser/chromeos/settings/cros_settings.h"
60 #include "chrome/browser/chromeos/settings/device_settings_service.h"
61 #endif
62
63 namespace extensions {
64
65 namespace {
66
67 const char kTestExtensionName[] = "FooBar";
68 const char kTestingUsername[] = "user1@example.com";
69
70 }  // namespace
71
72 // Helper class for asynchronous waiting.
73 class Waiter {
74  public:
75   Waiter() {}
76   ~Waiter() {}
77
78   // Waits until the asynchronous operation finishes.
79   void WaitUntilCompleted() {
80     run_loop_.reset(new base::RunLoop);
81     run_loop_->Run();
82   }
83
84   // Signals that the asynchronous operation finishes.
85   void SignalCompleted() {
86     if (run_loop_ && run_loop_->running())
87       run_loop_->Quit();
88   }
89
90   // Runs until UI loop becomes idle.
91   void PumpUILoop() {
92     base::MessageLoop::current()->RunUntilIdle();
93   }
94
95   // Runs until IO loop becomes idle.
96   void PumpIOLoop() {
97     content::BrowserThread::PostTask(
98         content::BrowserThread::IO,
99         FROM_HERE,
100         base::Bind(&Waiter::OnIOLoopPump, base::Unretained(this)));
101
102     WaitUntilCompleted();
103   }
104
105  private:
106   void PumpIOLoopCompleted() {
107     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
108
109     SignalCompleted();
110   }
111
112   void OnIOLoopPump() {
113     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
114
115     content::BrowserThread::PostTask(
116         content::BrowserThread::IO,
117         FROM_HERE,
118         base::Bind(&Waiter::OnIOLoopPumpCompleted, base::Unretained(this)));
119   }
120
121   void OnIOLoopPumpCompleted() {
122     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
123
124     content::BrowserThread::PostTask(
125         content::BrowserThread::UI,
126         FROM_HERE,
127         base::Bind(&Waiter::PumpIOLoopCompleted, base::Unretained(this)));
128   }
129
130   scoped_ptr<base::RunLoop> run_loop_;
131
132   DISALLOW_COPY_AND_ASSIGN(Waiter);
133 };
134
135 class FakeExtensionGCMAppHandler : public ExtensionGCMAppHandler {
136  public:
137   FakeExtensionGCMAppHandler(Profile* profile, Waiter* waiter)
138       : ExtensionGCMAppHandler(profile),
139         waiter_(waiter),
140         unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR),
141         app_handler_count_drop_to_zero_(false) {
142   }
143
144   virtual ~FakeExtensionGCMAppHandler() {
145   }
146
147   virtual void OnMessage(
148       const std::string& app_id,
149       const gcm::GCMClient::IncomingMessage& message) OVERRIDE {
150   }
151
152   virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE {
153   }
154
155   virtual void OnSendError(
156       const std::string& app_id,
157       const gcm::GCMClient::SendErrorDetails& send_error_details) OVERRIDE {
158   }
159
160   virtual void OnUnregisterCompleted(const std::string& app_id,
161                                      gcm::GCMClient::Result result) OVERRIDE {
162     unregistration_result_ = result;
163     waiter_->SignalCompleted();
164   }
165
166   virtual void RemoveAppHandler(const std::string& app_id) OVERRIDE{
167     ExtensionGCMAppHandler::RemoveAppHandler(app_id);
168     if (!GetGCMDriver()->app_handlers().size())
169       app_handler_count_drop_to_zero_ = true;
170   }
171
172   gcm::GCMClient::Result unregistration_result() const {
173     return unregistration_result_;
174   }
175   bool app_handler_count_drop_to_zero() const {
176     return app_handler_count_drop_to_zero_;
177   }
178
179  private:
180   Waiter* waiter_;
181   gcm::GCMClient::Result unregistration_result_;
182   bool app_handler_count_drop_to_zero_;
183
184   DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler);
185 };
186
187 class ExtensionGCMAppHandlerTest : public testing::Test {
188  public:
189   static KeyedService* BuildGCMProfileService(
190       content::BrowserContext* context) {
191     return new gcm::GCMProfileService(
192         Profile::FromBrowserContext(context),
193         scoped_ptr<gcm::GCMClientFactory>(new gcm::FakeGCMClientFactory(
194             gcm::FakeGCMClient::NO_DELAY_START,
195             content::BrowserThread::GetMessageLoopProxyForThread(
196                 content::BrowserThread::UI),
197             content::BrowserThread::GetMessageLoopProxyForThread(
198                 content::BrowserThread::IO))));
199   }
200
201   ExtensionGCMAppHandlerTest()
202       : extension_service_(NULL),
203         registration_result_(gcm::GCMClient::UNKNOWN_ERROR),
204         unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) {
205   }
206
207   virtual ~ExtensionGCMAppHandlerTest() {
208   }
209
210   // Overridden from test::Test:
211   virtual void SetUp() OVERRIDE {
212     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
213
214     // Make BrowserThread work in unittest.
215     thread_bundle_.reset(new content::TestBrowserThreadBundle(
216         content::TestBrowserThreadBundle::REAL_IO_THREAD));
217
218     // Allow extension update to unpack crx in process.
219     in_process_utility_thread_helper_.reset(
220         new content::InProcessUtilityThreadHelper);
221
222     // This is needed to create extension service under CrOS.
223 #if defined(OS_CHROMEOS)
224     test_user_manager_.reset(new chromeos::ScopedTestUserManager());
225 #endif
226
227     // Create a new profile.
228     TestingProfile::Builder builder;
229     builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
230                               gcm::FakeSigninManager::Build);
231     profile_ = builder.Build();
232     signin_manager_ = static_cast<gcm::FakeSigninManager*>(
233         SigninManagerFactory::GetInstance()->GetForProfile(profile_.get()));
234
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         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);
246
247     // Enable GCM such that tests could be run on all channels.
248     profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
249
250     // Create GCMProfileService that talks with fake GCMClient.
251     gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
252         profile(), &ExtensionGCMAppHandlerTest::BuildGCMProfileService);
253
254     // Create a fake version of ExtensionGCMAppHandler.
255     gcm_app_handler_.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_));
256   }
257
258   virtual void TearDown() OVERRIDE {
259 #if defined(OS_CHROMEOS)
260     test_user_manager_.reset();
261 #endif
262
263     waiter_.PumpUILoop();
264   }
265
266   // Returns a barebones test extension.
267   scoped_refptr<Extension> CreateExtension() {
268     base::DictionaryValue manifest;
269     manifest.SetString(manifest_keys::kVersion, "1.0.0.0");
270     manifest.SetString(manifest_keys::kName, kTestExtensionName);
271     base::ListValue* permission_list = new base::ListValue;
272     permission_list->Append(base::Value::CreateStringValue("gcm"));
273     manifest.Set(manifest_keys::kPermissions, permission_list);
274
275     std::string error;
276     scoped_refptr<Extension> extension = Extension::Create(
277         temp_dir_.path(),
278         Manifest::UNPACKED,
279         manifest,
280         Extension::NO_FLAGS,
281         "ldnnhddmnhbkjipkidpdiheffobcpfmf",
282         &error);
283     EXPECT_TRUE(extension.get()) << error;
284     EXPECT_TRUE(
285         extension->permissions_data()->HasAPIPermission(APIPermission::kGcm));
286
287     return extension;
288   }
289
290   void LoadExtension(const Extension* extension) {
291     extension_service_->AddExtension(extension);
292   }
293
294   static bool IsCrxInstallerDone(extensions::CrxInstaller** installer,
295                                  const content::NotificationSource& source,
296                                  const content::NotificationDetails& details) {
297     return content::Source<extensions::CrxInstaller>(source).ptr() ==
298            *installer;
299   }
300
301   void UpdateExtension(const Extension* extension,
302                        const std::string& update_crx) {
303     base::FilePath data_dir;
304     if (!PathService::Get(chrome::DIR_TEST_DATA, &data_dir)) {
305       ADD_FAILURE();
306       return;
307     }
308     data_dir = data_dir.AppendASCII("extensions");
309     data_dir = data_dir.AppendASCII(update_crx);
310
311     base::FilePath path = temp_dir_.path();
312     path = path.Append(data_dir.BaseName());
313     ASSERT_TRUE(base::CopyFile(data_dir, path));
314
315     extensions::CrxInstaller* installer = NULL;
316     content::WindowedNotificationObserver observer(
317         chrome::NOTIFICATION_CRX_INSTALLER_DONE,
318         base::Bind(&IsCrxInstallerDone, &installer));
319     extension_service_->UpdateExtension(
320         extension->id(), path, true, &installer);
321
322     if (installer)
323       observer.Wait();
324   }
325
326   void DisableExtension(const Extension* extension) {
327     extension_service_->DisableExtension(
328         extension->id(), Extension::DISABLE_USER_ACTION);
329   }
330
331   void EnableExtension(const Extension* extension) {
332     extension_service_->EnableExtension(extension->id());
333   }
334
335   void UninstallExtension(const Extension* extension) {
336     extension_service_->UninstallExtension(extension->id(), false, NULL);
337   }
338
339   void SignIn(const std::string& username) {
340     signin_manager_->SignIn(username);
341     waiter_.PumpIOLoop();
342   }
343
344   void SignOut() {
345     signin_manager_->SignOut(signin_metrics::SIGNOUT_TEST);
346     waiter_.PumpIOLoop();
347   }
348
349   void Register(const std::string& app_id,
350                 const std::vector<std::string>& sender_ids) {
351     GetGCMDriver()->Register(
352         app_id,
353         sender_ids,
354         base::Bind(&ExtensionGCMAppHandlerTest::RegisterCompleted,
355                    base::Unretained(this)));
356   }
357
358   void RegisterCompleted(const std::string& registration_id,
359                          gcm::GCMClient::Result result) {
360     registration_result_ = result;
361     waiter_.SignalCompleted();
362   }
363
364   gcm::GCMDriver* GetGCMDriver() const {
365     return gcm::GCMProfileServiceFactory::GetForProfile(profile())->driver();
366   }
367
368   bool HasAppHandlers(const std::string& app_id) const {
369     return GetGCMDriver()->app_handlers().count(app_id);
370   }
371
372   Profile* profile() const { return profile_.get(); }
373   Waiter* waiter() { return &waiter_; }
374   FakeExtensionGCMAppHandler* gcm_app_handler() const {
375     return gcm_app_handler_.get();
376   }
377   gcm::GCMClient::Result registration_result() const {
378     return registration_result_;
379   }
380   gcm::GCMClient::Result unregistration_result() const {
381     return unregistration_result_;
382   }
383
384  private:
385   scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
386   scoped_ptr<content::InProcessUtilityThreadHelper>
387       in_process_utility_thread_helper_;
388   scoped_ptr<TestingProfile> profile_;
389   ExtensionService* extension_service_;  // Not owned.
390   gcm::FakeSigninManager* signin_manager_;  // Not owned.
391   base::ScopedTempDir temp_dir_;
392
393   // This is needed to create extension service under CrOS.
394 #if defined(OS_CHROMEOS)
395   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
396   chromeos::ScopedTestCrosSettings test_cros_settings_;
397   scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
398 #endif
399
400   Waiter waiter_;
401   scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_;
402   gcm::GCMClient::Result registration_result_;
403   gcm::GCMClient::Result unregistration_result_;
404
405   DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest);
406 };
407
408 TEST_F(ExtensionGCMAppHandlerTest, AddAndRemoveAppHandler) {
409   scoped_refptr<Extension> extension(CreateExtension());
410
411   // App handler is added when extension is loaded.
412   LoadExtension(extension);
413   waiter()->PumpUILoop();
414   EXPECT_TRUE(HasAppHandlers(extension->id()));
415
416   // App handler is removed when extension is unloaded.
417   DisableExtension(extension);
418   waiter()->PumpUILoop();
419   EXPECT_FALSE(HasAppHandlers(extension->id()));
420
421   // App handler is added when extension is reloaded.
422   EnableExtension(extension);
423   waiter()->PumpUILoop();
424   EXPECT_TRUE(HasAppHandlers(extension->id()));
425
426   // App handler is removed when extension is uninstalled.
427   UninstallExtension(extension);
428   waiter()->PumpUILoop();
429   EXPECT_FALSE(HasAppHandlers(extension->id()));
430 }
431
432 TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
433   scoped_refptr<Extension> extension(CreateExtension());
434   LoadExtension(extension);
435
436   // Sign-in is needed for registration.
437   SignIn(kTestingUsername);
438
439   // Kick off registration.
440   std::vector<std::string> sender_ids;
441   sender_ids.push_back("sender1");
442   Register(extension->id(), sender_ids);
443   waiter()->WaitUntilCompleted();
444   EXPECT_EQ(gcm::GCMClient::SUCCESS, registration_result());
445
446   // Add another app handler in order to prevent the GCM service from being
447   // stopped when the extension is uninstalled. This is needed because otherwise
448   // we are not able to receive the unregistration result.
449   GetGCMDriver()->AddAppHandler("Foo", gcm_app_handler());
450
451   // Unregistration should be triggered when the extension is uninstalled.
452   UninstallExtension(extension);
453   waiter()->WaitUntilCompleted();
454   EXPECT_EQ(gcm::GCMClient::SUCCESS,
455             gcm_app_handler()->unregistration_result());
456
457   // Clean up.
458   GetGCMDriver()->RemoveAppHandler("Foo");
459 }
460
461 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionKept) {
462   scoped_refptr<Extension> extension(CreateExtension());
463
464   // App handler is added when the extension is loaded.
465   LoadExtension(extension);
466   waiter()->PumpUILoop();
467   EXPECT_TRUE(HasAppHandlers(extension->id()));
468
469   // App handler count should not drop to zero when the extension is updated.
470   UpdateExtension(extension, "gcm2.crx");
471   waiter()->PumpUILoop();
472   EXPECT_FALSE(gcm_app_handler()->app_handler_count_drop_to_zero());
473   EXPECT_TRUE(HasAppHandlers(extension->id()));
474 }
475
476 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionRemoved) {
477   scoped_refptr<Extension> extension(CreateExtension());
478
479   // App handler is added when the extension is loaded.
480   LoadExtension(extension);
481   waiter()->PumpUILoop();
482   EXPECT_TRUE(HasAppHandlers(extension->id()));
483
484   // App handler is removed when the extension is updated to the version that
485   // has GCM permission removed.
486   UpdateExtension(extension, "good2.crx");
487   waiter()->PumpUILoop();
488   EXPECT_TRUE(gcm_app_handler()->app_handler_count_drop_to_zero());
489   EXPECT_FALSE(HasAppHandlers(extension->id()));
490 }
491
492 }  // namespace extensions