Chromium Code Reviews| Index: chrome/browser/extensions/extension_garbage_collector_unittest.cc |
| diff --git a/chrome/browser/extensions/extension_garbage_collector_unittest.cc b/chrome/browser/extensions/extension_garbage_collector_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0e305a10456bd306d30aaddb325f8b5a61e9d556 |
| --- /dev/null |
| +++ b/chrome/browser/extensions/extension_garbage_collector_unittest.cc |
| @@ -0,0 +1,160 @@ |
| +// 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 "base/file_util.h" |
| +#include "base/files/file_enumerator.h" |
| +#include "base/files/file_path.h" |
| +#include "base/prefs/scoped_user_pref_update.h" |
| +#include "base/threading/sequenced_worker_pool.h" |
| +#include "base/values.h" |
| +#include "chrome/browser/extensions/extension_garbage_collector.h" |
| +#include "chrome/browser/extensions/extension_service_unittest.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/common/chrome_constants.h" |
| +#include "chrome/test/base/testing_profile.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/plugin_service.h" |
| +#include "content/public/test/test_browser_thread_bundle.h" |
| + |
| +namespace extensions { |
| + |
| +class ExtensionGarbageCollectorUnitTest : public ExtensionServiceTestBase { |
| + protected: |
| + void InitPluginService() { |
| +#if defined(ENABLE_PLUGINS) |
| + content::PluginService::GetInstance()->Init(); |
| +#endif |
| + } |
| + |
| + // Garbage collection, in production, runs on multiple threads. This is |
| + // important to test to make sure we don't violate thread safety. Use a real |
| + // task runner for these tests. |
| + // TODO (rdevlin.cronin): It's kind of hacky that we have to do these things |
|
Devlin
2014/03/28 22:42:44
Difference 2: We now use multiple threads when we
|
| + // since we're using ExtensionServiceTestBase. Instead, we should probably do |
| + // something like use an options flag, and handle it all in the base class. |
| + void InitFileTaskRunner() { |
| + service_->SetFileTaskRunnerForTesting( |
| + content::BrowserThread::GetBlockingPool() |
| + ->GetSequencedTaskRunnerWithShutdownBehavior( |
| + content::BrowserThread::GetBlockingPool() |
| + ->GetNamedSequenceToken("ext_install-"), |
| + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)); |
| + |
| + } |
| + |
| + // A delayed task to call GarbageCollectExtensions is posted by |
| + // ExtensionGarbageCollector's constructor. But, as the test won't wait for |
| + // the delayed task to be called, we have to call it manually instead. |
| + void GarbageCollectExtensions() { |
| + service_->garbage_collector()->GarbageCollectExtensionsForTest(); |
| + // Wait for GarbageCollectExtensions task to complete. |
| + content::BrowserThread::GetBlockingPool()->FlushForTesting(); |
| + } |
| +}; |
| + |
| +// Test that partially deleted extensions are cleaned up during startup |
| +// Test loading bad extensions from the profile directory. |
| +TEST_F(ExtensionGarbageCollectorUnitTest, CleanupOnStartup) { |
| + const std::string kExtensionId = "behllobkkfkfnphdnhnkndlbkcpglgmj"; |
| + |
| + InitPluginService(); |
| + InitializeGoodInstalledExtensionService(); |
| + InitFileTaskRunner(); |
| + |
| + // Simulate that one of them got partially deleted by clearing its pref. |
| + { |
| + DictionaryPrefUpdate update(profile_->GetPrefs(), "extensions.settings"); |
| + base::DictionaryValue* dict = update.Get(); |
| + ASSERT_TRUE(dict != NULL); |
| + dict->Remove(kExtensionId, NULL); |
| + } |
| + |
| + service_->Init(); |
| + GarbageCollectExtensions(); |
| + |
| + base::FileEnumerator dirs(extensions_install_dir_, |
| + false, // not recursive |
| + base::FileEnumerator::DIRECTORIES); |
| + size_t count = 0; |
| + while (!dirs.Next().empty()) |
| + count++; |
| + |
| + // We should have only gotten two extensions now. |
| + EXPECT_EQ(2u, count); |
| + |
| + // And extension1 dir should now be toast. |
| + base::FilePath extension_dir = |
| + extensions_install_dir_.AppendASCII(kExtensionId); |
| + ASSERT_FALSE(base::PathExists(extension_dir)); |
| +} |
| + |
| +// Test that GarbageCollectExtensions deletes the right versions of an |
| +// extension. |
| +TEST_F(ExtensionGarbageCollectorUnitTest, GarbageCollectWithPendingUpdates) { |
| + InitPluginService(); |
| + |
| + base::FilePath source_install_dir = |
| + data_dir_.AppendASCII("pending_updates").AppendASCII("Extensions"); |
| + base::FilePath pref_path = |
| + source_install_dir.DirName().Append(chrome::kPreferencesFilename); |
| + |
| + InitializeInstalledExtensionService(pref_path, source_install_dir); |
| + InitFileTaskRunner(); |
| + |
| + // This is the directory that is going to be deleted, so make sure it actually |
| + // is there before the garbage collection. |
| + ASSERT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "hpiknbiabeeppbpihjehijgoemciehgk/3"))); |
| + |
| + GarbageCollectExtensions(); |
| + |
| + // Verify that the pending update for the first extension didn't get |
| + // deleted. |
| + EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0"))); |
| + EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "bjafgdebaacbbbecmhlhpofkepfkgcpa/2.0"))); |
| + EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "hpiknbiabeeppbpihjehijgoemciehgk/2"))); |
| + EXPECT_FALSE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "hpiknbiabeeppbpihjehijgoemciehgk/3"))); |
| +} |
| + |
| +// Test that pending updates are properly handled on startup. |
| +TEST_F(ExtensionGarbageCollectorUnitTest, UpdateOnStartup) { |
| + InitPluginService(); |
| + |
| + base::FilePath source_install_dir = |
| + data_dir_.AppendASCII("pending_updates").AppendASCII("Extensions"); |
| + base::FilePath pref_path = |
| + source_install_dir.DirName().Append(chrome::kPreferencesFilename); |
| + |
| + InitializeInstalledExtensionService(pref_path, source_install_dir); |
| + InitFileTaskRunner(); |
| + |
| + // This is the directory that is going to be deleted, so make sure it actually |
| + // is there before the garbage collection. |
| + ASSERT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "hpiknbiabeeppbpihjehijgoemciehgk/3"))); |
| + |
| + service_->Init(); |
| + GarbageCollectExtensions(); |
| + |
| + // Verify that the pending update for the first extension got installed. |
| + EXPECT_FALSE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0"))); |
| + EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "bjafgdebaacbbbecmhlhpofkepfkgcpa/2.0"))); |
| + EXPECT_TRUE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "hpiknbiabeeppbpihjehijgoemciehgk/2"))); |
| + EXPECT_FALSE(base::PathExists(extensions_install_dir_.AppendASCII( |
| + "hpiknbiabeeppbpihjehijgoemciehgk/3"))); |
| + |
| + // Make sure update information got deleted. |
| + ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_.get()); |
| + EXPECT_FALSE( |
| + prefs->GetDelayedInstallInfo("bjafgdebaacbbbecmhlhpofkepfkgcpa")); |
| +} |
| + |
| +} // namespace extensions |