blob: 7571fb4e70a08826f8f55a84f24a3f18a537e65b [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
83gboolean StartProcessorInRunLoop(gpointer data) {
84 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
85 processor->StartProcessing();
86 return FALSE;
87}
88
Darin Petkov9d911fa2010-08-19 09:36:08 -070089void TestWithData(const vector<char>& data,
90 bool hash_test,
Darin Petkov50332f12010-09-24 11:44:47 -070091 bool size_test,
Darin Petkov9d911fa2010-08-19 09:36:08 -070092 bool use_download_delegate) {
rspangler@google.com49fdf182009-10-10 00:57:34 +000093 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
94
95 // TODO(adlr): see if we need a different file for build bots
Andrew de los Reyesf9185172010-05-03 11:07:05 -070096 ScopedTempFile output_temp_file;
97 DirectFileWriter writer;
98
rspangler@google.com49fdf182009-10-10 00:57:34 +000099 // takes ownership of passed in HttpFetcher
Darin Petkovc97435c2010-07-20 12:37:43 -0700100 string hash = hash_test ?
101 OmahaHashCalculator::OmahaHashOfString("random string") :
102 OmahaHashCalculator::OmahaHashOfData(data);
Darin Petkov50332f12010-09-24 11:44:47 -0700103 uint64_t size = data.size() + (size_test ? 1 : 0);
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700104 InstallPlan install_plan(true,
Darin Petkov0406e402010-10-06 21:33:11 -0700105 false,
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700106 "",
Darin Petkov50332f12010-09-24 11:44:47 -0700107 size,
Darin Petkovc97435c2010-07-20 12:37:43 -0700108 hash,
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700109 output_temp_file.GetPath(),
110 "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000111 ObjectFeederAction<InstallPlan> feeder_action;
112 feeder_action.set_obj(install_plan);
Darin Petkov73058b42010-10-06 16:32:19 -0700113 PrefsMock prefs;
114 DownloadAction download_action(&prefs, new MockHttpFetcher(&data[0],
115 data.size()));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700116 download_action.SetTestFileWriter(&writer);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000117 BondActions(&feeder_action, &download_action);
Darin Petkov9d911fa2010-08-19 09:36:08 -0700118 DownloadActionDelegateMock download_delegate;
119 if (use_download_delegate) {
120 InSequence s;
121 download_action.set_delegate(&download_delegate);
122 EXPECT_CALL(download_delegate, SetDownloadStatus(true)).Times(1);
123 EXPECT_CALL(download_delegate, BytesReceived(_, _)).Times(AtLeast(1));
124 EXPECT_CALL(download_delegate, SetDownloadStatus(false)).Times(1);
125 }
Darin Petkov50332f12010-09-24 11:44:47 -0700126 ActionExitCode expected_code = kActionCodeSuccess;
127 if (hash_test)
128 expected_code = kActionCodeDownloadHashMismatchError;
129 else if (size_test)
130 expected_code = kActionCodeDownloadSizeMismatchError;
131 DownloadActionTestProcessorDelegate delegate(expected_code);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000132 delegate.loop_ = loop;
133 delegate.expected_data_ = data;
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700134 delegate.path_ = output_temp_file.GetPath();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000135 ActionProcessor processor;
136 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000137 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000138 processor.EnqueueAction(&download_action);
139
140 g_timeout_add(0, &StartProcessorInRunLoop, &processor);
141 g_main_loop_run(loop);
142 g_main_loop_unref(loop);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000143}
144} // namespace {}
145
146TEST(DownloadActionTest, SimpleTest) {
147 vector<char> small;
148 const char* foo = "foo";
149 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700150 TestWithData(small,
151 false, // hash_test
152 false, // size_test
153 true); // use_download_delegate
rspangler@google.com49fdf182009-10-10 00:57:34 +0000154}
155
156TEST(DownloadActionTest, LargeTest) {
157 vector<char> big(5 * kMockHttpFetcherChunkSize);
158 char c = '0';
159 for (unsigned int i = 0; i < big.size(); i++) {
160 big[i] = c;
161 if ('9' == c)
162 c = '0';
163 else
164 c++;
165 }
Darin Petkov50332f12010-09-24 11:44:47 -0700166 TestWithData(big,
167 false, // hash_test
168 false, // size_test
169 true); // use_download_delegate
Darin Petkovc97435c2010-07-20 12:37:43 -0700170}
171
172TEST(DownloadActionTest, BadHashTest) {
173 vector<char> small;
174 const char* foo = "foo";
175 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700176 TestWithData(small,
177 true, // hash_test
178 false, // size_test
179 true); // use_download_delegate
180}
181
182TEST(DownloadActionTest, BadSizeTest) {
183 const char* something = "something";
184 vector<char> small(something, something + strlen(something));
185 TestWithData(small,
186 false, // hash_test
187 true, // size_test
188 true); // use_download_delegate
Darin Petkov9d911fa2010-08-19 09:36:08 -0700189}
190
191TEST(DownloadActionTest, NoDownloadDelegateTest) {
192 vector<char> small;
193 const char* foo = "foofoo";
194 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov50332f12010-09-24 11:44:47 -0700195 TestWithData(small,
196 false, // hash_test
197 false, // size_test
198 false); // use_download_delegate
rspangler@google.com49fdf182009-10-10 00:57:34 +0000199}
200
201namespace {
202class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
203 public:
204 void ProcessingStopped(const ActionProcessor* processor) {
205 ASSERT_TRUE(loop_);
206 g_main_loop_quit(loop_);
207 }
208 GMainLoop *loop_;
209};
210
211gboolean TerminateEarlyTestStarter(gpointer data) {
212 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
213 processor->StartProcessing();
214 CHECK(processor->IsRunning());
215 processor->StopProcessing();
216 return FALSE;
217}
218
Darin Petkov9d911fa2010-08-19 09:36:08 -0700219void TestTerminateEarly(bool use_download_delegate) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000220 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
221
222 vector<char> data(kMockHttpFetcherChunkSize + kMockHttpFetcherChunkSize / 2);
223 memset(&data[0], 0, data.size());
224
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700225 ScopedTempFile temp_file;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000226 {
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700227 DirectFileWriter writer;
228
rspangler@google.com49fdf182009-10-10 00:57:34 +0000229 // takes ownership of passed in HttpFetcher
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000230 ObjectFeederAction<InstallPlan> feeder_action;
Darin Petkov0406e402010-10-06 21:33:11 -0700231 InstallPlan install_plan(true, false, "", 0, "", temp_file.GetPath(), "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000232 feeder_action.set_obj(install_plan);
Darin Petkov73058b42010-10-06 16:32:19 -0700233 PrefsMock prefs;
234 DownloadAction download_action(&prefs,
235 new MockHttpFetcher(&data[0], data.size()));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700236 download_action.SetTestFileWriter(&writer);
Darin Petkov9d911fa2010-08-19 09:36:08 -0700237 DownloadActionDelegateMock download_delegate;
238 if (use_download_delegate) {
239 InSequence s;
240 download_action.set_delegate(&download_delegate);
241 EXPECT_CALL(download_delegate, SetDownloadStatus(true)).Times(1);
242 EXPECT_CALL(download_delegate, SetDownloadStatus(false)).Times(1);
243 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000244 TerminateEarlyTestProcessorDelegate delegate;
245 delegate.loop_ = loop;
246 ActionProcessor processor;
247 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000248 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000249 processor.EnqueueAction(&download_action);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000250 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000251
252 g_timeout_add(0, &TerminateEarlyTestStarter, &processor);
253 g_main_loop_run(loop);
254 g_main_loop_unref(loop);
255 }
256
257 // 1 or 0 chunks should have come through
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700258 const off_t resulting_file_size(utils::FileSize(temp_file.GetPath()));
259 EXPECT_GE(resulting_file_size, 0);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000260 if (resulting_file_size != 0)
261 EXPECT_EQ(kMockHttpFetcherChunkSize, resulting_file_size);
262}
263
Darin Petkov9d911fa2010-08-19 09:36:08 -0700264} // namespace {}
265
266TEST(DownloadActionTest, TerminateEarlyTest) {
267 TestTerminateEarly(true);
268}
269
270TEST(DownloadActionTest, TerminateEarlyNoDownloadDelegateTest) {
271 TestTerminateEarly(false);
272}
273
rspangler@google.com49fdf182009-10-10 00:57:34 +0000274class DownloadActionTestAction;
275
276template<>
277class ActionTraits<DownloadActionTestAction> {
278 public:
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000279 typedef InstallPlan OutputObjectType;
280 typedef InstallPlan InputObjectType;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000281};
282
283// This is a simple Action class for testing.
284struct DownloadActionTestAction : public Action<DownloadActionTestAction> {
285 DownloadActionTestAction() : did_run_(false) {}
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000286 typedef InstallPlan InputObjectType;
287 typedef InstallPlan OutputObjectType;
288 ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
289 ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000290 ActionProcessor* processor() { return processor_; }
291 void PerformAction() {
292 did_run_ = true;
293 ASSERT_TRUE(HasInputObject());
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000294 EXPECT_TRUE(expected_input_object_ == GetInputObject());
rspangler@google.com49fdf182009-10-10 00:57:34 +0000295 ASSERT_TRUE(processor());
Darin Petkovc1a8b422010-07-19 11:34:49 -0700296 processor()->ActionComplete(this, kActionCodeSuccess);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000297 }
298 string Type() const { return "DownloadActionTestAction"; }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000299 InstallPlan expected_input_object_;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000300 bool did_run_;
301};
302
303namespace {
304// This class is an ActionProcessorDelegate that simply terminates the
305// run loop when the ActionProcessor has completed processing. It's used
306// only by the test PassObjectOutTest.
307class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
308 public:
Darin Petkovc1a8b422010-07-19 11:34:49 -0700309 void ProcessingDone(const ActionProcessor* processor, ActionExitCode code) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000310 ASSERT_TRUE(loop_);
311 g_main_loop_quit(loop_);
312 }
313 GMainLoop *loop_;
314};
315
316gboolean PassObjectOutTestStarter(gpointer data) {
317 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
318 processor->StartProcessing();
319 return FALSE;
320}
321}
322
323TEST(DownloadActionTest, PassObjectOutTest) {
324 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
325
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700326 DirectFileWriter writer;
327
rspangler@google.com49fdf182009-10-10 00:57:34 +0000328 // takes ownership of passed in HttpFetcher
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700329 InstallPlan install_plan(true,
Darin Petkov0406e402010-10-06 21:33:11 -0700330 false,
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700331 "",
Darin Petkov50332f12010-09-24 11:44:47 -0700332 1,
Andrew de los Reyes1e338b82010-01-22 14:57:27 -0800333 OmahaHashCalculator::OmahaHashOfString("x"),
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700334 "/dev/null",
Andrew de los Reyes1e338b82010-01-22 14:57:27 -0800335 "/dev/null");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000336 ObjectFeederAction<InstallPlan> feeder_action;
337 feeder_action.set_obj(install_plan);
Darin Petkov73058b42010-10-06 16:32:19 -0700338 PrefsMock prefs;
339 DownloadAction download_action(&prefs, new MockHttpFetcher("x", 1));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700340 download_action.SetTestFileWriter(&writer);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000341
342 DownloadActionTestAction test_action;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000343 test_action.expected_input_object_ = install_plan;
344 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000345 BondActions(&download_action, &test_action);
346
347 ActionProcessor processor;
348 PassObjectOutTestProcessorDelegate delegate;
349 delegate.loop_ = loop;
350 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000351 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000352 processor.EnqueueAction(&download_action);
353 processor.EnqueueAction(&test_action);
354
355 g_timeout_add(0, &PassObjectOutTestStarter, &processor);
356 g_main_loop_run(loop);
357 g_main_loop_unref(loop);
358
359 EXPECT_EQ(true, test_action.did_run_);
360}
361
362TEST(DownloadActionTest, BadOutFileTest) {
363 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
364
365 const string path("/fake/path/that/cant/be/created/because/of/missing/dirs");
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700366 DirectFileWriter writer;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000367
368 // takes ownership of passed in HttpFetcher
Darin Petkov0406e402010-10-06 21:33:11 -0700369 InstallPlan install_plan(true, false, "", 0, "", path, "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000370 ObjectFeederAction<InstallPlan> feeder_action;
371 feeder_action.set_obj(install_plan);
Darin Petkov73058b42010-10-06 16:32:19 -0700372 PrefsMock prefs;
373 DownloadAction download_action(&prefs, new MockHttpFetcher("x", 1));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700374 download_action.SetTestFileWriter(&writer);
Darin Petkovc1a8b422010-07-19 11:34:49 -0700375
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000376 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000377
378 ActionProcessor processor;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000379 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000380 processor.EnqueueAction(&download_action);
381 processor.StartProcessing();
382 ASSERT_FALSE(processor.IsRunning());
383
384 g_main_loop_unref(loop);
385}
386
387} // namespace chromeos_update_engine