1 // Copyright (c) 2011 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_application_list_model.h" 11 12 #include "base/command_line.h" 13 #include "base/file_path.h" 14 #include "base/file_util.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/message_loop.h" 17 #include "base/stl_util-inl.h" 18 #include "chrome/browser/extensions/extension_service.h" 19 #include "chrome/common/extensions/extension.h" 20 #include "chrome/test/testing_profile.h" 21 #include "content/browser/browser_thread.h" 22 #include "content/common/notification_registrar.h" 23 #include "content/common/notification_service.h" 24 #include "content/common/notification_type.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 // This value is used to seed the PRNG at the beginning of a sequence of 28 // operations to produce a repeatable sequence. 29 #define RANDOM_SEED (0x33F7A7A7) 30 31 // For ExtensionService interface when it requires a path that is not used. 32 FilePath bogus_file_path() { 33 return FilePath(FILE_PATH_LITERAL("//foobar_nonexistent")); 34 } 35 36 class BackgroundApplicationListModelTest : public testing::Test { 37 public: 38 BackgroundApplicationListModelTest(); 39 ~BackgroundApplicationListModelTest(); 40 41 virtual void InitializeEmptyExtensionService(); 42 43 protected: 44 scoped_ptr<Profile> profile_; 45 scoped_refptr<ExtensionService> service_; 46 MessageLoop loop_; 47 BrowserThread ui_thread_; 48 }; 49 50 // The message loop may be used in tests which require it to be an IO loop. 51 BackgroundApplicationListModelTest::BackgroundApplicationListModelTest() 52 : loop_(MessageLoop::TYPE_IO), 53 ui_thread_(BrowserThread::UI, &loop_) { 54 } 55 56 BackgroundApplicationListModelTest::~BackgroundApplicationListModelTest() { 57 // Drop reference to ExtensionService and TestingProfile, so that they can be 58 // destroyed while BrowserThreads and MessageLoop are still around. They 59 // are used in the destruction process. 60 service_ = NULL; 61 profile_.reset(NULL); 62 MessageLoop::current()->RunAllPending(); 63 } 64 65 // This is modeled on a similar routine in ExtensionServiceTestBase. 66 void BackgroundApplicationListModelTest::InitializeEmptyExtensionService() { 67 TestingProfile* profile = new TestingProfile(); 68 profile_.reset(profile); 69 service_ = profile->CreateExtensionService( 70 CommandLine::ForCurrentProcess(), 71 bogus_file_path(), false); 72 service_->set_extensions_enabled(true); 73 service_->set_show_extensions_prompts(false); 74 service_->OnLoadedInstalledExtensions(); /* Sends EXTENSIONS_READY */ 75 } 76 77 // Returns a barebones test Extension object with the specified |name|. The 78 // returned extension will include background permission iff 79 // |background_permission| is true. 80 static scoped_refptr<Extension> CreateExtension(const std::string& name, 81 bool background_permission) { 82 DictionaryValue manifest; 83 manifest.SetString(extension_manifest_keys::kVersion, "1.0.0.0"); 84 manifest.SetString(extension_manifest_keys::kName, name); 85 if (background_permission) { 86 ListValue* permissions = new ListValue(); 87 manifest.Set(extension_manifest_keys::kPermissions, permissions); 88 permissions->Append(Value::CreateStringValue("background")); 89 } 90 std::string error; 91 scoped_refptr<Extension> extension = Extension::Create( 92 bogus_file_path().AppendASCII(name), Extension::INVALID, manifest, 93 Extension::STRICT_ERROR_CHECKS, &error); 94 // Cannot ASSERT_* here because that attempts an illegitimate return. 95 // Cannot EXPECT_NE here because that assumes non-pointers unlike EXPECT_EQ 96 EXPECT_TRUE(extension.get() != NULL) << error; 97 return extension; 98 } 99 100 // With minimal test logic, verifies behavior over an explicit set of 101 // extensions, of which some are Background Apps and others are not. 102 TEST_F(BackgroundApplicationListModelTest, LoadExplicitExtensions) { 103 InitializeEmptyExtensionService(); 104 ExtensionService* service = profile_->GetExtensionService(); 105 ASSERT_TRUE(service); 106 ASSERT_TRUE(service->is_ready()); 107 ASSERT_TRUE(service->extensions()); 108 ASSERT_TRUE(service->extensions()->empty()); 109 scoped_ptr<BackgroundApplicationListModel> model( 110 new BackgroundApplicationListModel(profile_.get())); 111 ASSERT_EQ(0U, model->size()); 112 113 scoped_refptr<Extension> ext1 = CreateExtension("alpha", false); 114 scoped_refptr<Extension> ext2 = CreateExtension("bravo", false); 115 scoped_refptr<Extension> ext3 = CreateExtension("charlie", false); 116 scoped_refptr<Extension> bgapp1 = CreateExtension("delta", true); 117 scoped_refptr<Extension> bgapp2 = CreateExtension("echo", true); 118 ASSERT_TRUE(service->extensions() != NULL); 119 ASSERT_EQ(0U, service->extensions()->size()); 120 ASSERT_EQ(0U, model->size()); 121 // Add alternating Extensions and Background Apps 122 ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext1)); 123 service->AddExtension(ext1); 124 ASSERT_EQ(1U, service->extensions()->size()); 125 ASSERT_EQ(0U, model->size()); 126 ASSERT_TRUE(BackgroundApplicationListModel::IsBackgroundApp(*bgapp1)); 127 service->AddExtension(bgapp1); 128 ASSERT_EQ(2U, service->extensions()->size()); 129 ASSERT_EQ(1U, model->size()); 130 ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext2)); 131 service->AddExtension(ext2); 132 ASSERT_EQ(3U, service->extensions()->size()); 133 ASSERT_EQ(1U, model->size()); 134 ASSERT_TRUE(BackgroundApplicationListModel::IsBackgroundApp(*bgapp2)); 135 service->AddExtension(bgapp2); 136 ASSERT_EQ(4U, service->extensions()->size()); 137 ASSERT_EQ(2U, model->size()); 138 ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext3)); 139 service->AddExtension(ext3); 140 ASSERT_EQ(5U, service->extensions()->size()); 141 ASSERT_EQ(2U, model->size()); 142 // Remove in FIFO order. 143 ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext1)); 144 service->UninstallExtension(ext1->id(), false, NULL); 145 ASSERT_EQ(4U, service->extensions()->size()); 146 ASSERT_EQ(2U, model->size()); 147 ASSERT_TRUE(BackgroundApplicationListModel::IsBackgroundApp(*bgapp1)); 148 service->UninstallExtension(bgapp1->id(), false, NULL); 149 ASSERT_EQ(3U, service->extensions()->size()); 150 ASSERT_EQ(1U, model->size()); 151 ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext2)); 152 service->UninstallExtension(ext2->id(), false, NULL); 153 ASSERT_EQ(2U, service->extensions()->size()); 154 ASSERT_EQ(1U, model->size()); 155 ASSERT_TRUE(BackgroundApplicationListModel::IsBackgroundApp(*bgapp2)); 156 service->UninstallExtension(bgapp2->id(), false, NULL); 157 ASSERT_EQ(1U, service->extensions()->size()); 158 ASSERT_EQ(0U, model->size()); 159 ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext3)); 160 service->UninstallExtension(ext3->id(), false, NULL); 161 ASSERT_EQ(0U, service->extensions()->size()); 162 ASSERT_EQ(0U, model->size()); 163 } 164 165 typedef std::set<scoped_refptr<Extension> > ExtensionSet; 166 167 namespace { 168 std::string GenerateUniqueExtensionName() { 169 static int uniqueness = 0; 170 std::ostringstream output; 171 output << "Unique Named Extension " << uniqueness; 172 ++uniqueness; 173 return output.str(); 174 } 175 } 176 177 // Verifies behavior with a pseudo-randomly generated set of actions: Adding and 178 // removing extensions, of which some are Background Apps and others are not. 179 TEST_F(BackgroundApplicationListModelTest, LoadRandomExtension) { 180 InitializeEmptyExtensionService(); 181 ExtensionService* service = profile_->GetExtensionService(); 182 ASSERT_TRUE(service); 183 ASSERT_TRUE(service->is_ready()); 184 ASSERT_TRUE(service->extensions()); 185 ASSERT_TRUE(service->extensions()->empty()); 186 scoped_ptr<BackgroundApplicationListModel> model( 187 new BackgroundApplicationListModel(profile_.get())); 188 ASSERT_EQ(0U, model->size()); 189 190 static const int kIterations = 500; 191 ExtensionSet extensions; 192 size_t count = 0; 193 size_t expected = 0; 194 srand(RANDOM_SEED); 195 for (int index = 0; index < kIterations; ++index) { 196 if (rand() % 2) { // Add an extension 197 std::string name = GenerateUniqueExtensionName(); 198 bool create_background = false; 199 if (rand() % 2) { 200 create_background = true; 201 ++expected; 202 } 203 scoped_refptr<Extension> extension = 204 CreateExtension(name, create_background); 205 ASSERT_EQ(BackgroundApplicationListModel::IsBackgroundApp(*extension), 206 create_background); 207 extensions.insert(extension); 208 ++count; 209 ASSERT_EQ(count, extensions.size()); 210 service->AddExtension(extension); 211 ASSERT_EQ(count, service->extensions()->size()); 212 ASSERT_EQ(expected, model->size()); 213 } else { // Maybe remove an extension. 214 ExtensionSet::iterator cursor = extensions.begin(); 215 if (cursor == extensions.end()) { 216 // Nothing to remove. Just verify accounting. 217 ASSERT_EQ(0U, count); 218 ASSERT_EQ(0U, expected); 219 ASSERT_EQ(0U, service->extensions()->size()); 220 ASSERT_EQ(0U, model->size()); 221 } else { 222 // Randomly select which extension to remove 223 if (extensions.size() > 1) { 224 int offset = rand() % (extensions.size() - 1); 225 for (int index = 0; index < offset; ++index) 226 ++cursor; 227 } 228 scoped_refptr<Extension> extension = cursor->get(); 229 std::string id = extension->id(); 230 if (BackgroundApplicationListModel::IsBackgroundApp(*extension)) 231 --expected; 232 extensions.erase(cursor); 233 --count; 234 ASSERT_EQ(count, extensions.size()); 235 service->UninstallExtension(extension->id(), false, NULL); 236 ASSERT_EQ(count, service->extensions()->size()); 237 ASSERT_EQ(expected, model->size()); 238 } 239 } 240 } 241 } 242