blob: 5f223fb59484bb0045d3c35fab07f3c3c304a49a [file] [log] [blame]
[email protected]90878c52014-04-04 18:21:021// 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.
4
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"
[email protected]f484f8d52014-06-12 08:38:1810#include "chrome/browser/extensions/extension_service_test_base.h"
[email protected]90878c52014-04-04 18:21:0211#include "chrome/browser/extensions/pending_extension_manager.h"
12#include "chrome/browser/extensions/shared_module_service.h"
[email protected]f043dba82014-05-09 17:13:4813#include "chrome/common/extensions/features/feature_channel.h"
[email protected]90878c52014-04-04 18:21:0214#include "extensions/browser/extension_registry.h"
[email protected]4a1d9c0d2014-06-13 12:50:1115#include "extensions/browser/install_flag.h"
[email protected]e43c61f2014-07-20 21:46:3416#include "extensions/browser/uninstall_reason.h"
[email protected]90878c52014-04-04 18:21:0217#include "extensions/common/extension_builder.h"
18#include "extensions/common/id_util.h"
19#include "extensions/common/value_builder.h"
20#include "sync/api/string_ordinal.h"
21
22namespace extensions {
23
24namespace {
25
[email protected]897147a2014-05-02 22:28:0426// Return an extension with |id| which imports a module with the given
27// |import_id|.
[email protected]90878c52014-04-04 18:21:0228scoped_refptr<Extension> CreateExtensionImportingModule(
[email protected]38e872532014-07-16 23:27:5129 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)));
39 }
40 scoped_ptr<base::DictionaryValue> manifest = builder.Build();
[email protected]90878c52014-04-04 18:21:0241
42 return ExtensionBuilder().SetManifest(manifest.Pass())
43 .AddFlags(Extension::FROM_WEBSTORE)
[email protected]897147a2014-05-02 22:28:0444 .SetID(id)
[email protected]90878c52014-04-04 18:21:0245 .Build();
46}
47
48} // namespace
49
50class SharedModuleServiceUnitTest : public ExtensionServiceTestBase {
[email protected]f043dba82014-05-09 17:13:4851 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) {}
[email protected]90878c52014-04-04 18:21:0256 protected:
57 virtual void SetUp() OVERRIDE;
58
59 // Install an extension and notify the ExtensionService.
[email protected]38e872532014-07-16 23:27:5160 testing::AssertionResult InstallExtension(const Extension* extension,
61 bool is_update);
[email protected]f043dba82014-05-09 17:13:4862 ScopedCurrentChannel current_channel_;
[email protected]90878c52014-04-04 18:21:0263};
64
65void SharedModuleServiceUnitTest::SetUp() {
66 ExtensionServiceTestBase::SetUp();
67 InitializeGoodInstalledExtensionService();
[email protected]38e872532014-07-16 23:27:5168 service()->Init();
[email protected]90878c52014-04-04 18:21:0269}
70
71testing::AssertionResult SharedModuleServiceUnitTest::InstallExtension(
[email protected]38e872532014-07-16 23:27:5172 const Extension* extension,
73 bool is_update) {
74
75 const Extension* old = registry()->GetExtensionById(
76 extension->id(),
77 ExtensionRegistry::ENABLED);
78
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.";
[email protected]90878c52014-04-04 18:21:0286 }
87
88 // Notify the service that the extension is installed. This adds it to the
89 // registry, notifies interested parties, etc.
[email protected]38e872532014-07-16 23:27:5190 service()->OnExtensionInstalled(
[email protected]4a1d9c0d2014-06-13 12:50:1191 extension, syncer::StringOrdinal(), kInstallFlagInstallImmediately);
[email protected]90878c52014-04-04 18:21:0292
93 // Verify that the extension is now installed.
[email protected]f484f8d52014-06-12 08:38:1894 if (!registry()->GetExtensionById(extension->id(),
95 ExtensionRegistry::ENABLED)) {
[email protected]90878c52014-04-04 18:21:0296 return testing::AssertionFailure() << "Could not install extension.";
97 }
98
99 return testing::AssertionSuccess();
100}
101
102TEST_F(SharedModuleServiceUnitTest, AddDependentSharedModules) {
103 // Create an extension that has a dependency.
104 std::string import_id = id_util::GenerateId("id");
[email protected]897147a2014-05-02 22:28:04105 std::string extension_id = id_util::GenerateId("extension_id");
[email protected]90878c52014-04-04 18:21:02106 scoped_refptr<Extension> extension =
[email protected]38e872532014-07-16 23:27:51107 CreateExtensionImportingModule(import_id, extension_id, "1.0");
[email protected]90878c52014-04-04 18:21:02108
109 PendingExtensionManager* pending_extension_manager =
[email protected]38e872532014-07-16 23:27:51110 service()->pending_extension_manager();
[email protected]90878c52014-04-04 18:21:02111
112 // Verify that we don't currently want to install the imported module.
113 EXPECT_FALSE(pending_extension_manager->IsIdPending(import_id));
114
115 // Try to satisfy imports for the extension. This should queue the imported
116 // module's installation.
[email protected]38e872532014-07-16 23:27:51117 service()->shared_module_service()->SatisfyImports(extension);
[email protected]90878c52014-04-04 18:21:02118 EXPECT_TRUE(pending_extension_manager->IsIdPending(import_id));
119}
120
121TEST_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().SetManifest(manifest.Pass())
133 .AddFlags(Extension::FROM_WEBSTORE)
134 .SetID(id_util::GenerateId("shared_module"))
135 .Build();
136
[email protected]38e872532014-07-16 23:27:51137 EXPECT_TRUE(InstallExtension(shared_module, false));
[email protected]90878c52014-04-04 18:21:02138
[email protected]897147a2014-05-02 22:28:04139 std::string extension_id = id_util::GenerateId("extension_id");
[email protected]90878c52014-04-04 18:21:02140 // Create and install an extension that imports our new module.
141 scoped_refptr<Extension> importing_extension =
[email protected]38e872532014-07-16 23:27:51142 CreateExtensionImportingModule(shared_module->id(), extension_id, "1.0");
143 EXPECT_TRUE(InstallExtension(importing_extension, false));
[email protected]90878c52014-04-04 18:21:02144
145 // Uninstall the extension that imports our module.
146 base::string16 error;
[email protected]38e872532014-07-16 23:27:51147 service()->UninstallExtension(importing_extension->id(),
[email protected]e43c61f2014-07-20 21:46:34148 extensions::UNINSTALL_REASON_FOR_TESTING,
[email protected]42d58f62014-07-31 01:32:45149 base::Bind(&base::DoNothing),
[email protected]e43c61f2014-07-20 21:46:34150 &error);
[email protected]90878c52014-04-04 18:21:02151 EXPECT_TRUE(error.empty());
152
153 // Since the module was only referenced by that single extension, it should
154 // have been uninstalled as a side-effect of uninstalling the extension that
155 // depended upon it.
[email protected]f484f8d52014-06-12 08:38:18156 EXPECT_FALSE(registry()->GetExtensionById(shared_module->id(),
157 ExtensionRegistry::EVERYTHING));
[email protected]90878c52014-04-04 18:21:02158}
159
[email protected]38e872532014-07-16 23:27:51160TEST_F(SharedModuleServiceUnitTest, PruneSharedModulesOnUpdate) {
161 // Create two modules which export a resource, and install them.
162 scoped_ptr<base::DictionaryValue> manifest_1 =
163 DictionaryBuilder()
164 .Set("name", "Shared Module 1")
165 .Set("version", "1.0")
166 .Set("manifest_version", 2)
167 .Set("export",
168 DictionaryBuilder().Set("resources",
169 ListBuilder().Append("foo.js"))).Build();
170 scoped_refptr<Extension> shared_module_1 =
171 ExtensionBuilder().SetManifest(manifest_1.Pass())
172 .AddFlags(Extension::FROM_WEBSTORE)
173 .SetID(id_util::GenerateId("shared_module_1"))
174 .Build();
175 EXPECT_TRUE(InstallExtension(shared_module_1, false));
176
177 scoped_ptr<base::DictionaryValue> manifest_2 =
178 DictionaryBuilder()
179 .Set("name", "Shared Module 2")
180 .Set("version", "1.0")
181 .Set("manifest_version", 2)
182 .Set("export",
183 DictionaryBuilder().Set("resources",
184 ListBuilder().Append("foo.js"))).Build();
185 scoped_refptr<Extension> shared_module_2 =
186 ExtensionBuilder().SetManifest(manifest_2.Pass())
187 .AddFlags(Extension::FROM_WEBSTORE)
188 .SetID(id_util::GenerateId("shared_module_2"))
189 .Build();
190 EXPECT_TRUE(InstallExtension(shared_module_2, false));
191
192 std::string extension_id = id_util::GenerateId("extension_id");
193
194 // Create and install an extension v1.0 that imports our new module 1.
195 scoped_refptr<Extension> importing_extension_1 =
196 CreateExtensionImportingModule(shared_module_1->id(),
197 extension_id,
198 "1.0");
199 EXPECT_TRUE(InstallExtension(importing_extension_1, false));
200
201 // Create and install a new version of the extension that imports our new
202 // module 2.
203 scoped_refptr<Extension> importing_extension_2 =
204 CreateExtensionImportingModule(shared_module_2->id(),
205 extension_id,
206 "1.1");
207 EXPECT_TRUE(InstallExtension(importing_extension_2, true));
208
209 // Since the extension v1.1 depends the module 2 insteand module 1.
210 // So the module 1 should be uninstalled.
211 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(),
212 ExtensionRegistry::EVERYTHING));
213 EXPECT_TRUE(registry()->GetExtensionById(shared_module_2->id(),
214 ExtensionRegistry::EVERYTHING));
215
216 // Create and install a new version of the extension that does not import any
217 // module.
218 scoped_refptr<Extension> importing_extension_3 =
219 CreateExtensionImportingModule("", extension_id, "1.2");
220 EXPECT_TRUE(InstallExtension(importing_extension_3, true));
221
222 // Since the extension v1.2 does not depend any module, so the all models
223 // should have been uninstalled.
224 EXPECT_FALSE(registry()->GetExtensionById(shared_module_1->id(),
225 ExtensionRegistry::EVERYTHING));
226 EXPECT_FALSE(registry()->GetExtensionById(shared_module_2->id(),
227 ExtensionRegistry::EVERYTHING));
228
229}
230
[email protected]f043dba82014-05-09 17:13:48231TEST_F(SharedModuleServiceUnitTest, WhitelistedImports) {
[email protected]897147a2014-05-02 22:28:04232 std::string whitelisted_id = id_util::GenerateId("whitelisted");
233 std::string nonwhitelisted_id = id_util::GenerateId("nonwhitelisted");
234 // Create a module which exports to a restricted whitelist.
235 scoped_ptr<base::DictionaryValue> manifest =
236 DictionaryBuilder()
237 .Set("name", "Shared Module")
238 .Set("version", "1.0")
239 .Set("manifest_version", 2)
240 .Set("export",
241 DictionaryBuilder().Set("whitelist",
242 ListBuilder()
243 .Append(whitelisted_id))
244 .Set("resources",
245 ListBuilder().Append("*"))).Build();
246 scoped_refptr<Extension> shared_module =
247 ExtensionBuilder().SetManifest(manifest.Pass())
248 .AddFlags(Extension::FROM_WEBSTORE)
249 .SetID(id_util::GenerateId("shared_module"))
250 .Build();
251
[email protected]38e872532014-07-16 23:27:51252 EXPECT_TRUE(InstallExtension(shared_module, false));
[email protected]897147a2014-05-02 22:28:04253
254 // Create and install an extension with the whitelisted ID.
255 scoped_refptr<Extension> whitelisted_extension =
[email protected]38e872532014-07-16 23:27:51256 CreateExtensionImportingModule(shared_module->id(),
257 whitelisted_id,
258 "1.0");
259 EXPECT_TRUE(InstallExtension(whitelisted_extension, false));
[email protected]897147a2014-05-02 22:28:04260
261 // Try to install an extension with an ID that is not whitelisted.
262 scoped_refptr<Extension> nonwhitelisted_extension =
[email protected]38e872532014-07-16 23:27:51263 CreateExtensionImportingModule(shared_module->id(),
264 nonwhitelisted_id,
265 "1.0");
266 EXPECT_FALSE(InstallExtension(nonwhitelisted_extension, false));
[email protected]897147a2014-05-02 22:28:04267}
268
[email protected]90878c52014-04-04 18:21:02269} // namespace extensions