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 "base/bind.h"
6#include "base/bind_helpers.h"
7#include "base/files/file_path.h"
8#include "base/files/file_util.h"
9#include "base/memory/ref_counted.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/path_service.h"
13#include "base/run_loop.h"
14#include "components/component_updater/crx_downloader.h"
15#include "net/base/net_errors.h"
16#include "net/url_request/test_url_request_interceptor.h"
17#include "net/url_request/url_request_test_util.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using base::ContentsEqual;
21
22namespace component_updater {
23
24namespace {
25
26// Intercepts HTTP GET requests sent to "localhost".
27typedef net::LocalHostTestURLRequestInterceptor GetInterceptor;
28
29const char kTestFileName[] = "jebgalgnebhfojomionfpkfelancnnkf.crx";
30
31base::FilePath MakeTestFilePath(const char* file) {
32  base::FilePath path;
33  PathService::Get(base::DIR_SOURCE_ROOT, &path);
34  return path.AppendASCII("components").AppendASCII("test").AppendASCII("data")
35      .AppendASCII("component_updater").AppendASCII(file);
36}
37
38}  // namespace
39
40class CrxDownloaderTest : public testing::Test {
41 public:
42  CrxDownloaderTest();
43  virtual ~CrxDownloaderTest();
44
45  // Overrides from testing::Test.
46  virtual void SetUp() OVERRIDE;
47  virtual void TearDown() OVERRIDE;
48
49  void Quit();
50  void RunThreads();
51  void RunThreadsUntilIdle();
52
53  void DownloadComplete(int crx_context, const CrxDownloader::Result& result);
54
55  void DownloadProgress(int crx_context, const CrxDownloader::Result& result);
56
57 protected:
58  scoped_ptr<CrxDownloader> crx_downloader_;
59
60  scoped_ptr<GetInterceptor> get_interceptor_;
61
62  CrxDownloader::DownloadCallback callback_;
63  CrxDownloader::ProgressCallback progress_callback_;
64
65  int crx_context_;
66
67  int num_download_complete_calls_;
68  CrxDownloader::Result download_complete_result_;
69
70  // These members are updated by DownloadProgress.
71  int num_progress_calls_;
72  CrxDownloader::Result download_progress_result_;
73
74  // A magic value for the context to be used in the tests.
75  static const int kExpectedContext = 0xaabb;
76
77 private:
78  base::MessageLoopForIO loop_;
79  scoped_refptr<net::TestURLRequestContextGetter> context_;
80  base::Closure quit_closure_;
81};
82
83const int CrxDownloaderTest::kExpectedContext;
84
85CrxDownloaderTest::CrxDownloaderTest()
86    : callback_(base::Bind(&CrxDownloaderTest::DownloadComplete,
87                           base::Unretained(this),
88                           kExpectedContext)),
89      progress_callback_(base::Bind(&CrxDownloaderTest::DownloadProgress,
90                                    base::Unretained(this),
91                                    kExpectedContext)),
92      crx_context_(0),
93      num_download_complete_calls_(0),
94      num_progress_calls_(0),
95      context_(new net::TestURLRequestContextGetter(
96          base::MessageLoopProxy::current())) {
97}
98
99CrxDownloaderTest::~CrxDownloaderTest() {
100  context_ = NULL;
101
102  // The GetInterceptor requires the message loop to run to destruct correctly.
103  get_interceptor_.reset();
104  RunThreadsUntilIdle();
105}
106
107void CrxDownloaderTest::SetUp() {
108  num_download_complete_calls_ = 0;
109  download_complete_result_ = CrxDownloader::Result();
110  num_progress_calls_ = 0;
111  download_progress_result_ = CrxDownloader::Result();
112
113  crx_downloader_.reset(CrxDownloader::Create(
114      false,  // Do not use the background downloader in these tests.
115      context_.get(),
116      base::MessageLoopProxy::current(),
117      NULL));  // No |background_task_runner| because no background downloader.
118  crx_downloader_->set_progress_callback(progress_callback_);
119
120  get_interceptor_.reset(new GetInterceptor(base::MessageLoopProxy::current(),
121                                            base::MessageLoopProxy::current()));
122}
123
124void CrxDownloaderTest::TearDown() {
125  crx_downloader_.reset();
126}
127
128void CrxDownloaderTest::Quit() {
129  if (!quit_closure_.is_null())
130    quit_closure_.Run();
131}
132
133void CrxDownloaderTest::DownloadComplete(int crx_context,
134                                         const CrxDownloader::Result& result) {
135  ++num_download_complete_calls_;
136  crx_context_ = crx_context;
137  download_complete_result_ = result;
138  Quit();
139}
140
141void CrxDownloaderTest::DownloadProgress(int crx_context,
142                                         const CrxDownloader::Result& result) {
143  ++num_progress_calls_;
144  download_progress_result_ = result;
145}
146
147void CrxDownloaderTest::RunThreads() {
148  base::RunLoop runloop;
149  quit_closure_ = runloop.QuitClosure();
150  runloop.Run();
151
152  // Since some tests need to drain currently enqueued tasks such as network
153  // intercepts on the IO thread, run the threads until they are
154  // idle. The component updater service won't loop again until the loop count
155  // is set and the service is started.
156  RunThreadsUntilIdle();
157}
158
159void CrxDownloaderTest::RunThreadsUntilIdle() {
160  base::RunLoop().RunUntilIdle();
161}
162
163// Tests that starting a download without a url results in an error.
164TEST_F(CrxDownloaderTest, NoUrl) {
165  std::vector<GURL> urls;
166  crx_downloader_->StartDownload(urls, callback_);
167  RunThreadsUntilIdle();
168
169  EXPECT_EQ(1, num_download_complete_calls_);
170  EXPECT_EQ(kExpectedContext, crx_context_);
171  EXPECT_EQ(-1, download_complete_result_.error);
172  EXPECT_TRUE(download_complete_result_.response.empty());
173  EXPECT_EQ(-1, download_complete_result_.downloaded_bytes);
174  EXPECT_EQ(-1, download_complete_result_.total_bytes);
175  EXPECT_EQ(0, num_progress_calls_);
176}
177
178// Tests that downloading from one url is successful.
179TEST_F(CrxDownloaderTest, OneUrl) {
180  const GURL expected_crx_url =
181      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
182
183  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
184  get_interceptor_->SetResponse(expected_crx_url, test_file);
185
186  crx_downloader_->StartDownloadFromUrl(expected_crx_url, callback_);
187  RunThreads();
188
189  EXPECT_EQ(1, get_interceptor_->GetHitCount());
190
191  EXPECT_EQ(1, num_download_complete_calls_);
192  EXPECT_EQ(kExpectedContext, crx_context_);
193  EXPECT_EQ(0, download_complete_result_.error);
194  EXPECT_EQ(1843, download_complete_result_.downloaded_bytes);
195  EXPECT_EQ(1843, download_complete_result_.total_bytes);
196  EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
197
198  EXPECT_TRUE(base::DeleteFile(download_complete_result_.response, false));
199
200  EXPECT_LE(1, num_progress_calls_);
201  EXPECT_EQ(1843, download_progress_result_.downloaded_bytes);
202  EXPECT_EQ(1843, download_progress_result_.total_bytes);
203}
204
205// Tests that specifying from two urls has no side effects. Expect a successful
206// download, and only one download request be made.
207// This test is flaky on Android. crbug.com/329883
208#if defined(OS_ANDROID)
209#define MAYBE_TwoUrls DISABLED_TwoUrls
210#else
211#define MAYBE_TwoUrls TwoUrls
212#endif
213TEST_F(CrxDownloaderTest, MAYBE_TwoUrls) {
214  const GURL expected_crx_url =
215      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
216
217  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
218  get_interceptor_->SetResponse(expected_crx_url, test_file);
219
220  std::vector<GURL> urls;
221  urls.push_back(expected_crx_url);
222  urls.push_back(expected_crx_url);
223
224  crx_downloader_->StartDownload(urls, callback_);
225  RunThreads();
226
227  EXPECT_EQ(1, get_interceptor_->GetHitCount());
228
229  EXPECT_EQ(1, num_download_complete_calls_);
230  EXPECT_EQ(kExpectedContext, crx_context_);
231  EXPECT_EQ(0, download_complete_result_.error);
232  EXPECT_EQ(1843, download_complete_result_.downloaded_bytes);
233  EXPECT_EQ(1843, download_complete_result_.total_bytes);
234  EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
235
236  EXPECT_TRUE(base::DeleteFile(download_complete_result_.response, false));
237
238  EXPECT_LE(1, num_progress_calls_);
239  EXPECT_EQ(1843, download_progress_result_.downloaded_bytes);
240  EXPECT_EQ(1843, download_progress_result_.total_bytes);
241}
242
243// Tests that an invalid host results in a download error.
244TEST_F(CrxDownloaderTest, OneUrl_InvalidHost) {
245  const GURL expected_crx_url =
246      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
247
248  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
249  get_interceptor_->SetResponse(expected_crx_url, test_file);
250
251  crx_downloader_->StartDownloadFromUrl(
252      GURL("http://no.such.host"
253           "/download/jebgalgnebhfojomionfpkfelancnnkf.crx"),
254      callback_);
255  RunThreads();
256
257  EXPECT_EQ(0, get_interceptor_->GetHitCount());
258
259  EXPECT_EQ(1, num_download_complete_calls_);
260  EXPECT_EQ(kExpectedContext, crx_context_);
261  EXPECT_NE(0, download_complete_result_.error);
262  EXPECT_TRUE(download_complete_result_.response.empty());
263}
264
265// Tests that an invalid path results in a download error.
266TEST_F(CrxDownloaderTest, OneUrl_InvalidPath) {
267  const GURL expected_crx_url =
268      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
269
270  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
271  get_interceptor_->SetResponse(expected_crx_url, test_file);
272
273  crx_downloader_->StartDownloadFromUrl(GURL("http://localhost/no/such/file"),
274                                        callback_);
275  RunThreads();
276
277  EXPECT_EQ(0, get_interceptor_->GetHitCount());
278
279  EXPECT_EQ(1, num_download_complete_calls_);
280  EXPECT_EQ(kExpectedContext, crx_context_);
281  EXPECT_NE(0, download_complete_result_.error);
282  EXPECT_TRUE(download_complete_result_.response.empty());
283}
284
285// Tests that the fallback to a valid url is successful.
286// This test is flaky on Android. crbug.com/329883
287#if defined(OS_ANDROID)
288#define MAYBE_TwoUrls_FirstInvalid DISABLED_TwoUrls_FirstInvalid
289#else
290#define MAYBE_TwoUrls_FirstInvalid TwoUrls_FirstInvalid
291#endif
292TEST_F(CrxDownloaderTest, MAYBE_TwoUrls_FirstInvalid) {
293  const GURL expected_crx_url =
294      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
295
296  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
297  get_interceptor_->SetResponse(expected_crx_url, test_file);
298
299  std::vector<GURL> urls;
300  urls.push_back(GURL("http://localhost/no/such/file"));
301  urls.push_back(expected_crx_url);
302
303  crx_downloader_->StartDownload(urls, callback_);
304  RunThreads();
305
306  EXPECT_EQ(1, get_interceptor_->GetHitCount());
307
308  EXPECT_EQ(1, num_download_complete_calls_);
309  EXPECT_EQ(kExpectedContext, crx_context_);
310  EXPECT_EQ(0, download_complete_result_.error);
311  EXPECT_EQ(1843, download_complete_result_.downloaded_bytes);
312  EXPECT_EQ(1843, download_complete_result_.total_bytes);
313  EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
314
315  EXPECT_TRUE(base::DeleteFile(download_complete_result_.response, false));
316
317  EXPECT_LE(1, num_progress_calls_);
318  EXPECT_EQ(1843, download_progress_result_.downloaded_bytes);
319  EXPECT_EQ(1843, download_progress_result_.total_bytes);
320}
321
322// Tests that the download succeeds if the first url is correct and the
323// second bad url does not have a side-effect.
324TEST_F(CrxDownloaderTest, TwoUrls_SecondInvalid) {
325  const GURL expected_crx_url =
326      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
327
328  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
329  get_interceptor_->SetResponse(expected_crx_url, test_file);
330
331  std::vector<GURL> urls;
332  urls.push_back(expected_crx_url);
333  urls.push_back(GURL("http://localhost/no/such/file"));
334
335  crx_downloader_->StartDownload(urls, callback_);
336  RunThreads();
337
338  EXPECT_EQ(1, get_interceptor_->GetHitCount());
339
340  EXPECT_EQ(1, num_download_complete_calls_);
341  EXPECT_EQ(kExpectedContext, crx_context_);
342  EXPECT_EQ(0, download_complete_result_.error);
343  EXPECT_EQ(1843, download_complete_result_.downloaded_bytes);
344  EXPECT_EQ(1843, download_complete_result_.total_bytes);
345  EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
346
347  EXPECT_TRUE(base::DeleteFile(download_complete_result_.response, false));
348
349  EXPECT_LE(1, num_progress_calls_);
350  EXPECT_EQ(1843, download_progress_result_.downloaded_bytes);
351  EXPECT_EQ(1843, download_progress_result_.total_bytes);
352}
353
354// Tests that the download fails if both urls are bad.
355TEST_F(CrxDownloaderTest, TwoUrls_BothInvalid) {
356  const GURL expected_crx_url =
357      GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
358
359  const base::FilePath test_file(MakeTestFilePath(kTestFileName));
360  get_interceptor_->SetResponse(expected_crx_url, test_file);
361
362  std::vector<GURL> urls;
363  urls.push_back(GURL("http://localhost/no/such/file"));
364  urls.push_back(GURL("http://no.such.host/"
365                      "/download/jebgalgnebhfojomionfpkfelancnnkf.crx"));
366
367  crx_downloader_->StartDownload(urls, callback_);
368  RunThreads();
369
370  EXPECT_EQ(0, get_interceptor_->GetHitCount());
371
372  EXPECT_EQ(1, num_download_complete_calls_);
373  EXPECT_EQ(kExpectedContext, crx_context_);
374  EXPECT_NE(0, download_complete_result_.error);
375  EXPECT_TRUE(download_complete_result_.response.empty());
376}
377
378}  // namespace component_updater
379