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"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/run_loop.h"
18 #include "base/values.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/extensions/test_extension_service.h"
21 #include "chrome/browser/extensions/test_extension_system.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/services/gcm/fake_gcm_client_factory.h"
24 #include "chrome/browser/services/gcm/fake_signin_manager.h"
25 #include "chrome/browser/services/gcm/gcm_client_factory.h"
26 #include "chrome/browser/services/gcm/gcm_client_mock.h"
27 #include "chrome/browser/services/gcm/gcm_profile_service.h"
28 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
29 #include "chrome/browser/signin/signin_manager_factory.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/test/base/testing_profile.h"
32 #include "components/keyed_service/core/keyed_service.h"
33 #include "content/public/browser/browser_context.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/test/test_browser_thread_bundle.h"
36 #include "extensions/browser/extension_system.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/manifest.h"
39 #include "extensions/common/manifest_constants.h"
40 #include "extensions/common/permissions/api_permission.h"
41 #include "testing/gtest/include/gtest/gtest.h"
43 #if !defined(OS_ANDROID)
44 #include "chrome/browser/extensions/api/gcm/gcm_api.h"
47 #if defined(OS_CHROMEOS)
48 #include "chrome/browser/chromeos/login/user_manager.h"
49 #include "chrome/browser/chromeos/settings/cros_settings.h"
50 #include "chrome/browser/chromeos/settings/device_settings_service.h"
53 namespace extensions
{
57 const char kTestExtensionName
[] = "FooBar";
58 const char kTestingUsername
[] = "user1@example.com";
62 // Helper class for asynchronous waiting.
68 // Waits until the asynchronous operation finishes.
69 void WaitUntilCompleted() {
70 run_loop_
.reset(new base::RunLoop
);
74 // Signals that the asynchronous operation finishes.
75 void SignalCompleted() {
76 if (run_loop_
&& run_loop_
->running())
80 // Runs until UI loop becomes idle.
82 base::MessageLoop::current()->RunUntilIdle();
85 // Runs until IO loop becomes idle.
87 content::BrowserThread::PostTask(
88 content::BrowserThread::IO
,
90 base::Bind(&Waiter::OnIOLoopPump
, base::Unretained(this)));
96 void PumpIOLoopCompleted() {
97 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
102 void OnIOLoopPump() {
103 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
105 content::BrowserThread::PostTask(
106 content::BrowserThread::IO
,
108 base::Bind(&Waiter::OnIOLoopPumpCompleted
, base::Unretained(this)));
111 void OnIOLoopPumpCompleted() {
112 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
114 content::BrowserThread::PostTask(
115 content::BrowserThread::UI
,
117 base::Bind(&Waiter::PumpIOLoopCompleted
, base::Unretained(this)));
120 scoped_ptr
<base::RunLoop
> run_loop_
;
122 DISALLOW_COPY_AND_ASSIGN(Waiter
);
125 class FakeExtensionGCMAppHandler
: public ExtensionGCMAppHandler
{
127 FakeExtensionGCMAppHandler(Profile
* profile
, Waiter
* waiter
)
128 : ExtensionGCMAppHandler(profile
),
130 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR
) {
133 virtual ~FakeExtensionGCMAppHandler() {
136 virtual void OnMessage(
137 const std::string
& app_id
,
138 const gcm::GCMClient::IncomingMessage
& message
) OVERRIDE
{
141 virtual void OnMessagesDeleted(const std::string
& app_id
) OVERRIDE
{
144 virtual void OnSendError(
145 const std::string
& app_id
,
146 const gcm::GCMClient::SendErrorDetails
& send_error_details
) OVERRIDE
{
149 virtual void OnUnregisterCompleted(const std::string
& app_id
,
150 gcm::GCMClient::Result result
) OVERRIDE
{
151 unregistration_result_
= result
;
152 waiter_
->SignalCompleted();
155 gcm::GCMClient::Result
unregistration_result() const {
156 return unregistration_result_
;
161 gcm::GCMClient::Result unregistration_result_
;
163 DISALLOW_COPY_AND_ASSIGN(FakeExtensionGCMAppHandler
);
166 class ExtensionGCMAppHandlerTest
: public testing::Test
{
168 static KeyedService
* BuildGCMProfileService(
169 content::BrowserContext
* context
) {
170 return new gcm::GCMProfileService(static_cast<Profile
*>(context
));
173 ExtensionGCMAppHandlerTest()
174 : extension_service_(NULL
),
175 registration_result_(gcm::GCMClient::UNKNOWN_ERROR
),
176 unregistration_result_(gcm::GCMClient::UNKNOWN_ERROR
) {
179 virtual ~ExtensionGCMAppHandlerTest() {
182 // Overridden from test::Test:
183 virtual void SetUp() OVERRIDE
{
184 // Make BrowserThread work in unittest.
185 thread_bundle_
.reset(new content::TestBrowserThreadBundle(
186 content::TestBrowserThreadBundle::REAL_IO_THREAD
));
188 // This is needed to create extension service under CrOS.
189 #if defined(OS_CHROMEOS)
190 test_user_manager_
.reset(new chromeos::ScopedTestUserManager());
193 // Create a new profile.
194 TestingProfile::Builder builder
;
195 builder
.AddTestingFactory(SigninManagerFactory::GetInstance(),
196 gcm::FakeSigninManager::Build
);
197 profile_
= builder
.Build();
198 signin_manager_
= static_cast<gcm::FakeSigninManager
*>(
199 SigninManagerFactory::GetInstance()->GetForProfile(profile_
.get()));
201 // Create extension service in order to uninstall the extension.
202 TestExtensionSystem
* extension_system(
203 static_cast<TestExtensionSystem
*>(ExtensionSystem::Get(profile())));
204 extension_system
->CreateExtensionService(
205 CommandLine::ForCurrentProcess(), base::FilePath(), false);
206 extension_service_
= extension_system
->Get(profile())->extension_service();
208 // Enable GCM such that tests could be run on all channels.
209 profile()->GetPrefs()->SetBoolean(prefs::kGCMChannelEnabled
, true);
211 // Create GCMProfileService that talks with fake GCMClient.
212 gcm::GCMProfileService
* gcm_profile_service
=
213 static_cast<gcm::GCMProfileService
*>(
214 gcm::GCMProfileServiceFactory::GetInstance()->
215 SetTestingFactoryAndUse(
217 &ExtensionGCMAppHandlerTest::BuildGCMProfileService
));
218 scoped_ptr
<gcm::GCMClientFactory
> gcm_client_factory(
219 new gcm::FakeGCMClientFactory(gcm::GCMClientMock::NO_DELAY_LOADING
));
220 gcm_profile_service
->Initialize(gcm_client_factory
.Pass());
222 // Create a fake version of ExtensionGCMAppHandler.
223 gcm_app_handler_
.reset(new FakeExtensionGCMAppHandler(profile(), &waiter_
));
226 virtual void TearDown() OVERRIDE
{
227 #if defined(OS_CHROMEOS)
228 test_user_manager_
.reset();
231 waiter_
.PumpUILoop();
234 // Returns a barebones test extension.
235 scoped_refptr
<Extension
> CreateExtension() {
237 base::FilePath
path(FILE_PATH_LITERAL("c:\\foo"));
238 #elif defined(OS_POSIX)
239 base::FilePath
path(FILE_PATH_LITERAL("/foo"));
242 base::DictionaryValue manifest
;
243 manifest
.SetString(manifest_keys::kVersion
, "1.0.0.0");
244 manifest
.SetString(manifest_keys::kName
, kTestExtensionName
);
245 base::ListValue
* permission_list
= new base::ListValue
;
246 permission_list
->Append(base::Value::CreateStringValue("gcm"));
247 manifest
.Set(manifest_keys::kPermissions
, permission_list
);
250 scoped_refptr
<Extension
> extension
= Extension::Create(
251 path
.AppendASCII(kTestExtensionName
),
252 Manifest::INVALID_LOCATION
,
256 EXPECT_TRUE(extension
.get()) << error
;
257 EXPECT_TRUE(extension
->HasAPIPermission(APIPermission::kGcm
));
262 void LoadExtension(const Extension
* extension
) {
263 extension_service_
->AddExtension(extension
);
266 void DisableExtension(const Extension
* extension
) {
267 extension_service_
->DisableExtension(
268 extension
->id(), Extension::DISABLE_USER_ACTION
);
271 void EnableExtension(const Extension
* extension
) {
272 extension_service_
->EnableExtension(extension
->id());
275 void UninstallExtension(const Extension
* extension
) {
276 extension_service_
->UninstallExtension(extension
->id(), false, NULL
);
279 void SignIn(const std::string
& username
) {
280 signin_manager_
->SignIn(username
);
281 waiter_
.PumpIOLoop();
285 signin_manager_
->SignOut();
286 waiter_
.PumpIOLoop();
289 void Register(const std::string
& app_id
,
290 const std::vector
<std::string
>& sender_ids
) {
291 GetGCMProfileService()->Register(
294 base::Bind(&ExtensionGCMAppHandlerTest::RegisterCompleted
,
295 base::Unretained(this)));
298 void RegisterCompleted(const std::string
& registration_id
,
299 gcm::GCMClient::Result result
) {
300 registration_result_
= result
;
301 waiter_
.SignalCompleted();
304 gcm::GCMProfileService
* GetGCMProfileService() const {
305 return gcm::GCMProfileServiceFactory::GetForProfile(profile());
308 bool HasAppHandlers(const std::string
& app_id
) const {
309 return GetGCMProfileService()->app_handlers_
.count(app_id
);
312 Profile
* profile() const { return profile_
.get(); }
313 Waiter
* waiter() { return &waiter_
; }
314 FakeExtensionGCMAppHandler
* gcm_app_handler() const {
315 return gcm_app_handler_
.get();
317 gcm::GCMClient::Result
registration_result() const {
318 return registration_result_
;
320 gcm::GCMClient::Result
unregistration_result() const {
321 return unregistration_result_
;
325 scoped_ptr
<content::TestBrowserThreadBundle
> thread_bundle_
;
326 scoped_ptr
<TestingProfile
> profile_
;
327 ExtensionService
* extension_service_
; // Not owned.
328 gcm::FakeSigninManager
* signin_manager_
; // Not owned.
330 // This is needed to create extension service under CrOS.
331 #if defined(OS_CHROMEOS)
332 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_
;
333 chromeos::ScopedTestCrosSettings test_cros_settings_
;
334 scoped_ptr
<chromeos::ScopedTestUserManager
> test_user_manager_
;
338 scoped_ptr
<FakeExtensionGCMAppHandler
> gcm_app_handler_
;
339 gcm::GCMClient::Result registration_result_
;
340 gcm::GCMClient::Result unregistration_result_
;
342 DISALLOW_COPY_AND_ASSIGN(ExtensionGCMAppHandlerTest
);
345 TEST_F(ExtensionGCMAppHandlerTest
, AddAndRemoveAppHandler
) {
346 scoped_refptr
<Extension
> extension(CreateExtension());
348 // App handler is added when extension is loaded.
349 LoadExtension(extension
);
350 waiter()->PumpUILoop();
351 EXPECT_TRUE(HasAppHandlers(extension
->id()));
353 // App handler is removed when extension is unloaded.
354 DisableExtension(extension
);
355 waiter()->PumpUILoop();
356 EXPECT_FALSE(HasAppHandlers(extension
->id()));
358 // App handler is added when extension is reloaded.
359 EnableExtension(extension
);
360 waiter()->PumpUILoop();
361 EXPECT_TRUE(HasAppHandlers(extension
->id()));
363 // App handler is removed when extension is uninstalled.
364 UninstallExtension(extension
);
365 waiter()->PumpUILoop();
366 EXPECT_FALSE(HasAppHandlers(extension
->id()));
369 TEST_F(ExtensionGCMAppHandlerTest
, UnregisterOnExtensionUninstall
) {
370 scoped_refptr
<Extension
> extension(CreateExtension());
371 LoadExtension(extension
);
373 // Sign-in is needed for registration.
374 SignIn(kTestingUsername
);
376 // Kick off registration.
377 std::vector
<std::string
> sender_ids
;
378 sender_ids
.push_back("sender1");
379 Register(extension
->id(), sender_ids
);
380 waiter()->WaitUntilCompleted();
381 EXPECT_EQ(gcm::GCMClient::SUCCESS
, registration_result());
383 // Unregistration should be triggered when the extension is uninstalled.
384 UninstallExtension(extension
);
385 waiter()->WaitUntilCompleted();
386 EXPECT_EQ(gcm::GCMClient::SUCCESS
,
387 gcm_app_handler()->unregistration_result());
390 } // namespace extensions