Add a webstorePrivate API to show a permission prompt for delegated bundle installs
[chromium-blink-merge.git] / chrome / browser / extensions / shared_module_service_unittest.cc
blobe9b8a53a24747788824d65434a38bebec3759b31
1 // Copyright 2014 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.
5 #include "base/memory/ref_counted.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/strings/string16.h"
8 #include "base/values.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/extension_service_test_base.h"
11 #include "chrome/browser/extensions/pending_extension_manager.h"
12 #include "chrome/browser/extensions/shared_module_service.h"
13 #include "chrome/common/extensions/features/feature_channel.h"
14 #include "components/crx_file/id_util.h"
15 #include "extensions/browser/extension_registry.h"
16 #include "extensions/browser/install_flag.h"
17 #include "extensions/browser/uninstall_reason.h"
18 #include "extensions/common/extension_builder.h"
19 #include "extensions/common/value_builder.h"
20 #include "sync/api/string_ordinal.h"
22 namespace extensions {
24 namespace {
26 // Return an extension with |id| which imports a module with the given
27 // |import_id|.
28 scoped_refptr<Extension> CreateExtensionImportingModule(
29 const std::string& import_id,
30 const std::string& id,
31 const std::string& version) {
32 DictionaryBuilder builder;
33 builder.Set("name", "Has Dependent Modules")
34 .Set("version", version)
35 .Set("manifest_version", 2);
36 if (!import_id.empty()) {
37 builder.Set("import",
38 ListBuilder().Append(DictionaryBuilder().Set("id", import_id)));
40 scoped_ptr<base::DictionaryValue> manifest = builder.Build();
42 return ExtensionBuilder().SetManifest(manifest.Pass())
43 .AddFlags(Extension::FROM_WEBSTORE)
44 .SetID(id)
45 .Build();
48 } // namespace
50 class SharedModuleServiceUnitTest : public ExtensionServiceTestBase {
51 public:
52 SharedModuleServiceUnitTest() :
53 // The "export" key is open for dev-channel only, but unit tests
54 // run as stable channel on the official Windows build.
55 current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) {}
56 protected:
57 void SetUp() override;
59 // Install an extension and notify the ExtensionService.
60 testing::AssertionResult InstallExtension(const Extension* extension,
61 bool is_update);
62 ScopedCurrentChannel current_channel_;
65 void SharedModuleServiceUnitTest::SetUp() {
66 ExtensionServiceTestBase::SetUp();
67 InitializeGoodInstalledExtensionService();
68 service()->Init();
71 testing::AssertionResult SharedModuleServiceUnitTest::InstallExtension(
72 const Extension* extension,
73 bool is_update) {
75 const Extension* old = registry()->GetExtensionById(
76 extension->id(),
77 ExtensionRegistry::ENABLED);
79 // Verify the extension is not already installed, if it is not update.
80 if (!is_update) {
81 if (old)
82 return testing::AssertionFailure() << "Extension already installed.";
83 } else {
84 if (!old)
85 return testing::AssertionFailure() << "The extension does not exist.";
88 // Notify the service that the extension is installed. This adds it to the
89 // registry, notifies interested parties, etc.
90 service()->OnExtensionInstalled(
91 extension, syncer::StringOrdinal(), kInstallFlagInstallImmediately);
93 // Verify that the extension is now installed.
94 if (!registry()->GetExtensionById(extension->id(),
95 ExtensionRegistry::ENABLED)) {
96 return testing::AssertionFailure() << "Could not install extension.";
99 return testing::AssertionSuccess();
102 TEST_F(SharedModuleServiceUnitTest, AddDependentSharedModules) {
103 // Create an extension that has a dependency.
104 std::string import_id = crx_file::id_util::GenerateId("id");
105 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
106 scoped_refptr<Extension> extension =
107 CreateExtensionImportingModule(import_id, extension_id, "1.0");
109 PendingExtensionManager* pending_extension_manager =
110 service()->pending_extension_manager();
112 // Verify that we don't currently want to install the imported module.
113 EXPECT_FALSE(pending_extension_manager->IsIdPending(import_id));
115 // Try to satisfy imports for the extension. This should queue the imported
116 // module's installation.
117 service()->shared_module_service()->SatisfyImports(extension.get());
118 EXPECT_TRUE(pending_extension_manager->IsIdPending(import_id));
121 TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUninstall) {
122 // Create a module which exports a resource, and install it.
123 scoped_ptr<base::DictionaryValue> manifest =
124 DictionaryBuilder()
125 .Set("name", "Shared Module")
126 .Set("version", "1.0")
127 .Set("manifest_version", 2)
128 .Set("export",
129 DictionaryBuilder().Set("resources",
130 ListBuilder().Append("foo.js"))).Build();
131 scoped_refptr<Extension> shared_module =
132 ExtensionBuilder()
133 .SetManifest(manifest.Pass())
134 .AddFlags(Extension::FROM_WEBSTORE)
135 .SetID(crx_file::id_util::GenerateId("shared_module"))
136 .Build();
138 EXPECT_TRUE(InstallExtension(shared_module.get(), false));
140 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
141 // Create and install an extension that imports our new module.
142 scoped_refptr<Extension> importing_extension =
143 CreateExtensionImportingModule(shared_module->id(), extension_id, "1.0");
144 EXPECT_TRUE(InstallExtension(importing_extension.get(), false));
146 // Uninstall the extension that imports our module.
147 base::string16 error;
148 service()->UninstallExtension(importing_extension->id(),
149 extensions::UNINSTALL_REASON_FOR_TESTING,
150 base::Bind(&base::DoNothing),
151 &error);
152 EXPECT_TRUE(error.empty());
154 // Since the module was only referenced by that single extension, it should
155 // have been uninstalled as a side-effect of uninstalling the extension that
156 // depended upon it.
157 EXPECT_FALSE(registry()->GetExtensionById(shared_module->id(),
158 ExtensionRegistry::EVERYTHING));
161 TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUpdate) {
162 // Create two modules which export a resource, and install them.
163 scoped_ptr<base::DictionaryValue> manifest_1 =
164 DictionaryBuilder()
165 .Set("name", "Shared Module 1")
166 .Set("version", "1.0")
167 .Set("manifest_version", 2)
168 .Set("export",
169 DictionaryBuilder().Set("resources",
170 ListBuilder().Append("foo.js"))).Build();
171 scoped_refptr<Extension> shared_module_1 =
172 ExtensionBuilder()
173 .SetManifest(manifest_1.Pass())
174 .AddFlags(Extension::FROM_WEBSTORE)
175 .SetID(crx_file::id_util::GenerateId("shared_module_1"))
176 .Build();
177 EXPECT_TRUE(InstallExtension(shared_module_1.get(), false));
179 scoped_ptr<base::DictionaryValue> manifest_2 =
180 DictionaryBuilder()
181 .Set("name", "Shared Module 2")
182 .Set("version", "1.0")
183 .Set("manifest_version", 2)
184 .Set("export",
185 DictionaryBuilder().Set("resources",
186 ListBuilder().Append("foo.js"))).Build();
187 scoped_refptr<Extension> shared_module_2 =
188 ExtensionBuilder()
189 .SetManifest(manifest_2.Pass())
190 .AddFlags(Extension::FROM_WEBSTORE)
191 .SetID(crx_file::id_util::GenerateId("shared_module_2"))
192 .Build();
193 EXPECT_TRUE(InstallExtension(shared_module_2.get(), false));
195 std::string extension_id = crx_file::id_util::GenerateId("extension_id");
197 // Create and install an extension v1.0 that imports our new module 1.
198 scoped_refptr<Extension> importing_extension_1 =
199 CreateExtensionImportingModule(shared_module_1->id(),
200 extension_id,
201 "1.0");
202 EXPECT_TRUE(InstallExtension(importing_extension_1.get(), false));
204 // Create and install a new version of the extension that imports our new
205 // module 2.
206 scoped_refptr<Extension> importing_extension_2 =
207 CreateExtensionImportingModule(shared_module_2->id(),
208 extension_id,
209 "1.1");
210 EXPECT_TRUE(InstallExtension(importing_extension_2.get(), true));
212 // Since the extension v1.1 depends the module 2 insteand module 1.
213 // So the module 1 should be uninstalled.
214 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(),
215 ExtensionRegistry::EVERYTHING));
216 EXPECT_TRUE(registry()->GetExtensionById(shared_module_2->id(),
217 ExtensionRegistry::EVERYTHING));
219 // Create and install a new version of the extension that does not import any
220 // module.
221 scoped_refptr<Extension> importing_extension_3 =
222 CreateExtensionImportingModule("", extension_id, "1.2");
223 EXPECT_TRUE(InstallExtension(importing_extension_3.get(), true));
225 // Since the extension v1.2 does not depend any module, so the all models
226 // should have been uninstalled.
227 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(),
228 ExtensionRegistry::EVERYTHING));
229 EXPECT_FALSE(registry()->GetExtensionById(shared_module_2->id(),
230 ExtensionRegistry::EVERYTHING));
234 TEST_F(SharedModuleServiceUnitTest, WhitelistedImports) {
235 std::string whitelisted_id = crx_file::id_util::GenerateId("whitelisted");
236 std::string nonwhitelisted_id =
237 crx_file::id_util::GenerateId("nonwhitelisted");
238 // Create a module which exports to a restricted whitelist.
239 scoped_ptr<base::DictionaryValue> manifest =
240 DictionaryBuilder()
241 .Set("name", "Shared Module")
242 .Set("version", "1.0")
243 .Set("manifest_version", 2)
244 .Set("export",
245 DictionaryBuilder().Set("whitelist",
246 ListBuilder()
247 .Append(whitelisted_id))
248 .Set("resources",
249 ListBuilder().Append("*"))).Build();
250 scoped_refptr<Extension> shared_module =
251 ExtensionBuilder()
252 .SetManifest(manifest.Pass())
253 .AddFlags(Extension::FROM_WEBSTORE)
254 .SetID(crx_file::id_util::GenerateId("shared_module"))
255 .Build();
257 EXPECT_TRUE(InstallExtension(shared_module.get(), false));
259 // Create and install an extension with the whitelisted ID.
260 scoped_refptr<Extension> whitelisted_extension =
261 CreateExtensionImportingModule(shared_module->id(),
262 whitelisted_id,
263 "1.0");
264 EXPECT_TRUE(InstallExtension(whitelisted_extension.get(), false));
266 // Try to install an extension with an ID that is not whitelisted.
267 scoped_refptr<Extension> nonwhitelisted_extension =
268 CreateExtensionImportingModule(shared_module->id(),
269 nonwhitelisted_id,
270 "1.0");
271 // This should succeed because only CRX installer (and by extension the
272 // WebStore Installer) checks the shared module whitelist. InstallExtension
273 // bypasses the whitelist check because the SharedModuleService does not
274 // care about whitelists.
275 EXPECT_TRUE(InstallExtension(nonwhitelisted_extension.get(), false));
278 } // namespace extensions