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/files/file_enumerator.h"
6 #include "base/files/file_path.h"
7 #include "base/files/file_util.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.h"
13 #include "chrome/browser/extensions/extension_service_test_base.h"
14 #include "chrome/browser/extensions/install_tracker.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/chrome_constants.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/plugin_service.h"
20 #include "content/public/test/test_browser_thread_bundle.h"
21 #include "extensions/browser/extension_prefs.h"
23 namespace extensions
{
25 class ExtensionGarbageCollectorUnitTest
: public ExtensionServiceTestBase
{
27 void InitPluginService() {
28 #if defined(ENABLE_PLUGINS)
29 content::PluginService::GetInstance()->Init();
33 // Garbage collection, in production, runs on multiple threads. This is
34 // important to test to make sure we don't violate thread safety. Use a real
35 // task runner for these tests.
36 // TODO (rdevlin.cronin): It's kind of hacky that we have to do these things
37 // since we're using ExtensionServiceTestBase. Instead, we should probably do
38 // something like use an options flag, and handle it all in the base class.
39 void InitFileTaskRunner() {
40 service_
->SetFileTaskRunnerForTesting(
41 content::BrowserThread::GetBlockingPool()
42 ->GetSequencedTaskRunnerWithShutdownBehavior(
43 content::BrowserThread::GetBlockingPool()
44 ->GetNamedSequenceToken("ext_install-"),
45 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN
));
49 // A delayed task to call GarbageCollectExtensions is posted by
50 // ExtensionGarbageCollector's constructor. But, as the test won't wait for
51 // the delayed task to be called, we have to call it manually instead.
52 void GarbageCollectExtensions() {
53 ExtensionGarbageCollector::Get(profile_
.get())
54 ->GarbageCollectExtensionsForTest();
55 // Wait for GarbageCollectExtensions task to complete.
56 content::BrowserThread::GetBlockingPool()->FlushForTesting();
60 // Test that partially deleted extensions are cleaned up during startup.
61 TEST_F(ExtensionGarbageCollectorUnitTest
, CleanupOnStartup
) {
62 const std::string kExtensionId
= "behllobkkfkfnphdnhnkndlbkcpglgmj";
65 InitializeGoodInstalledExtensionService();
68 // Simulate that one of them got partially deleted by clearing its pref.
70 DictionaryPrefUpdate
update(profile_
->GetPrefs(), "extensions.settings");
71 base::DictionaryValue
* dict
= update
.Get();
72 ASSERT_TRUE(dict
!= NULL
);
73 dict
->Remove(kExtensionId
, NULL
);
77 GarbageCollectExtensions();
79 base::FileEnumerator
dirs(extensions_install_dir(),
80 false, // not recursive
81 base::FileEnumerator::DIRECTORIES
);
83 while (!dirs
.Next().empty())
86 // We should have only gotten two extensions now.
89 // And extension1 dir should now be toast.
90 base::FilePath extension_dir
=
91 extensions_install_dir().AppendASCII(kExtensionId
);
92 ASSERT_FALSE(base::PathExists(extension_dir
));
95 // Test that garbage collection doesn't delete anything while a crx is being
97 TEST_F(ExtensionGarbageCollectorUnitTest
, NoCleanupDuringInstall
) {
98 const std::string kExtensionId
= "behllobkkfkfnphdnhnkndlbkcpglgmj";
101 InitializeGoodInstalledExtensionService();
102 InitFileTaskRunner();
104 // Simulate that one of them got partially deleted by clearing its pref.
106 DictionaryPrefUpdate
update(profile_
->GetPrefs(), "extensions.settings");
107 base::DictionaryValue
* dict
= update
.Get();
108 ASSERT_TRUE(dict
!= NULL
);
109 dict
->Remove(kExtensionId
, NULL
);
114 // Simulate a CRX installation.
115 InstallTracker::Get(profile_
.get())->OnBeginCrxInstall(kExtensionId
);
117 GarbageCollectExtensions();
119 // extension1 dir should still exist.
120 base::FilePath extension_dir
=
121 extensions_install_dir().AppendASCII(kExtensionId
);
122 ASSERT_TRUE(base::PathExists(extension_dir
));
124 // Finish CRX installation and re-run garbage collection.
125 InstallTracker::Get(profile_
.get())->OnFinishCrxInstall(kExtensionId
, false);
126 GarbageCollectExtensions();
128 // extension1 dir should be gone
129 ASSERT_FALSE(base::PathExists(extension_dir
));
132 // Test that GarbageCollectExtensions deletes the right versions of an
134 TEST_F(ExtensionGarbageCollectorUnitTest
, GarbageCollectWithPendingUpdates
) {
137 base::FilePath source_install_dir
=
138 data_dir().AppendASCII("pending_updates").AppendASCII("Extensions");
139 base::FilePath pref_path
=
140 source_install_dir
.DirName().Append(chrome::kPreferencesFilename
);
142 InitializeInstalledExtensionService(pref_path
, source_install_dir
);
143 InitFileTaskRunner();
145 // This is the directory that is going to be deleted, so make sure it actually
146 // is there before the garbage collection.
147 ASSERT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
148 "hpiknbiabeeppbpihjehijgoemciehgk/3")));
150 GarbageCollectExtensions();
152 // Verify that the pending update for the first extension didn't get
154 EXPECT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
155 "bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0")));
156 EXPECT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
157 "bjafgdebaacbbbecmhlhpofkepfkgcpa/2.0")));
158 EXPECT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
159 "hpiknbiabeeppbpihjehijgoemciehgk/2")));
160 EXPECT_FALSE(base::PathExists(extensions_install_dir().AppendASCII(
161 "hpiknbiabeeppbpihjehijgoemciehgk/3")));
164 // Test that pending updates are properly handled on startup.
165 TEST_F(ExtensionGarbageCollectorUnitTest
, UpdateOnStartup
) {
168 base::FilePath source_install_dir
=
169 data_dir().AppendASCII("pending_updates").AppendASCII("Extensions");
170 base::FilePath pref_path
=
171 source_install_dir
.DirName().Append(chrome::kPreferencesFilename
);
173 InitializeInstalledExtensionService(pref_path
, source_install_dir
);
174 InitFileTaskRunner();
176 // This is the directory that is going to be deleted, so make sure it actually
177 // is there before the garbage collection.
178 ASSERT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
179 "hpiknbiabeeppbpihjehijgoemciehgk/3")));
182 GarbageCollectExtensions();
184 // Verify that the pending update for the first extension got installed.
185 EXPECT_FALSE(base::PathExists(extensions_install_dir().AppendASCII(
186 "bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0")));
187 EXPECT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
188 "bjafgdebaacbbbecmhlhpofkepfkgcpa/2.0")));
189 EXPECT_TRUE(base::PathExists(extensions_install_dir().AppendASCII(
190 "hpiknbiabeeppbpihjehijgoemciehgk/2")));
191 EXPECT_FALSE(base::PathExists(extensions_install_dir().AppendASCII(
192 "hpiknbiabeeppbpihjehijgoemciehgk/3")));
194 // Make sure update information got deleted.
195 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(profile_
.get());
197 prefs
->GetDelayedInstallInfo("bjafgdebaacbbbecmhlhpofkepfkgcpa"));
200 } // namespace extensions