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