OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 "chrome/browser/extensions/extension_service_test_with_install.h" | |
6 | |
7 #include "base/files/file_util.h" | |
8 #include "base/strings/utf_string_conversions.h" | |
9 #include "chrome/browser/extensions/crx_installer.h" | |
10 #include "chrome/browser/extensions/extension_creator.h" | |
11 #include "chrome/browser/extensions/extension_error_reporter.h" | |
12 #include "chrome/browser/profiles/profile.h" | |
13 #include "content/public/browser/notification_service.h" | |
14 #include "extensions/browser/extension_registry.h" | |
15 #include "extensions/browser/notification_types.h" | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 | |
18 namespace extensions { | |
19 | |
20 namespace { | |
21 | |
22 struct ExtensionsOrder { | |
23 bool operator()(const scoped_refptr<const Extension>& a, | |
24 const scoped_refptr<const Extension>& b) { | |
25 return a->name() < b->name(); | |
26 } | |
27 }; | |
28 | |
29 // Helper method to set up a WindowedNotificationObserver to wait for a | |
30 // specific CrxInstaller to finish if we don't know the value of the | |
31 // |installer| yet. | |
32 bool IsCrxInstallerDone(extensions::CrxInstaller** installer, | |
33 const content::NotificationSource& source, | |
34 const content::NotificationDetails& details) { | |
35 return content::Source<extensions::CrxInstaller>(source).ptr() == *installer; | |
36 } | |
37 | |
38 } // namespace | |
39 | |
40 ExtensionServiceTestWithInstall::ExtensionServiceTestWithInstall() | |
41 : installed_(nullptr), | |
42 was_update_(false), | |
43 unloaded_reason_(UnloadedExtensionInfo::REASON_UNDEFINED), | |
44 expected_extensions_count_(0){ | |
45 registrar_.Add(this, | |
46 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, | |
Devlin
2015/10/20 17:42:15
If you don't mind the slight extra churn, making t
Marc Treib
2015/10/21 12:51:53
Yes, let's try and keep this CL to trivial changes
| |
47 content::NotificationService::AllSources()); | |
48 registrar_.Add(this, | |
49 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, | |
50 content::NotificationService::AllSources()); | |
51 registrar_.Add( | |
52 this, | |
53 extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED, | |
54 content::NotificationService::AllSources()); | |
55 } | |
56 | |
57 ExtensionServiceTestWithInstall::~ExtensionServiceTestWithInstall() {} | |
58 | |
59 // static | |
60 std::vector<base::string16> ExtensionServiceTestWithInstall::GetErrors() { | |
61 const std::vector<base::string16>* errors = | |
62 ExtensionErrorReporter::GetInstance()->GetErrors(); | |
63 std::vector<base::string16> ret_val; | |
64 | |
65 for (std::vector<base::string16>::const_iterator iter = errors->begin(); | |
Devlin
2015/10/20 17:42:15
range-based
Marc Treib
2015/10/21 12:51:53
Done.
| |
66 iter != errors->end(); ++iter) { | |
67 std::string utf8_error = base::UTF16ToUTF8(*iter); | |
68 if (utf8_error.find(".svn") == std::string::npos) { | |
69 ret_val.push_back(*iter); | |
70 } | |
71 } | |
72 | |
73 // The tests rely on the errors being in a certain order, which can vary | |
74 // depending on how filesystem iteration works. | |
75 std::stable_sort(ret_val.begin(), ret_val.end()); | |
76 | |
77 return ret_val; | |
78 } | |
79 | |
80 void ExtensionServiceTestWithInstall::PackCRX(const base::FilePath& dir_path, | |
81 const base::FilePath& pem_path, | |
82 const base::FilePath& crx_path) { | |
83 // Use the existing pem key, if provided. | |
84 base::FilePath pem_output_path; | |
85 if (pem_path.value().empty()) { | |
86 pem_output_path = crx_path.DirName().AppendASCII("temp.pem"); | |
87 } else { | |
88 ASSERT_TRUE(base::PathExists(pem_path)); | |
89 } | |
Devlin
2015/10/20 17:42:15
indentation
Marc Treib
2015/10/21 12:51:54
Done.
| |
90 | |
91 ASSERT_TRUE(base::DeleteFile(crx_path, false)); | |
92 | |
93 scoped_ptr<ExtensionCreator> creator(new ExtensionCreator()); | |
94 ASSERT_TRUE(creator->Run(dir_path, | |
95 crx_path, | |
96 pem_path, | |
97 pem_output_path, | |
98 ExtensionCreator::kOverwriteCRX)); | |
99 | |
100 ASSERT_TRUE(base::PathExists(crx_path)); | |
101 } | |
102 | |
103 const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( | |
104 const base::FilePath& dir_path, | |
105 const base::FilePath& pem_path, | |
106 InstallState install_state, | |
107 int creation_flags) { | |
108 base::FilePath crx_path; | |
109 base::ScopedTempDir temp_dir; | |
110 EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); | |
111 crx_path = temp_dir.path().AppendASCII("temp.crx"); | |
112 | |
113 PackCRX(dir_path, pem_path, crx_path); | |
114 return InstallCRX(crx_path, install_state, creation_flags); | |
115 } | |
116 | |
117 const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( | |
118 const base::FilePath& dir_path, | |
119 const base::FilePath& pem_path, | |
120 InstallState install_state) { | |
121 return PackAndInstallCRX(dir_path, pem_path, install_state, | |
122 Extension::NO_FLAGS); | |
123 } | |
124 | |
125 const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( | |
126 const base::FilePath& dir_path, | |
127 InstallState install_state) { | |
128 return PackAndInstallCRX(dir_path, base::FilePath(), install_state, | |
129 Extension::NO_FLAGS); | |
130 } | |
131 | |
132 // Attempts to install an extension. Use INSTALL_FAILED if the installation | |
133 // is expected to fail. | |
134 // If |install_state| is INSTALL_UPDATED, and |expected_old_name| is | |
135 // non-empty, expects that the existing extension's title was | |
136 // |expected_old_name|. | |
137 const Extension* ExtensionServiceTestWithInstall::InstallCRX( | |
138 const base::FilePath& path, | |
139 InstallState install_state, | |
140 int creation_flags, | |
141 const std::string& expected_old_name) { | |
142 InstallCRXInternal(path, creation_flags); | |
143 return VerifyCrxInstall(path, install_state, expected_old_name); | |
144 } | |
145 | |
146 // Attempts to install an extension. Use INSTALL_FAILED if the installation | |
147 // is expected to fail. | |
148 const Extension* ExtensionServiceTestWithInstall::InstallCRX( | |
149 const base::FilePath& path, | |
150 InstallState install_state, | |
151 int creation_flags) { | |
152 return InstallCRX(path, install_state, creation_flags, std::string()); | |
153 } | |
154 | |
155 // Attempts to install an extension. Use INSTALL_FAILED if the installation | |
156 // is expected to fail. | |
157 const Extension* ExtensionServiceTestWithInstall::InstallCRX( | |
158 const base::FilePath& path, | |
159 InstallState install_state) { | |
160 return InstallCRX(path, install_state, Extension::NO_FLAGS); | |
161 } | |
162 | |
163 const Extension* ExtensionServiceTestWithInstall::InstallCRXFromWebStore( | |
164 const base::FilePath& path, | |
165 InstallState install_state) { | |
166 InstallCRXInternal(path, Extension::FROM_WEBSTORE); | |
167 return VerifyCrxInstall(path, install_state); | |
168 } | |
169 | |
170 const Extension* ExtensionServiceTestWithInstall::InstallCRXWithLocation( | |
171 const base::FilePath& crx_path, | |
172 Manifest::Location install_location, | |
173 InstallState install_state) { | |
174 EXPECT_TRUE(base::PathExists(crx_path)) | |
175 << "Path does not exist: "<< crx_path.value().c_str(); | |
176 // no client (silent install) | |
177 scoped_refptr<CrxInstaller> installer( | |
178 CrxInstaller::CreateSilent(service())); | |
179 installer->set_install_source(install_location); | |
180 | |
181 content::WindowedNotificationObserver observer( | |
182 extensions::NOTIFICATION_CRX_INSTALLER_DONE, | |
183 content::NotificationService::AllSources()); | |
184 installer->InstallCrx(crx_path); | |
185 observer.Wait(); | |
186 | |
187 return VerifyCrxInstall(crx_path, install_state); | |
188 } | |
189 | |
190 // Verifies the result of a CRX installation. Used by InstallCRX. Set the | |
Devlin
2015/10/20 17:42:15
Seems like these comments should go in the .h?
Marc Treib
2015/10/21 12:51:54
Indeed. Done.
| |
191 // |install_state| to INSTALL_FAILED if the installation is expected to fail. | |
192 // Returns an Extension pointer if the install succeeded, NULL otherwise. | |
193 const Extension* ExtensionServiceTestWithInstall::VerifyCrxInstall( | |
194 const base::FilePath& path, | |
195 InstallState install_state) { | |
196 return VerifyCrxInstall(path, install_state, std::string()); | |
197 } | |
198 | |
199 // Verifies the result of a CRX installation. Used by InstallCRX. Set the | |
200 // |install_state| to INSTALL_FAILED if the installation is expected to fail. | |
201 // If |install_state| is INSTALL_UPDATED, and |expected_old_name| is | |
202 // non-empty, expects that the existing extension's title was | |
203 // |expected_old_name|. | |
204 // Returns an Extension pointer if the install succeeded, NULL otherwise. | |
Devlin
2015/10/20 17:42:15
nit: s/NULL/nullptr||null
Marc Treib
2015/10/21 12:51:53
Done.
| |
205 const Extension* ExtensionServiceTestWithInstall::VerifyCrxInstall( | |
206 const base::FilePath& path, | |
207 InstallState install_state, | |
208 const std::string& expected_old_name) { | |
209 std::vector<base::string16> errors = GetErrors(); | |
210 const Extension* extension = NULL; | |
211 if (install_state != INSTALL_FAILED) { | |
212 if (install_state == INSTALL_NEW) | |
213 ++expected_extensions_count_; | |
214 | |
215 EXPECT_TRUE(installed_) << path.value(); | |
216 // If and only if INSTALL_UPDATED, it should have the is_update flag. | |
217 EXPECT_EQ(install_state == INSTALL_UPDATED, was_update_) | |
218 << path.value(); | |
219 // If INSTALL_UPDATED, old_name_ should match the given string. | |
220 if (install_state == INSTALL_UPDATED && !expected_old_name.empty()) | |
221 EXPECT_EQ(expected_old_name, old_name_); | |
222 EXPECT_EQ(0u, errors.size()) << path.value(); | |
223 | |
224 if (install_state == INSTALL_WITHOUT_LOAD) { | |
225 EXPECT_EQ(0u, loaded_.size()) << path.value(); | |
226 } else { | |
227 EXPECT_EQ(1u, loaded_.size()) << path.value(); | |
228 size_t actual_extension_count = | |
229 registry()->enabled_extensions().size() + | |
230 registry()->disabled_extensions().size(); | |
231 EXPECT_EQ(expected_extensions_count_, actual_extension_count) << | |
232 path.value(); | |
233 extension = loaded_[0].get(); | |
234 EXPECT_TRUE(service()->GetExtensionById(extension->id(), false)) | |
235 << path.value(); | |
236 } | |
237 | |
238 for (std::vector<base::string16>::iterator err = errors.begin(); | |
239 err != errors.end(); ++err) { | |
240 LOG(ERROR) << *err; | |
241 } | |
242 } else { | |
243 EXPECT_FALSE(installed_) << path.value(); | |
244 EXPECT_EQ(0u, loaded_.size()) << path.value(); | |
245 EXPECT_EQ(1u, errors.size()) << path.value(); | |
246 } | |
247 | |
248 installed_ = NULL; | |
249 was_update_ = false; | |
250 old_name_ = ""; | |
251 loaded_.clear(); | |
252 ExtensionErrorReporter::GetInstance()->ClearErrors(); | |
253 return extension; | |
254 } | |
255 | |
256 void ExtensionServiceTestWithInstall::PackCRXAndUpdateExtension( | |
257 const std::string& id, | |
258 const base::FilePath& dir_path, | |
259 const base::FilePath& pem_path, | |
260 UpdateState expected_state) { | |
261 base::ScopedTempDir temp_dir; | |
262 EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); | |
263 base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx"); | |
264 | |
265 PackCRX(dir_path, pem_path, crx_path); | |
266 UpdateExtension(id, crx_path, expected_state); | |
267 } | |
268 | |
269 void ExtensionServiceTestWithInstall::UpdateExtension( | |
270 const std::string& id, | |
271 const base::FilePath& in_path, | |
272 UpdateState expected_state) { | |
273 ASSERT_TRUE(base::PathExists(in_path)); | |
274 | |
275 // We need to copy this to a temporary location because Update() will delete | |
276 // it. | |
277 base::FilePath path = temp_dir().path(); | |
278 path = path.Append(in_path.BaseName()); | |
279 ASSERT_TRUE(base::CopyFile(in_path, path)); | |
280 | |
281 int previous_enabled_extension_count = | |
282 registry()->enabled_extensions().size(); | |
283 int previous_installed_extension_count = | |
284 previous_enabled_extension_count + | |
285 registry()->disabled_extensions().size(); | |
286 | |
287 extensions::CrxInstaller* installer = NULL; | |
288 content::WindowedNotificationObserver observer( | |
289 extensions::NOTIFICATION_CRX_INSTALLER_DONE, | |
290 base::Bind(&IsCrxInstallerDone, &installer)); | |
291 service()->UpdateExtension(extensions::CRXFileInfo(id, path), true, | |
292 &installer); | |
293 | |
294 if (installer) | |
295 observer.Wait(); | |
296 else | |
297 base::RunLoop().RunUntilIdle(); | |
298 | |
299 std::vector<base::string16> errors = GetErrors(); | |
300 int error_count = errors.size(); | |
301 int enabled_extension_count = registry()->enabled_extensions().size(); | |
302 int installed_extension_count = | |
303 enabled_extension_count + registry()->disabled_extensions().size(); | |
304 | |
305 int expected_error_count = (expected_state == FAILED) ? 1 : 0; | |
306 EXPECT_EQ(expected_error_count, error_count) << path.value(); | |
307 | |
308 if (expected_state <= FAILED) { | |
309 EXPECT_EQ(previous_enabled_extension_count, | |
310 enabled_extension_count); | |
311 EXPECT_EQ(previous_installed_extension_count, | |
312 installed_extension_count); | |
313 } else { | |
314 int expected_installed_extension_count = | |
315 (expected_state >= INSTALLED) ? 1 : 0; | |
316 int expected_enabled_extension_count = | |
317 (expected_state >= ENABLED) ? 1 : 0; | |
318 EXPECT_EQ(expected_installed_extension_count, | |
319 installed_extension_count); | |
320 EXPECT_EQ(expected_enabled_extension_count, | |
321 enabled_extension_count); | |
322 } | |
323 | |
324 // Update() should the temporary input file. | |
325 EXPECT_FALSE(base::PathExists(path)); | |
326 } | |
327 | |
328 void ExtensionServiceTestWithInstall::UninstallExtension(const std::string& id, | |
329 bool use_helper) { | |
330 UninstallExtension(id, use_helper, Extension::ENABLED); | |
331 } | |
332 | |
333 void ExtensionServiceTestWithInstall::UninstallExtension( | |
334 const std::string& id, | |
335 bool use_helper, | |
336 Extension::State expected_state) { | |
337 // Verify that the extension is installed. | |
338 base::FilePath extension_path = extensions_install_dir().AppendASCII(id); | |
339 EXPECT_TRUE(base::PathExists(extension_path)); | |
340 size_t pref_key_count = GetPrefKeyCount(); | |
341 EXPECT_GT(pref_key_count, 0u); | |
342 ValidateIntegerPref(id, "state", expected_state); | |
343 | |
344 // Uninstall it. | |
345 if (use_helper) { | |
346 EXPECT_TRUE(ExtensionService::UninstallExtensionHelper( | |
347 service(), id, extensions::UNINSTALL_REASON_FOR_TESTING)); | |
348 } else { | |
349 EXPECT_TRUE(service()->UninstallExtension( | |
350 id, | |
351 extensions::UNINSTALL_REASON_FOR_TESTING, | |
352 base::Bind(&base::DoNothing), | |
353 NULL)); | |
354 } | |
355 --expected_extensions_count_; | |
356 | |
357 // We should get an unload notification. | |
358 EXPECT_FALSE(unloaded_id_.empty()); | |
359 EXPECT_EQ(id, unloaded_id_); | |
360 | |
361 // Verify uninstalled state. | |
362 size_t new_pref_key_count = GetPrefKeyCount(); | |
363 if (new_pref_key_count == pref_key_count) { | |
364 ValidateIntegerPref(id, "state", | |
365 Extension::EXTERNAL_EXTENSION_UNINSTALLED); | |
366 } else { | |
367 EXPECT_EQ(new_pref_key_count, pref_key_count - 1); | |
368 } | |
369 | |
370 // The extension should not be in the service anymore. | |
371 EXPECT_FALSE(service()->GetInstalledExtension(id)); | |
372 base::RunLoop().RunUntilIdle(); | |
373 | |
374 // The directory should be gone. | |
375 EXPECT_FALSE(base::PathExists(extension_path)); | |
376 } | |
377 | |
378 void ExtensionServiceTestWithInstall::TerminateExtension( | |
379 const std::string& id) { | |
380 const Extension* extension = service()->GetInstalledExtension(id); | |
381 if (!extension) { | |
382 ADD_FAILURE(); | |
383 return; | |
384 } | |
385 service()->TrackTerminatedExtensionForTest(extension); | |
386 } | |
387 | |
388 void ExtensionServiceTestWithInstall::Observe( | |
389 int type, | |
390 const content::NotificationSource& source, | |
391 const content::NotificationDetails& details) { | |
392 switch (type) { | |
393 case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: { | |
394 const Extension* extension = | |
395 content::Details<const Extension>(details).ptr(); | |
396 loaded_.push_back(make_scoped_refptr(extension)); | |
397 // The tests rely on the errors being in a certain order, which can vary | |
398 // depending on how filesystem iteration works. | |
399 std::stable_sort(loaded_.begin(), loaded_.end(), ExtensionsOrder()); | |
400 break; | |
401 } | |
402 | |
403 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: { | |
404 UnloadedExtensionInfo* unloaded_info = | |
405 content::Details<UnloadedExtensionInfo>(details).ptr(); | |
406 const Extension* e = unloaded_info->extension; | |
407 unloaded_id_ = e->id(); | |
408 unloaded_reason_ = unloaded_info->reason; | |
409 extensions::ExtensionList::iterator i = | |
410 std::find(loaded_.begin(), loaded_.end(), e); | |
411 // TODO(erikkay) fix so this can be an assert. Right now the tests | |
412 // are manually calling clear() on loaded_, so this isn't doable. | |
413 if (i == loaded_.end()) | |
414 return; | |
415 loaded_.erase(i); | |
416 break; | |
417 } | |
418 case extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED: { | |
419 const extensions::InstalledExtensionInfo* installed_info = | |
420 content::Details<const extensions::InstalledExtensionInfo>(details) | |
421 .ptr(); | |
422 installed_ = installed_info->extension; | |
423 was_update_ = installed_info->is_update; | |
424 old_name_ = installed_info->old_name; | |
425 break; | |
426 } | |
427 | |
428 default: | |
429 DCHECK(false); | |
430 } | |
431 } | |
432 | |
433 // Create a CrxInstaller and install the CRX file. | |
434 // Instead of calling this method yourself, use InstallCRX(), which does extra | |
435 // error checking. | |
436 void ExtensionServiceTestWithInstall::InstallCRXInternal( | |
437 const base::FilePath& crx_path, | |
438 int creation_flags) { | |
439 ASSERT_TRUE(base::PathExists(crx_path)) | |
440 << "Path does not exist: "<< crx_path.value().c_str(); | |
441 scoped_refptr<CrxInstaller> installer( | |
442 CrxInstaller::CreateSilent(service())); | |
443 installer->set_creation_flags(creation_flags); | |
444 if (!(creation_flags & Extension::WAS_INSTALLED_BY_DEFAULT)) | |
445 installer->set_allow_silent_install(true); | |
446 | |
447 content::WindowedNotificationObserver observer( | |
448 extensions::NOTIFICATION_CRX_INSTALLER_DONE, | |
449 content::Source<extensions::CrxInstaller>(installer.get())); | |
450 | |
451 installer->InstallCrx(crx_path); | |
452 | |
453 observer.Wait(); | |
454 } | |
455 | |
456 } // namespace extensions | |
OLD | NEW |