Index: extensions/browser/updater/update_service_unittest.cc |
diff --git a/extensions/browser/updater/update_service_unittest.cc b/extensions/browser/updater/update_service_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f570303f382156c211bc2edefa307961040bba63 |
--- /dev/null |
+++ b/extensions/browser/updater/update_service_unittest.cc |
@@ -0,0 +1,248 @@ |
+// Copyright 2014 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 <vector> |
+ |
+#include "base/files/file_util.h" |
+#include "base/files/scoped_temp_dir.h" |
+#include "base/run_loop.h" |
+#include "base/values.h" |
+#include "components/crx_file/id_util.h" |
+#include "components/update_client/update_client.h" |
+#include "content/public/test/test_browser_thread_bundle.h" |
+#include "content/public/test/test_utils.h" |
+#include "extensions/browser/extension_registry.h" |
+#include "extensions/browser/extensions_test.h" |
+#include "extensions/browser/mock_extension_system.h" |
+#include "extensions/browser/test_extensions_browser_client.h" |
+#include "extensions/browser/updater/update_service.h" |
+#include "extensions/common/extension_builder.h" |
+#include "extensions/common/value_builder.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace { |
+ |
+class FakeUpdateClient : public update_client::UpdateClient { |
+ public: |
+ FakeUpdateClient(); |
+ |
+ // Returns the data we've gotten from the CrxDataCallback for ids passed to |
+ // the Update function. |
+ std::vector<update_client::CrxComponent>* data() { return &data_; } |
+ |
+ // update_client::UpdateClient |
+ void AddObserver(Observer* observer) override {} |
+ void RemoveObserver(Observer* observer) override {} |
+ void Install(const std::string& id, |
+ const CrxDataCallback& crx_data_callback, |
+ const CompletionCallback& completion_callback) override {} |
+ void Update(const std::vector<std::string>& ids, |
+ const CrxDataCallback& crx_data_callback, |
+ const CompletionCallback& completion_callback) override; |
+ bool GetCrxUpdateState( |
+ const std::string& id, |
+ update_client::CrxUpdateItem* update_item) const override { |
+ return false; |
+ } |
+ bool IsUpdating(const std::string& id) const override { return false; } |
+ |
+ protected: |
+ friend class base::RefCounted<FakeUpdateClient>; |
+ ~FakeUpdateClient() override {} |
+ |
+ std::vector<update_client::CrxComponent> data_; |
+}; |
+ |
+FakeUpdateClient::FakeUpdateClient() {} |
+ |
+void FakeUpdateClient::Update(const std::vector<std::string>& ids, |
+ const CrxDataCallback& crx_data_callback, |
+ const CompletionCallback& completion_callback) { |
+ crx_data_callback.Run(ids, &data_); |
+} |
+ |
+} // namespace |
+ |
+namespace extensions { |
+ |
+namespace { |
+ |
+// A fake ExtensionSystem that lets us intercept calls to install new |
+// versions of an extension. |
+class FakeExtensionSystem : public MockExtensionSystem { |
+ public: |
+ explicit FakeExtensionSystem(content::BrowserContext* context) |
+ : MockExtensionSystem(context) {} |
+ ~FakeExtensionSystem() override {} |
+ |
+ struct InstallUpdateRequest { |
+ std::string extension_id; |
+ base::FilePath temp_dir; |
+ }; |
+ |
+ std::vector<InstallUpdateRequest>* install_requests() { |
+ return &install_requests_; |
+ } |
+ |
+ void set_install_callback(const base::Closure& callback) { |
+ next_install_callback_ = callback; |
+ } |
+ |
+ // ExtensionSystem override |
+ void InstallUpdate(const std::string& extension_id, |
+ const base::FilePath& temp_dir) override { |
+ base::DeleteFile(temp_dir, true /*recursive*/); |
+ InstallUpdateRequest request; |
+ request.extension_id = extension_id; |
+ request.temp_dir = temp_dir; |
+ install_requests_.push_back(request); |
+ if (!next_install_callback_.is_null()) { |
+ base::Closure tmp = next_install_callback_; |
+ next_install_callback_.Reset(); |
+ tmp.Run(); |
+ } |
+ } |
+ |
+ private: |
+ std::vector<InstallUpdateRequest> install_requests_; |
+ base::Closure next_install_callback_; |
+}; |
+ |
+class UpdateServiceTest : public ExtensionsTest { |
+ public: |
+ UpdateServiceTest() { |
+ extensions_browser_client()->set_extension_system_factory( |
+ &fake_extension_system_factory_); |
+ } |
+ ~UpdateServiceTest() override {} |
+ |
+ void SetUp() override { |
+ ExtensionsTest::SetUp(); |
+ browser_threads_.reset(new content::TestBrowserThreadBundle( |
+ content::TestBrowserThreadBundle::DEFAULT)); |
+ |
+ extensions_browser_client()->SetUpdateClientFactory(base::Bind( |
+ &UpdateServiceTest::CreateUpdateClient, base::Unretained(this))); |
+ |
+ update_service_ = UpdateService::Get(browser_context()); |
+ } |
+ |
+ protected: |
+ UpdateService* update_service() const { return update_service_; } |
+ FakeUpdateClient* update_client() const { return update_client_.get(); } |
+ |
+ update_client::UpdateClient* CreateUpdateClient() { |
+ // We only expect that this will get called once, so consider it an error |
+ // if our update_client_ is already non-null. |
+ EXPECT_EQ(nullptr, update_client_.get()); |
+ update_client_ = new FakeUpdateClient(); |
+ return update_client_.get(); |
+ } |
+ |
+ // Helper function that creates a file at |relative_path| within |directory| |
+ // and fills it with |content|. |
+ bool AddFileToDirectory(const base::FilePath& directory, |
+ const base::FilePath& relative_path, |
+ const std::string& content) { |
+ base::FilePath full_path = directory.Append(relative_path); |
+ if (!CreateDirectory(full_path.DirName())) |
+ return false; |
+ int result = base::WriteFile(full_path, content.data(), content.size()); |
+ return (static_cast<size_t>(result) == content.size()); |
+ } |
+ |
+ FakeExtensionSystem* extension_system() { |
+ return static_cast<FakeExtensionSystem*>( |
+ fake_extension_system_factory_.GetForBrowserContext(browser_context())); |
+ } |
+ |
+ private: |
+ UpdateService* update_service_; |
+ scoped_refptr<FakeUpdateClient> update_client_; |
+ scoped_ptr<content::TestBrowserThreadBundle> browser_threads_; |
+ MockExtensionSystemFactory<FakeExtensionSystem> |
+ fake_extension_system_factory_; |
+}; |
+ |
+TEST_F(UpdateServiceTest, BasicUpdateOperations) { |
+ // Create a temporary directory that a fake extension will live in and fill |
+ // it with some test files. |
+ base::ScopedTempDir temp_dir; |
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
+ base::FilePath foo_js(FILE_PATH_LITERAL("foo.js")); |
+ base::FilePath bar_html(FILE_PATH_LITERAL("bar/bar.html")); |
+ ASSERT_TRUE(AddFileToDirectory(temp_dir.path(), foo_js, "hello")) |
+ << "Failed to write " << temp_dir.path().value() << "/" << foo_js.value(); |
+ ASSERT_TRUE(AddFileToDirectory(temp_dir.path(), bar_html, "world")); |
+ |
+ ExtensionBuilder builder; |
+ builder.SetManifest(DictionaryBuilder() |
+ .Set("name", "Foo") |
+ .Set("version", "1.0") |
+ .Set("manifest_version", 2)); |
+ builder.SetID(crx_file::id_util::GenerateId("whatever")); |
+ builder.SetPath(temp_dir.path()); |
+ |
+ scoped_refptr<Extension> extension1(builder.Build()); |
+ |
+ ExtensionRegistry::Get(browser_context())->AddEnabled(extension1); |
+ std::vector<std::string> ids; |
+ ids.push_back(extension1->id()); |
+ |
+ // Start an update check and verify that the UpdateClient was sent the right |
+ // data. |
+ update_service()->StartUpdateCheck(ids); |
+ std::vector<update_client::CrxComponent>* data = update_client()->data(); |
+ ASSERT_NE(nullptr, data); |
+ ASSERT_EQ(1u, data->size()); |
+ |
+ ASSERT_TRUE(data->at(0).version.Equals(*extension1->version())); |
+ update_client::CrxInstaller* installer = data->at(0).installer.get(); |
+ ASSERT_NE(installer, nullptr); |
+ |
+ // The GetInstalledFile method is used when processing differential updates |
+ // to get a path to an existing file in an extension. We want to test a |
+ // number of scenarios to be user we handle invalid relative paths, don't |
+ // accidentally return paths outside the extension's dir, etc. |
+ base::FilePath tmp; |
+ EXPECT_TRUE(installer->GetInstalledFile(foo_js.MaybeAsASCII(), &tmp)); |
+ EXPECT_EQ(temp_dir.path().Append(foo_js), tmp) << tmp.value(); |
+ |
+ EXPECT_TRUE(installer->GetInstalledFile(bar_html.MaybeAsASCII(), &tmp)); |
+ EXPECT_EQ(temp_dir.path().Append(bar_html), tmp) << tmp.value(); |
+ |
+ EXPECT_FALSE(installer->GetInstalledFile("does_not_exist", &tmp)); |
+ EXPECT_FALSE(installer->GetInstalledFile("does/not/exist", &tmp)); |
+ EXPECT_FALSE(installer->GetInstalledFile("/does/not/exist", &tmp)); |
+ EXPECT_FALSE(installer->GetInstalledFile("C:\\tmp", &tmp)); |
+ |
+ base::FilePath system_temp_dir; |
+ ASSERT_TRUE(base::GetTempDir(&system_temp_dir)); |
+ EXPECT_FALSE( |
+ installer->GetInstalledFile(system_temp_dir.MaybeAsASCII(), &tmp)); |
+ |
+ // Test the install callback. |
+ base::ScopedTempDir new_version_dir; |
+ ASSERT_TRUE(new_version_dir.CreateUniqueTempDir()); |
+ scoped_ptr<base::DictionaryValue> new_manifest( |
+ extension1->manifest()->value()->DeepCopy()); |
+ new_manifest->SetString("version", "2.0"); |
+ |
+ installer->Install(*new_manifest, new_version_dir.path()); |
+ |
+ scoped_refptr<content::MessageLoopRunner> loop_runner = |
+ new content::MessageLoopRunner(); |
+ extension_system()->set_install_callback(loop_runner->QuitClosure()); |
+ loop_runner->Run(); |
+ |
+ std::vector<FakeExtensionSystem::InstallUpdateRequest>* requests = |
+ extension_system()->install_requests(); |
+ ASSERT_EQ(1u, requests->size()); |
+ EXPECT_EQ(requests->at(0).extension_id, extension1->id()); |
+ EXPECT_NE(requests->at(0).temp_dir.value(), new_version_dir.path().value()); |
+} |
+ |
+} // namespace |
+ |
+} // namespace extensions |