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/extensions/external_provider_impl.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/test/test_browser_thread_bundle.h"
20 #include "extensions/common/extension_urls.h"
21 #include "net/url_request/test_url_fetcher_factory.h"
22 #include "net/url_request/url_fetcher_impl.h"
23 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
28 const char kTestExtensionId1
[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
29 const char kTestExtensionId2
[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
30 const char kTestExtensionId3
[] = "cccccccccccccccccccccccccccccccc";
31 const char kTestExtensionId4
[] = "dddddddddddddddddddddddddddddddd";
32 const char kNonWebstoreUpdateUrl
[] = "https://localhost/service/update2/crx";
38 class ExternalCacheTest
: public testing::Test
,
39 public ExternalCache::Delegate
{
42 : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD
) {
44 virtual ~ExternalCacheTest() {}
46 scoped_refptr
<base::SequencedTaskRunner
> background_task_runner() {
47 return background_task_runner_
;
50 net::URLRequestContextGetter
* request_context_getter() {
51 return request_context_getter_
.get();
54 const base::DictionaryValue
* provided_prefs() {
58 // testing::Test overrides:
59 virtual void SetUp() override
{
60 request_context_getter_
= new net::TestURLRequestContextGetter(
61 content::BrowserThread::GetMessageLoopProxyForThread(
62 content::BrowserThread::IO
));
63 fetcher_factory_
.reset(new net::TestURLFetcherFactory());
66 new base::SequencedWorkerPoolOwner(3, "Background Pool"));
67 background_task_runner_
= pool_owner_
->pool()->GetSequencedTaskRunner(
68 pool_owner_
->pool()->GetNamedSequenceToken("background"));
71 virtual void TearDown() override
{
72 pool_owner_
->pool()->Shutdown();
73 base::RunLoop().RunUntilIdle();
76 // ExternalCache::Delegate:
77 virtual void OnExtensionListsUpdated(
78 const base::DictionaryValue
* prefs
) override
{
79 prefs_
.reset(prefs
->DeepCopy());
82 virtual std::string
GetInstalledExtensionVersion(
83 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 DISALLOW_COPY_AND_ASSIGN(ExternalCacheTest
);
159 TEST_F(ExternalCacheTest
, Basic
) {
160 base::FilePath
cache_dir(CreateCacheDir(false));
161 ExternalCache
external_cache(cache_dir
, request_context_getter(),
162 background_task_runner(), this, true, false);
164 scoped_ptr
<base::DictionaryValue
> prefs(new base::DictionaryValue
);
165 base::DictionaryValue
* dict
= CreateEntryWithUpdateUrl(true);
166 prefs
->Set(kTestExtensionId1
, dict
);
167 CreateExtensionFile(cache_dir
, kTestExtensionId1
, "1");
168 dict
= CreateEntryWithUpdateUrl(true);
169 prefs
->Set(kTestExtensionId2
, dict
);
170 prefs
->Set(kTestExtensionId3
, CreateEntryWithUpdateUrl(false));
171 CreateExtensionFile(cache_dir
, kTestExtensionId3
, "3");
172 prefs
->Set(kTestExtensionId4
, CreateEntryWithUpdateUrl(false));
174 external_cache
.UpdateExtensionsList(prefs
.Pass());
177 ASSERT_TRUE(provided_prefs());
178 EXPECT_EQ(provided_prefs()->size(), 2ul);
180 // File in cache from Webstore.
181 const base::DictionaryValue
* entry1
= NULL
;
182 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1
, &entry1
));
183 EXPECT_FALSE(entry1
->HasKey(
184 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
185 EXPECT_TRUE(entry1
->HasKey(
186 extensions::ExternalProviderImpl::kExternalCrx
));
187 EXPECT_TRUE(entry1
->HasKey(
188 extensions::ExternalProviderImpl::kExternalVersion
));
189 bool from_webstore
= false;
190 EXPECT_TRUE(entry1
->GetBoolean(
191 extensions::ExternalProviderImpl::kIsFromWebstore
, &from_webstore
));
192 EXPECT_TRUE(from_webstore
);
194 // File in cache not from Webstore.
195 const base::DictionaryValue
* entry3
= NULL
;
196 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId3
, &entry3
));
197 EXPECT_FALSE(entry3
->HasKey(
198 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
199 EXPECT_TRUE(entry3
->HasKey(
200 extensions::ExternalProviderImpl::kExternalCrx
));
201 EXPECT_TRUE(entry3
->HasKey(
202 extensions::ExternalProviderImpl::kExternalVersion
));
203 EXPECT_FALSE(entry3
->HasKey(
204 extensions::ExternalProviderImpl::kIsFromWebstore
));
206 // Update from Webstore.
207 base::FilePath
temp_dir(CreateTempDir());
208 base::FilePath temp_file2
= temp_dir
.Append("b.crx");
209 CreateFile(temp_file2
);
210 external_cache
.OnExtensionDownloadFinished(kTestExtensionId2
,
215 extensions::ExtensionDownloaderDelegate::PingResult(),
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(kTestExtensionId4
,
244 extensions::ExtensionDownloaderDelegate::PingResult(),
248 EXPECT_EQ(provided_prefs()->size(), 4ul);
250 const base::DictionaryValue
* entry4
= NULL
;
251 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId4
, &entry4
));
252 EXPECT_FALSE(entry4
->HasKey(
253 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
254 EXPECT_TRUE(entry4
->HasKey(
255 extensions::ExternalProviderImpl::kExternalCrx
));
256 EXPECT_TRUE(entry4
->HasKey(
257 extensions::ExternalProviderImpl::kExternalVersion
));
258 EXPECT_FALSE(entry4
->HasKey(
259 extensions::ExternalProviderImpl::kIsFromWebstore
));
260 EXPECT_TRUE(base::PathExists(
261 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4")));
263 // Damaged file should be removed from disk.
264 external_cache
.OnDamagedFileDetected(
265 GetExtensionFile(cache_dir
, kTestExtensionId2
, "2"));
267 EXPECT_EQ(provided_prefs()->size(), 3ul);
268 EXPECT_FALSE(base::PathExists(
269 GetExtensionFile(cache_dir
, kTestExtensionId2
, "2")));
271 // Shutdown with callback OnExtensionListsUpdated that clears prefs.
272 scoped_ptr
<base::DictionaryValue
> empty(new base::DictionaryValue
);
273 external_cache
.Shutdown(
274 base::Bind(&ExternalCacheTest::OnExtensionListsUpdated
,
275 base::Unretained(this),
276 base::Unretained(empty
.get())));
278 EXPECT_EQ(provided_prefs()->size(), 0ul);
280 // After Shutdown directory shouldn't be touched.
281 external_cache
.OnDamagedFileDetected(
282 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4"));
284 EXPECT_TRUE(base::PathExists(
285 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4")));
288 TEST_F(ExternalCacheTest
, PreserveInstalled
) {
289 base::FilePath
cache_dir(CreateCacheDir(false));
290 ExternalCache
external_cache(cache_dir
, request_context_getter(),
291 background_task_runner(), this, true, false);
293 scoped_ptr
<base::DictionaryValue
> prefs(new base::DictionaryValue
);
294 prefs
->Set(kTestExtensionId1
, CreateEntryWithUpdateUrl(true));
295 prefs
->Set(kTestExtensionId2
, CreateEntryWithUpdateUrl(true));
297 AddInstalledExtension(kTestExtensionId1
, "1");
299 external_cache
.UpdateExtensionsList(prefs
.Pass());
302 ASSERT_TRUE(provided_prefs());
303 EXPECT_EQ(provided_prefs()->size(), 1ul);
305 // File not in cache but extension installed.
306 const base::DictionaryValue
* entry1
= NULL
;
307 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1
, &entry1
));
308 EXPECT_TRUE(entry1
->HasKey(
309 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
310 EXPECT_FALSE(entry1
->HasKey(
311 extensions::ExternalProviderImpl::kExternalCrx
));
312 EXPECT_FALSE(entry1
->HasKey(
313 extensions::ExternalProviderImpl::kExternalVersion
));
316 } // namespace chromeos