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