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/file_util.h"
12 #include "base/files/file_path.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 "chrome/common/extensions/extension_constants.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/test/test_browser_thread_bundle.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(file_util::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
,
214 extensions::ExtensionDownloaderDelegate::PingResult(),
218 EXPECT_EQ(provided_prefs()->size(), 3ul);
220 const base::DictionaryValue
* entry2
= NULL
;
221 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId2
, &entry2
));
222 EXPECT_FALSE(entry2
->HasKey(
223 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
224 EXPECT_TRUE(entry2
->HasKey(
225 extensions::ExternalProviderImpl::kExternalCrx
));
226 EXPECT_TRUE(entry2
->HasKey(
227 extensions::ExternalProviderImpl::kExternalVersion
));
228 from_webstore
= false;
229 EXPECT_TRUE(entry2
->GetBoolean(
230 extensions::ExternalProviderImpl::kIsFromWebstore
, &from_webstore
));
231 EXPECT_TRUE(from_webstore
);
232 EXPECT_TRUE(base::PathExists(
233 GetExtensionFile(cache_dir
, kTestExtensionId2
, "2")));
235 // Update not from Webstore.
236 base::FilePath temp_file4
= temp_dir
.Append("d.crx");
237 CreateFile(temp_file4
);
238 external_cache
.OnExtensionDownloadFinished(kTestExtensionId4
,
242 extensions::ExtensionDownloaderDelegate::PingResult(),
246 EXPECT_EQ(provided_prefs()->size(), 4ul);
248 const base::DictionaryValue
* entry4
= NULL
;
249 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId4
, &entry4
));
250 EXPECT_FALSE(entry4
->HasKey(
251 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
252 EXPECT_TRUE(entry4
->HasKey(
253 extensions::ExternalProviderImpl::kExternalCrx
));
254 EXPECT_TRUE(entry4
->HasKey(
255 extensions::ExternalProviderImpl::kExternalVersion
));
256 EXPECT_FALSE(entry4
->HasKey(
257 extensions::ExternalProviderImpl::kIsFromWebstore
));
258 EXPECT_TRUE(base::PathExists(
259 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4")));
261 // Damaged file should be removed from disk.
262 external_cache
.OnDamagedFileDetected(
263 GetExtensionFile(cache_dir
, kTestExtensionId2
, "2"));
265 EXPECT_EQ(provided_prefs()->size(), 3ul);
266 EXPECT_FALSE(base::PathExists(
267 GetExtensionFile(cache_dir
, kTestExtensionId2
, "2")));
269 // Shutdown with callback OnExtensionListsUpdated that clears prefs.
270 scoped_ptr
<base::DictionaryValue
> empty(new base::DictionaryValue
);
271 external_cache
.Shutdown(
272 base::Bind(&ExternalCacheTest::OnExtensionListsUpdated
,
273 base::Unretained(this),
274 base::Unretained(empty
.get())));
276 EXPECT_EQ(provided_prefs()->size(), 0ul);
278 // After Shutdown directory shouldn't be touched.
279 external_cache
.OnDamagedFileDetected(
280 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4"));
282 EXPECT_TRUE(base::PathExists(
283 GetExtensionFile(cache_dir
, kTestExtensionId4
, "4")));
286 TEST_F(ExternalCacheTest
, PreserveInstalled
) {
287 base::FilePath
cache_dir(CreateCacheDir(false));
288 ExternalCache
external_cache(cache_dir
, request_context_getter(),
289 background_task_runner(), this, true, false);
291 scoped_ptr
<base::DictionaryValue
> prefs(new base::DictionaryValue
);
292 prefs
->Set(kTestExtensionId1
, CreateEntryWithUpdateUrl(true));
293 prefs
->Set(kTestExtensionId2
, CreateEntryWithUpdateUrl(true));
295 AddInstalledExtension(kTestExtensionId1
, "1");
297 external_cache
.UpdateExtensionsList(prefs
.Pass());
300 ASSERT_TRUE(provided_prefs());
301 EXPECT_EQ(provided_prefs()->size(), 1ul);
303 // File not in cache but extension installed.
304 const base::DictionaryValue
* entry1
= NULL
;
305 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1
, &entry1
));
306 EXPECT_TRUE(entry1
->HasKey(
307 extensions::ExternalProviderImpl::kExternalUpdateUrl
));
308 EXPECT_FALSE(entry1
->HasKey(
309 extensions::ExternalProviderImpl::kExternalCrx
));
310 EXPECT_FALSE(entry1
->HasKey(
311 extensions::ExternalProviderImpl::kExternalVersion
));
314 } // namespace chromeos