blob: d63ae2d89604a20359e3c545ead292c3062ecee4 [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,
88 bool use_download_delegate) {
rspangler@google.com49fdf182009-10-10 00:57:34 +000089 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
90
91 // TODO(adlr): see if we need a different file for build bots
Andrew de los Reyesf9185172010-05-03 11:07:05 -070092 ScopedTempFile output_temp_file;
93 DirectFileWriter writer;
94
rspangler@google.com49fdf182009-10-10 00:57:34 +000095 // takes ownership of passed in HttpFetcher
Darin Petkovc97435c2010-07-20 12:37:43 -070096 string hash = hash_test ?
97 OmahaHashCalculator::OmahaHashOfString("random string") :
98 OmahaHashCalculator::OmahaHashOfData(data);
Andrew de los Reyesf9185172010-05-03 11:07:05 -070099 InstallPlan install_plan(true,
100 "",
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700101 0,
Darin Petkovc97435c2010-07-20 12:37:43 -0700102 hash,
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700103 output_temp_file.GetPath(),
104 "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000105 ObjectFeederAction<InstallPlan> feeder_action;
106 feeder_action.set_obj(install_plan);
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700107 DownloadAction download_action(new MockHttpFetcher(&data[0],
108 data.size()));
109 download_action.SetTestFileWriter(&writer);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000110 BondActions(&feeder_action, &download_action);
Darin Petkov9d911fa2010-08-19 09:36:08 -0700111 DownloadActionDelegateMock download_delegate;
112 if (use_download_delegate) {
113 InSequence s;
114 download_action.set_delegate(&download_delegate);
115 EXPECT_CALL(download_delegate, SetDownloadStatus(true)).Times(1);
116 EXPECT_CALL(download_delegate, BytesReceived(_, _)).Times(AtLeast(1));
117 EXPECT_CALL(download_delegate, SetDownloadStatus(false)).Times(1);
118 }
Darin Petkovc97435c2010-07-20 12:37:43 -0700119 DownloadActionTestProcessorDelegate delegate(
120 hash_test ? kActionCodeDownloadHashMismatchError : kActionCodeSuccess);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000121 delegate.loop_ = loop;
122 delegate.expected_data_ = data;
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700123 delegate.path_ = output_temp_file.GetPath();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000124 ActionProcessor processor;
125 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000126 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000127 processor.EnqueueAction(&download_action);
128
129 g_timeout_add(0, &StartProcessorInRunLoop, &processor);
130 g_main_loop_run(loop);
131 g_main_loop_unref(loop);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000132}
133} // namespace {}
134
135TEST(DownloadActionTest, SimpleTest) {
136 vector<char> small;
137 const char* foo = "foo";
138 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov9d911fa2010-08-19 09:36:08 -0700139 TestWithData(small, false, true);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000140}
141
142TEST(DownloadActionTest, LargeTest) {
143 vector<char> big(5 * kMockHttpFetcherChunkSize);
144 char c = '0';
145 for (unsigned int i = 0; i < big.size(); i++) {
146 big[i] = c;
147 if ('9' == c)
148 c = '0';
149 else
150 c++;
151 }
Darin Petkov9d911fa2010-08-19 09:36:08 -0700152 TestWithData(big, false, true);
Darin Petkovc97435c2010-07-20 12:37:43 -0700153}
154
155TEST(DownloadActionTest, BadHashTest) {
156 vector<char> small;
157 const char* foo = "foo";
158 small.insert(small.end(), foo, foo + strlen(foo));
Darin Petkov9d911fa2010-08-19 09:36:08 -0700159 TestWithData(small, true, true);
160}
161
162TEST(DownloadActionTest, NoDownloadDelegateTest) {
163 vector<char> small;
164 const char* foo = "foofoo";
165 small.insert(small.end(), foo, foo + strlen(foo));
166 TestWithData(small, false, false);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000167}
168
169namespace {
170class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
171 public:
172 void ProcessingStopped(const ActionProcessor* processor) {
173 ASSERT_TRUE(loop_);
174 g_main_loop_quit(loop_);
175 }
176 GMainLoop *loop_;
177};
178
179gboolean TerminateEarlyTestStarter(gpointer data) {
180 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
181 processor->StartProcessing();
182 CHECK(processor->IsRunning());
183 processor->StopProcessing();
184 return FALSE;
185}
186
Darin Petkov9d911fa2010-08-19 09:36:08 -0700187void TestTerminateEarly(bool use_download_delegate) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000188 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
189
190 vector<char> data(kMockHttpFetcherChunkSize + kMockHttpFetcherChunkSize / 2);
191 memset(&data[0], 0, data.size());
192
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700193 ScopedTempFile temp_file;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000194 {
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700195 DirectFileWriter writer;
196
rspangler@google.com49fdf182009-10-10 00:57:34 +0000197 // takes ownership of passed in HttpFetcher
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000198 ObjectFeederAction<InstallPlan> feeder_action;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700199 InstallPlan install_plan(true, "", 0, "", temp_file.GetPath(), "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000200 feeder_action.set_obj(install_plan);
201 DownloadAction download_action(new MockHttpFetcher(&data[0], data.size()));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700202 download_action.SetTestFileWriter(&writer);
Darin Petkov9d911fa2010-08-19 09:36:08 -0700203 DownloadActionDelegateMock download_delegate;
204 if (use_download_delegate) {
205 InSequence s;
206 download_action.set_delegate(&download_delegate);
207 EXPECT_CALL(download_delegate, SetDownloadStatus(true)).Times(1);
208 EXPECT_CALL(download_delegate, SetDownloadStatus(false)).Times(1);
209 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000210 TerminateEarlyTestProcessorDelegate delegate;
211 delegate.loop_ = loop;
212 ActionProcessor processor;
213 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000214 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000215 processor.EnqueueAction(&download_action);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000216 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000217
218 g_timeout_add(0, &TerminateEarlyTestStarter, &processor);
219 g_main_loop_run(loop);
220 g_main_loop_unref(loop);
221 }
222
223 // 1 or 0 chunks should have come through
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700224 const off_t resulting_file_size(utils::FileSize(temp_file.GetPath()));
225 EXPECT_GE(resulting_file_size, 0);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000226 if (resulting_file_size != 0)
227 EXPECT_EQ(kMockHttpFetcherChunkSize, resulting_file_size);
228}
229
Darin Petkov9d911fa2010-08-19 09:36:08 -0700230} // namespace {}
231
232TEST(DownloadActionTest, TerminateEarlyTest) {
233 TestTerminateEarly(true);
234}
235
236TEST(DownloadActionTest, TerminateEarlyNoDownloadDelegateTest) {
237 TestTerminateEarly(false);
238}
239
rspangler@google.com49fdf182009-10-10 00:57:34 +0000240class DownloadActionTestAction;
241
242template<>
243class ActionTraits<DownloadActionTestAction> {
244 public:
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000245 typedef InstallPlan OutputObjectType;
246 typedef InstallPlan InputObjectType;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000247};
248
249// This is a simple Action class for testing.
250struct DownloadActionTestAction : public Action<DownloadActionTestAction> {
251 DownloadActionTestAction() : did_run_(false) {}
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000252 typedef InstallPlan InputObjectType;
253 typedef InstallPlan OutputObjectType;
254 ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
255 ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000256 ActionProcessor* processor() { return processor_; }
257 void PerformAction() {
258 did_run_ = true;
259 ASSERT_TRUE(HasInputObject());
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000260 EXPECT_TRUE(expected_input_object_ == GetInputObject());
rspangler@google.com49fdf182009-10-10 00:57:34 +0000261 ASSERT_TRUE(processor());
Darin Petkovc1a8b422010-07-19 11:34:49 -0700262 processor()->ActionComplete(this, kActionCodeSuccess);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000263 }
264 string Type() const { return "DownloadActionTestAction"; }
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000265 InstallPlan expected_input_object_;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000266 bool did_run_;
267};
268
269namespace {
270// This class is an ActionProcessorDelegate that simply terminates the
271// run loop when the ActionProcessor has completed processing. It's used
272// only by the test PassObjectOutTest.
273class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
274 public:
Darin Petkovc1a8b422010-07-19 11:34:49 -0700275 void ProcessingDone(const ActionProcessor* processor, ActionExitCode code) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000276 ASSERT_TRUE(loop_);
277 g_main_loop_quit(loop_);
278 }
279 GMainLoop *loop_;
280};
281
282gboolean PassObjectOutTestStarter(gpointer data) {
283 ActionProcessor *processor = reinterpret_cast<ActionProcessor*>(data);
284 processor->StartProcessing();
285 return FALSE;
286}
287}
288
289TEST(DownloadActionTest, PassObjectOutTest) {
290 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
291
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700292 DirectFileWriter writer;
293
rspangler@google.com49fdf182009-10-10 00:57:34 +0000294 // takes ownership of passed in HttpFetcher
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700295 InstallPlan install_plan(true,
296 "",
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700297 0,
Andrew de los Reyes1e338b82010-01-22 14:57:27 -0800298 OmahaHashCalculator::OmahaHashOfString("x"),
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700299 "/dev/null",
Andrew de los Reyes1e338b82010-01-22 14:57:27 -0800300 "/dev/null");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000301 ObjectFeederAction<InstallPlan> feeder_action;
302 feeder_action.set_obj(install_plan);
303 DownloadAction download_action(new MockHttpFetcher("x", 1));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700304 download_action.SetTestFileWriter(&writer);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000305
306 DownloadActionTestAction test_action;
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000307 test_action.expected_input_object_ = install_plan;
308 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000309 BondActions(&download_action, &test_action);
310
311 ActionProcessor processor;
312 PassObjectOutTestProcessorDelegate delegate;
313 delegate.loop_ = loop;
314 processor.set_delegate(&delegate);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000315 processor.EnqueueAction(&feeder_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000316 processor.EnqueueAction(&download_action);
317 processor.EnqueueAction(&test_action);
318
319 g_timeout_add(0, &PassObjectOutTestStarter, &processor);
320 g_main_loop_run(loop);
321 g_main_loop_unref(loop);
322
323 EXPECT_EQ(true, test_action.did_run_);
324}
325
326TEST(DownloadActionTest, BadOutFileTest) {
327 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
328
329 const string path("/fake/path/that/cant/be/created/because/of/missing/dirs");
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700330 DirectFileWriter writer;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000331
332 // takes ownership of passed in HttpFetcher
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700333 InstallPlan install_plan(true, "", 0, "", path, "");
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000334 ObjectFeederAction<InstallPlan> feeder_action;
335 feeder_action.set_obj(install_plan);
336 DownloadAction download_action(new MockHttpFetcher("x", 1));
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700337 download_action.SetTestFileWriter(&writer);
Darin Petkovc1a8b422010-07-19 11:34:49 -0700338
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000339 BondActions(&feeder_action, &download_action);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000340
341 ActionProcessor processor;
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.StartProcessing();
345 ASSERT_FALSE(processor.IsRunning());
346
347 g_main_loop_unref(loop);
348}
349
350} // namespace chromeos_update_engine