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_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 34 using extensions::APIPermission; 35 using extensions::Extension; 36 37 // For ExtensionService interface when it requires a path that is not used. 38 base::FilePath bogus_file_path() { 39 return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent")); 40 } 41 42 class 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. 62 static 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 85 namespace { 86 std::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 94 void 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 109 void RemoveBackgroundPermission(ExtensionService* service, 110 Extension* extension) { 111 if (!BackgroundApplicationListModel::IsBackgroundApp(*extension, 112 service->profile())) { 113 return; 114 } 115 extensions::PermissionsUpdater(service->profile()) 116 .RemovePermissions(extension, extension->GetActivePermissions().get()); 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. 129 TEST_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.get())); 152 service->AddExtension(ext1.get()); 153 ASSERT_EQ(1U, service->extensions()->size()); 154 ASSERT_EQ(0U, model->size()); 155 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get())); 156 service->AddExtension(bgapp1.get()); 157 ASSERT_EQ(2U, service->extensions()->size()); 158 ASSERT_EQ(1U, model->size()); 159 ASSERT_FALSE(IsBackgroundApp(*ext2.get())); 160 service->AddExtension(ext2.get()); 161 ASSERT_EQ(3U, service->extensions()->size()); 162 ASSERT_EQ(1U, model->size()); 163 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get())); 164 service->AddExtension(bgapp2.get()); 165 ASSERT_EQ(4U, service->extensions()->size()); 166 ASSERT_EQ(2U, model->size()); 167 ASSERT_FALSE(IsBackgroundApp(*ext3.get())); 168 service->AddExtension(ext3.get()); 169 ASSERT_EQ(5U, service->extensions()->size()); 170 ASSERT_EQ(2U, model->size()); 171 172 // Remove in FIFO order. 173 ASSERT_FALSE(IsBackgroundApp(*ext1.get())); 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.get())); 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.get())); 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.get())); 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.get())); 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. 196 TEST_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.get())); 218 service->AddExtension(ext.get()); 219 ASSERT_EQ(1U, service->extensions()->size()); 220 ASSERT_EQ(0U, model->size()); 221 ASSERT_TRUE(IsBackgroundApp(*bgapp.get())); 222 service->AddExtension(bgapp.get()); 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 245 typedef std::set<scoped_refptr<Extension> > ExtensionCollection; 246 247 namespace { 248 void 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.get(), 261 service->profile()), 262 create_background); 263 extensions->insert(extension); 264 ++*count; 265 ASSERT_EQ(*count, extensions->size()); 266 service->AddExtension(extension.get()); 267 ASSERT_EQ(*count, service->extensions()->size()); 268 ASSERT_EQ(*expected, model->size()); 269 } 270 271 void 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.get(), 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 305 void 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.get(), 327 service->profile())) { 328 --*expected; 329 ASSERT_EQ(*count, extensions->size()); 330 RemoveBackgroundPermission(service, extension.get()); 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.get()); 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. 346 TEST_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 = 20; 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