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 "chrome/browser/extensions/extension_garbage_collector_chromeos.h"
6
7#include <string>
8#include <vector>
9
10#include "base/files/file_util.h"
11#include "base/prefs/scoped_user_pref_update.h"
12#include "base/prefs/testing_pref_service.h"
13#include "base/strings/string_util.h"
14#include "base/threading/sequenced_worker_pool.h"
15#include "base/values.h"
16#include "chrome/browser/chromeos/login/users/fake_user_manager.h"
17#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
18#include "chrome/browser/chromeos/profiles/profile_helper.h"
19#include "chrome/browser/extensions/extension_assets_manager_chromeos.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/extensions/extension_service_test_base.h"
22#include "chrome/browser/prefs/browser_prefs.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/test/base/testing_browser_process.h"
25#include "chrome/test/base/testing_profile.h"
26#include "chromeos/login/user_names.h"
27#include "components/user_manager/user_manager.h"
28#include "content/public/browser/browser_thread.h"
29#include "content/public/browser/plugin_service.h"
30#include "content/public/test/test_utils.h"
31#include "extensions/browser/extension_prefs.h"
32#include "extensions/browser/install_flag.h"
33#include "extensions/common/manifest_constants.h"
34
35namespace {
36const char kExtensionId1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
37const char kExtensionId2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
38}  // namespace
39
40namespace extensions {
41
42class ExtensionGarbageCollectorChromeOSUnitTest
43    : public ExtensionServiceTestBase {
44 protected:
45  PrefService& local_state() { return local_state_; }
46  const base::FilePath& cache_dir() { return cache_dir_.path(); }
47
48  virtual void SetUp() OVERRIDE {
49    TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
50    chrome::RegisterLocalState(local_state_.registry());
51
52#if defined(ENABLE_PLUGINS)
53    content::PluginService::GetInstance()->Init();
54#endif
55    InitializeGoodInstalledExtensionService();
56
57    // Need real IO thread.
58    service_->SetFileTaskRunnerForTesting(
59        content::BrowserThread::GetBlockingPool()
60            ->GetSequencedTaskRunnerWithShutdownBehavior(
61                content::BrowserThread::GetBlockingPool()
62                    ->GetNamedSequenceToken("ext_install-"),
63                base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
64
65    CHECK(cache_dir_.CreateUniqueTempDir());
66    ExtensionAssetsManagerChromeOS::SetSharedInstallDirForTesting(cache_dir());
67    ExtensionGarbageCollectorChromeOS::ClearGarbageCollectedForTesting();
68
69    // Initialize the UserManager singleton to a fresh FakeUserManager instance.
70    user_manager_enabler_.reset(
71        new chromeos::ScopedUserManagerEnabler(new chromeos::FakeUserManager));
72
73    GetFakeUserManager()->AddUser(chromeos::login::kStubUser);
74    GetFakeUserManager()->LoginUser(chromeos::login::kStubUser);
75    chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(
76        GetFakeUserManager()->GetActiveUser(), profile_.get());
77  }
78
79  virtual void TearDown() OVERRIDE {
80    TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
81  }
82
83  void GarbageCollectExtensions() {
84    ExtensionGarbageCollector::Get(profile_.get())
85        ->GarbageCollectExtensionsForTest();
86    // Wait for GarbageCollectExtensions task to complete.
87    content::RunAllBlockingPoolTasksUntilIdle();
88  }
89
90  base::FilePath CreateSharedExtensionDir(const std::string& id,
91                                          const std::string& version,
92                                          const base::FilePath& shared_dir) {
93    base::FilePath path = shared_dir.AppendASCII(id).AppendASCII(version);
94    CreateDirectory(path);
95    return path;
96  }
97
98  void CreateSharedExtensionPrefs(const std::string& id,
99                                  const std::string& version,
100                                  const std::string& users_string,
101                                  const base::FilePath& path) {
102    DictionaryPrefUpdate shared_extensions(&local_state_,
103        ExtensionAssetsManagerChromeOS::kSharedExtensions);
104
105    base::DictionaryValue* extension_info = NULL;
106    if (!shared_extensions->GetDictionary(id, &extension_info)) {
107      extension_info = new base::DictionaryValue;
108      shared_extensions->Set(id, extension_info);
109    }
110
111    base::DictionaryValue* version_info = new base::DictionaryValue;
112    extension_info->SetWithoutPathExpansion(version, version_info);
113    version_info->SetString(
114        ExtensionAssetsManagerChromeOS::kSharedExtensionPath, path.value());
115
116    base::ListValue* users = new base::ListValue;
117    version_info->Set(ExtensionAssetsManagerChromeOS::kSharedExtensionUsers,
118                      users);
119    std::vector<std::string> users_list;
120    if (Tokenize(users_string, ",", &users_list)) {
121      for (size_t i = 0; i < users_list.size(); i++) {
122        users->AppendString(users_list[i]);
123      }
124    }
125  }
126
127  scoped_refptr<Extension> CreateExtension(const std::string& id,
128                                           const std::string& version,
129                                           const base::FilePath& path) {
130    base::DictionaryValue manifest;
131    manifest.SetString(manifest_keys::kName, "test");
132    manifest.SetString(manifest_keys::kVersion, version);
133
134    std::string error;
135    scoped_refptr<Extension> extension = Extension::Create(
136        path, Manifest::INTERNAL, manifest, Extension::NO_FLAGS, id, &error);
137    CHECK(extension.get()) << error;
138    CHECK_EQ(id, extension->id());
139
140    return extension;
141  }
142
143  ExtensionPrefs* GetExtensionPrefs() {
144    return ExtensionPrefs::Get(profile_.get());
145  }
146
147  chromeos::FakeUserManager* GetFakeUserManager() {
148    return static_cast<chromeos::FakeUserManager*>(
149        user_manager::UserManager::Get());
150  }
151
152 private:
153  scoped_ptr<chromeos::ScopedUserManagerEnabler> user_manager_enabler_;
154  TestingPrefServiceSimple local_state_;
155  base::ScopedTempDir cache_dir_;
156};
157
158// Test shared extensions clean up.
159TEST_F(ExtensionGarbageCollectorChromeOSUnitTest, SharedExtensions) {
160  // Version for non-existing user.
161  base::FilePath path_id1_1 = CreateSharedExtensionDir(
162      kExtensionId1, "1.0", cache_dir());
163  CreateSharedExtensionPrefs(kExtensionId1, "1.0", "test@test.com", path_id1_1);
164  EXPECT_TRUE(base::PathExists(path_id1_1));
165
166  // Version for current user but the extension is not installed.
167  base::FilePath path_id1_2 = CreateSharedExtensionDir(
168      kExtensionId1, "2.0", cache_dir());
169  CreateSharedExtensionPrefs(
170      kExtensionId1, "2.0", chromeos::login::kStubUser, path_id1_2);
171  EXPECT_TRUE(base::PathExists(path_id1_2));
172
173  // Version for current user that delayed install.
174  base::FilePath path_id2_1 = CreateSharedExtensionDir(
175      kExtensionId2, "1.0", cache_dir());
176  CreateSharedExtensionPrefs(
177      kExtensionId2, "1.0", chromeos::login::kStubUser, path_id2_1);
178  scoped_refptr<Extension> extension2 = CreateExtension(kExtensionId2, "1.0",
179                                                        path_id2_1);
180  GetExtensionPrefs()->SetDelayedInstallInfo(
181      extension2.get(),
182      Extension::ENABLED,
183      kInstallFlagNone,
184      ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE,
185      syncer::StringOrdinal(),
186      std::string());
187  EXPECT_TRUE(base::PathExists(path_id2_1));
188
189  GarbageCollectExtensions();
190
191  EXPECT_FALSE(base::PathExists(path_id1_1));
192  EXPECT_FALSE(base::PathExists(path_id1_2));
193  EXPECT_FALSE(base::PathExists(cache_dir().AppendASCII(kExtensionId1)));
194
195  EXPECT_TRUE(base::PathExists(path_id2_1));
196
197  const base::DictionaryValue* shared_extensions = local_state().GetDictionary(
198      ExtensionAssetsManagerChromeOS::kSharedExtensions);
199  ASSERT_TRUE(shared_extensions);
200
201  EXPECT_FALSE(shared_extensions->HasKey(kExtensionId1));
202  EXPECT_TRUE(shared_extensions->HasKey(kExtensionId2));
203}
204
205}  // namespace extensions
206