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/files/file_util.h"
6#include "base/files/scoped_temp_dir.h"
7#include "base/message_loop/message_loop.h"
8#include "base/run_loop.h"
9#include "chrome/browser/extensions/api/image_writer_private/error_messages.h"
10#include "chrome/browser/extensions/api/image_writer_private/operation.h"
11#include "chrome/browser/extensions/api/image_writer_private/operation_manager.h"
12#include "chrome/browser/extensions/api/image_writer_private/test_utils.h"
13#include "chrome/test/base/testing_profile.h"
14#include "content/public/browser/browser_thread.h"
15#include "content/public/test/test_browser_thread_bundle.h"
16#include "testing/gmock/include/gmock/gmock.h"
17#include "testing/gtest/include/gtest/gtest.h"
18#include "third_party/zlib/google/zip.h"
19
20namespace extensions {
21namespace image_writer {
22
23namespace {
24
25using testing::_;
26using testing::AnyNumber;
27using testing::AtLeast;
28using testing::Gt;
29using testing::Lt;
30
31// This class gives us a generic Operation with the ability to set or inspect
32// the current path to the image file.
33class OperationForTest : public Operation {
34 public:
35  OperationForTest(base::WeakPtr<OperationManager> manager_,
36                   const ExtensionId& extension_id,
37                   const std::string& device_path)
38      : Operation(manager_, extension_id, device_path) {}
39
40  virtual void StartImpl() OVERRIDE {}
41
42  // Expose internal stages for testing.
43  void Unzip(const base::Closure& continuation) {
44    Operation::Unzip(continuation);
45  }
46
47  void Write(const base::Closure& continuation) {
48    Operation::Write(continuation);
49  }
50
51  void VerifyWrite(const base::Closure& continuation) {
52    Operation::VerifyWrite(continuation);
53  }
54
55  // Helpers to set-up state for intermediate stages.
56  void SetImagePath(const base::FilePath image_path) {
57    image_path_ = image_path;
58  }
59
60  base::FilePath GetImagePath() { return image_path_; }
61
62 private:
63  virtual ~OperationForTest() {};
64};
65
66class ImageWriterOperationTest : public ImageWriterUnitTestBase {
67 protected:
68  ImageWriterOperationTest()
69      : profile_(new TestingProfile), manager_(profile_.get()) {}
70  virtual void SetUp() OVERRIDE {
71    ImageWriterUnitTestBase::SetUp();
72
73    // Create the zip file.
74    base::FilePath image_dir = test_utils_.GetTempDir().AppendASCII("zip");
75    ASSERT_TRUE(base::CreateDirectory(image_dir));
76    ASSERT_TRUE(base::CreateTemporaryFileInDir(image_dir, &image_path_));
77
78    test_utils_.FillFile(image_path_, kImagePattern, kTestFileSize);
79
80    zip_file_ = test_utils_.GetTempDir().AppendASCII("test_image.zip");
81    ASSERT_TRUE(zip::Zip(image_dir, zip_file_, true));
82
83    // Operation setup.
84    operation_ =
85        new OperationForTest(manager_.AsWeakPtr(),
86                             kDummyExtensionId,
87                             test_utils_.GetDevicePath().AsUTF8Unsafe());
88    operation_->SetImagePath(test_utils_.GetImagePath());
89  }
90
91  virtual void TearDown() OVERRIDE {
92    // Ensure all callbacks have been destroyed and cleanup occurs.
93    operation_->Cancel();
94
95    ImageWriterUnitTestBase::TearDown();
96  }
97
98  base::FilePath image_path_;
99  base::FilePath zip_file_;
100
101  scoped_ptr<TestingProfile> profile_;
102
103  MockOperationManager manager_;
104  scoped_refptr<OperationForTest> operation_;
105};
106
107} // namespace
108
109// Unizpping a non-zip should do nothing.
110TEST_F(ImageWriterOperationTest, UnzipNonZipFile) {
111  EXPECT_CALL(manager_, OnProgress(kDummyExtensionId, _, _)).Times(0);
112
113  EXPECT_CALL(manager_, OnError(kDummyExtensionId, _, _, _)).Times(0);
114  EXPECT_CALL(manager_, OnProgress(kDummyExtensionId, _, _)).Times(0);
115  EXPECT_CALL(manager_, OnComplete(kDummyExtensionId)).Times(0);
116
117  operation_->Start();
118  content::BrowserThread::PostTask(
119      content::BrowserThread::FILE,
120      FROM_HERE,
121      base::Bind(
122          &OperationForTest::Unzip, operation_, base::Bind(&base::DoNothing)));
123
124  base::RunLoop().RunUntilIdle();
125}
126
127TEST_F(ImageWriterOperationTest, UnzipZipFile) {
128  EXPECT_CALL(manager_, OnError(kDummyExtensionId, _, _, _)).Times(0);
129  EXPECT_CALL(manager_,
130              OnProgress(kDummyExtensionId, image_writer_api::STAGE_UNZIP, _))
131      .Times(AtLeast(1));
132  EXPECT_CALL(manager_,
133              OnProgress(kDummyExtensionId, image_writer_api::STAGE_UNZIP, 0))
134      .Times(AtLeast(1));
135  EXPECT_CALL(manager_,
136              OnProgress(kDummyExtensionId, image_writer_api::STAGE_UNZIP, 100))
137      .Times(AtLeast(1));
138
139  operation_->SetImagePath(zip_file_);
140
141  operation_->Start();
142  content::BrowserThread::PostTask(
143      content::BrowserThread::FILE,
144      FROM_HERE,
145      base::Bind(
146          &OperationForTest::Unzip, operation_, base::Bind(&base::DoNothing)));
147
148  base::RunLoop().RunUntilIdle();
149
150  EXPECT_TRUE(base::ContentsEqual(image_path_, operation_->GetImagePath()));
151}
152
153#if defined(OS_LINUX)
154TEST_F(ImageWriterOperationTest, WriteImageToDevice) {
155  EXPECT_CALL(manager_, OnError(kDummyExtensionId, _, _, _)).Times(0);
156  EXPECT_CALL(manager_,
157              OnProgress(kDummyExtensionId, image_writer_api::STAGE_WRITE, _))
158      .Times(AtLeast(1));
159  EXPECT_CALL(manager_,
160              OnProgress(kDummyExtensionId, image_writer_api::STAGE_WRITE, 0))
161      .Times(AtLeast(1));
162  EXPECT_CALL(manager_,
163              OnProgress(kDummyExtensionId, image_writer_api::STAGE_WRITE, 100))
164      .Times(AtLeast(1));
165
166  operation_->Start();
167  content::BrowserThread::PostTask(
168      content::BrowserThread::FILE,
169      FROM_HERE,
170      base::Bind(
171          &OperationForTest::Write, operation_, base::Bind(&base::DoNothing)));
172
173  base::RunLoop().RunUntilIdle();
174
175#if !defined(OS_CHROMEOS)
176  test_utils_.GetUtilityClient()->Progress(0);
177  test_utils_.GetUtilityClient()->Progress(kTestFileSize / 2);
178  test_utils_.GetUtilityClient()->Progress(kTestFileSize);
179  test_utils_.GetUtilityClient()->Success();
180
181  base::RunLoop().RunUntilIdle();
182#endif
183}
184#endif
185
186#if !defined(OS_CHROMEOS)
187// Chrome OS doesn't support verification in the ImageBurner, so these two tests
188// are skipped.
189
190TEST_F(ImageWriterOperationTest, VerifyFileSuccess) {
191  EXPECT_CALL(manager_, OnError(kDummyExtensionId, _, _, _)).Times(0);
192  EXPECT_CALL(
193      manager_,
194      OnProgress(kDummyExtensionId, image_writer_api::STAGE_VERIFYWRITE, _))
195      .Times(AtLeast(1));
196  EXPECT_CALL(
197      manager_,
198      OnProgress(kDummyExtensionId, image_writer_api::STAGE_VERIFYWRITE, 0))
199      .Times(AtLeast(1));
200  EXPECT_CALL(
201      manager_,
202      OnProgress(kDummyExtensionId, image_writer_api::STAGE_VERIFYWRITE, 100))
203      .Times(AtLeast(1));
204
205  test_utils_.FillFile(
206      test_utils_.GetDevicePath(), kImagePattern, kTestFileSize);
207
208  operation_->Start();
209  content::BrowserThread::PostTask(content::BrowserThread::FILE,
210                                   FROM_HERE,
211                                   base::Bind(&OperationForTest::VerifyWrite,
212                                              operation_,
213                                              base::Bind(&base::DoNothing)));
214
215  base::RunLoop().RunUntilIdle();
216
217#if !defined(OS_CHROMEOS)
218  test_utils_.GetUtilityClient()->Progress(0);
219  test_utils_.GetUtilityClient()->Progress(kTestFileSize / 2);
220  test_utils_.GetUtilityClient()->Progress(kTestFileSize);
221  test_utils_.GetUtilityClient()->Success();
222#endif
223
224  base::RunLoop().RunUntilIdle();
225}
226
227TEST_F(ImageWriterOperationTest, VerifyFileFailure) {
228  EXPECT_CALL(
229      manager_,
230      OnProgress(kDummyExtensionId, image_writer_api::STAGE_VERIFYWRITE, _))
231      .Times(AnyNumber());
232  EXPECT_CALL(
233      manager_,
234      OnProgress(kDummyExtensionId, image_writer_api::STAGE_VERIFYWRITE, 100))
235      .Times(0);
236  EXPECT_CALL(manager_, OnComplete(kDummyExtensionId)).Times(0);
237  EXPECT_CALL(
238      manager_,
239      OnError(kDummyExtensionId, image_writer_api::STAGE_VERIFYWRITE, _, _))
240      .Times(1);
241
242  test_utils_.FillFile(
243      test_utils_.GetDevicePath(), kDevicePattern, kTestFileSize);
244
245  operation_->Start();
246  content::BrowserThread::PostTask(content::BrowserThread::FILE,
247                                   FROM_HERE,
248                                   base::Bind(&OperationForTest::VerifyWrite,
249                                              operation_,
250                                              base::Bind(&base::DoNothing)));
251
252  base::RunLoop().RunUntilIdle();
253
254  test_utils_.GetUtilityClient()->Progress(0);
255  test_utils_.GetUtilityClient()->Progress(kTestFileSize / 2);
256  test_utils_.GetUtilityClient()->Error(error::kVerificationFailed);
257
258  base::RunLoop().RunUntilIdle();
259}
260#endif
261
262// Tests that on creation the operation_ has the expected state.
263TEST_F(ImageWriterOperationTest, Creation) {
264  EXPECT_EQ(0, operation_->GetProgress());
265  EXPECT_EQ(image_writer_api::STAGE_UNKNOWN, operation_->GetStage());
266}
267
268}  // namespace image_writer
269}  // namespace extensions
270