Home | History | Annotate | Download | only in extensions
      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 "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.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"
     22 
     23 namespace extensions {
     24 
     25 class ExtensionGarbageCollectorUnitTest : public ExtensionServiceTestBase {
     26  protected:
     27   void InitPluginService() {
     28 #if defined(ENABLE_PLUGINS)
     29     content::PluginService::GetInstance()->Init();
     30 #endif
     31   }
     32 
     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));
     46 
     47   }
     48 
     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();
     57   }
     58 };
     59 
     60 // Test that partially deleted extensions are cleaned up during startup.
     61 TEST_F(ExtensionGarbageCollectorUnitTest, CleanupOnStartup) {
     62   const std::string kExtensionId = "behllobkkfkfnphdnhnkndlbkcpglgmj";
     63 
     64   InitPluginService();
     65   InitializeGoodInstalledExtensionService();
     66   InitFileTaskRunner();
     67 
     68   // Simulate that one of them got partially deleted by clearing its pref.
     69   {
     70     DictionaryPrefUpdate update(profile_->GetPrefs(), "extensions.settings");
     71     base::DictionaryValue* dict = update.Get();
     72     ASSERT_TRUE(dict != NULL);
     73     dict->Remove(kExtensionId, NULL);
     74   }
     75 
     76   service_->Init();
     77   GarbageCollectExtensions();
     78 
     79   base::FileEnumerator dirs(extensions_install_dir(),
     80                             false,  // not recursive
     81                             base::FileEnumerator::DIRECTORIES);
     82   size_t count = 0;
     83   while (!dirs.Next().empty())
     84     count++;
     85 
     86   // We should have only gotten two extensions now.
     87   EXPECT_EQ(2u, count);
     88 
     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));
     93 }
     94 
     95 // Test that garbage collection doesn't delete anything while a crx is being
     96 // installed.
     97 TEST_F(ExtensionGarbageCollectorUnitTest, NoCleanupDuringInstall) {
     98   const std::string kExtensionId = "behllobkkfkfnphdnhnkndlbkcpglgmj";
     99 
    100   InitPluginService();
    101   InitializeGoodInstalledExtensionService();
    102   InitFileTaskRunner();
    103 
    104   // Simulate that one of them got partially deleted by clearing its pref.
    105   {
    106     DictionaryPrefUpdate update(profile_->GetPrefs(), "extensions.settings");
    107     base::DictionaryValue* dict = update.Get();
    108     ASSERT_TRUE(dict != NULL);
    109     dict->Remove(kExtensionId, NULL);
    110   }
    111 
    112   service_->Init();
    113 
    114   // Simulate a CRX installation.
    115   InstallTracker::Get(profile_.get())->OnBeginCrxInstall(kExtensionId);
    116 
    117   GarbageCollectExtensions();
    118 
    119   // extension1 dir should still exist.
    120   base::FilePath extension_dir =
    121       extensions_install_dir().AppendASCII(kExtensionId);
    122   ASSERT_TRUE(base::PathExists(extension_dir));
    123 
    124   // Finish CRX installation and re-run garbage collection.
    125   InstallTracker::Get(profile_.get())->OnFinishCrxInstall(kExtensionId, false);
    126   GarbageCollectExtensions();
    127 
    128   // extension1 dir should be gone
    129   ASSERT_FALSE(base::PathExists(extension_dir));
    130 }
    131 
    132 // Test that GarbageCollectExtensions deletes the right versions of an
    133 // extension.
    134 TEST_F(ExtensionGarbageCollectorUnitTest, GarbageCollectWithPendingUpdates) {
    135   InitPluginService();
    136 
    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);
    141 
    142   InitializeInstalledExtensionService(pref_path, source_install_dir);
    143   InitFileTaskRunner();
    144 
    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")));
    149 
    150   GarbageCollectExtensions();
    151 
    152   // Verify that the pending update for the first extension didn't get
    153   // deleted.
    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")));
    162 }
    163 
    164 // Test that pending updates are properly handled on startup.
    165 TEST_F(ExtensionGarbageCollectorUnitTest, UpdateOnStartup) {
    166   InitPluginService();
    167 
    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);
    172 
    173   InitializeInstalledExtensionService(pref_path, source_install_dir);
    174   InitFileTaskRunner();
    175 
    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")));
    180 
    181   service_->Init();
    182   GarbageCollectExtensions();
    183 
    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")));
    193 
    194   // Make sure update information got deleted.
    195   ExtensionPrefs* prefs = ExtensionPrefs::Get(profile_.get());
    196   EXPECT_FALSE(
    197       prefs->GetDelayedInstallInfo("bjafgdebaacbbbecmhlhpofkepfkgcpa"));
    198 }
    199 
    200 }  // namespace extensions
    201