1// Copyright 2014 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/extensions/api/image_writer_private/error_messages.h"
6#include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
7#include "chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h"
8#include "chrome/test/base/testing_profile.h"
9#include "content/public/browser/browser_thread.h"
10#include "net/url_request/test_url_request_interceptor.h"
11#include "net/url_request/url_fetcher.h"
12
13namespace extensions {
14namespace image_writer {
15
16namespace {
17
18using content::BrowserThread;
19using testing::_;
20using testing::AnyNumber;
21using testing::AtLeast;
22using testing::Gt;
23using testing::Lt;
24
25const char kTestImageUrl[] = "http://localhost/test/image.zip";
26
27typedef net::LocalHostTestURLRequestInterceptor GetInterceptor;
28
29// This class gives us a generic Operation with the ability to set or inspect
30// the current path to the image file.
31class OperationForTest : public WriteFromUrlOperation {
32 public:
33  OperationForTest(base::WeakPtr<OperationManager> manager_,
34                   const ExtensionId& extension_id,
35                   net::URLRequestContextGetter* request_context,
36                   GURL url,
37                   const std::string& hash,
38                   const std::string& storage_unit_id)
39      : WriteFromUrlOperation(manager_,
40                              extension_id,
41                              request_context,
42                              url,
43                              hash,
44                              storage_unit_id) {}
45
46  virtual void StartImpl() OVERRIDE {}
47
48  // Expose stages for testing.
49  void GetDownloadTarget(const base::Closure& continuation) {
50    WriteFromUrlOperation::GetDownloadTarget(continuation);
51  }
52
53  void Download(const base::Closure& continuation) {
54    WriteFromUrlOperation::Download(continuation);
55  }
56
57  void VerifyDownload(const base::Closure& continuation) {
58    WriteFromUrlOperation::VerifyDownload(continuation);
59  }
60
61  // Helpers to set-up state for intermediate stages.
62  void SetImagePath(const base::FilePath image_path) {
63    image_path_ = image_path;
64  }
65
66  base::FilePath GetImagePath() { return image_path_; }
67
68 private:
69  virtual ~OperationForTest() {};
70};
71
72class ImageWriterWriteFromUrlOperationTest : public ImageWriterUnitTestBase {
73 protected:
74  ImageWriterWriteFromUrlOperationTest() : manager_(&test_profile_) {}
75
76  virtual void SetUp() OVERRIDE {
77    ImageWriterUnitTestBase::SetUp();
78
79    // Turn on interception and set up our dummy file.
80    net::URLFetcher::SetEnableInterceptionForTests(true);
81    get_interceptor_.reset(new GetInterceptor(
82        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
83        BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
84            base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));
85    get_interceptor_->SetResponse(GURL(kTestImageUrl),
86                                  test_utils_.GetImagePath());
87  }
88
89  virtual void TearDown() OVERRIDE {
90    ImageWriterUnitTestBase::TearDown();
91
92    // Remember to turn off global interception.
93    net::URLFetcher::SetEnableInterceptionForTests(false);
94  }
95
96  scoped_refptr<OperationForTest> CreateOperation(const GURL& url,
97                                                  const std::string& hash) {
98    scoped_refptr<OperationForTest> operation(
99        new OperationForTest(manager_.AsWeakPtr(),
100                             kDummyExtensionId,
101                             test_profile_.GetRequestContext(),
102                             url,
103                             hash,
104                             test_utils_.GetDevicePath().AsUTF8Unsafe()));
105    operation->Start();
106    return operation;
107  }
108
109  TestingProfile test_profile_;
110  scoped_ptr<GetInterceptor> get_interceptor_;
111
112  MockOperationManager manager_;
113};
114
115TEST_F(ImageWriterWriteFromUrlOperationTest, SelectTargetWithoutExtension) {
116  scoped_refptr<OperationForTest> operation =
117      CreateOperation(GURL("http://localhost/foo/bar"), "");
118
119  operation->GetDownloadTarget(base::Bind(&base::DoNothing));
120
121  EXPECT_EQ(FILE_PATH_LITERAL("bar"),
122            operation->GetImagePath().BaseName().value());
123
124  operation->Cancel();
125}
126
127TEST_F(ImageWriterWriteFromUrlOperationTest, SelectTargetWithExtension) {
128  scoped_refptr<OperationForTest> operation =
129      CreateOperation(GURL("http://localhost/foo/bar.zip"), "");
130
131  operation->GetDownloadTarget(base::Bind(&base::DoNothing));
132
133  EXPECT_EQ(FILE_PATH_LITERAL("bar.zip"),
134            operation->GetImagePath().BaseName().value());
135
136  operation->Cancel();
137}
138
139TEST_F(ImageWriterWriteFromUrlOperationTest, DownloadFile) {
140  // This test actually triggers the URL fetch code, which will drain the
141  // message queues while waiting for IO, thus we have to run until the
142  // operation completes.
143  base::RunLoop runloop;
144  base::Closure quit_closure = runloop.QuitClosure();
145  base::FilePath download_target_path;
146  scoped_refptr<OperationForTest> operation =
147      CreateOperation(GURL(kTestImageUrl), "");
148
149  EXPECT_TRUE(base::CreateTemporaryFileInDir(test_utils_.GetTempDir(),
150                                             &download_target_path));
151  operation->SetImagePath(download_target_path);
152
153  EXPECT_CALL(
154      manager_,
155      OnProgress(kDummyExtensionId, image_writer_api::STAGE_DOWNLOAD, _))
156      .Times(AtLeast(1));
157  EXPECT_CALL(
158      manager_,
159      OnProgress(kDummyExtensionId, image_writer_api::STAGE_DOWNLOAD, 0))
160      .Times(AnyNumber());
161  EXPECT_CALL(
162      manager_,
163      OnProgress(kDummyExtensionId, image_writer_api::STAGE_DOWNLOAD, 100))
164      .Times(AnyNumber());
165
166  content::BrowserThread::PostTask(
167      content::BrowserThread::FILE,
168      FROM_HERE,
169      base::Bind(&OperationForTest::Download, operation, quit_closure));
170
171  runloop.Run();
172
173  EXPECT_TRUE(base::ContentsEqual(test_utils_.GetImagePath(),
174                                  operation->GetImagePath()));
175
176  EXPECT_EQ(1, get_interceptor_->GetHitCount());
177
178  operation->Cancel();
179}
180
181TEST_F(ImageWriterWriteFromUrlOperationTest, VerifyFile) {
182  scoped_ptr<char[]> data_buffer(new char[kTestFileSize]);
183  base::ReadFile(test_utils_.GetImagePath(), data_buffer.get(), kTestFileSize);
184  base::MD5Digest expected_digest;
185  base::MD5Sum(data_buffer.get(), kTestFileSize, &expected_digest);
186  std::string expected_hash = base::MD5DigestToBase16(expected_digest);
187
188  scoped_refptr<OperationForTest> operation =
189      CreateOperation(GURL(""), expected_hash);
190
191  EXPECT_CALL(
192      manager_,
193      OnProgress(kDummyExtensionId, image_writer_api::STAGE_VERIFYDOWNLOAD, _))
194      .Times(AtLeast(1));
195  EXPECT_CALL(
196      manager_,
197      OnProgress(kDummyExtensionId, image_writer_api::STAGE_VERIFYDOWNLOAD, 0))
198      .Times(AtLeast(1));
199  EXPECT_CALL(manager_,
200              OnProgress(kDummyExtensionId,
201                         image_writer_api::STAGE_VERIFYDOWNLOAD,
202                         100)).Times(AtLeast(1));
203
204  operation->SetImagePath(test_utils_.GetImagePath());
205  content::BrowserThread::PostTask(content::BrowserThread::FILE,
206                                   FROM_HERE,
207                                   base::Bind(&OperationForTest::VerifyDownload,
208                                              operation,
209                                              base::Bind(&base::DoNothing)));
210
211  base::RunLoop().RunUntilIdle();
212
213  operation->Cancel();
214}
215
216}  // namespace
217
218}  // namespace image_writer
219}  // namespace extensions
220