external_cache_unittest.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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 <set>
8
9#include "base/file_util.h"
10#include "base/files/file_path.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/run_loop.h"
13#include "base/test/sequenced_worker_pool_owner.h"
14#include "base/values.h"
15#include "chrome/browser/extensions/external_provider_impl.h"
16#include "chrome/common/extensions/extension_constants.h"
17#include "content/public/browser/browser_thread.h"
18#include "content/public/test/test_browser_thread_bundle.h"
19#include "net/url_request/test_url_fetcher_factory.h"
20#include "net/url_request/url_fetcher_impl.h"
21#include "net/url_request/url_request_test_util.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24namespace {
25
26const char kTestExtensionId1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
27const char kTestExtensionId2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
28const char kTestExtensionId3[] = "cccccccccccccccccccccccccccccccc";
29const char kTestExtensionId4[] = "dddddddddddddddddddddddddddddddd";
30const char kNonWebstoreUpdateUrl[] = "https://localhost/service/update2/crx";
31
32}  // namespace
33
34namespace chromeos {
35
36class ExternalCacheTest : public testing::Test,
37                          public ExternalCache::Delegate {
38 public:
39  ExternalCacheTest()
40    : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD) {
41  }
42  virtual ~ExternalCacheTest() {}
43
44  scoped_refptr<base::SequencedTaskRunner> background_task_runner() {
45    return background_task_runner_;
46  }
47
48  net::URLRequestContextGetter* request_context_getter() {
49    return request_context_getter_.get();
50  }
51
52  const base::DictionaryValue* provided_prefs() {
53    return prefs_.get();
54  }
55
56  // testing::Test overrides:
57  virtual void SetUp() OVERRIDE {
58    request_context_getter_ = new net::TestURLRequestContextGetter(
59        content::BrowserThread::GetMessageLoopProxyForThread(
60            content::BrowserThread::IO));
61    fetcher_factory_.reset(new net::TestURLFetcherFactory());
62
63    pool_owner_.reset(
64        new base::SequencedWorkerPoolOwner(3, "Background Pool"));
65    background_task_runner_ = pool_owner_->pool()->GetSequencedTaskRunner(
66        pool_owner_->pool()->GetNamedSequenceToken("background"));
67  }
68
69  virtual void TearDown() OVERRIDE {
70    pool_owner_->pool()->Shutdown();
71    base::RunLoop().RunUntilIdle();
72  }
73
74  // ExternalCache::Delegate:
75  virtual void OnExtensionListsUpdated(
76      const base::DictionaryValue* prefs) OVERRIDE {
77    prefs_.reset(prefs->DeepCopy());
78  }
79
80  base::FilePath CreateCacheDir(bool initialized) {
81    EXPECT_TRUE(cache_dir_.CreateUniqueTempDir());
82    if (initialized)
83      CreateFlagFile(cache_dir_.path());
84    return cache_dir_.path();
85  }
86
87  base::FilePath CreateTempDir() {
88    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
89    return temp_dir_.path();
90  }
91
92  void CreateFlagFile(const base::FilePath& dir) {
93    CreateFile(dir.Append(ExternalCache::kCacheReadyFlagFileName));
94  }
95
96  void CreateExtensionFile(const base::FilePath& dir,
97                           const std::string& id,
98                           const std::string& version) {
99    CreateFile(GetExtensionFile(dir, id, version));
100  }
101
102  void CreateFile(const base::FilePath& file) {
103    EXPECT_EQ(file_util::WriteFile(file, NULL, 0), 0);
104  }
105
106  base::FilePath GetExtensionFile(const base::FilePath& dir,
107                                  const std::string& id,
108                                  const std::string& version) {
109    return dir.Append(id + "-" + version + ".crx");
110  }
111
112  base::DictionaryValue* CreateEntryWithUpdateUrl(bool from_webstore) {
113    base::DictionaryValue* entry = new base::DictionaryValue;
114    entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
115        from_webstore ? extension_urls::GetWebstoreUpdateUrl().spec()
116                      : kNonWebstoreUpdateUrl);
117    return entry;
118  }
119
120  void WaitForCompletion() {
121    // Wait for background task completion that sends replay to UI thread.
122    pool_owner_->pool()->FlushForTesting();
123    // Wait for UI thread task completion.
124    base::RunLoop().RunUntilIdle();
125  }
126
127 private:
128  content::TestBrowserThreadBundle thread_bundle_;
129
130  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
131  scoped_ptr<net::TestURLFetcherFactory> fetcher_factory_;
132
133  scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_;
134  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
135
136  base::ScopedTempDir cache_dir_;
137  base::ScopedTempDir temp_dir_;
138  scoped_ptr<base::DictionaryValue> prefs_;
139
140  DISALLOW_COPY_AND_ASSIGN(ExternalCacheTest);
141};
142
143TEST_F(ExternalCacheTest, Basic) {
144  base::FilePath cache_dir(CreateCacheDir(false));
145  ExternalCache external_cache(cache_dir, request_context_getter(),
146      background_task_runner(), this, true, false);
147
148  scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
149  base::DictionaryValue* dict = CreateEntryWithUpdateUrl(true);
150  dict->SetBoolean(
151      extensions::ExternalProviderImpl::kRequirePermissionsConsent, true);
152  prefs->Set(kTestExtensionId1, dict);
153  CreateExtensionFile(cache_dir, kTestExtensionId1, "1");
154  dict = CreateEntryWithUpdateUrl(true);
155  dict->SetBoolean(
156      extensions::ExternalProviderImpl::kRequirePermissionsConsent, true);
157  prefs->Set(kTestExtensionId2, dict);
158  prefs->Set(kTestExtensionId3, CreateEntryWithUpdateUrl(false));
159  CreateExtensionFile(cache_dir, kTestExtensionId3, "3");
160  prefs->Set(kTestExtensionId4, CreateEntryWithUpdateUrl(false));
161
162  external_cache.UpdateExtensionsList(prefs.Pass());
163  WaitForCompletion();
164
165  ASSERT_TRUE(provided_prefs());
166  EXPECT_EQ(provided_prefs()->size(), 2ul);
167
168  // File in cache from Webstore.
169  const base::DictionaryValue* entry1 = NULL;
170  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1, &entry1));
171  EXPECT_FALSE(entry1->HasKey(
172      extensions::ExternalProviderImpl::kExternalUpdateUrl));
173  EXPECT_TRUE(entry1->HasKey(
174      extensions::ExternalProviderImpl::kExternalCrx));
175  EXPECT_TRUE(entry1->HasKey(
176      extensions::ExternalProviderImpl::kExternalVersion));
177  bool from_webstore = false;
178  EXPECT_TRUE(entry1->GetBoolean(
179      extensions::ExternalProviderImpl::kIsFromWebstore, &from_webstore));
180  EXPECT_TRUE(from_webstore);
181  bool require_permissions_consent = false;
182  EXPECT_TRUE(entry1->GetBoolean(
183      extensions::ExternalProviderImpl::kRequirePermissionsConsent,
184      &require_permissions_consent));
185  EXPECT_TRUE(require_permissions_consent);
186
187  // File in cache not from Webstore.
188  const base::DictionaryValue* entry3 = NULL;
189  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId3, &entry3));
190  EXPECT_FALSE(entry3->HasKey(
191      extensions::ExternalProviderImpl::kExternalUpdateUrl));
192  EXPECT_TRUE(entry3->HasKey(
193      extensions::ExternalProviderImpl::kExternalCrx));
194  EXPECT_TRUE(entry3->HasKey(
195      extensions::ExternalProviderImpl::kExternalVersion));
196  EXPECT_FALSE(entry3->HasKey(
197      extensions::ExternalProviderImpl::kIsFromWebstore));
198
199  // Update from Webstore.
200  base::FilePath temp_dir(CreateTempDir());
201  base::FilePath temp_file2 = temp_dir.Append("b.crx");
202  CreateFile(temp_file2);
203  external_cache.OnExtensionDownloadFinished(kTestExtensionId2,
204      temp_file2,
205      GURL(),
206      "2",
207      extensions::ExtensionDownloaderDelegate::PingResult(),
208      std::set<int>());
209
210  WaitForCompletion();
211  EXPECT_EQ(provided_prefs()->size(), 3ul);
212
213  const base::DictionaryValue* entry2 = NULL;
214  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId2, &entry2));
215  EXPECT_FALSE(entry2->HasKey(
216      extensions::ExternalProviderImpl::kExternalUpdateUrl));
217  EXPECT_TRUE(entry2->HasKey(
218      extensions::ExternalProviderImpl::kExternalCrx));
219  EXPECT_TRUE(entry2->HasKey(
220      extensions::ExternalProviderImpl::kExternalVersion));
221  from_webstore = false;
222  EXPECT_TRUE(entry2->GetBoolean(
223      extensions::ExternalProviderImpl::kIsFromWebstore, &from_webstore));
224  EXPECT_TRUE(from_webstore);
225  require_permissions_consent = false;
226  EXPECT_TRUE(entry2->GetBoolean(
227      extensions::ExternalProviderImpl::kRequirePermissionsConsent,
228      &require_permissions_consent));
229  EXPECT_TRUE(require_permissions_consent);
230  EXPECT_TRUE(base::PathExists(
231      GetExtensionFile(cache_dir, kTestExtensionId2, "2")));
232
233  // Update not from Webstore.
234  base::FilePath temp_file4 = temp_dir.Append("d.crx");
235  CreateFile(temp_file4);
236  external_cache.OnExtensionDownloadFinished(kTestExtensionId4,
237      temp_file4,
238      GURL(),
239      "4",
240      extensions::ExtensionDownloaderDelegate::PingResult(),
241      std::set<int>());
242
243  WaitForCompletion();
244  EXPECT_EQ(provided_prefs()->size(), 4ul);
245
246  const base::DictionaryValue* entry4 = NULL;
247  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId4, &entry4));
248  EXPECT_FALSE(entry4->HasKey(
249      extensions::ExternalProviderImpl::kExternalUpdateUrl));
250  EXPECT_TRUE(entry4->HasKey(
251      extensions::ExternalProviderImpl::kExternalCrx));
252  EXPECT_TRUE(entry4->HasKey(
253      extensions::ExternalProviderImpl::kExternalVersion));
254  EXPECT_FALSE(entry4->HasKey(
255      extensions::ExternalProviderImpl::kIsFromWebstore));
256  EXPECT_TRUE(base::PathExists(
257      GetExtensionFile(cache_dir, kTestExtensionId4, "4")));
258
259  // Damaged file should be removed from disk.
260  external_cache.OnDamagedFileDetected(
261      GetExtensionFile(cache_dir, kTestExtensionId2, "2"));
262  WaitForCompletion();
263  EXPECT_EQ(provided_prefs()->size(), 3ul);
264  EXPECT_FALSE(base::PathExists(
265      GetExtensionFile(cache_dir, kTestExtensionId2, "2")));
266
267  // Shutdown with callback OnExtensionListsUpdated that clears prefs.
268  scoped_ptr<base::DictionaryValue> empty(new base::DictionaryValue);
269  external_cache.Shutdown(
270        base::Bind(&ExternalCacheTest::OnExtensionListsUpdated,
271                   base::Unretained(this),
272                   base::Unretained(empty.get())));
273  WaitForCompletion();
274  EXPECT_EQ(provided_prefs()->size(), 0ul);
275
276  // After Shutdown directory shouldn't be touched.
277  external_cache.OnDamagedFileDetected(
278      GetExtensionFile(cache_dir, kTestExtensionId4, "4"));
279  WaitForCompletion();
280  EXPECT_TRUE(base::PathExists(
281      GetExtensionFile(cache_dir, kTestExtensionId4, "4")));
282}
283
284}  // namespace chromeos
285