Update To 11.40.268.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   ~FakeExtensionGCMAppHandler() override {}
144
145   void OnMessage(const std::string& app_id,
146                  const gcm::GCMClient::IncomingMessage& message) override {}
147
148   void OnMessagesDeleted(const std::string& app_id) override {}
149
150   void OnSendError(
151       const std::string& app_id,
152       const gcm::GCMClient::SendErrorDetails& send_error_details) override {}
153
154   void OnUnregisterCompleted(const std::string& app_id,
155                              gcm::GCMClient::Result result) override {
156     unregistration_result_ = result;
157     waiter_->SignalCompleted();
158   }
159
160   void RemoveAppHandler(const std::string& app_id) override {
161     ExtensionGCMAppHandler::RemoveAppHandler(app_id);
162     if (!GetGCMDriver()->app_handlers().size())
163       app_handler_count_drop_to_zero_ = true;
164   }
165
166   gcm::GCMClient::Result unregistration_result() const {
167     return unregistration_result_;
168   }
169   bool app_handler_count_drop_to_zero() const {
170     return app_handler_count_drop_to_zero_;
171   }
172
173  private:
174   Waiter* waiter_;
175   gcm::GCMClient::Result unregistration_result_;
176   bool app_handler_count_drop_to_zero_;
177
178   DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler);
179 };
180
181 class ExtensionGCMAppHandlerTest : public testing::Test {
182  public:
183   static KeyedService* BuildGCMProfileService(
184       content::BrowserContext* context) {
185     return new gcm::GCMProfileService(
186         Profile::FromBrowserContext(context),
187         scoped_ptr<gcm::GCMClientFactory>(new gcm::FakeGCMClientFactory(
188             gcm::FakeGCMClient::NO_DELAY_START,
189             content::BrowserThread::GetMessageLoopProxyForThread(
190                 content::BrowserThread::UI),
191             content::BrowserThread::GetMessageLoopProxyForThread(
192                 content::BrowserThread::IO))));
193   }
194
195   ExtensionGCMAppHandlerTest()
196       : extension_service_(NULL),
197         registration_result_(gcm::GCMClient::UNKNOWN_ERROR),
198         unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR) {
199   }
200
201   ~ExtensionGCMAppHandlerTest() override {}
202
203   // Overridden from test::Test:
204   void SetUp() override {
205     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
206
207     // Make BrowserThread work in unittest.
208     thread_bundle_.reset(new content::TestBrowserThreadBundle(
209         content::TestBrowserThreadBundle::REAL_IO_THREAD));
210
211     // Allow extension update to unpack crx in process.
212     in_process_utility_thread_helper_.reset(
213         new content::InProcessUtilityThreadHelper);
214
215     // This is needed to create extension service under CrOS.
216 #if defined(OS_CHROMEOS)
217     test_user_manager_.reset(new chromeos::ScopedTestUserManager());
218     // Creating a DBus thread manager setter has the side effect of
219     // creating a DBusThreadManager, which is needed for testing.
220     // We don't actually need the setter so we ignore the return value.
221     chromeos::DBusThreadManager::GetSetterForTesting();
222 #endif
223
224     // Create a new profile.
225     TestingProfile::Builder builder;
226     builder.AddTestingFactory(SigninManagerFactory::GetInstance(),
227                               gcm::FakeSigninManager::Build);
228     profile_ = builder.Build();
229     signin_manager_ = static_cast<gcm::FakeSigninManager*>(
230         SigninManagerFactory::GetInstance()->GetForProfile(profile_.get()));
231
232     // Create extension service in order to uninstall the extension.
233     TestExtensionSystem* extension_system(
234         static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile())));
235     base::FilePath extensions_install_dir =
236         temp_dir_.path().Append(FILE_PATH_LITERAL("Extensions"));
237     extension_system->CreateExtensionService(
238         CommandLine::ForCurrentProcess(), extensions_install_dir, false);
239     extension_service_ = extension_system->Get(profile())->extension_service();
240     extension_service_->set_extensions_enabled(true);
241     extension_service_->set_show_extensions_prompts(false);
242     extension_service_->set_install_updates_when_idle_for_test(false);
243
244     // Enable GCM such that tests could be run on all channels.
245     profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled, true);
246
247     // Create GCMProfileService that talks with fake GCMClient.
248     gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
249         profile(), &ExtensionGCMAppHandlerTest::BuildGCMProfileService);
250
251     // Create a fake version of ExtensionGCMAppHandler.
252     gcm_app_handler_.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_));
253   }
254
255   void TearDown() override {
256 #if defined(OS_CHROMEOS)
257     test_user_manager_.reset();
258 #endif
259
260     waiter_.PumpUILoop();
261   }
262
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);
271
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));
283
284     return extension;
285   }
286
287   void LoadExtension(const Extension* extension) {
288     extension_service_->AddExtension(extension);
289   }
290
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;
296   }
297
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;
304     }
305     data_dir = data_dir.AppendASCII("extensions");
306     data_dir = data_dir.AppendASCII(update_crx);
307
308     base::FilePath path = temp_dir_.path();
309     path = path.Append(data_dir.BaseName());
310     ASSERT_TRUE(base::CopyFile(data_dir, path));
311
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         extension->id(), path, true, &installer);
318
319     if (installer)
320       observer.Wait();
321   }
322
323   void DisableExtension(const Extension* extension) {
324     extension_service_->DisableExtension(
325         extension->id(), Extension::DISABLE_USER_ACTION);
326   }
327
328   void EnableExtension(const Extension* extension) {
329     extension_service_->EnableExtension(extension->id());
330   }
331
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);
338   }
339
340   void SignIn(const std::string& username) {
341     signin_manager_->SignIn(username);
342     waiter_.PumpIOLoop();
343   }
344
345   void SignOut() {
346     signin_manager_->SignOut(signin_metrics::SIGNOUT_TEST);
347     waiter_.PumpIOLoop();
348   }
349
350   void Register(const std::string& app_id,
351                 const std::vector<std::string>& sender_ids) {
352     GetGCMDriver()->Register(
353         app_id,
354         sender_ids,
355         base::Bind(&ExtensionGCMAppHandlerTest::RegisterCompleted,
356                    base::Unretained(this)));
357   }
358
359   void RegisterCompleted(const std::string& registration_id,
360                          gcm::GCMClient::Result result) {
361     registration_result_ = result;
362     waiter_.SignalCompleted();
363   }
364
365   gcm::GCMDriver* GetGCMDriver() const {
366     return gcm::GCMProfileServiceFactory::GetForProfile(profile())->driver();
367   }
368
369   bool HasAppHandlers(const std::string& app_id) const {
370     return GetGCMDriver()->app_handlers().count(app_id);
371   }
372
373   Profile* profile() const { return profile_.get(); }
374   Waiter* waiter() { return &waiter_; }
375   FakeExtensionGCMAppHandler* gcm_app_handler() const {
376     return gcm_app_handler_.get();
377   }
378   gcm::GCMClient::Result registration_result() const {
379     return registration_result_;
380   }
381   gcm::GCMClient::Result unregistration_result() const {
382     return unregistration_result_;
383   }
384
385  private:
386   scoped_ptr<content::TestBrowserThreadBundle> thread_bundle_;
387   scoped_ptr<content::InProcessUtilityThreadHelper>
388       in_process_utility_thread_helper_;
389   scoped_ptr<TestingProfile> profile_;
390   ExtensionService* extension_service_;  // Not owned.
391   gcm::FakeSigninManager* signin_manager_;  // Not owned.
392   base::ScopedTempDir temp_dir_;
393
394   // This is needed to create extension service under CrOS.
395 #if defined(OS_CHROMEOS)
396   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
397   chromeos::ScopedTestCrosSettings test_cros_settings_;
398   scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
399 #endif
400
401   Waiter waiter_;
402   scoped_ptr<FakeExtensionGCMAppHandler> gcm_app_handler_;
403   gcm::GCMClient::Result registration_result_;
404   gcm::GCMClient::Result unregistration_result_;
405
406   DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest);
407 };
408
409 TEST_F(ExtensionGCMAppHandlerTest, AddAndRemoveAppHandler) {
410   scoped_refptr<Extension> extension(CreateExtension());
411
412   // App handler is added when extension is loaded.
413   LoadExtension(extension.get());
414   waiter()->PumpUILoop();
415   EXPECT_TRUE(HasAppHandlers(extension->id()));
416
417   // App handler is removed when extension is unloaded.
418   DisableExtension(extension.get());
419   waiter()->PumpUILoop();
420   EXPECT_FALSE(HasAppHandlers(extension->id()));
421
422   // App handler is added when extension is reloaded.
423   EnableExtension(extension.get());
424   waiter()->PumpUILoop();
425   EXPECT_TRUE(HasAppHandlers(extension->id()));
426
427   // App handler is removed when extension is uninstalled.
428   UninstallExtension(extension.get());
429   waiter()->PumpUILoop();
430   EXPECT_FALSE(HasAppHandlers(extension->id()));
431 }
432
433 TEST_F(ExtensionGCMAppHandlerTest, UnregisterOnExtensionUninstall) {
434   scoped_refptr<Extension> extension(CreateExtension());
435   LoadExtension(extension.get());
436
437   // Sign-in is needed for registration.
438   SignIn(kTestingUsername);
439
440   // Kick off registration.
441   std::vector<std::string> sender_ids;
442   sender_ids.push_back("sender1");
443   Register(extension->id(), sender_ids);
444   waiter()->WaitUntilCompleted();
445   EXPECT_EQ(gcm::GCMClient::SUCCESS, registration_result());
446
447   // Add another app handler in order to prevent the GCM service from being
448   // stopped when the extension is uninstalled. This is needed because otherwise
449   // we are not able to receive the unregistration result.
450   GetGCMDriver()->AddAppHandler("Foo", gcm_app_handler());
451
452   // Unregistration should be triggered when the extension is uninstalled.
453   UninstallExtension(extension.get());
454   waiter()->WaitUntilCompleted();
455   EXPECT_EQ(gcm::GCMClient::SUCCESS,
456             gcm_app_handler()->unregistration_result());
457
458   // Clean up.
459   GetGCMDriver()->RemoveAppHandler("Foo");
460 }
461
462 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionKept) {
463   scoped_refptr<Extension> extension(CreateExtension());
464
465   // App handler is added when the extension is loaded.
466   LoadExtension(extension.get());
467   waiter()->PumpUILoop();
468   EXPECT_TRUE(HasAppHandlers(extension->id()));
469
470   // App handler count should not drop to zero when the extension is updated.
471   UpdateExtension(extension.get(), "gcm2.crx");
472   waiter()->PumpUILoop();
473   EXPECT_FALSE(gcm_app_handler()->app_handler_count_drop_to_zero());
474   EXPECT_TRUE(HasAppHandlers(extension->id()));
475 }
476
477 TEST_F(ExtensionGCMAppHandlerTest, UpdateExtensionWithGcmPermissionRemoved) {
478   scoped_refptr<Extension> extension(CreateExtension());
479
480   // App handler is added when the extension is loaded.
481   LoadExtension(extension.get());
482   waiter()->PumpUILoop();
483   EXPECT_TRUE(HasAppHandlers(extension->id()));
484
485   // App handler is removed when the extension is updated to the version that
486   // has GCM permission removed.
487   UpdateExtension(extension.get(), "good2.crx");
488   waiter()->PumpUILoop();
489   EXPECT_TRUE(gcm_app_handler()->app_handler_count_drop_to_zero());
490   EXPECT_FALSE(HasAppHandlers(extension->id()));
491 }
492
493 }  // namespace extensions