1 // Copyright 2013 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 "chrome/browser/chromeos/extensions/external_cache.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/run_loop.h"
15 #include "base/test/sequenced_worker_pool_owner.h"
16 #include "base/values.h"
17 #include "chrome/browser/chromeos/settings/cros_settings.h"
18 #include "chrome/browser/chromeos/settings/device_settings_service.h"
19 #include "chrome/browser/extensions/external_provider_impl.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/test/test_browser_thread_bundle.h"
22 #include "extensions/common/extension_urls.h"
23 #include "net/url_request/test_url_fetcher_factory.h"
24 #include "net/url_request/url_fetcher_impl.h"
25 #include "net/url_request/url_request_test_util.h"
26 #include "testing/gtest/include/gtest/gtest.h"
30 const char kTestExtensionId1
[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
31 const char kTestExtensionId2
[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
32 const char kTestExtensionId3
[] = "cccccccccccccccccccccccccccccccc";
33 const char kTestExtensionId4
[] = "dddddddddddddddddddddddddddddddd";
34 const char kNonWebstoreUpdateUrl
[] = "https://localhost/service/update2/crx";
40 class ExternalCacheTest
: public testing::Test
,
41 public ExternalCache::Delegate
{
44 : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD
) {
46 ~ExternalCacheTest() override
{}
48 scoped_refptr
<base::SequencedTaskRunner
> background_task_runner() {
49 return background_task_runner_
;
52 net::URLRequestContextGetter
* request_context_getter() {
53 return request_context_getter_
.get();
56 const base::DictionaryValue
* provided_prefs() {
60 // testing::Test overrides:
61 void SetUp() override
{
62 request_context_getter_
= new net::TestURLRequestContextGetter(
63 content::BrowserThread::GetMessageLoopProxyForThread(
64 content::BrowserThread::IO
));
65 fetcher_factory_
.reset(new net::TestURLFetcherFactory());
68 new base::SequencedWorkerPoolOwner(3, "Background Pool"));
69 background_task_runner_
= pool_owner_
->pool()->GetSequencedTaskRunner(
70 pool_owner_
->pool()->GetNamedSequenceToken("background"));
73 void TearDown() override
{
74 pool_owner_
->pool()->Shutdown();
75 base::RunLoop().RunUntilIdle();
78 // ExternalCache::Delegate:
79 void OnExtensionListsUpdated(const base::DictionaryValue
* prefs
) override
{
80 prefs_
.reset(prefs
->DeepCopy());
83 std::string
GetInstalledExtensionVersion(const std::string
& id
) override
{
84 std::map
<std::string
, std::string
>::iterator it
=
85 installed_extensions_
.find(id
);
86 return it
!= installed_extensions_
.end() ? it
->second
: std::string();
89 base::FilePath
CreateCacheDir(bool initialized
) {
90 EXPECT_TRUE(cache_dir_
.CreateUniqueTempDir());
92 CreateFlagFile(cache_dir_
.path());
93 return cache_dir_
.path();
96 base::FilePath
CreateTempDir() {
97 EXPECT_TRUE(temp_dir_
.CreateUniqueTempDir());
98 return temp_dir_
.path();
101 void CreateFlagFile(const base::FilePath
& dir
) {
102 CreateFile(dir
.Append(
103 extensions::LocalExtensionCache::kCacheReadyFlagFileName
));
106 void CreateExtensionFile(const base::FilePath
& dir
,
107 const std::string
& id
,
108 const std::string
& version
) {
109 CreateFile(GetExtensionFile(dir
, id
, version
));
112 void CreateFile(const base::FilePath
& file
) {
113 EXPECT_EQ(base::WriteFile(file
, NULL
, 0), 0);
116 base::FilePath
GetExtensionFile(const base::FilePath
& dir
,
117 const std::string
& id
,
118 const std::string
& version
) {
119 return dir
.Append(id
+ "-" + version
+ ".crx");
122 base::DictionaryValue
* CreateEntryWithUpdateUrl(bool from_webstore
) {
123 base::DictionaryValue
* entry
= new base::DictionaryValue
;
124 entry
->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl
,
125 from_webstore
? extension_urls::GetWebstoreUpdateUrl().spec()
126 : kNonWebstoreUpdateUrl
);
130 void WaitForCompletion() {
131 // Wait for background task completion that sends replay to UI thread.
132 pool_owner_
->pool()->FlushForTesting();
133 // Wait for UI thread task completion.
134 base::RunLoop().RunUntilIdle();
137 void AddInstalledExtension(const std::string
& id
,
138 const std::string
& version
) {
139 installed_extensions_
[id
] = version
;
143 content::TestBrowserThreadBundle thread_bundle_
;
145 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter_
;
146 scoped_ptr
<net::TestURLFetcherFactory
> fetcher_factory_
;
148 scoped_ptr
<base::SequencedWorkerPoolOwner
> pool_owner_
;
149 scoped_refptr
<base::SequencedTaskRunner
> background_task_runner_
;
151 base::ScopedTempDir cache_dir_
;
152 base::ScopedTempDir temp_dir_
;
153 scoped_ptr
<base::DictionaryValue
> prefs_
;
154 std::map
<std::string
, std::string
> installed_extensions_
;
156 ScopedTestDeviceSettingsService test_device_settings_service_
;
157 ScopedTestCrosSettings test_cros_settings_
;
159 DISALLOW_COPY_AND_ASSIGN(ExternalCacheTest
);
162 TEST_F(ExternalCacheTest
, Basic
) {
163 base::FilePath
cache_dir(CreateCacheDir(false));
164 ExternalCache
external_cache(cache_dir
, request_context_getter(),
165 background_task_runner(), this, true, false);
167 scoped_ptr
<base::DictionaryValue
> prefs(new base::DictionaryValue
);
168 base::DictionaryValue
* dict
= CreateEntryWithUpdateUrl(true);
169 prefs
->Set(kTestExtensionId1
, dict
);
170 CreateExtensionFile(cache_dir
, kTestExtensionId1
, "1");
171 dict
= CreateEntryWithUpdateUrl(true);
172 prefs
->Set(kTestExtensionId2
, dict
);
173 prefs
->Set(kTestExtensionId3
, CreateEntryWithUpdateUrl(false));
174 CreateExtensionFile(cache_dir
, kTestExtensionId3
, "3");
175 prefs
->Set(kTestExtensionId4
, CreateEntryWithUpdateUrl(false));
177 external_cache
.UpdateExtensionsList(prefs
.Pass());
180 ASSERT_TRUE(provided_prefs());
181 EXPECT_EQ(provided_prefs()->size(), 2ul);
183 // File in cache from Webstore.
184 const base::DictionaryValue
* entry1
= NULL
;
185 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1
, &entry1
));
186 EXPECT_FALSE(entry1
->HasKey(
187 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
188 EXPECT_TRUE(entry1
->HasKey(
189 extensions::ExternalProviderImpl::kExternalCrx
));
190 EXPECT_TRUE(entry1
->HasKey(
191 extensions::ExternalProviderImpl::kExternalVersion
));
192 bool from_webstore
= false;
193 EXPECT_TRUE(entry1
->GetBoolean(
194 extensions::ExternalProviderImpl::kIsFromWebstore
, &from_webstore
));
195 EXPECT_TRUE(from_webstore
);
197 // File in cache not from Webstore.
198 const base::DictionaryValue
* entry3
= NULL
;
199 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId3
, &entry3
));
200 EXPECT_FALSE(entry3
->HasKey(
201 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
202 EXPECT_TRUE(entry3
->HasKey(
203 extensions::ExternalProviderImpl::kExternalCrx
));
204 EXPECT_TRUE(entry3
->HasKey(
205 extensions::ExternalProviderImpl::kExternalVersion
));
206 EXPECT_FALSE(entry3
->HasKey(
207 extensions::ExternalProviderImpl::kIsFromWebstore
));
209 // Update from Webstore.
210 base::FilePath
temp_dir(CreateTempDir());
211 base::FilePath temp_file2
= temp_dir
.Append("b.crx");
212 CreateFile(temp_file2
);
213 external_cache
.OnExtensionDownloadFinished(
214 extensions::CRXFileInfo(kTestExtensionId2
, temp_file2
), true, GURL(), "2",
215 extensions::ExtensionDownloaderDelegate::PingResult(), std::set
<int>(),
216 extensions::ExtensionDownloaderDelegate::InstallCallback());
219 EXPECT_EQ(provided_prefs()->size(), 3ul);
221 const base::DictionaryValue
* entry2
= NULL
;
222 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId2
, &entry2
));
223 EXPECT_FALSE(entry2
->HasKey(
224 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
225 EXPECT_TRUE(entry2
->HasKey(
226 extensions::ExternalProviderImpl::kExternalCrx
));
227 EXPECT_TRUE(entry2
->HasKey(
228 extensions::ExternalProviderImpl::kExternalVersion
));
229 from_webstore
= false;
230 EXPECT_TRUE(entry2
->GetBoolean(
231 extensions::ExternalProviderImpl::kIsFromWebstore
, &from_webstore
));
232 EXPECT_TRUE(from_webstore
);
233 EXPECT_TRUE(base::PathExists(
234 GetExtensionFile(cache_dir
, kTestExtensionId2
, "2")));
236 // Update not from Webstore.
237 base::FilePath temp_file4
= temp_dir
.Append("d.crx");
238 CreateFile(temp_file4
);
239 external_cache
.OnExtensionDownloadFinished(
240 extensions::CRXFileInfo(kTestExtensionId4
, temp_file4
), true, GURL(), "4",
241 extensions::ExtensionDownloaderDelegate::PingResult(), std::set
<int>(),
242 extensions::ExtensionDownloaderDelegate::InstallCallback());
245 EXPECT_EQ(provided_prefs()->size(), 4ul);
247 const base::DictionaryValue
* entry4
= NULL
;
248 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId4
, &entry4
));
249 EXPECT_FALSE(entry4
->HasKey(
250 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
251 EXPECT_TRUE(entry4
->HasKey(
252 extensions::ExternalProviderImpl::kExternalCrx
));
253 EXPECT_TRUE(entry4
->HasKey(
254 extensions::ExternalProviderImpl::kExternalVersion
));
255 EXPECT_FALSE(entry4
->HasKey(
256 extensions::ExternalProviderImpl::kIsFromWebstore
));
257 EXPECT_TRUE(base::PathExists(
258 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4")));
260 // Damaged file should be removed from disk.
261 external_cache
.OnDamagedFileDetected(
262 GetExtensionFile(cache_dir
, kTestExtensionId2
, "2"));
264 EXPECT_EQ(provided_prefs()->size(), 3ul);
265 EXPECT_FALSE(base::PathExists(
266 GetExtensionFile(cache_dir
, kTestExtensionId2
, "2")));
268 // Shutdown with callback OnExtensionListsUpdated that clears prefs.
269 scoped_ptr
<base::DictionaryValue
> empty(new base::DictionaryValue
);
270 external_cache
.Shutdown(
271 base::Bind(&ExternalCacheTest::OnExtensionListsUpdated
,
272 base::Unretained(this),
273 base::Unretained(empty
.get())));
275 EXPECT_EQ(provided_prefs()->size(), 0ul);
277 // After Shutdown directory shouldn't be touched.
278 external_cache
.OnDamagedFileDetected(
279 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4"));
281 EXPECT_TRUE(base::PathExists(
282 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4")));
285 TEST_F(ExternalCacheTest
, PreserveInstalled
) {
286 base::FilePath
cache_dir(CreateCacheDir(false));
287 ExternalCache
external_cache(cache_dir
, request_context_getter(),
288 background_task_runner(), this, true, false);
290 scoped_ptr
<base::DictionaryValue
> prefs(new base::DictionaryValue
);
291 prefs
->Set(kTestExtensionId1
, CreateEntryWithUpdateUrl(true));
292 prefs
->Set(kTestExtensionId2
, CreateEntryWithUpdateUrl(true));
294 AddInstalledExtension(kTestExtensionId1
, "1");
296 external_cache
.UpdateExtensionsList(prefs
.Pass());
299 ASSERT_TRUE(provided_prefs());
300 EXPECT_EQ(provided_prefs()->size(), 1ul);
302 // File not in cache but extension installed.
303 const base::DictionaryValue
* entry1
= NULL
;
304 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1
, &entry1
));
305 EXPECT_TRUE(entry1
->HasKey(
306 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
307 EXPECT_FALSE(entry1
->HasKey(
308 extensions::ExternalProviderImpl::kExternalCrx
));
309 EXPECT_FALSE(entry1
->HasKey(
310 extensions::ExternalProviderImpl::kExternalVersion
));
313 } // namespace chromeos