background_application_list_model_unittest.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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.h"
16#include "base/stl_util.h"
17#include "chrome/browser/extensions/extension_service.h"
18#include "chrome/browser/extensions/extension_service_unittest.h"
19#include "chrome/browser/extensions/extension_system.h"
20#include "chrome/browser/extensions/permissions_updater.h"
21#include "chrome/common/extensions/extension.h"
22#include "chrome/common/extensions/extension_manifest_constants.h"
23#include "chrome/common/extensions/permissions/api_permission.h"
24#include "chrome/common/extensions/permissions/permission_set.h"
25#include "chrome/test/base/testing_profile.h"
26#include "content/public/browser/notification_registrar.h"
27#include "content/public/browser/notification_types.h"
28#include "testing/gtest/include/gtest/gtest.h"
29
30// This value is used to seed the PRNG at the beginning of a sequence of
31// operations to produce a repeatable sequence.
32#define RANDOM_SEED (0x33F7A7A7)
33
34using extensions::APIPermission;
35using extensions::Extension;
36
37// For ExtensionService interface when it requires a path that is not used.
38base::FilePath bogus_file_path() {
39  return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent"));
40}
41
42class BackgroundApplicationListModelTest : public ExtensionServiceTestBase {
43 public:
44  BackgroundApplicationListModelTest() {}
45  virtual ~BackgroundApplicationListModelTest() {}
46
47 protected:
48  void InitializeAndLoadEmptyExtensionService() {
49    InitializeEmptyExtensionService();
50    service_->Init(); /* Sends EXTENSIONS_READY */
51  }
52
53  bool IsBackgroundApp(const Extension& app) {
54    return BackgroundApplicationListModel::IsBackgroundApp(app,
55                                                           profile_.get());
56  }
57};
58
59// Returns a barebones test Extension object with the specified |name|.  The
60// returned extension will include background permission iff
61// |background_permission| is true.
62static scoped_refptr<Extension> CreateExtension(const std::string& name,
63                                                bool background_permission) {
64  DictionaryValue manifest;
65  manifest.SetString(extension_manifest_keys::kVersion, "1.0.0.0");
66  manifest.SetString(extension_manifest_keys::kName, name);
67  if (background_permission) {
68    ListValue* permissions = new ListValue();
69    manifest.Set(extension_manifest_keys::kPermissions, permissions);
70    permissions->Append(Value::CreateStringValue("background"));
71  }
72  std::string error;
73  scoped_refptr<Extension> extension = Extension::Create(
74      bogus_file_path().AppendASCII(name),
75      extensions::Manifest::INVALID_LOCATION,
76      manifest,
77      Extension::NO_FLAGS,
78      &error);
79  // Cannot ASSERT_* here because that attempts an illegitimate return.
80  // Cannot EXPECT_NE here because that assumes non-pointers unlike EXPECT_EQ
81  EXPECT_TRUE(extension.get() != NULL) << error;
82  return extension;
83}
84
85namespace {
86std::string GenerateUniqueExtensionName() {
87  static int uniqueness = 0;
88  std::ostringstream output;
89  output << "Unique Named Extension " << uniqueness;
90  ++uniqueness;
91  return output.str();
92}
93
94void AddBackgroundPermission(ExtensionService* service,
95                             Extension* extension) {
96  if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
97                                                      service->profile())) {
98    return;
99  }
100
101  scoped_refptr<Extension> temporary =
102      CreateExtension(GenerateUniqueExtensionName(), true);
103  scoped_refptr<const extensions::PermissionSet> permissions =
104      temporary->GetActivePermissions();
105  extensions::PermissionsUpdater(service->profile()).AddPermissions(
106      extension, permissions.get());
107}
108
109void RemoveBackgroundPermission(ExtensionService* service,
110                                Extension* extension) {
111  if (!BackgroundApplicationListModel::IsBackgroundApp(*extension,
112                                                       service->profile())) {
113    return;
114  }
115  extensions::PermissionsUpdater(service->profile()).RemovePermissions(
116      extension, extension->GetActivePermissions());
117}
118}  // namespace
119
120// Crashes on Mac tryslaves.
121// http://crbug.com/165458
122#if defined(OS_MACOSX) || defined(OS_LINUX)
123#define MAYBE_ExplicitTest DISABLED_ExplicitTest
124#else
125#define MAYBE_ExplicitTest ExplicitTest
126#endif
127// With minimal test logic, verifies behavior over an explicit set of
128// extensions, of which some are Background Apps and others are not.
129TEST_F(BackgroundApplicationListModelTest, MAYBE_ExplicitTest) {
130  InitializeAndLoadEmptyExtensionService();
131  ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
132      extension_service();
133  ASSERT_TRUE(service);
134  ASSERT_TRUE(service->is_ready());
135  ASSERT_TRUE(service->extensions());
136  ASSERT_TRUE(service->extensions()->is_empty());
137  scoped_ptr<BackgroundApplicationListModel> model(
138      new BackgroundApplicationListModel(profile_.get()));
139  ASSERT_EQ(0U, model->size());
140
141  scoped_refptr<Extension> ext1 = CreateExtension("alpha", false);
142  scoped_refptr<Extension> ext2 = CreateExtension("bravo", false);
143  scoped_refptr<Extension> ext3 = CreateExtension("charlie", false);
144  scoped_refptr<Extension> bgapp1 = CreateExtension("delta", true);
145  scoped_refptr<Extension> bgapp2 = CreateExtension("echo", true);
146  ASSERT_TRUE(service->extensions() != NULL);
147  ASSERT_EQ(0U, service->extensions()->size());
148  ASSERT_EQ(0U, model->size());
149
150  // Add alternating Extensions and Background Apps
151  ASSERT_FALSE(IsBackgroundApp(*ext1));
152  service->AddExtension(ext1);
153  ASSERT_EQ(1U, service->extensions()->size());
154  ASSERT_EQ(0U, model->size());
155  ASSERT_TRUE(IsBackgroundApp(*bgapp1));
156  service->AddExtension(bgapp1);
157  ASSERT_EQ(2U, service->extensions()->size());
158  ASSERT_EQ(1U, model->size());
159  ASSERT_FALSE(IsBackgroundApp(*ext2));
160  service->AddExtension(ext2);
161  ASSERT_EQ(3U, service->extensions()->size());
162  ASSERT_EQ(1U, model->size());
163  ASSERT_TRUE(IsBackgroundApp(*bgapp2));
164  service->AddExtension(bgapp2);
165  ASSERT_EQ(4U, service->extensions()->size());
166  ASSERT_EQ(2U, model->size());
167  ASSERT_FALSE(IsBackgroundApp(*ext3));
168  service->AddExtension(ext3);
169  ASSERT_EQ(5U, service->extensions()->size());
170  ASSERT_EQ(2U, model->size());
171
172  // Remove in FIFO order.
173  ASSERT_FALSE(IsBackgroundApp(*ext1));
174  service->UninstallExtension(ext1->id(), false, NULL);
175  ASSERT_EQ(4U, service->extensions()->size());
176  ASSERT_EQ(2U, model->size());
177  ASSERT_TRUE(IsBackgroundApp(*bgapp1));
178  service->UninstallExtension(bgapp1->id(), false, NULL);
179  ASSERT_EQ(3U, service->extensions()->size());
180  ASSERT_EQ(1U, model->size());
181  ASSERT_FALSE(IsBackgroundApp(*ext2));
182  service->UninstallExtension(ext2->id(), false, NULL);
183  ASSERT_EQ(2U, service->extensions()->size());
184  ASSERT_EQ(1U, model->size());
185  ASSERT_TRUE(IsBackgroundApp(*bgapp2));
186  service->UninstallExtension(bgapp2->id(), false, NULL);
187  ASSERT_EQ(1U, service->extensions()->size());
188  ASSERT_EQ(0U, model->size());
189  ASSERT_FALSE(IsBackgroundApp(*ext3));
190  service->UninstallExtension(ext3->id(), false, NULL);
191  ASSERT_EQ(0U, service->extensions()->size());
192  ASSERT_EQ(0U, model->size());
193}
194
195// With minimal test logic, verifies behavior with dynamic permissions.
196TEST_F(BackgroundApplicationListModelTest, AddRemovePermissionsTest) {
197  InitializeAndLoadEmptyExtensionService();
198  ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
199      extension_service();
200  ASSERT_TRUE(service);
201  ASSERT_TRUE(service->is_ready());
202  ASSERT_TRUE(service->extensions());
203  ASSERT_TRUE(service->extensions()->is_empty());
204  scoped_ptr<BackgroundApplicationListModel> model(
205      new BackgroundApplicationListModel(profile_.get()));
206  ASSERT_EQ(0U, model->size());
207
208  scoped_refptr<Extension> ext = CreateExtension("extension", false);
209  ASSERT_FALSE(ext->HasAPIPermission(APIPermission::kBackground));
210  scoped_refptr<Extension> bgapp = CreateExtension("application", true);
211  ASSERT_TRUE(bgapp->HasAPIPermission(APIPermission::kBackground));
212  ASSERT_TRUE(service->extensions() != NULL);
213  ASSERT_EQ(0U, service->extensions()->size());
214  ASSERT_EQ(0U, model->size());
215
216  // Add one (non-background) extension and one background application
217  ASSERT_FALSE(IsBackgroundApp(*ext));
218  service->AddExtension(ext);
219  ASSERT_EQ(1U, service->extensions()->size());
220  ASSERT_EQ(0U, model->size());
221  ASSERT_TRUE(IsBackgroundApp(*bgapp));
222  service->AddExtension(bgapp);
223  ASSERT_EQ(2U, service->extensions()->size());
224  ASSERT_EQ(1U, model->size());
225
226  // Change permissions back and forth
227  AddBackgroundPermission(service, ext.get());
228  ASSERT_TRUE(ext->HasAPIPermission(APIPermission::kBackground));
229  ASSERT_EQ(2U, service->extensions()->size());
230  ASSERT_EQ(2U, model->size());
231  RemoveBackgroundPermission(service, bgapp.get());
232  ASSERT_FALSE(bgapp->HasAPIPermission(APIPermission::kBackground));
233  ASSERT_EQ(2U, service->extensions()->size());
234  ASSERT_EQ(1U, model->size());
235  RemoveBackgroundPermission(service, ext.get());
236  ASSERT_FALSE(ext->HasAPIPermission(APIPermission::kBackground));
237  ASSERT_EQ(2U, service->extensions()->size());
238  ASSERT_EQ(0U, model->size());
239  AddBackgroundPermission(service, bgapp.get());
240  ASSERT_TRUE(bgapp->HasAPIPermission(APIPermission::kBackground));
241  ASSERT_EQ(2U, service->extensions()->size());
242  ASSERT_EQ(1U, model->size());
243}
244
245typedef std::set<scoped_refptr<Extension> > ExtensionCollection;
246
247namespace {
248void AddExtension(ExtensionService* service,
249                  ExtensionCollection* extensions,
250                  BackgroundApplicationListModel* model,
251                  size_t* expected,
252                  size_t* count) {
253  bool create_background = false;
254  if (rand() % 2) {
255    create_background = true;
256    ++*expected;
257  }
258  scoped_refptr<Extension> extension =
259      CreateExtension(GenerateUniqueExtensionName(), create_background);
260  ASSERT_EQ(BackgroundApplicationListModel::IsBackgroundApp(*extension,
261                                                            service->profile()),
262            create_background);
263  extensions->insert(extension);
264  ++*count;
265  ASSERT_EQ(*count, extensions->size());
266  service->AddExtension(extension);
267  ASSERT_EQ(*count, service->extensions()->size());
268  ASSERT_EQ(*expected, model->size());
269}
270
271void RemoveExtension(ExtensionService* service,
272                     ExtensionCollection* extensions,
273                     BackgroundApplicationListModel* model,
274                     size_t* expected,
275                     size_t* count) {  // Maybe remove an extension.
276  ExtensionCollection::iterator cursor = extensions->begin();
277  if (cursor == extensions->end()) {
278    // Nothing to remove.  Just verify accounting.
279    ASSERT_EQ(0U, *count);
280    ASSERT_EQ(0U, *expected);
281    ASSERT_EQ(0U, service->extensions()->size());
282    ASSERT_EQ(0U, model->size());
283  } else {
284    // Randomly select which extension to remove
285    if (extensions->size() > 1) {
286      int offset = rand() % (extensions->size() - 1);
287      for (int index = 0; index < offset; ++index)
288        ++cursor;
289    }
290    scoped_refptr<Extension> extension = cursor->get();
291    std::string id = extension->id();
292    if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
293                                                        service->profile())) {
294      --*expected;
295    }
296    extensions->erase(cursor);
297    --*count;
298    ASSERT_EQ(*count, extensions->size());
299    service->UninstallExtension(extension->id(), false, NULL);
300    ASSERT_EQ(*count, service->extensions()->size());
301    ASSERT_EQ(*expected, model->size());
302  }
303}
304
305void TogglePermission(ExtensionService* service,
306                      ExtensionCollection* extensions,
307                      BackgroundApplicationListModel* model,
308                      size_t* expected,
309                      size_t* count) {
310  ExtensionCollection::iterator cursor = extensions->begin();
311  if (cursor == extensions->end()) {
312    // Nothing to toggle.  Just verify accounting.
313    ASSERT_EQ(0U, *count);
314    ASSERT_EQ(0U, *expected);
315    ASSERT_EQ(0U, service->extensions()->size());
316    ASSERT_EQ(0U, model->size());
317  } else {
318    // Randomly select which extension to toggle.
319    if (extensions->size() > 1) {
320      int offset = rand() % (extensions->size() - 1);
321      for (int index = 0; index < offset; ++index)
322        ++cursor;
323    }
324    scoped_refptr<Extension> extension = cursor->get();
325    std::string id = extension->id();
326    if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
327                                                        service->profile())) {
328      --*expected;
329      ASSERT_EQ(*count, extensions->size());
330      RemoveBackgroundPermission(service, extension);
331      ASSERT_EQ(*count, service->extensions()->size());
332      ASSERT_EQ(*expected, model->size());
333    } else {
334      ++*expected;
335      ASSERT_EQ(*count, extensions->size());
336      AddBackgroundPermission(service, extension);
337      ASSERT_EQ(*count, service->extensions()->size());
338      ASSERT_EQ(*expected, model->size());
339    }
340  }
341}
342}  // namespace
343
344// Verifies behavior with a pseudo-randomly generated set of actions: Adding and
345// removing extensions, of which some are Background Apps and others are not.
346TEST_F(BackgroundApplicationListModelTest, RandomTest) {
347  InitializeAndLoadEmptyExtensionService();
348  ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
349      extension_service();
350  ASSERT_TRUE(service);
351  ASSERT_TRUE(service->is_ready());
352  ASSERT_TRUE(service->extensions());
353  ASSERT_TRUE(service->extensions()->is_empty());
354  scoped_ptr<BackgroundApplicationListModel> model(
355      new BackgroundApplicationListModel(profile_.get()));
356  ASSERT_EQ(0U, model->size());
357
358  static const int kIterations = 500;
359  ExtensionCollection extensions;
360  size_t count = 0;
361  size_t expected = 0;
362  srand(RANDOM_SEED);
363  for (int index = 0; index < kIterations; ++index) {
364    switch (rand() % 3) {
365      case 0:
366        AddExtension(service, &extensions, model.get(), &expected, &count);
367        break;
368      case 1:
369        RemoveExtension(service, &extensions, model.get(), &expected, &count);
370        break;
371      case 2:
372        TogglePermission(service, &extensions, model.get(), &expected, &count);
373        break;
374      default:
375        NOTREACHED();
376        break;
377    }
378  }
379}
380