blob: 9ae952299e6d74bece936e903298c0c41549c0ab [file] [log] [blame]
rspangler@google.com49fdf182009-10-10 00:57:34 +00001// Copyright (c) 2009 The Chromium OS 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 <string>
6#include <vector>
7#include <glib.h>
Darin Petkov9d911fa2010-08-19 09:36:08 -07008#include <gmock/gmock.h>
rspangler@google.com49fdf182009-10-10 00:57:34 +00009#include <gtest/gtest.h>
10#include "update_engine/action_pipe.h"
11#include "update_engine/download_action.h"
12#include "update_engine/mock_http_fetcher.h"
13#include "update_engine/omaha_hash_calculator.h"
14#include "update_engine/test_utils.h"
adlr@google.comc98a7ed2009-12-04 18:54:03 +000015#include "update_engine/utils.h"
rspangler@google.com49fdf182009-10-10 00:57:34 +000016
17namespace chromeos_update_engine {
18
19using std::string;
20using std::vector;
Darin Petkov9d911fa2010-08-19 09:36:08 -070021using testing::_;
22using testing::AtLeast;
23using testing::InSequence;
rspangler@google.com49fdf182009-10-10 00:57:34 +000024
25class DownloadActionTest : public ::testing::Test { };
26
27namespace {
Darin Petkov9d911fa2010-08-19 09:36:08 -070028class DownloadActionDelegateMock : public DownloadActionDelegate {
29 public:
30 MOCK_METHOD1(SetDownloadStatus, void(bool active));
31 MOCK_METHOD2(BytesReceived, void(uint64_t bytes_received, uint64_t total));
32};
33
rspangler@google.com49fdf182009-10-10 00:57:34 +000034class DownloadActionTestProcessorDelegate : public ActionProcessorDelegate {
35 public:
Darin Petkovc97435c2010-07-20 12:37:43 -070036 explicit DownloadActionTestProcessorDelegate(ActionExitCode expected_code)
37 : loop_(NULL),
38 processing_done_called_(false),
39 expected_code_(expected_code) {}
rspangler@google.com49fdf182009-10-10 00:57:34 +000040 virtual ~DownloadActionTestProcessorDelegate() {
41 EXPECT_TRUE(processing_done_called_);
42 }
Darin Petkovc1a8b422010-07-19 11:34:49 -070043 virtual void ProcessingDone(const ActionProcessor* processor,
44 ActionExitCode code) {
rspangler@google.com49fdf182009-10-10 00:57:34 +000045 ASSERT_TRUE(loop_);
46 g_main_loop_quit(loop_);
adlr@google.comc98a7ed2009-12-04 18:54:03 +000047 vector<char> found_data;
48 ASSERT_TRUE(utils::ReadFile(path_, &found_data));
rspangler@google.com49fdf182009-10-10 00:57:34 +000049 ASSERT_EQ(expected_data_.size(), found_data.size());
50 for (unsigned i = 0; i < expected_data_.size(); i++) {
51 EXPECT_EQ(expected_data_[i], found_data[i]);
52 }
53 processing_done_called_ = true;
54 }
55
adlr@google.comc98a7ed2009-12-04 18:54:03 +000056 virtual void ActionCompleted(ActionProcessor* processor,
57 AbstractAction* action,
Darin Petkovc1a8b422010-07-19 11:34:49 -070058 ActionExitCode code) {
Darin Petkovc97435c2010-07-20 12:37:43 -070059 const string type = action->Type();
60 if (type == DownloadAction::StaticType()) {
61 EXPECT_EQ(expected_code_, code);
62 } else {
63 EXPECT_EQ(kActionCodeSuccess, code);
64 }
rspangler@google.com49fdf182009-10-10 00:57:34 +000065 }
66
67 GMainLoop *loop_;
68 string path_;
69 vector<char> expected_data_;
70 bool processing_done_called_;
Darin Petkovc97435c2010-07-20 12:37:43 -070071 ActionExitCode expected_code_;
rspangler@google.com49fdf182009-10-10 00:57:34 +000072};
73
74struct EntryPointArgs {
75 const vector<char> *data;
76 GMainLoop *loop;
77 ActionProcessor *processor;
78};
79
80gboolean StartProcessorInRunLoop(gpointer data) {
81 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
82 processor->StartProcessing();
83 return FALSE;
84}
85
Darin Petkov9d911fa2010-08-19 09:36:08 -070086void TestWithData(const vector<char>& data,
87 bool hash_test,
Darin Petkov50332f12010-09-24 11:44:47 -070088 bool size_test,
Darin Petkov9d911fa2010-08-19 09:36:08 -070089 bool use_download_delegate) {
rspangler@google.com49fdf182009-10-10 00:57:34 +000090 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
91
92 // TODO(adlr): see if we need a different file for build bots
Andrew de los Reyesf9185172010-05-03 11:07:05 -070093 ScopedTempFile output_temp_file;
94 DirectFileWriter writer;
95
rspangler@google.com49fdf182009-10-10 00:57:34 +000096 // takes ownership of passed in HttpFetcher
Darin Petkovc97435c2010-07-20 12:37:43 -070097 string hash = hash_test ?
98 OmahaHashCalculator::OmahaHashOfString("random string") :
99 OmahaHashCalculator::OmahaHashOfData(data);
Darin Petkov50332f12010-09-24 11:44:47 -0700100 uint64_t size = data.size() + (size_test ? 1 : 0);
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700101 InstallPlan install_plan(true,
102 "",
Darin Petkov50332f12010-09-24 11:44:47 -0700103 size,
Darin Petkovc97435c2010-07-20 12:37:43 -0700104 hash,
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700105 output_temp_file.GetPath(),
106 "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000107 ObjectFeederAction<InstallPlan> feeder_action;
108 feeder_action.set_obj(install_plan);
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700109 DownloadAction download_action(new MockHttpFetcher(&data[0],
110 data.size()));
111 download_action.SetTestFileWriter(&writer);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000112 BondActions(&feeder_action, &download_action);
Darin Petkov9d911fa2010-08-19 09:36:08 -0700113 DownloadActionDelegateMock download_delegate;
114 if (use_download_delegate) {
115 InSequence s;
116 download_action.set_delegate(&download_delegate);
117 EXPECT_CALL(download_delegate, SetDownloadStatus(true)).Times(1);
118 EXPECT_CALL(download_delegate, BytesReceived(_, _)).Times(AtLeast(1));
119 EXPECT_CALL(download_delegate, SetDownloadStatus(false)).Times(1);
120 }
Darin Petkov50332f12010-09-24 11:44:47 -0700121 ActionExitCode expected_code = kActionCodeSuccess;
122 if (hash_test)
123 expected_code = kActionCodeDownloadHashMismatchError;
124 else if (size_test)
125 expected_code = kActionCodeDownloadSizeMismatchError;
126 DownloadActionTestProcessorDelegate delegate(expected_code);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000127 delegate.loop_ = loop;
128 delegate.expected_data_ = data;
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700129 delegate.path_ = output_temp_file.GetPath();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000130 ActionProcessor processor;
131 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000132 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000133 processor.EnqueueAction(&download_action);
134
135 g_timeout_add(0, &StartProcessorInRunLoop, &processor);
136 g_main_loop_run(loop);
137 g_main_loop_unref(loop);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000138}
139} // namespace {}
140
141TEST(DownloadActionTest, SimpleTest) {
142 vector<char> small;
143 const char* foo = "foo";
144 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700145 TestWithData(small,
146 false, // hash_test
147 false, // size_test
148 true); // use_download_delegate
rspangler@google.com49fdf182009-10-10 00:57:34 +0000149}
150
151TEST(DownloadActionTest, LargeTest) {
152 vector<char> big(5 * kMockHttpFetcherChunkSize);
153 char c = '0';
154 for (unsigned int i = 0; i < big.size(); i++) {
155 big[i] = c;
156 if ('9' == c)
157 c = '0';
158 else
159 c++;
160 }
Darin Petkov50332f12010-09-24 11:44:47 -0700161 TestWithData(big,
162 false, // hash_test
163 false, // size_test
164 true); // use_download_delegate
Darin Petkovc97435c2010-07-20 12:37:43 -0700165}
166
167TEST(DownloadActionTest, BadHashTest) {
168 vector<char> small;
169 const char* foo = "foo";
170 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700171 TestWithData(small,
172 true, // hash_test
173 false, // size_test
174 true); // use_download_delegate
175}
176
177TEST(DownloadActionTest, BadSizeTest) {
178 const char* something = "something";
179 vector<char> small(something, something + strlen(something));
180 TestWithData(small,
181 false, // hash_test
182 true, // size_test
183 true); // use_download_delegate
Darin Petkov9d911fa2010-08-19 09:36:08 -0700184}
185
186TEST(DownloadActionTest, NoDownloadDelegateTest) {
187 vector<char> small;
188 const char* foo = "foofoo";
189 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700190 TestWithData(small,
191 false, // hash_test
192 false, // size_test
193 false); // use_download_delegate
rspangler@google.com49fdf182009-10-10 00:57:34 +0000194}
195
196namespace {
197class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
198 public:
199 void ProcessingStopped(const ActionProcessor* processor) {
200 ASSERT_TRUE(loop_);
201 g_main_loop_quit(loop_);
202 }
203 GMainLoop *loop_;
204};
205
206gboolean TerminateEarlyTestStarter(gpointer data) {
207 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
208 processor->StartProcessing();
209 CHECK(processor->IsRunning());
210 processor->StopProcessing();
211 return FALSE;
212}
213
Darin Petkov9d911fa2010-08-19 09:36:08 -0700214void TestTerminateEarly(bool use_download_delegate) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000215 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
216
217 vector<char> data(kMockHttpFetcherChunkSize + kMockHttpFetcherChunkSize / 2);
218 memset(&data[0], 0, data.size());
219
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700220 ScopedTempFile temp_file;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000221 {
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700222 DirectFileWriter writer;
223
rspangler@google.com49fdf182009-10-10 00:57:34 +0000224 // takes ownership of passed in HttpFetcher
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000225 ObjectFeederAction<InstallPlan> feeder_action;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700226 InstallPlan install_plan(true, "", 0, "", temp_file.GetPath(), "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000227 feeder_action.set_obj(install_plan);
228 DownloadAction download_action(new MockHttpFetcher(&data[0], data.size()));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700229 download_action.SetTestFileWriter(&writer);
Darin Petkov9d911fa2010-08-19 09:36:08 -0700230 DownloadActionDelegateMock download_delegate;
231 if (use_download_delegate) {
232 InSequence s;
233 download_action.set_delegate(&download_delegate);
234 EXPECT_CALL(download_delegate, SetDownloadStatus(true)).Times(1);
235 EXPECT_CALL(download_delegate, SetDownloadStatus(false)).Times(1);
236 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000237 TerminateEarlyTestProcessorDelegate delegate;
238 delegate.loop_ = loop;
239 ActionProcessor processor;
240 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000241 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000242 processor.EnqueueAction(&download_action);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000243 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000244
245 g_timeout_add(0, &TerminateEarlyTestStarter, &processor);
246 g_main_loop_run(loop);
247 g_main_loop_unref(loop);
248 }
249
250 // 1 or 0 chunks should have come through
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700251 const off_t resulting_file_size(utils::FileSize(temp_file.GetPath()));
252 EXPECT_GE(resulting_file_size, 0);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000253 if (resulting_file_size != 0)
254 EXPECT_EQ(kMockHttpFetcherChunkSize, resulting_file_size);
255}
256
Darin Petkov9d911fa2010-08-19 09:36:08 -0700257} // namespace {}
258
259TEST(DownloadActionTest, TerminateEarlyTest) {
260 TestTerminateEarly(true);
261}
262
263TEST(DownloadActionTest, TerminateEarlyNoDownloadDelegateTest) {
264 TestTerminateEarly(false);
265}
266
rspangler@google.com49fdf182009-10-10 00:57:34 +0000267class DownloadActionTestAction;
268
269template<>
270class ActionTraits<DownloadActionTestAction> {
271 public:
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000272 typedef InstallPlan OutputObjectType;
273 typedef InstallPlan InputObjectType;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000274};
275
276// This is a simple Action class for testing.
277struct DownloadActionTestAction : public Action<DownloadActionTestAction> {
278 DownloadActionTestAction() : did_run_(false) {}
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000279 typedef InstallPlan InputObjectType;
280 typedef InstallPlan OutputObjectType;
281 ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
282 ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000283 ActionProcessor* processor() { return processor_; }
284 void PerformAction() {
285 did_run_ = true;
286 ASSERT_TRUE(HasInputObject());
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000287 EXPECT_TRUE(expected_input_object_ == GetInputObject());
rspangler@google.com49fdf182009-10-10 00:57:34 +0000288 ASSERT_TRUE(processor());
Darin Petkovc1a8b422010-07-19 11:34:49 -0700289 processor()->ActionComplete(this, kActionCodeSuccess);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000290 }
291 string Type() const { return "DownloadActionTestAction"; }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000292 InstallPlan expected_input_object_;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000293 bool did_run_;
294};
295
296namespace {
297// This class is an ActionProcessorDelegate that simply terminates the
298// run loop when the ActionProcessor has completed processing. It's used
299// only by the test PassObjectOutTest.
300class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
301 public:
Darin Petkovc1a8b422010-07-19 11:34:49 -0700302 void ProcessingDone(const ActionProcessor* processor, ActionExitCode code) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000303 ASSERT_TRUE(loop_);
304 g_main_loop_quit(loop_);
305 }
306 GMainLoop *loop_;
307};
308
309gboolean PassObjectOutTestStarter(gpointer data) {
310 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
311 processor->StartProcessing();
312 return FALSE;
313}
314}
315
316TEST(DownloadActionTest, PassObjectOutTest) {
317 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
318
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700319 DirectFileWriter writer;
320
rspangler@google.com49fdf182009-10-10 00:57:34 +0000321 // takes ownership of passed in HttpFetcher
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700322 InstallPlan install_plan(true,
323 "",
Darin Petkov50332f12010-09-24 11:44:47 -0700324 1,
Andrew de los Reyes1e338b82010-01-22 14:57:27 -0800325 OmahaHashCalculator::OmahaHashOfString("x"),
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700326 "/dev/null",
Andrew de los Reyes1e338b82010-01-22 14:57:27 -0800327 "/dev/null");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000328 ObjectFeederAction<InstallPlan> feeder_action;
329 feeder_action.set_obj(install_plan);
330 DownloadAction download_action(new MockHttpFetcher("x", 1));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700331 download_action.SetTestFileWriter(&writer);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000332
333 DownloadActionTestAction test_action;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000334 test_action.expected_input_object_ = install_plan;
335 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000336 BondActions(&download_action, &test_action);
337
338 ActionProcessor processor;
339 PassObjectOutTestProcessorDelegate delegate;
340 delegate.loop_ = loop;
341 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000342 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000343 processor.EnqueueAction(&download_action);
344 processor.EnqueueAction(&test_action);
345
346 g_timeout_add(0, &PassObjectOutTestStarter, &processor);
347 g_main_loop_run(loop);
348 g_main_loop_unref(loop);
349
350 EXPECT_EQ(true, test_action.did_run_);
351}
352
353TEST(DownloadActionTest, BadOutFileTest) {
354 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
355
356 const string path("/fake/path/that/cant/be/created/because/of/missing/dirs");
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700357 DirectFileWriter writer;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000358
359 // takes ownership of passed in HttpFetcher
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700360 InstallPlan install_plan(true, "", 0, "", path, "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000361 ObjectFeederAction<InstallPlan> feeder_action;
362 feeder_action.set_obj(install_plan);
363 DownloadAction download_action(new MockHttpFetcher("x", 1));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700364 download_action.SetTestFileWriter(&writer);
Darin Petkovc1a8b422010-07-19 11:34:49 -0700365
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000366 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000367
368 ActionProcessor processor;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000369 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000370 processor.EnqueueAction(&download_action);
371 processor.StartProcessing();
372 ASSERT_FALSE(processor.IsRunning());
373
374 g_main_loop_unref(loop);
375}
376
377} // namespace chromeos_update_engine