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.
5 #include "base/file_util.h"
6 #include "base/files/file_enumerator.h"
7 #include "base/files/file_path.h"
8 #include "base/prefs/scoped_user_pref_update.h"
9 #include "base/threading/sequenced_worker_pool.h"
10 #include "base/values.h"
11 #include "chrome/browser/extensions/extension_garbage_collector.h"
12 #include "chrome/browser/extensions/extension_service_unittest.h"
13 #include "chrome/browser/extensions/install_tracker.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/chrome_constants.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/plugin_service.h"
19 #include "content/public/test/test_browser_thread_bundle.h"
21 namespace extensions {
23 class ExtensionGarbageCollectorUnitTest : public ExtensionServiceTestBase {
25 void InitPluginService() {
26 #if defined(ENABLE_PLUGINS)
27 content::PluginService::GetInstance()->Init();
31 // Garbage collection, in production, runs on multiple threads. This is
32 // important to test to make sure we don't violate thread safety. Use a real
33 // task runner for these tests.
34 // TODO (rdevlin.cronin): It's kind of hacky that we have to do these things
35 // since we're using ExtensionServiceTestBase. Instead, we should probably do
36 // something like use an options flag, and handle it all in the base class.
37 void InitFileTaskRunner() {
38 service_->SetFileTaskRunnerForTesting(
39 content::BrowserThread::GetBlockingPool()
40 ->GetSequencedTaskRunnerWithShutdownBehavior(
41 content::BrowserThread::GetBlockingPool()
42 ->GetNamedSequenceToken("ext_install-"),
43 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
47 // A delayed task to call GarbageCollectExtensions is posted by
48 // ExtensionGarbageCollector's constructor. But, as the test won't wait for
49 // the delayed task to be called, we have to call it manually instead.
50 void GarbageCollectExtensions() {
51 ExtensionGarbageCollector::Get(profile_.get())
52 ->GarbageCollectExtensionsForTest();
53 // Wait for GarbageCollectExtensions task to complete.
54 content::BrowserThread::GetBlockingPool()->FlushForTesting();
58 // Test that partially deleted extensions are cleaned up during startup.
59 TEST_F(ExtensionGarbageCollectorUnitTest, CleanupOnStartup) {
60 const std::string kExtensionId = "behllobkkfkfnphdnhnkndlbkcpglgmj";
63 InitializeGoodInstalledExtensionService();
66 // Simulate that one of them got partially deleted by clearing its pref.
68 DictionaryPrefUpdate update(profile_->GetPrefs(), "extensions.settings");
69 base::DictionaryValue* dict = update.Get();
70 ASSERT_TRUE(dict != NULL);
71 dict->Remove(kExtensionId, NULL);
75 GarbageCollectExtensions();
77 base::FileEnumerator dirs(extensions_install_dir_,
78 false, // not recursive
79 base::FileEnumerator::DIRECTORIES);
81 while (!dirs.Next().empty())
84 // We should have only gotten two extensions now.
87 // And extension1 dir should now be toast.
88 base::FilePath extension_dir =
89 extensions_install_dir_.AppendASCII(kExtensionId);
90 ASSERT_FALSE(base::PathExists(extension_dir));
93 // Test that garbage collection doesn't delete anything while a crx is being
95 TEST_F(ExtensionGarbageCollectorUnitTest, NoCleanupDuringInstall) {
96 const std::string kExtensionId = "behllobkkfkfnphdnhnkndlbkcpglgmj";
99 InitializeGoodInstalledExtensionService();
100 InitFileTaskRunner();
102 // Simulate that one of them got partially deleted by clearing its pref.
104 DictionaryPrefUpdate update(profile_->GetPrefs(), "extensions.settings");
105 base::DictionaryValue* dict = update.Get();
106 ASSERT_TRUE(dict != NULL);
107 dict->Remove(kExtensionId, NULL);
112 // Simulate a CRX installation.
113 InstallTracker::Get(profile_.get())->OnBeginCrxInstall(kExtensionId);
115 GarbageCollectExtensions();
117 // extension1 dir should still exist.
118 base::FilePath extension_dir =
119 extensions_install_dir_.AppendASCII(kExtensionId);
120 ASSERT_TRUE(base::PathExists(extension_dir));
122 // Finish CRX installation and re-run garbage collection.
123 InstallTracker::Get(profile_.get())->OnFinishCrxInstall(kExtensionId, false);
124 GarbageCollectExtensions();
126 // extension1 dir should be gone
127 ASSERT_FALSE(base::PathExists(extension_dir));
130 // Test that GarbageCollectExtensions deletes the right versions of an
132 TEST_F(ExtensionGarbageCollectorUnitTest, GarbageCollectWithPendingUpdates) {
135 base::FilePath source_install_dir =
136 data_dir_.AppendASCII("pending_updates").AppendASCII("Extensions");
137 base::FilePath pref_path =
138 source_install_dir.DirName().Append(chrome::kPreferencesFilename);
140 InitializeInstalledExtensionService(pref_path, source_install_dir);
141 InitFileTaskRunner();
143 // This is the directory that is going to be deleted, so make sure it actually
144 // is there before the garbage collection.
145 ASSERT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII(
146 "hpiknbiabeeppbpihjehijgoemciehgk/3")));
148 GarbageCollectExtensions();
150 // Verify that the pending update for the first extension didn't get
152 EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII(
153 "bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0")));
154 EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII(
155 "bjafgdebaacbbbecmhlhpofkepfkgcpa/2.0")));
156 EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII(
157 "hpiknbiabeeppbpihjehijgoemciehgk/2")));
158 EXPECT_FALSE(base::PathExists(extensions_install_dir_.AppendASCII(
159 "hpiknbiabeeppbpihjehijgoemciehgk/3")));
162 // Test that pending updates are properly handled on startup.
163 TEST_F(ExtensionGarbageCollectorUnitTest, UpdateOnStartup) {
166 base::FilePath source_install_dir =
167 data_dir_.AppendASCII("pending_updates").AppendASCII("Extensions");
168 base::FilePath pref_path =
169 source_install_dir.DirName().Append(chrome::kPreferencesFilename);
171 InitializeInstalledExtensionService(pref_path, source_install_dir);
172 InitFileTaskRunner();
174 // This is the directory that is going to be deleted, so make sure it actually
175 // is there before the garbage collection.
176 ASSERT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII(
177 "hpiknbiabeeppbpihjehijgoemciehgk/3")));
180 GarbageCollectExtensions();
182 // Verify that the pending update for the first extension got installed.
183 EXPECT_FALSE(base::PathExists(extensions_install_dir_.AppendASCII(
184 "bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0")));
185 EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII(
186 "bjafgdebaacbbbecmhlhpofkepfkgcpa/2.0")));
187 EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII(
188 "hpiknbiabeeppbpihjehijgoemciehgk/2")));
189 EXPECT_FALSE(base::PathExists(extensions_install_dir_.AppendASCII(
190 "hpiknbiabeeppbpihjehijgoemciehgk/3")));
192 // Make sure update information got deleted.
193 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_.get());
195 prefs->GetDelayedInstallInfo("bjafgdebaacbbbecmhlhpofkepfkgcpa"));
198 } // namespace extensions