OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <vector> |
| 6 |
| 7 #include "base/files/file_util.h" |
| 8 #include "base/files/scoped_temp_dir.h" |
| 9 #include "base/run_loop.h" |
| 10 #include "base/values.h" |
| 11 #include "components/crx_file/id_util.h" |
| 12 #include "components/update_client/update_client.h" |
| 13 #include "content/public/test/test_browser_thread_bundle.h" |
| 14 #include "content/public/test/test_utils.h" |
| 15 #include "extensions/browser/extension_registry.h" |
| 16 #include "extensions/browser/extensions_test.h" |
| 17 #include "extensions/browser/mock_extension_system.h" |
| 18 #include "extensions/browser/test_extensions_browser_client.h" |
| 19 #include "extensions/browser/updater/update_service.h" |
| 20 #include "extensions/common/extension_builder.h" |
| 21 #include "extensions/common/value_builder.h" |
| 22 #include "testing/gtest/include/gtest/gtest.h" |
| 23 |
| 24 namespace { |
| 25 |
| 26 class FakeUpdateClient : public update_client::UpdateClient { |
| 27 public: |
| 28 FakeUpdateClient(); |
| 29 |
| 30 // Returns the data we've gotten from the CrxDataCallback for ids passed to |
| 31 // the Update function. |
| 32 std::vector<update_client::CrxComponent>* data() { return &data_; } |
| 33 |
| 34 // update_client::UpdateClient |
| 35 void AddObserver(Observer* observer) override {} |
| 36 void RemoveObserver(Observer* observer) override {} |
| 37 void Install(const std::string& id, |
| 38 const CrxDataCallback& crx_data_callback, |
| 39 const CompletionCallback& completion_callback) override {} |
| 40 void Update(const std::vector<std::string>& ids, |
| 41 const CrxDataCallback& crx_data_callback, |
| 42 const CompletionCallback& completion_callback) override; |
| 43 bool GetCrxUpdateState( |
| 44 const std::string& id, |
| 45 update_client::CrxUpdateItem* update_item) const override { |
| 46 return false; |
| 47 } |
| 48 bool IsUpdating(const std::string& id) const override { return false; } |
| 49 |
| 50 protected: |
| 51 friend class base::RefCounted<FakeUpdateClient>; |
| 52 ~FakeUpdateClient() override {} |
| 53 |
| 54 std::vector<update_client::CrxComponent> data_; |
| 55 }; |
| 56 |
| 57 FakeUpdateClient::FakeUpdateClient() {} |
| 58 |
| 59 void FakeUpdateClient::Update(const std::vector<std::string>& ids, |
| 60 const CrxDataCallback& crx_data_callback, |
| 61 const CompletionCallback& completion_callback) { |
| 62 crx_data_callback.Run(ids, &data_); |
| 63 } |
| 64 |
| 65 } // namespace |
| 66 |
| 67 namespace extensions { |
| 68 |
| 69 namespace { |
| 70 |
| 71 // A fake ExtensionSystem that lets us intercept calls to install new |
| 72 // versions of an extension. |
| 73 class FakeExtensionSystem : public MockExtensionSystem { |
| 74 public: |
| 75 explicit FakeExtensionSystem(content::BrowserContext* context) |
| 76 : MockExtensionSystem(context) {} |
| 77 ~FakeExtensionSystem() override {} |
| 78 |
| 79 struct InstallUpdateRequest { |
| 80 std::string extension_id; |
| 81 base::FilePath temp_dir; |
| 82 }; |
| 83 |
| 84 std::vector<InstallUpdateRequest>* install_requests() { |
| 85 return &install_requests_; |
| 86 } |
| 87 |
| 88 void set_install_callback(const base::Closure& callback) { |
| 89 next_install_callback_ = callback; |
| 90 } |
| 91 |
| 92 // ExtensionSystem override |
| 93 void InstallUpdate(const std::string& extension_id, |
| 94 const base::FilePath& temp_dir) override { |
| 95 base::DeleteFile(temp_dir, true /*recursive*/); |
| 96 InstallUpdateRequest request; |
| 97 request.extension_id = extension_id; |
| 98 request.temp_dir = temp_dir; |
| 99 install_requests_.push_back(request); |
| 100 if (!next_install_callback_.is_null()) { |
| 101 base::Closure tmp = next_install_callback_; |
| 102 next_install_callback_.Reset(); |
| 103 tmp.Run(); |
| 104 } |
| 105 } |
| 106 |
| 107 private: |
| 108 std::vector<InstallUpdateRequest> install_requests_; |
| 109 base::Closure next_install_callback_; |
| 110 }; |
| 111 |
| 112 class UpdateServiceTest : public ExtensionsTest { |
| 113 public: |
| 114 UpdateServiceTest() { |
| 115 extensions_browser_client()->set_extension_system_factory( |
| 116 &fake_extension_system_factory_); |
| 117 } |
| 118 ~UpdateServiceTest() override {} |
| 119 |
| 120 void SetUp() override { |
| 121 ExtensionsTest::SetUp(); |
| 122 browser_threads_.reset(new content::TestBrowserThreadBundle( |
| 123 content::TestBrowserThreadBundle::DEFAULT)); |
| 124 |
| 125 extensions_browser_client()->SetUpdateClientFactory(base::Bind( |
| 126 &UpdateServiceTest::CreateUpdateClient, base::Unretained(this))); |
| 127 |
| 128 update_service_ = UpdateService::Get(browser_context()); |
| 129 } |
| 130 |
| 131 protected: |
| 132 UpdateService* update_service() const { return update_service_; } |
| 133 FakeUpdateClient* update_client() const { return update_client_.get(); } |
| 134 |
| 135 update_client::UpdateClient* CreateUpdateClient() { |
| 136 // We only expect that this will get called once, so consider it an error |
| 137 // if our update_client_ is already non-null. |
| 138 EXPECT_EQ(nullptr, update_client_.get()); |
| 139 update_client_ = new FakeUpdateClient(); |
| 140 return update_client_.get(); |
| 141 } |
| 142 |
| 143 // Helper function that creates a file at |relative_path| within |directory| |
| 144 // and fills it with |content|. |
| 145 bool AddFileToDirectory(const base::FilePath& directory, |
| 146 const base::FilePath& relative_path, |
| 147 const std::string& content) { |
| 148 base::FilePath full_path = directory.Append(relative_path); |
| 149 if (!CreateDirectory(full_path.DirName())) |
| 150 return false; |
| 151 int result = base::WriteFile(full_path, content.data(), content.size()); |
| 152 return (static_cast<size_t>(result) == content.size()); |
| 153 } |
| 154 |
| 155 FakeExtensionSystem* extension_system() { |
| 156 return static_cast<FakeExtensionSystem*>( |
| 157 fake_extension_system_factory_.GetForBrowserContext(browser_context())); |
| 158 } |
| 159 |
| 160 private: |
| 161 UpdateService* update_service_; |
| 162 scoped_refptr<FakeUpdateClient> update_client_; |
| 163 scoped_ptr<content::TestBrowserThreadBundle> browser_threads_; |
| 164 MockExtensionSystemFactory<FakeExtensionSystem> |
| 165 fake_extension_system_factory_; |
| 166 }; |
| 167 |
| 168 TEST_F(UpdateServiceTest, BasicUpdateOperations) { |
| 169 // Create a temporary directory that a fake extension will live in and fill |
| 170 // it with some test files. |
| 171 base::ScopedTempDir temp_dir; |
| 172 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| 173 base::FilePath foo_js(FILE_PATH_LITERAL("foo.js")); |
| 174 base::FilePath bar_html(FILE_PATH_LITERAL("bar/bar.html")); |
| 175 ASSERT_TRUE(AddFileToDirectory(temp_dir.path(), foo_js, "hello")) |
| 176 << "Failed to write " << temp_dir.path().value() << "/" << foo_js.value(); |
| 177 ASSERT_TRUE(AddFileToDirectory(temp_dir.path(), bar_html, "world")); |
| 178 |
| 179 ExtensionBuilder builder; |
| 180 builder.SetManifest(DictionaryBuilder() |
| 181 .Set("name", "Foo") |
| 182 .Set("version", "1.0") |
| 183 .Set("manifest_version", 2)); |
| 184 builder.SetID(crx_file::id_util::GenerateId("whatever")); |
| 185 builder.SetPath(temp_dir.path()); |
| 186 |
| 187 scoped_refptr<Extension> extension1(builder.Build()); |
| 188 |
| 189 ExtensionRegistry::Get(browser_context())->AddEnabled(extension1); |
| 190 std::vector<std::string> ids; |
| 191 ids.push_back(extension1->id()); |
| 192 |
| 193 // Start an update check and verify that the UpdateClient was sent the right |
| 194 // data. |
| 195 update_service()->StartUpdateCheck(ids); |
| 196 std::vector<update_client::CrxComponent>* data = update_client()->data(); |
| 197 ASSERT_NE(nullptr, data); |
| 198 ASSERT_EQ(1u, data->size()); |
| 199 |
| 200 ASSERT_TRUE(data->at(0).version.Equals(*extension1->version())); |
| 201 update_client::CrxInstaller* installer = data->at(0).installer.get(); |
| 202 ASSERT_NE(installer, nullptr); |
| 203 |
| 204 // The GetInstalledFile method is used when processing differential updates |
| 205 // to get a path to an existing file in an extension. We want to test a |
| 206 // number of scenarios to be user we handle invalid relative paths, don't |
| 207 // accidentally return paths outside the extension's dir, etc. |
| 208 base::FilePath tmp; |
| 209 EXPECT_TRUE(installer->GetInstalledFile(foo_js.MaybeAsASCII(), &tmp)); |
| 210 EXPECT_EQ(temp_dir.path().Append(foo_js), tmp) << tmp.value(); |
| 211 |
| 212 EXPECT_TRUE(installer->GetInstalledFile(bar_html.MaybeAsASCII(), &tmp)); |
| 213 EXPECT_EQ(temp_dir.path().Append(bar_html), tmp) << tmp.value(); |
| 214 |
| 215 EXPECT_FALSE(installer->GetInstalledFile("does_not_exist", &tmp)); |
| 216 EXPECT_FALSE(installer->GetInstalledFile("does/not/exist", &tmp)); |
| 217 EXPECT_FALSE(installer->GetInstalledFile("/does/not/exist", &tmp)); |
| 218 EXPECT_FALSE(installer->GetInstalledFile("C:\\tmp", &tmp)); |
| 219 |
| 220 base::FilePath system_temp_dir; |
| 221 ASSERT_TRUE(base::GetTempDir(&system_temp_dir)); |
| 222 EXPECT_FALSE( |
| 223 installer->GetInstalledFile(system_temp_dir.MaybeAsASCII(), &tmp)); |
| 224 |
| 225 // Test the install callback. |
| 226 base::ScopedTempDir new_version_dir; |
| 227 ASSERT_TRUE(new_version_dir.CreateUniqueTempDir()); |
| 228 scoped_ptr<base::DictionaryValue> new_manifest( |
| 229 extension1->manifest()->value()->DeepCopy()); |
| 230 new_manifest->SetString("version", "2.0"); |
| 231 |
| 232 installer->Install(*new_manifest, new_version_dir.path()); |
| 233 |
| 234 scoped_refptr<content::MessageLoopRunner> loop_runner = |
| 235 new content::MessageLoopRunner(); |
| 236 extension_system()->set_install_callback(loop_runner->QuitClosure()); |
| 237 loop_runner->Run(); |
| 238 |
| 239 std::vector<FakeExtensionSystem::InstallUpdateRequest>* requests = |
| 240 extension_system()->install_requests(); |
| 241 ASSERT_EQ(1u, requests->size()); |
| 242 EXPECT_EQ(requests->at(0).extension_id, extension1->id()); |
| 243 EXPECT_NE(requests->at(0).temp_dir.value(), new_version_dir.path().value()); |
| 244 } |
| 245 |
| 246 } // namespace |
| 247 |
| 248 } // namespace extensions |
OLD | NEW |