Index: chrome/browser/extensions/extension_service_test_with_install.cc |
diff --git a/chrome/browser/extensions/extension_service_test_with_install.cc b/chrome/browser/extensions/extension_service_test_with_install.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..82f863bc30398d34e0f431af32480e9793f357c1 |
--- /dev/null |
+++ b/chrome/browser/extensions/extension_service_test_with_install.cc |
@@ -0,0 +1,456 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/extensions/extension_service_test_with_install.h" |
+ |
+#include "base/files/file_util.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "chrome/browser/extensions/crx_installer.h" |
+#include "chrome/browser/extensions/extension_creator.h" |
+#include "chrome/browser/extensions/extension_error_reporter.h" |
+#include "chrome/browser/profiles/profile.h" |
+#include "content/public/browser/notification_service.h" |
+#include "extensions/browser/extension_registry.h" |
+#include "extensions/browser/notification_types.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace extensions { |
+ |
+namespace { |
+ |
+struct ExtensionsOrder { |
+ bool operator()(const scoped_refptr<const Extension>& a, |
+ const scoped_refptr<const Extension>& b) { |
+ return a->name() < b->name(); |
+ } |
+}; |
+ |
+// Helper method to set up a WindowedNotificationObserver to wait for a |
+// specific CrxInstaller to finish if we don't know the value of the |
+// |installer| yet. |
+bool IsCrxInstallerDone(extensions::CrxInstaller** installer, |
+ const content::NotificationSource& source, |
+ const content::NotificationDetails& details) { |
+ return content::Source<extensions::CrxInstaller>(source).ptr() == *installer; |
+} |
+ |
+} // namespace |
+ |
+ExtensionServiceTestWithInstall::ExtensionServiceTestWithInstall() |
+ : installed_(nullptr), |
+ was_update_(false), |
+ unloaded_reason_(UnloadedExtensionInfo::REASON_UNDEFINED), |
+ expected_extensions_count_(0){ |
+ registrar_.Add(this, |
+ 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
|
+ content::NotificationService::AllSources()); |
+ registrar_.Add(this, |
+ extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, |
+ content::NotificationService::AllSources()); |
+ registrar_.Add( |
+ this, |
+ extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED, |
+ content::NotificationService::AllSources()); |
+} |
+ |
+ExtensionServiceTestWithInstall::~ExtensionServiceTestWithInstall() {} |
+ |
+// static |
+std::vector<base::string16> ExtensionServiceTestWithInstall::GetErrors() { |
+ const std::vector<base::string16>* errors = |
+ ExtensionErrorReporter::GetInstance()->GetErrors(); |
+ std::vector<base::string16> ret_val; |
+ |
+ 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.
|
+ iter != errors->end(); ++iter) { |
+ std::string utf8_error = base::UTF16ToUTF8(*iter); |
+ if (utf8_error.find(".svn") == std::string::npos) { |
+ ret_val.push_back(*iter); |
+ } |
+ } |
+ |
+ // The tests rely on the errors being in a certain order, which can vary |
+ // depending on how filesystem iteration works. |
+ std::stable_sort(ret_val.begin(), ret_val.end()); |
+ |
+ return ret_val; |
+} |
+ |
+void ExtensionServiceTestWithInstall::PackCRX(const base::FilePath& dir_path, |
+ const base::FilePath& pem_path, |
+ const base::FilePath& crx_path) { |
+ // Use the existing pem key, if provided. |
+ base::FilePath pem_output_path; |
+ if (pem_path.value().empty()) { |
+ pem_output_path = crx_path.DirName().AppendASCII("temp.pem"); |
+ } else { |
+ ASSERT_TRUE(base::PathExists(pem_path)); |
+} |
Devlin
2015/10/20 17:42:15
indentation
Marc Treib
2015/10/21 12:51:54
Done.
|
+ |
+ ASSERT_TRUE(base::DeleteFile(crx_path, false)); |
+ |
+ scoped_ptr<ExtensionCreator> creator(new ExtensionCreator()); |
+ ASSERT_TRUE(creator->Run(dir_path, |
+ crx_path, |
+ pem_path, |
+ pem_output_path, |
+ ExtensionCreator::kOverwriteCRX)); |
+ |
+ ASSERT_TRUE(base::PathExists(crx_path)); |
+} |
+ |
+const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( |
+ const base::FilePath& dir_path, |
+ const base::FilePath& pem_path, |
+ InstallState install_state, |
+ int creation_flags) { |
+ base::FilePath crx_path; |
+ base::ScopedTempDir temp_dir; |
+ EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); |
+ crx_path = temp_dir.path().AppendASCII("temp.crx"); |
+ |
+ PackCRX(dir_path, pem_path, crx_path); |
+ return InstallCRX(crx_path, install_state, creation_flags); |
+} |
+ |
+const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( |
+ const base::FilePath& dir_path, |
+ const base::FilePath& pem_path, |
+ InstallState install_state) { |
+ return PackAndInstallCRX(dir_path, pem_path, install_state, |
+ Extension::NO_FLAGS); |
+} |
+ |
+const Extension* ExtensionServiceTestWithInstall::PackAndInstallCRX( |
+ const base::FilePath& dir_path, |
+ InstallState install_state) { |
+ return PackAndInstallCRX(dir_path, base::FilePath(), install_state, |
+ Extension::NO_FLAGS); |
+} |
+ |
+// Attempts to install an extension. Use INSTALL_FAILED if the installation |
+// is expected to fail. |
+// If |install_state| is INSTALL_UPDATED, and |expected_old_name| is |
+// non-empty, expects that the existing extension's title was |
+// |expected_old_name|. |
+const Extension* ExtensionServiceTestWithInstall::InstallCRX( |
+ const base::FilePath& path, |
+ InstallState install_state, |
+ int creation_flags, |
+ const std::string& expected_old_name) { |
+ InstallCRXInternal(path, creation_flags); |
+ return VerifyCrxInstall(path, install_state, expected_old_name); |
+} |
+ |
+// Attempts to install an extension. Use INSTALL_FAILED if the installation |
+// is expected to fail. |
+const Extension* ExtensionServiceTestWithInstall::InstallCRX( |
+ const base::FilePath& path, |
+ InstallState install_state, |
+ int creation_flags) { |
+ return InstallCRX(path, install_state, creation_flags, std::string()); |
+} |
+ |
+// Attempts to install an extension. Use INSTALL_FAILED if the installation |
+// is expected to fail. |
+const Extension* ExtensionServiceTestWithInstall::InstallCRX( |
+ const base::FilePath& path, |
+ InstallState install_state) { |
+ return InstallCRX(path, install_state, Extension::NO_FLAGS); |
+} |
+ |
+const Extension* ExtensionServiceTestWithInstall::InstallCRXFromWebStore( |
+ const base::FilePath& path, |
+ InstallState install_state) { |
+ InstallCRXInternal(path, Extension::FROM_WEBSTORE); |
+ return VerifyCrxInstall(path, install_state); |
+} |
+ |
+const Extension* ExtensionServiceTestWithInstall::InstallCRXWithLocation( |
+ const base::FilePath& crx_path, |
+ Manifest::Location install_location, |
+ InstallState install_state) { |
+ EXPECT_TRUE(base::PathExists(crx_path)) |
+ << "Path does not exist: "<< crx_path.value().c_str(); |
+ // no client (silent install) |
+ scoped_refptr<CrxInstaller> installer( |
+ CrxInstaller::CreateSilent(service())); |
+ installer->set_install_source(install_location); |
+ |
+ content::WindowedNotificationObserver observer( |
+ extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
+ content::NotificationService::AllSources()); |
+ installer->InstallCrx(crx_path); |
+ observer.Wait(); |
+ |
+ return VerifyCrxInstall(crx_path, install_state); |
+} |
+ |
+// 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.
|
+// |install_state| to INSTALL_FAILED if the installation is expected to fail. |
+// Returns an Extension pointer if the install succeeded, NULL otherwise. |
+const Extension* ExtensionServiceTestWithInstall::VerifyCrxInstall( |
+ const base::FilePath& path, |
+ InstallState install_state) { |
+ return VerifyCrxInstall(path, install_state, std::string()); |
+} |
+ |
+// Verifies the result of a CRX installation. Used by InstallCRX. Set the |
+// |install_state| to INSTALL_FAILED if the installation is expected to fail. |
+// If |install_state| is INSTALL_UPDATED, and |expected_old_name| is |
+// non-empty, expects that the existing extension's title was |
+// |expected_old_name|. |
+// 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.
|
+const Extension* ExtensionServiceTestWithInstall::VerifyCrxInstall( |
+ const base::FilePath& path, |
+ InstallState install_state, |
+ const std::string& expected_old_name) { |
+ std::vector<base::string16> errors = GetErrors(); |
+ const Extension* extension = NULL; |
+ if (install_state != INSTALL_FAILED) { |
+ if (install_state == INSTALL_NEW) |
+ ++expected_extensions_count_; |
+ |
+ EXPECT_TRUE(installed_) << path.value(); |
+ // If and only if INSTALL_UPDATED, it should have the is_update flag. |
+ EXPECT_EQ(install_state == INSTALL_UPDATED, was_update_) |
+ << path.value(); |
+ // If INSTALL_UPDATED, old_name_ should match the given string. |
+ if (install_state == INSTALL_UPDATED && !expected_old_name.empty()) |
+ EXPECT_EQ(expected_old_name, old_name_); |
+ EXPECT_EQ(0u, errors.size()) << path.value(); |
+ |
+ if (install_state == INSTALL_WITHOUT_LOAD) { |
+ EXPECT_EQ(0u, loaded_.size()) << path.value(); |
+ } else { |
+ EXPECT_EQ(1u, loaded_.size()) << path.value(); |
+ size_t actual_extension_count = |
+ registry()->enabled_extensions().size() + |
+ registry()->disabled_extensions().size(); |
+ EXPECT_EQ(expected_extensions_count_, actual_extension_count) << |
+ path.value(); |
+ extension = loaded_[0].get(); |
+ EXPECT_TRUE(service()->GetExtensionById(extension->id(), false)) |
+ << path.value(); |
+ } |
+ |
+ for (std::vector<base::string16>::iterator err = errors.begin(); |
+ err != errors.end(); ++err) { |
+ LOG(ERROR) << *err; |
+ } |
+ } else { |
+ EXPECT_FALSE(installed_) << path.value(); |
+ EXPECT_EQ(0u, loaded_.size()) << path.value(); |
+ EXPECT_EQ(1u, errors.size()) << path.value(); |
+ } |
+ |
+ installed_ = NULL; |
+ was_update_ = false; |
+ old_name_ = ""; |
+ loaded_.clear(); |
+ ExtensionErrorReporter::GetInstance()->ClearErrors(); |
+ return extension; |
+} |
+ |
+void ExtensionServiceTestWithInstall::PackCRXAndUpdateExtension( |
+ const std::string& id, |
+ const base::FilePath& dir_path, |
+ const base::FilePath& pem_path, |
+ UpdateState expected_state) { |
+ base::ScopedTempDir temp_dir; |
+ EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); |
+ base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx"); |
+ |
+ PackCRX(dir_path, pem_path, crx_path); |
+ UpdateExtension(id, crx_path, expected_state); |
+} |
+ |
+void ExtensionServiceTestWithInstall::UpdateExtension( |
+ const std::string& id, |
+ const base::FilePath& in_path, |
+ UpdateState expected_state) { |
+ ASSERT_TRUE(base::PathExists(in_path)); |
+ |
+ // We need to copy this to a temporary location because Update() will delete |
+ // it. |
+ base::FilePath path = temp_dir().path(); |
+ path = path.Append(in_path.BaseName()); |
+ ASSERT_TRUE(base::CopyFile(in_path, path)); |
+ |
+ int previous_enabled_extension_count = |
+ registry()->enabled_extensions().size(); |
+ int previous_installed_extension_count = |
+ previous_enabled_extension_count + |
+ registry()->disabled_extensions().size(); |
+ |
+ extensions::CrxInstaller* installer = NULL; |
+ content::WindowedNotificationObserver observer( |
+ extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
+ base::Bind(&IsCrxInstallerDone, &installer)); |
+ service()->UpdateExtension(extensions::CRXFileInfo(id, path), true, |
+ &installer); |
+ |
+ if (installer) |
+ observer.Wait(); |
+ else |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ std::vector<base::string16> errors = GetErrors(); |
+ int error_count = errors.size(); |
+ int enabled_extension_count = registry()->enabled_extensions().size(); |
+ int installed_extension_count = |
+ enabled_extension_count + registry()->disabled_extensions().size(); |
+ |
+ int expected_error_count = (expected_state == FAILED) ? 1 : 0; |
+ EXPECT_EQ(expected_error_count, error_count) << path.value(); |
+ |
+ if (expected_state <= FAILED) { |
+ EXPECT_EQ(previous_enabled_extension_count, |
+ enabled_extension_count); |
+ EXPECT_EQ(previous_installed_extension_count, |
+ installed_extension_count); |
+ } else { |
+ int expected_installed_extension_count = |
+ (expected_state >= INSTALLED) ? 1 : 0; |
+ int expected_enabled_extension_count = |
+ (expected_state >= ENABLED) ? 1 : 0; |
+ EXPECT_EQ(expected_installed_extension_count, |
+ installed_extension_count); |
+ EXPECT_EQ(expected_enabled_extension_count, |
+ enabled_extension_count); |
+ } |
+ |
+ // Update() should the temporary input file. |
+ EXPECT_FALSE(base::PathExists(path)); |
+} |
+ |
+void ExtensionServiceTestWithInstall::UninstallExtension(const std::string& id, |
+ bool use_helper) { |
+ UninstallExtension(id, use_helper, Extension::ENABLED); |
+} |
+ |
+void ExtensionServiceTestWithInstall::UninstallExtension( |
+ const std::string& id, |
+ bool use_helper, |
+ Extension::State expected_state) { |
+ // Verify that the extension is installed. |
+ base::FilePath extension_path = extensions_install_dir().AppendASCII(id); |
+ EXPECT_TRUE(base::PathExists(extension_path)); |
+ size_t pref_key_count = GetPrefKeyCount(); |
+ EXPECT_GT(pref_key_count, 0u); |
+ ValidateIntegerPref(id, "state", expected_state); |
+ |
+ // Uninstall it. |
+ if (use_helper) { |
+ EXPECT_TRUE(ExtensionService::UninstallExtensionHelper( |
+ service(), id, extensions::UNINSTALL_REASON_FOR_TESTING)); |
+ } else { |
+ EXPECT_TRUE(service()->UninstallExtension( |
+ id, |
+ extensions::UNINSTALL_REASON_FOR_TESTING, |
+ base::Bind(&base::DoNothing), |
+ NULL)); |
+ } |
+ --expected_extensions_count_; |
+ |
+ // We should get an unload notification. |
+ EXPECT_FALSE(unloaded_id_.empty()); |
+ EXPECT_EQ(id, unloaded_id_); |
+ |
+ // Verify uninstalled state. |
+ size_t new_pref_key_count = GetPrefKeyCount(); |
+ if (new_pref_key_count == pref_key_count) { |
+ ValidateIntegerPref(id, "state", |
+ Extension::EXTERNAL_EXTENSION_UNINSTALLED); |
+ } else { |
+ EXPECT_EQ(new_pref_key_count, pref_key_count - 1); |
+ } |
+ |
+ // The extension should not be in the service anymore. |
+ EXPECT_FALSE(service()->GetInstalledExtension(id)); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ // The directory should be gone. |
+ EXPECT_FALSE(base::PathExists(extension_path)); |
+} |
+ |
+void ExtensionServiceTestWithInstall::TerminateExtension( |
+ const std::string& id) { |
+ const Extension* extension = service()->GetInstalledExtension(id); |
+ if (!extension) { |
+ ADD_FAILURE(); |
+ return; |
+ } |
+ service()->TrackTerminatedExtensionForTest(extension); |
+} |
+ |
+void ExtensionServiceTestWithInstall::Observe( |
+ int type, |
+ const content::NotificationSource& source, |
+ const content::NotificationDetails& details) { |
+ switch (type) { |
+ case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: { |
+ const Extension* extension = |
+ content::Details<const Extension>(details).ptr(); |
+ loaded_.push_back(make_scoped_refptr(extension)); |
+ // The tests rely on the errors being in a certain order, which can vary |
+ // depending on how filesystem iteration works. |
+ std::stable_sort(loaded_.begin(), loaded_.end(), ExtensionsOrder()); |
+ break; |
+ } |
+ |
+ case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: { |
+ UnloadedExtensionInfo* unloaded_info = |
+ content::Details<UnloadedExtensionInfo>(details).ptr(); |
+ const Extension* e = unloaded_info->extension; |
+ unloaded_id_ = e->id(); |
+ unloaded_reason_ = unloaded_info->reason; |
+ extensions::ExtensionList::iterator i = |
+ std::find(loaded_.begin(), loaded_.end(), e); |
+ // TODO(erikkay) fix so this can be an assert. Right now the tests |
+ // are manually calling clear() on loaded_, so this isn't doable. |
+ if (i == loaded_.end()) |
+ return; |
+ loaded_.erase(i); |
+ break; |
+ } |
+ case extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED: { |
+ const extensions::InstalledExtensionInfo* installed_info = |
+ content::Details<const extensions::InstalledExtensionInfo>(details) |
+ .ptr(); |
+ installed_ = installed_info->extension; |
+ was_update_ = installed_info->is_update; |
+ old_name_ = installed_info->old_name; |
+ break; |
+ } |
+ |
+ default: |
+ DCHECK(false); |
+ } |
+} |
+ |
+// Create a CrxInstaller and install the CRX file. |
+// Instead of calling this method yourself, use InstallCRX(), which does extra |
+// error checking. |
+void ExtensionServiceTestWithInstall::InstallCRXInternal( |
+ const base::FilePath& crx_path, |
+ int creation_flags) { |
+ ASSERT_TRUE(base::PathExists(crx_path)) |
+ << "Path does not exist: "<< crx_path.value().c_str(); |
+ scoped_refptr<CrxInstaller> installer( |
+ CrxInstaller::CreateSilent(service())); |
+ installer->set_creation_flags(creation_flags); |
+ if (!(creation_flags & Extension::WAS_INSTALLED_BY_DEFAULT)) |
+ installer->set_allow_silent_install(true); |
+ |
+ content::WindowedNotificationObserver observer( |
+ extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
+ content::Source<extensions::CrxInstaller>(installer.get())); |
+ |
+ installer->InstallCrx(crx_path); |
+ |
+ observer.Wait(); |
+} |
+ |
+} // namespace extensions |