• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/chromeos/extensions/external_cache.h"
6 
7 #include <map>
8 #include <set>
9 #include <string>
10 
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"
25 
26 namespace {
27 
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";
33 
34 }  // namespace
35 
36 namespace chromeos {
37 
38 class ExternalCacheTest : public testing::Test,
39                           public ExternalCache::Delegate {
40  public:
ExternalCacheTest()41   ExternalCacheTest()
42     : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD) {
43   }
~ExternalCacheTest()44   virtual ~ExternalCacheTest() {}
45 
background_task_runner()46   scoped_refptr<base::SequencedTaskRunner> background_task_runner() {
47     return background_task_runner_;
48   }
49 
request_context_getter()50   net::URLRequestContextGetter* request_context_getter() {
51     return request_context_getter_.get();
52   }
53 
provided_prefs()54   const base::DictionaryValue* provided_prefs() {
55     return prefs_.get();
56   }
57 
58   // testing::Test overrides:
SetUp()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());
64 
65     pool_owner_.reset(
66         new base::SequencedWorkerPoolOwner(3, "Background Pool"));
67     background_task_runner_ = pool_owner_->pool()->GetSequencedTaskRunner(
68         pool_owner_->pool()->GetNamedSequenceToken("background"));
69   }
70 
TearDown()71   virtual void TearDown() OVERRIDE {
72     pool_owner_->pool()->Shutdown();
73     base::RunLoop().RunUntilIdle();
74   }
75 
76   // ExternalCache::Delegate:
OnExtensionListsUpdated(const base::DictionaryValue * prefs)77   virtual void OnExtensionListsUpdated(
78       const base::DictionaryValue* prefs) OVERRIDE {
79     prefs_.reset(prefs->DeepCopy());
80   }
81 
GetInstalledExtensionVersion(const std::string & id)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();
87   }
88 
CreateCacheDir(bool initialized)89   base::FilePath CreateCacheDir(bool initialized) {
90     EXPECT_TRUE(cache_dir_.CreateUniqueTempDir());
91     if (initialized)
92       CreateFlagFile(cache_dir_.path());
93     return cache_dir_.path();
94   }
95 
CreateTempDir()96   base::FilePath CreateTempDir() {
97     EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
98     return temp_dir_.path();
99   }
100 
CreateFlagFile(const base::FilePath & dir)101   void CreateFlagFile(const base::FilePath& dir) {
102     CreateFile(dir.Append(
103         extensions::LocalExtensionCache::kCacheReadyFlagFileName));
104   }
105 
CreateExtensionFile(const base::FilePath & dir,const std::string & id,const std::string & version)106   void CreateExtensionFile(const base::FilePath& dir,
107                            const std::string& id,
108                            const std::string& version) {
109     CreateFile(GetExtensionFile(dir, id, version));
110   }
111 
CreateFile(const base::FilePath & file)112   void CreateFile(const base::FilePath& file) {
113     EXPECT_EQ(base::WriteFile(file, NULL, 0), 0);
114   }
115 
GetExtensionFile(const base::FilePath & dir,const std::string & id,const std::string & version)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");
120   }
121 
CreateEntryWithUpdateUrl(bool from_webstore)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);
127     return entry;
128   }
129 
WaitForCompletion()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();
135   }
136 
AddInstalledExtension(const std::string & id,const std::string & version)137   void AddInstalledExtension(const std::string& id,
138                              const std::string& version) {
139     installed_extensions_[id] = version;
140   }
141 
142  private:
143   content::TestBrowserThreadBundle thread_bundle_;
144 
145   scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
146   scoped_ptr<net::TestURLFetcherFactory> fetcher_factory_;
147 
148   scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_;
149   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
150 
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_;
155 
156   DISALLOW_COPY_AND_ASSIGN(ExternalCacheTest);
157 };
158 
TEST_F(ExternalCacheTest,Basic)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);
163 
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));
173 
174   external_cache.UpdateExtensionsList(prefs.Pass());
175   WaitForCompletion();
176 
177   ASSERT_TRUE(provided_prefs());
178   EXPECT_EQ(provided_prefs()->size(), 2ul);
179 
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);
193 
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));
205 
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,
211       temp_file2,
212       true,
213       GURL(),
214       "2",
215       extensions::ExtensionDownloaderDelegate::PingResult(),
216       std::set<int>());
217 
218   WaitForCompletion();
219   EXPECT_EQ(provided_prefs()->size(), 3ul);
220 
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")));
235 
236   // Update not from Webstore.
237   base::FilePath temp_file4 = temp_dir.Append("d.crx");
238   CreateFile(temp_file4);
239   external_cache.OnExtensionDownloadFinished(kTestExtensionId4,
240       temp_file4,
241       true,
242       GURL(),
243       "4",
244       extensions::ExtensionDownloaderDelegate::PingResult(),
245       std::set<int>());
246 
247   WaitForCompletion();
248   EXPECT_EQ(provided_prefs()->size(), 4ul);
249 
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")));
262 
263   // Damaged file should be removed from disk.
264   external_cache.OnDamagedFileDetected(
265       GetExtensionFile(cache_dir, kTestExtensionId2, "2"));
266   WaitForCompletion();
267   EXPECT_EQ(provided_prefs()->size(), 3ul);
268   EXPECT_FALSE(base::PathExists(
269       GetExtensionFile(cache_dir, kTestExtensionId2, "2")));
270 
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())));
277   WaitForCompletion();
278   EXPECT_EQ(provided_prefs()->size(), 0ul);
279 
280   // After Shutdown directory shouldn't be touched.
281   external_cache.OnDamagedFileDetected(
282       GetExtensionFile(cache_dir, kTestExtensionId4, "4"));
283   WaitForCompletion();
284   EXPECT_TRUE(base::PathExists(
285       GetExtensionFile(cache_dir, kTestExtensionId4, "4")));
286 }
287 
TEST_F(ExternalCacheTest,PreserveInstalled)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);
292 
293   scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
294   prefs->Set(kTestExtensionId1, CreateEntryWithUpdateUrl(true));
295   prefs->Set(kTestExtensionId2, CreateEntryWithUpdateUrl(true));
296 
297   AddInstalledExtension(kTestExtensionId1, "1");
298 
299   external_cache.UpdateExtensionsList(prefs.Pass());
300   WaitForCompletion();
301 
302   ASSERT_TRUE(provided_prefs());
303   EXPECT_EQ(provided_prefs()->size(), 1ul);
304 
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));
314 }
315 
316 }  // namespace chromeos
317