blob: 2f37dfbfdbc59a0d527cc29e2889d9a8fcf7ccd1 [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>
Darin Petkov73058b42010-10-06 16:32:19 -07007
rspangler@google.com49fdf182009-10-10 00:57:34 +00008#include <glib.h>
Darin Petkov9d911fa2010-08-19 09:36:08 -07009#include <gmock/gmock.h>
rspangler@google.com49fdf182009-10-10 00:57:34 +000010#include <gtest/gtest.h>
Darin Petkov73058b42010-10-06 16:32:19 -070011
rspangler@google.com49fdf182009-10-10 00:57:34 +000012#include "update_engine/action_pipe.h"
13#include "update_engine/download_action.h"
14#include "update_engine/mock_http_fetcher.h"
15#include "update_engine/omaha_hash_calculator.h"
Darin Petkov73058b42010-10-06 16:32:19 -070016#include "update_engine/prefs_mock.h"
rspangler@google.com49fdf182009-10-10 00:57:34 +000017#include "update_engine/test_utils.h"
adlr@google.comc98a7ed2009-12-04 18:54:03 +000018#include "update_engine/utils.h"
rspangler@google.com49fdf182009-10-10 00:57:34 +000019
20namespace chromeos_update_engine {
21
22using std::string;
23using std::vector;
Darin Petkov9d911fa2010-08-19 09:36:08 -070024using testing::_;
25using testing::AtLeast;
26using testing::InSequence;
rspangler@google.com49fdf182009-10-10 00:57:34 +000027
28class DownloadActionTest : public ::testing::Test { };
29
30namespace {
Darin Petkov9d911fa2010-08-19 09:36:08 -070031class DownloadActionDelegateMock : public DownloadActionDelegate {
32 public:
33 MOCK_METHOD1(SetDownloadStatus, void(bool active));
34 MOCK_METHOD2(BytesReceived, void(uint64_t bytes_received, uint64_t total));
35};
36
rspangler@google.com49fdf182009-10-10 00:57:34 +000037class DownloadActionTestProcessorDelegate : public ActionProcessorDelegate {
38 public:
Darin Petkovc97435c2010-07-20 12:37:43 -070039 explicit DownloadActionTestProcessorDelegate(ActionExitCode expected_code)
40 : loop_(NULL),
41 processing_done_called_(false),
42 expected_code_(expected_code) {}
rspangler@google.com49fdf182009-10-10 00:57:34 +000043 virtual ~DownloadActionTestProcessorDelegate() {
44 EXPECT_TRUE(processing_done_called_);
45 }
Darin Petkovc1a8b422010-07-19 11:34:49 -070046 virtual void ProcessingDone(const ActionProcessor* processor,
47 ActionExitCode code) {
rspangler@google.com49fdf182009-10-10 00:57:34 +000048 ASSERT_TRUE(loop_);
49 g_main_loop_quit(loop_);
adlr@google.comc98a7ed2009-12-04 18:54:03 +000050 vector<char> found_data;
51 ASSERT_TRUE(utils::ReadFile(path_, &found_data));
rspangler@google.com49fdf182009-10-10 00:57:34 +000052 ASSERT_EQ(expected_data_.size(), found_data.size());
53 for (unsigned i = 0; i < expected_data_.size(); i++) {
54 EXPECT_EQ(expected_data_[i], found_data[i]);
55 }
56 processing_done_called_ = true;
57 }
58
adlr@google.comc98a7ed2009-12-04 18:54:03 +000059 virtual void ActionCompleted(ActionProcessor* processor,
60 AbstractAction* action,
Darin Petkovc1a8b422010-07-19 11:34:49 -070061 ActionExitCode code) {
Darin Petkovc97435c2010-07-20 12:37:43 -070062 const string type = action->Type();
63 if (type == DownloadAction::StaticType()) {
64 EXPECT_EQ(expected_code_, code);
65 } else {
66 EXPECT_EQ(kActionCodeSuccess, code);
67 }
rspangler@google.com49fdf182009-10-10 00:57:34 +000068 }
69
70 GMainLoop *loop_;
71 string path_;
72 vector<char> expected_data_;
73 bool processing_done_called_;
Darin Petkovc97435c2010-07-20 12:37:43 -070074 ActionExitCode expected_code_;
rspangler@google.com49fdf182009-10-10 00:57:34 +000075};
76
77struct EntryPointArgs {
78 const vector<char> *data;
79 GMainLoop *loop;
80 ActionProcessor *processor;
81};
82
Andrew de los Reyes34e41a12010-10-26 20:07:58 -070083struct StartProcessorInRunLoopArgs {
84 ActionProcessor* processor;
85 MockHttpFetcher* http_fetcher;
86};
87
rspangler@google.com49fdf182009-10-10 00:57:34 +000088gboolean StartProcessorInRunLoop(gpointer data) {
Andrew de los Reyes34e41a12010-10-26 20:07:58 -070089 ActionProcessor* processor =
90 reinterpret_cast<StartProcessorInRunLoopArgs*>(data)->processor;
rspangler@google.com49fdf182009-10-10 00:57:34 +000091 processor->StartProcessing();
Andrew de los Reyes34e41a12010-10-26 20:07:58 -070092 MockHttpFetcher* http_fetcher =
93 reinterpret_cast<StartProcessorInRunLoopArgs*>(data)->http_fetcher;
94 http_fetcher->SetOffset(1);
rspangler@google.com49fdf182009-10-10 00:57:34 +000095 return FALSE;
96}
97
Darin Petkov9d911fa2010-08-19 09:36:08 -070098void TestWithData(const vector<char>& data,
99 bool hash_test,
Darin Petkov50332f12010-09-24 11:44:47 -0700100 bool size_test,
Darin Petkov9d911fa2010-08-19 09:36:08 -0700101 bool use_download_delegate) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000102 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
103
104 // TODO(adlr): see if we need a different file for build bots
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700105 ScopedTempFile output_temp_file;
106 DirectFileWriter writer;
107
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700108 // We pull off the first byte from data and seek past it.
109
Darin Petkovc97435c2010-07-20 12:37:43 -0700110 string hash = hash_test ?
111 OmahaHashCalculator::OmahaHashOfString("random string") :
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700112 OmahaHashCalculator::OmahaHashOfBytes(&data[1], data.size() - 1);
Darin Petkov50332f12010-09-24 11:44:47 -0700113 uint64_t size = data.size() + (size_test ? 1 : 0);
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700114 InstallPlan install_plan(true,
Darin Petkov0406e402010-10-06 21:33:11 -0700115 false,
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700116 "",
Darin Petkov50332f12010-09-24 11:44:47 -0700117 size,
Darin Petkovc97435c2010-07-20 12:37:43 -0700118 hash,
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700119 output_temp_file.GetPath(),
120 "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000121 ObjectFeederAction<InstallPlan> feeder_action;
122 feeder_action.set_obj(install_plan);
Darin Petkov73058b42010-10-06 16:32:19 -0700123 PrefsMock prefs;
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700124 MockHttpFetcher* http_fetcher = new MockHttpFetcher(&data[0], data.size());
125 // takes ownership of passed in HttpFetcher
126 DownloadAction download_action(&prefs, http_fetcher);
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700127 download_action.SetTestFileWriter(&writer);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000128 BondActions(&feeder_action, &download_action);
Darin Petkov9d911fa2010-08-19 09:36:08 -0700129 DownloadActionDelegateMock download_delegate;
130 if (use_download_delegate) {
131 InSequence s;
132 download_action.set_delegate(&download_delegate);
133 EXPECT_CALL(download_delegate, SetDownloadStatus(true)).Times(1);
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700134 if (data.size() > kMockHttpFetcherChunkSize)
135 EXPECT_CALL(download_delegate,
136 BytesReceived(1 + kMockHttpFetcherChunkSize, _));
137
138 EXPECT_CALL(download_delegate, BytesReceived(_, _)).Times(AtLeast(1));
Darin Petkov9d911fa2010-08-19 09:36:08 -0700139 EXPECT_CALL(download_delegate, SetDownloadStatus(false)).Times(1);
140 }
Darin Petkov50332f12010-09-24 11:44:47 -0700141 ActionExitCode expected_code = kActionCodeSuccess;
142 if (hash_test)
143 expected_code = kActionCodeDownloadHashMismatchError;
144 else if (size_test)
145 expected_code = kActionCodeDownloadSizeMismatchError;
146 DownloadActionTestProcessorDelegate delegate(expected_code);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000147 delegate.loop_ = loop;
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700148 delegate.expected_data_ = vector<char>(data.begin() + 1, data.end());
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700149 delegate.path_ = output_temp_file.GetPath();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000150 ActionProcessor processor;
151 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000152 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000153 processor.EnqueueAction(&download_action);
154
Andrew de los Reyes34e41a12010-10-26 20:07:58 -0700155 StartProcessorInRunLoopArgs args;
156 args.processor = &processor;
157 args.http_fetcher = http_fetcher;
158 g_timeout_add(0, &StartProcessorInRunLoop, &args);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000159 g_main_loop_run(loop);
160 g_main_loop_unref(loop);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000161}
162} // namespace {}
163
164TEST(DownloadActionTest, SimpleTest) {
165 vector<char> small;
166 const char* foo = "foo";
167 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700168 TestWithData(small,
169 false, // hash_test
170 false, // size_test
171 true); // use_download_delegate
rspangler@google.com49fdf182009-10-10 00:57:34 +0000172}
173
174TEST(DownloadActionTest, LargeTest) {
175 vector<char> big(5 * kMockHttpFetcherChunkSize);
176 char c = '0';
177 for (unsigned int i = 0; i < big.size(); i++) {
178 big[i] = c;
179 if ('9' == c)
180 c = '0';
181 else
182 c++;
183 }
Darin Petkov50332f12010-09-24 11:44:47 -0700184 TestWithData(big,
185 false, // hash_test
186 false, // size_test
187 true); // use_download_delegate
Darin Petkovc97435c2010-07-20 12:37:43 -0700188}
189
190TEST(DownloadActionTest, BadHashTest) {
191 vector<char> small;
192 const char* foo = "foo";
193 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700194 TestWithData(small,
195 true, // hash_test
196 false, // size_test
197 true); // use_download_delegate
198}
199
200TEST(DownloadActionTest, BadSizeTest) {
201 const char* something = "something";
202 vector<char> small(something, something + strlen(something));
203 TestWithData(small,
204 false, // hash_test
205 true, // size_test
206 true); // use_download_delegate
Darin Petkov9d911fa2010-08-19 09:36:08 -0700207}
208
209TEST(DownloadActionTest, NoDownloadDelegateTest) {
210 vector<char> small;
211 const char* foo = "foofoo";
212 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700213 TestWithData(small,
214 false, // hash_test
215 false, // size_test
216 false); // use_download_delegate
rspangler@google.com49fdf182009-10-10 00:57:34 +0000217}
218
219namespace {
220class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
221 public:
222 void ProcessingStopped(const ActionProcessor* processor) {
223 ASSERT_TRUE(loop_);
224 g_main_loop_quit(loop_);
225 }
226 GMainLoop *loop_;
227};
228
229gboolean TerminateEarlyTestStarter(gpointer data) {
230 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
231 processor->StartProcessing();
232 CHECK(processor->IsRunning());
233 processor->StopProcessing();
234 return FALSE;
235}
236
Darin Petkov9d911fa2010-08-19 09:36:08 -0700237void TestTerminateEarly(bool use_download_delegate) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000238 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
239
240 vector<char> data(kMockHttpFetcherChunkSize + kMockHttpFetcherChunkSize / 2);
241 memset(&data[0], 0, data.size());
242
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700243 ScopedTempFile temp_file;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000244 {
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700245 DirectFileWriter writer;
246
rspangler@google.com49fdf182009-10-10 00:57:34 +0000247 // takes ownership of passed in HttpFetcher
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000248 ObjectFeederAction<InstallPlan> feeder_action;
Darin Petkov0406e402010-10-06 21:33:11 -0700249 InstallPlan install_plan(true, false, "", 0, "", temp_file.GetPath(), "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000250 feeder_action.set_obj(install_plan);
Darin Petkov73058b42010-10-06 16:32:19 -0700251 PrefsMock prefs;
252 DownloadAction download_action(&prefs,
253 new MockHttpFetcher(&data[0], data.size()));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700254 download_action.SetTestFileWriter(&writer);
Darin Petkov9d911fa2010-08-19 09:36:08 -0700255 DownloadActionDelegateMock download_delegate;
256 if (use_download_delegate) {
257 InSequence s;
258 download_action.set_delegate(&download_delegate);
259 EXPECT_CALL(download_delegate, SetDownloadStatus(true)).Times(1);
260 EXPECT_CALL(download_delegate, SetDownloadStatus(false)).Times(1);
261 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000262 TerminateEarlyTestProcessorDelegate delegate;
263 delegate.loop_ = loop;
264 ActionProcessor processor;
265 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000266 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000267 processor.EnqueueAction(&download_action);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000268 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000269
270 g_timeout_add(0, &TerminateEarlyTestStarter, &processor);
271 g_main_loop_run(loop);
272 g_main_loop_unref(loop);
273 }
274
275 // 1 or 0 chunks should have come through
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700276 const off_t resulting_file_size(utils::FileSize(temp_file.GetPath()));
277 EXPECT_GE(resulting_file_size, 0);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000278 if (resulting_file_size != 0)
279 EXPECT_EQ(kMockHttpFetcherChunkSize, resulting_file_size);
280}
281
Darin Petkov9d911fa2010-08-19 09:36:08 -0700282} // namespace {}
283
284TEST(DownloadActionTest, TerminateEarlyTest) {
285 TestTerminateEarly(true);
286}
287
288TEST(DownloadActionTest, TerminateEarlyNoDownloadDelegateTest) {
289 TestTerminateEarly(false);
290}
291
rspangler@google.com49fdf182009-10-10 00:57:34 +0000292class DownloadActionTestAction;
293
294template<>
295class ActionTraits<DownloadActionTestAction> {
296 public:
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000297 typedef InstallPlan OutputObjectType;
298 typedef InstallPlan InputObjectType;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000299};
300
301// This is a simple Action class for testing.
302struct DownloadActionTestAction : public Action<DownloadActionTestAction> {
303 DownloadActionTestAction() : did_run_(false) {}
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000304 typedef InstallPlan InputObjectType;
305 typedef InstallPlan OutputObjectType;
306 ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
307 ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000308 ActionProcessor* processor() { return processor_; }
309 void PerformAction() {
310 did_run_ = true;
311 ASSERT_TRUE(HasInputObject());
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000312 EXPECT_TRUE(expected_input_object_ == GetInputObject());
rspangler@google.com49fdf182009-10-10 00:57:34 +0000313 ASSERT_TRUE(processor());
Darin Petkovc1a8b422010-07-19 11:34:49 -0700314 processor()->ActionComplete(this, kActionCodeSuccess);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000315 }
316 string Type() const { return "DownloadActionTestAction"; }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000317 InstallPlan expected_input_object_;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000318 bool did_run_;
319};
320
321namespace {
322// This class is an ActionProcessorDelegate that simply terminates the
323// run loop when the ActionProcessor has completed processing. It's used
324// only by the test PassObjectOutTest.
325class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
326 public:
Darin Petkovc1a8b422010-07-19 11:34:49 -0700327 void ProcessingDone(const ActionProcessor* processor, ActionExitCode code) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000328 ASSERT_TRUE(loop_);
329 g_main_loop_quit(loop_);
330 }
331 GMainLoop *loop_;
332};
333
334gboolean PassObjectOutTestStarter(gpointer data) {
335 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
336 processor->StartProcessing();
337 return FALSE;
338}
339}
340
341TEST(DownloadActionTest, PassObjectOutTest) {
342 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
343
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700344 DirectFileWriter writer;
345
rspangler@google.com49fdf182009-10-10 00:57:34 +0000346 // takes ownership of passed in HttpFetcher
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700347 InstallPlan install_plan(true,
Darin Petkov0406e402010-10-06 21:33:11 -0700348 false,
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700349 "",
Darin Petkov50332f12010-09-24 11:44:47 -0700350 1,
Andrew de los Reyes1e338b82010-01-22 14:57:27 -0800351 OmahaHashCalculator::OmahaHashOfString("x"),
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700352 "/dev/null",
Andrew de los Reyes1e338b82010-01-22 14:57:27 -0800353 "/dev/null");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000354 ObjectFeederAction<InstallPlan> feeder_action;
355 feeder_action.set_obj(install_plan);
Darin Petkov73058b42010-10-06 16:32:19 -0700356 PrefsMock prefs;
357 DownloadAction download_action(&prefs, new MockHttpFetcher("x", 1));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700358 download_action.SetTestFileWriter(&writer);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000359
360 DownloadActionTestAction test_action;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000361 test_action.expected_input_object_ = install_plan;
362 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000363 BondActions(&download_action, &test_action);
364
365 ActionProcessor processor;
366 PassObjectOutTestProcessorDelegate delegate;
367 delegate.loop_ = loop;
368 processor.set_delegate(&delegate);
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.EnqueueAction(&test_action);
372
373 g_timeout_add(0, &PassObjectOutTestStarter, &processor);
374 g_main_loop_run(loop);
375 g_main_loop_unref(loop);
376
377 EXPECT_EQ(true, test_action.did_run_);
378}
379
380TEST(DownloadActionTest, BadOutFileTest) {
381 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
382
383 const string path("/fake/path/that/cant/be/created/because/of/missing/dirs");
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700384 DirectFileWriter writer;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000385
386 // takes ownership of passed in HttpFetcher
Darin Petkov0406e402010-10-06 21:33:11 -0700387 InstallPlan install_plan(true, false, "", 0, "", path, "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000388 ObjectFeederAction<InstallPlan> feeder_action;
389 feeder_action.set_obj(install_plan);
Darin Petkov73058b42010-10-06 16:32:19 -0700390 PrefsMock prefs;
391 DownloadAction download_action(&prefs, new MockHttpFetcher("x", 1));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700392 download_action.SetTestFileWriter(&writer);
Darin Petkovc1a8b422010-07-19 11:34:49 -0700393
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000394 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000395
396 ActionProcessor processor;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000397 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000398 processor.EnqueueAction(&download_action);
399 processor.StartProcessing();
400 ASSERT_FALSE(processor.IsRunning());
401
402 g_main_loop_unref(loop);
403}
404
405} // namespace chromeos_update_engine