1 // Copyright (c) 2012 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 // TODO(rickcam): Bug 73183: Add unit tests for image loading
6
7 #include <cstdlib>
8 #include <set>
9
10 #include "chrome/browser/background/background_application_list_model.h"
11
12 #include "base/command_line.h"
13 #include "base/files/file_path.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/stl_util.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_service_test_base.h"
19 #include "chrome/browser/extensions/permissions_updater.h"
20 #include "chrome/test/base/testing_profile.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "content/public/browser/notification_types.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/manifest_constants.h"
26 #include "extensions/common/permissions/api_permission.h"
27 #include "extensions/common/permissions/permission_set.h"
28 #include "extensions/common/permissions/permissions_data.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30
31 // This value is used to seed the PRNG at the beginning of a sequence of
32 // operations to produce a repeatable sequence.
33 #define RANDOM_SEED (0x33F7A7A7)
34
35 using extensions::APIPermission;
36 using extensions::Extension;
37
38 // For ExtensionService interface when it requires a path that is not used.
bogus_file_pathname(const std::string & name)39 base::FilePath bogus_file_pathname(const std::string& name) {
40 return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent"))
41 .AppendASCII(name);
42 }
43
44 class BackgroundApplicationListModelTest
45 : public extensions::ExtensionServiceTestBase {
46 public:
BackgroundApplicationListModelTest()47 BackgroundApplicationListModelTest() {}
~BackgroundApplicationListModelTest()48 virtual ~BackgroundApplicationListModelTest() {}
49
50 protected:
InitializeAndLoadEmptyExtensionService()51 void InitializeAndLoadEmptyExtensionService() {
52 InitializeEmptyExtensionService();
53 service_->Init(); /* Sends EXTENSIONS_READY */
54 }
55
IsBackgroundApp(const Extension & app)56 bool IsBackgroundApp(const Extension& app) {
57 return BackgroundApplicationListModel::IsBackgroundApp(app,
58 profile_.get());
59 }
60 };
61
62 enum PushMessagingOption {
63 NO_PUSH_MESSAGING,
64 PUSH_MESSAGING_PERMISSION,
65 PUSH_MESSAGING_BUT_NOT_BACKGROUND
66 };
67
68 // Returns a barebones test Extension object with the specified |name|. The
69 // returned extension will include background permission iff
70 // |background_permission| is true and pushMessaging permission if requested
71 // by |push_messaging| value. Also the extension may have a specific id set
72 // to test the case when it has a pushMessaging permission but is not
73 // considered a background app based on a whitelist.
CreateExtensionBase(const std::string & name,bool background_permission,PushMessagingOption push_messaging)74 static scoped_refptr<Extension> CreateExtensionBase(
75 const std::string& name,
76 bool background_permission,
77 PushMessagingOption push_messaging) {
78 base::DictionaryValue manifest;
79 manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
80 manifest.SetString(extensions::manifest_keys::kName, name);
81 base::ListValue* permissions = new base::ListValue();
82 manifest.Set(extensions::manifest_keys::kPermissions, permissions);
83 if (background_permission) {
84 permissions->Append(base::Value::CreateStringValue("background"));
85 }
86 if (push_messaging == PUSH_MESSAGING_PERMISSION ||
87 push_messaging == PUSH_MESSAGING_BUT_NOT_BACKGROUND) {
88 permissions->Append(base::Value::CreateStringValue("pushMessaging"));
89 }
90
91 std::string error;
92 scoped_refptr<Extension> extension;
93
94 // There is a whitelist for extensions that have pushMessaging permission but
95 // are not considered a background app. Create a test extension with a known
96 // test id if needed.
97 if (push_messaging == PUSH_MESSAGING_BUT_NOT_BACKGROUND) {
98 extension = Extension::Create(
99 bogus_file_pathname(name),
100 extensions::Manifest::INVALID_LOCATION,
101 manifest,
102 Extension::NO_FLAGS,
103 "aaaabbbbccccddddeeeeffffgggghhhh",
104 &error);
105 } else {
106 extension = Extension::Create(
107 bogus_file_pathname(name),
108 extensions::Manifest::INVALID_LOCATION,
109 manifest,
110 Extension::NO_FLAGS,
111 &error);
112 }
113
114 // Cannot ASSERT_* here because that attempts an illegitimate return.
115 // Cannot EXPECT_NE here because that assumes non-pointers unlike EXPECT_EQ
116 EXPECT_TRUE(extension.get() != NULL) << error;
117 return extension;
118 }
119
CreateExtension(const std::string & name,bool background_permission)120 static scoped_refptr<Extension> CreateExtension(const std::string& name,
121 bool background_permission) {
122 return CreateExtensionBase(name, background_permission, NO_PUSH_MESSAGING);
123 }
124
125 namespace {
GenerateUniqueExtensionName()126 std::string GenerateUniqueExtensionName() {
127 static int uniqueness = 0;
128 std::ostringstream output;
129 output << "Unique Named Extension " << uniqueness;
130 ++uniqueness;
131 return output.str();
132 }
133
AddBackgroundPermission(ExtensionService * service,Extension * extension)134 void AddBackgroundPermission(ExtensionService* service,
135 Extension* extension) {
136 if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
137 service->profile())) {
138 return;
139 }
140
141 scoped_refptr<Extension> temporary =
142 CreateExtension(GenerateUniqueExtensionName(), true);
143 scoped_refptr<const extensions::PermissionSet> permissions =
144 temporary->permissions_data()->active_permissions();
145 extensions::PermissionsUpdater(service->profile()).AddPermissions(
146 extension, permissions.get());
147 }
148
RemoveBackgroundPermission(ExtensionService * service,Extension * extension)149 void RemoveBackgroundPermission(ExtensionService* service,
150 Extension* extension) {
151 if (!BackgroundApplicationListModel::IsBackgroundApp(*extension,
152 service->profile())) {
153 return;
154 }
155 extensions::PermissionsUpdater(service->profile()).RemovePermissions(
156 extension, extension->permissions_data()->active_permissions().get());
157 }
158 } // namespace
159
160 // Crashes on Mac tryslaves.
161 // http://crbug.com/165458
162 #if defined(OS_MACOSX) || defined(OS_LINUX)
163 #define MAYBE_ExplicitTest DISABLED_ExplicitTest
164 #else
165 #define MAYBE_ExplicitTest ExplicitTest
166 #endif
167 // With minimal test logic, verifies behavior over an explicit set of
168 // extensions, of which some are Background Apps and others are not.
TEST_F(BackgroundApplicationListModelTest,MAYBE_ExplicitTest)169 TEST_F(BackgroundApplicationListModelTest, MAYBE_ExplicitTest) {
170 InitializeAndLoadEmptyExtensionService();
171 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
172 extension_service();
173 ASSERT_TRUE(service);
174 ASSERT_TRUE(service->is_ready());
175 ASSERT_TRUE(service->extensions());
176 ASSERT_TRUE(service->extensions()->is_empty());
177 scoped_ptr<BackgroundApplicationListModel> model(
178 new BackgroundApplicationListModel(profile_.get()));
179 ASSERT_EQ(0U, model->size());
180
181 scoped_refptr<Extension> ext1 = CreateExtension("alpha", false);
182 scoped_refptr<Extension> ext2 = CreateExtension("bravo", false);
183 scoped_refptr<Extension> ext3 = CreateExtension("charlie", false);
184 scoped_refptr<Extension> bgapp1 = CreateExtension("delta", true);
185 scoped_refptr<Extension> bgapp2 = CreateExtension("echo", true);
186 ASSERT_TRUE(service->extensions() != NULL);
187 ASSERT_EQ(0U, service->extensions()->size());
188 ASSERT_EQ(0U, model->size());
189
190 // Add alternating Extensions and Background Apps
191 ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
192 service->AddExtension(ext1.get());
193 ASSERT_EQ(1U, service->extensions()->size());
194 ASSERT_EQ(0U, model->size());
195 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
196 service->AddExtension(bgapp1.get());
197 ASSERT_EQ(2U, service->extensions()->size());
198 ASSERT_EQ(1U, model->size());
199 ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
200 service->AddExtension(ext2.get());
201 ASSERT_EQ(3U, service->extensions()->size());
202 ASSERT_EQ(1U, model->size());
203 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
204 service->AddExtension(bgapp2.get());
205 ASSERT_EQ(4U, service->extensions()->size());
206 ASSERT_EQ(2U, model->size());
207 ASSERT_FALSE(IsBackgroundApp(*ext3.get()));
208 service->AddExtension(ext3.get());
209 ASSERT_EQ(5U, service->extensions()->size());
210 ASSERT_EQ(2U, model->size());
211
212 // Remove in FIFO order.
213 ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
214 service->UninstallExtension(ext1->id(), false, NULL);
215 ASSERT_EQ(4U, service->extensions()->size());
216 ASSERT_EQ(2U, model->size());
217 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
218 service->UninstallExtension(bgapp1->id(), false, NULL);
219 ASSERT_EQ(3U, service->extensions()->size());
220 ASSERT_EQ(1U, model->size());
221 ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
222 service->UninstallExtension(ext2->id(), false, NULL);
223 ASSERT_EQ(2U, service->extensions()->size());
224 ASSERT_EQ(1U, model->size());
225 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
226 service->UninstallExtension(bgapp2->id(), false, NULL);
227 ASSERT_EQ(1U, service->extensions()->size());
228 ASSERT_EQ(0U, model->size());
229 ASSERT_FALSE(IsBackgroundApp(*ext3.get()));
230 service->UninstallExtension(ext3->id(), false, NULL);
231 ASSERT_EQ(0U, service->extensions()->size());
232 ASSERT_EQ(0U, model->size());
233 }
234
235 // Verifies that pushMessaging also triggers background detection, except
236 // when extension is in a whitelist.
TEST_F(BackgroundApplicationListModelTest,PushMessagingTest)237 TEST_F(BackgroundApplicationListModelTest, PushMessagingTest) {
238 InitializeAndLoadEmptyExtensionService();
239 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
240 extension_service();
241 ASSERT_TRUE(service);
242 ASSERT_TRUE(service->is_ready());
243 ASSERT_TRUE(service->extensions());
244 ASSERT_TRUE(service->extensions()->is_empty());
245 scoped_ptr<BackgroundApplicationListModel> model(
246 new BackgroundApplicationListModel(profile_.get()));
247 ASSERT_EQ(0U, model->size());
248
249 scoped_refptr<Extension> ext1 = CreateExtension("alpha", false);
250 scoped_refptr<Extension> ext2 =
251 CreateExtensionBase("charlie", false, PUSH_MESSAGING_BUT_NOT_BACKGROUND);
252 scoped_refptr<Extension> bgapp1 =
253 CreateExtensionBase("bravo", false, PUSH_MESSAGING_PERMISSION);
254 scoped_refptr<Extension> bgapp2 =
255 CreateExtensionBase("delta", true, PUSH_MESSAGING_PERMISSION);
256 scoped_refptr<Extension> bgapp3 =
257 CreateExtensionBase("echo", true, PUSH_MESSAGING_BUT_NOT_BACKGROUND);
258 ASSERT_TRUE(service->extensions() != NULL);
259 ASSERT_EQ(0U, service->extensions()->size());
260 ASSERT_EQ(0U, model->size());
261
262 // Add alternating Extensions and Background Apps
263 ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
264 service->AddExtension(ext1.get());
265 ASSERT_EQ(1U, service->extensions()->size());
266 ASSERT_EQ(0U, model->size());
267 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
268 service->AddExtension(bgapp1.get());
269 ASSERT_EQ(2U, service->extensions()->size());
270 ASSERT_EQ(1U, model->size());
271 ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
272 service->AddExtension(ext2.get());
273 ASSERT_EQ(3U, service->extensions()->size());
274 ASSERT_EQ(1U, model->size());
275 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
276 service->AddExtension(bgapp2.get());
277 ASSERT_EQ(4U, service->extensions()->size());
278 ASSERT_EQ(2U, model->size());
279 // Need to remove ext2 because it uses same id as bgapp3.
280 ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
281 service->UninstallExtension(ext2->id(), false, NULL);
282 ASSERT_EQ(3U, service->extensions()->size());
283 ASSERT_EQ(2U, model->size());
284 ASSERT_TRUE(IsBackgroundApp(*bgapp3.get()));
285 service->AddExtension(bgapp3.get());
286 ASSERT_EQ(4U, service->extensions()->size());
287 ASSERT_EQ(3U, model->size());
288
289 // Remove in FIFO order.
290 ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
291 service->UninstallExtension(ext1->id(), false, NULL);
292 ASSERT_EQ(3U, service->extensions()->size());
293 ASSERT_EQ(3U, model->size());
294 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
295 service->UninstallExtension(bgapp1->id(), false, NULL);
296 ASSERT_EQ(2U, service->extensions()->size());
297 ASSERT_EQ(2U, model->size());
298 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
299 service->UninstallExtension(bgapp2->id(), false, NULL);
300 ASSERT_EQ(1U, service->extensions()->size());
301 ASSERT_EQ(1U, model->size());
302 ASSERT_TRUE(IsBackgroundApp(*bgapp3.get()));
303 service->UninstallExtension(bgapp3->id(), false, NULL);
304 ASSERT_EQ(0U, service->extensions()->size());
305 ASSERT_EQ(0U, model->size());
306 }
307
308
309
310 // With minimal test logic, verifies behavior with dynamic permissions.
TEST_F(BackgroundApplicationListModelTest,AddRemovePermissionsTest)311 TEST_F(BackgroundApplicationListModelTest, AddRemovePermissionsTest) {
312 InitializeAndLoadEmptyExtensionService();
313 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
314 extension_service();
315 ASSERT_TRUE(service);
316 ASSERT_TRUE(service->is_ready());
317 ASSERT_TRUE(service->extensions());
318 ASSERT_TRUE(service->extensions()->is_empty());
319 scoped_ptr<BackgroundApplicationListModel> model(
320 new BackgroundApplicationListModel(profile_.get()));
321 ASSERT_EQ(0U, model->size());
322
323 scoped_refptr<Extension> ext = CreateExtension("extension", false);
324 ASSERT_FALSE(
325 ext->permissions_data()->HasAPIPermission(APIPermission::kBackground));
326 scoped_refptr<Extension> bgapp = CreateExtension("application", true);
327 ASSERT_TRUE(
328 bgapp->permissions_data()->HasAPIPermission(APIPermission::kBackground));
329 ASSERT_TRUE(service->extensions() != NULL);
330 ASSERT_EQ(0U, service->extensions()->size());
331 ASSERT_EQ(0U, model->size());
332
333 // Add one (non-background) extension and one background application
334 ASSERT_FALSE(IsBackgroundApp(*ext.get()));
335 service->AddExtension(ext.get());
336 ASSERT_EQ(1U, service->extensions()->size());
337 ASSERT_EQ(0U, model->size());
338 ASSERT_TRUE(IsBackgroundApp(*bgapp.get()));
339 service->AddExtension(bgapp.get());
340 ASSERT_EQ(2U, service->extensions()->size());
341 ASSERT_EQ(1U, model->size());
342
343 // Change permissions back and forth
344 AddBackgroundPermission(service, ext.get());
345 ASSERT_TRUE(
346 ext->permissions_data()->HasAPIPermission(APIPermission::kBackground));
347 ASSERT_EQ(2U, service->extensions()->size());
348 ASSERT_EQ(2U, model->size());
349 RemoveBackgroundPermission(service, bgapp.get());
350 ASSERT_FALSE(
351 bgapp->permissions_data()->HasAPIPermission(APIPermission::kBackground));
352 ASSERT_EQ(2U, service->extensions()->size());
353 ASSERT_EQ(1U, model->size());
354 RemoveBackgroundPermission(service, ext.get());
355 ASSERT_FALSE(
356 ext->permissions_data()->HasAPIPermission(APIPermission::kBackground));
357 ASSERT_EQ(2U, service->extensions()->size());
358 ASSERT_EQ(0U, model->size());
359 AddBackgroundPermission(service, bgapp.get());
360 ASSERT_TRUE(
361 bgapp->permissions_data()->HasAPIPermission(APIPermission::kBackground));
362 ASSERT_EQ(2U, service->extensions()->size());
363 ASSERT_EQ(1U, model->size());
364 }
365
366 typedef std::set<scoped_refptr<Extension> > ExtensionCollection;
367
368 namespace {
AddExtension(ExtensionService * service,ExtensionCollection * extensions,BackgroundApplicationListModel * model,size_t * expected,size_t * count)369 void AddExtension(ExtensionService* service,
370 ExtensionCollection* extensions,
371 BackgroundApplicationListModel* model,
372 size_t* expected,
373 size_t* count) {
374 bool create_background = false;
375 if (rand() % 2) {
376 create_background = true;
377 ++*expected;
378 }
379 scoped_refptr<Extension> extension =
380 CreateExtension(GenerateUniqueExtensionName(), create_background);
381 ASSERT_EQ(BackgroundApplicationListModel::IsBackgroundApp(*extension.get(),
382 service->profile()),
383 create_background);
384 extensions->insert(extension);
385 ++*count;
386 ASSERT_EQ(*count, extensions->size());
387 service->AddExtension(extension.get());
388 ASSERT_EQ(*count, service->extensions()->size());
389 ASSERT_EQ(*expected, model->size());
390 }
391
RemoveExtension(ExtensionService * service,ExtensionCollection * extensions,BackgroundApplicationListModel * model,size_t * expected,size_t * count)392 void RemoveExtension(ExtensionService* service,
393 ExtensionCollection* extensions,
394 BackgroundApplicationListModel* model,
395 size_t* expected,
396 size_t* count) { // Maybe remove an extension.
397 ExtensionCollection::iterator cursor = extensions->begin();
398 if (cursor == extensions->end()) {
399 // Nothing to remove. Just verify accounting.
400 ASSERT_EQ(0U, *count);
401 ASSERT_EQ(0U, *expected);
402 ASSERT_EQ(0U, service->extensions()->size());
403 ASSERT_EQ(0U, model->size());
404 } else {
405 // Randomly select which extension to remove
406 if (extensions->size() > 1) {
407 int offset = rand() % (extensions->size() - 1);
408 for (int index = 0; index < offset; ++index)
409 ++cursor;
410 }
411 scoped_refptr<Extension> extension = cursor->get();
412 std::string id = extension->id();
413 if (BackgroundApplicationListModel::IsBackgroundApp(*extension.get(),
414 service->profile())) {
415 --*expected;
416 }
417 extensions->erase(cursor);
418 --*count;
419 ASSERT_EQ(*count, extensions->size());
420 service->UninstallExtension(extension->id(), false, NULL);
421 ASSERT_EQ(*count, service->extensions()->size());
422 ASSERT_EQ(*expected, model->size());
423 }
424 }
425
TogglePermission(ExtensionService * service,ExtensionCollection * extensions,BackgroundApplicationListModel * model,size_t * expected,size_t * count)426 void TogglePermission(ExtensionService* service,
427 ExtensionCollection* extensions,
428 BackgroundApplicationListModel* model,
429 size_t* expected,
430 size_t* count) {
431 ExtensionCollection::iterator cursor = extensions->begin();
432 if (cursor == extensions->end()) {
433 // Nothing to toggle. Just verify accounting.
434 ASSERT_EQ(0U, *count);
435 ASSERT_EQ(0U, *expected);
436 ASSERT_EQ(0U, service->extensions()->size());
437 ASSERT_EQ(0U, model->size());
438 } else {
439 // Randomly select which extension to toggle.
440 if (extensions->size() > 1) {
441 int offset = rand() % (extensions->size() - 1);
442 for (int index = 0; index < offset; ++index)
443 ++cursor;
444 }
445 scoped_refptr<Extension> extension = cursor->get();
446 std::string id = extension->id();
447 if (BackgroundApplicationListModel::IsBackgroundApp(*extension.get(),
448 service->profile())) {
449 --*expected;
450 ASSERT_EQ(*count, extensions->size());
451 RemoveBackgroundPermission(service, extension.get());
452 ASSERT_EQ(*count, service->extensions()->size());
453 ASSERT_EQ(*expected, model->size());
454 } else {
455 ++*expected;
456 ASSERT_EQ(*count, extensions->size());
457 AddBackgroundPermission(service, extension.get());
458 ASSERT_EQ(*count, service->extensions()->size());
459 ASSERT_EQ(*expected, model->size());
460 }
461 }
462 }
463 } // namespace
464
465 // Verifies behavior with a pseudo-randomly generated set of actions: Adding and
466 // removing extensions, of which some are Background Apps and others are not.
TEST_F(BackgroundApplicationListModelTest,RandomTest)467 TEST_F(BackgroundApplicationListModelTest, RandomTest) {
468 InitializeAndLoadEmptyExtensionService();
469 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
470 extension_service();
471 ASSERT_TRUE(service);
472 ASSERT_TRUE(service->is_ready());
473 ASSERT_TRUE(service->extensions());
474 ASSERT_TRUE(service->extensions()->is_empty());
475 scoped_ptr<BackgroundApplicationListModel> model(
476 new BackgroundApplicationListModel(profile_.get()));
477 ASSERT_EQ(0U, model->size());
478
479 static const int kIterations = 20;
480 ExtensionCollection extensions;
481 size_t count = 0;
482 size_t expected = 0;
483 srand(RANDOM_SEED);
484 for (int index = 0; index < kIterations; ++index) {
485 switch (rand() % 3) {
486 case 0:
487 AddExtension(service, &extensions, model.get(), &expected, &count);
488 break;
489 case 1:
490 RemoveExtension(service, &extensions, model.get(), &expected, &count);
491 break;
492 case 2:
493 TogglePermission(service, &extensions, model.get(), &expected, &count);
494 break;
495 default:
496 NOTREACHED();
497 break;
498 }
499 }
500 }
501