1 //
2 // Copyright (C) 2011 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/payload_consumer/download_action.h"
18
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26
27 #include <base/bind.h>
28 #include <base/files/file_path.h>
29 #include <base/files/file_util.h>
30 #include <base/location.h>
31 #include <base/strings/stringprintf.h>
32 #include <brillo/message_loops/fake_message_loop.h>
33 #include <brillo/message_loops/message_loop.h>
34
35 #include "update_engine/common/action_pipe.h"
36 #include "update_engine/common/hash_calculator.h"
37 #include "update_engine/common/mock_http_fetcher.h"
38 #include "update_engine/common/mock_prefs.h"
39 #include "update_engine/common/test_utils.h"
40 #include "update_engine/common/utils.h"
41 #include "update_engine/fake_p2p_manager_configuration.h"
42 #include "update_engine/fake_system_state.h"
43 #include "update_engine/mock_file_writer.h"
44 #include "update_engine/payload_consumer/mock_download_action.h"
45 #include "update_engine/update_manager/fake_update_manager.h"
46
47 namespace chromeos_update_engine {
48
49 using base::FilePath;
50 using base::ReadFileToString;
51 using base::WriteFile;
52 using std::string;
53 using std::unique_ptr;
54 using test_utils::ScopedTempFile;
55 using testing::_;
56 using testing::AtLeast;
57 using testing::InSequence;
58 using testing::Return;
59 using testing::SetArgPointee;
60
61 class DownloadActionTest : public ::testing::Test {};
62
63 namespace {
64
65 class DownloadActionTestProcessorDelegate : public ActionProcessorDelegate {
66 public:
DownloadActionTestProcessorDelegate()67 DownloadActionTestProcessorDelegate()
68 : processing_done_called_(false), expected_code_(ErrorCode::kSuccess) {}
~DownloadActionTestProcessorDelegate()69 ~DownloadActionTestProcessorDelegate() override {
70 EXPECT_TRUE(processing_done_called_);
71 }
ProcessingDone(const ActionProcessor * processor,ErrorCode code)72 void ProcessingDone(const ActionProcessor* processor,
73 ErrorCode code) override {
74 brillo::MessageLoop::current()->BreakLoop();
75 brillo::Blob found_data;
76 ASSERT_TRUE(utils::ReadFile(path_, &found_data));
77 if (expected_code_ != ErrorCode::kDownloadWriteError) {
78 ASSERT_EQ(expected_data_.size(), found_data.size());
79 for (unsigned i = 0; i < expected_data_.size(); i++) {
80 EXPECT_EQ(expected_data_[i], found_data[i]);
81 }
82 }
83 processing_done_called_ = true;
84 }
85
ActionCompleted(ActionProcessor * processor,AbstractAction * action,ErrorCode code)86 void ActionCompleted(ActionProcessor* processor,
87 AbstractAction* action,
88 ErrorCode code) override {
89 const string type = action->Type();
90 if (type == DownloadAction::StaticType()) {
91 EXPECT_EQ(expected_code_, code);
92 p2p_file_id_ = static_cast<DownloadAction*>(action)->p2p_file_id();
93 } else {
94 EXPECT_EQ(ErrorCode::kSuccess, code);
95 }
96 }
97
98 string path_;
99 brillo::Blob expected_data_;
100 bool processing_done_called_;
101 ErrorCode expected_code_;
102 string p2p_file_id_;
103 };
104
105 class TestDirectFileWriter : public DirectFileWriter {
106 public:
TestDirectFileWriter()107 TestDirectFileWriter() : fail_write_(0), current_write_(0) {}
set_fail_write(int fail_write)108 void set_fail_write(int fail_write) { fail_write_ = fail_write; }
109
Write(const void * bytes,size_t count)110 virtual bool Write(const void* bytes, size_t count) {
111 if (++current_write_ == fail_write_) {
112 return false;
113 }
114 return DirectFileWriter::Write(bytes, count);
115 }
116
117 private:
118 // If positive, fail on the |fail_write_| call to Write.
119 int fail_write_;
120 int current_write_;
121 };
122
StartProcessorInRunLoop(ActionProcessor * processor,MockHttpFetcher * http_fetcher)123 void StartProcessorInRunLoop(ActionProcessor* processor,
124 MockHttpFetcher* http_fetcher) {
125 processor->StartProcessing();
126 http_fetcher->SetOffset(1);
127 }
128
TestWithData(const brillo::Blob & data,int fail_write,bool use_download_delegate)129 void TestWithData(const brillo::Blob& data,
130 int fail_write,
131 bool use_download_delegate) {
132 brillo::FakeMessageLoop loop(nullptr);
133 loop.SetAsCurrent();
134 FakeSystemState fake_system_state;
135
136 // TODO(adlr): see if we need a different file for build bots
137 ScopedTempFile output_temp_file;
138 TestDirectFileWriter writer;
139 EXPECT_EQ(
140 0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
141 writer.set_fail_write(fail_write);
142
143 uint64_t size = data.size() - 1;
144 InstallPlan install_plan;
145 install_plan.payloads.push_back(
146 {.size = size, .type = InstallPayloadType::kDelta});
147 // We pull off the first byte from data and seek past it.
148 EXPECT_TRUE(HashCalculator::RawHashOfBytes(
149 &data[1], data.size() - 1, &install_plan.payloads[0].hash));
150 install_plan.source_slot = 0;
151 install_plan.target_slot = 1;
152 // We mark both slots as bootable. Only the target slot should be unbootable
153 // after the download starts.
154 fake_system_state.fake_boot_control()->SetSlotBootable(
155 install_plan.source_slot, true);
156 fake_system_state.fake_boot_control()->SetSlotBootable(
157 install_plan.target_slot, true);
158 auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
159 feeder_action->set_obj(install_plan);
160 MockPrefs prefs;
161 MockHttpFetcher* http_fetcher =
162 new MockHttpFetcher(data.data(), data.size(), nullptr);
163 // takes ownership of passed in HttpFetcher
164 auto download_action =
165 std::make_unique<DownloadAction>(&prefs,
166 fake_system_state.boot_control(),
167 fake_system_state.hardware(),
168 &fake_system_state,
169 http_fetcher,
170 false /* interactive */);
171 download_action->SetTestFileWriter(&writer);
172 BondActions(feeder_action.get(), download_action.get());
173 MockDownloadActionDelegate download_delegate;
174 if (use_download_delegate) {
175 InSequence s;
176 download_action->set_delegate(&download_delegate);
177 if (data.size() > kMockHttpFetcherChunkSize)
178 EXPECT_CALL(download_delegate,
179 BytesReceived(_, kMockHttpFetcherChunkSize, _));
180 EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(AtLeast(1));
181 EXPECT_CALL(download_delegate, DownloadComplete())
182 .Times(fail_write == 0 ? 1 : 0);
183 }
184 DownloadActionTestProcessorDelegate delegate;
185 delegate.expected_code_ =
186 (fail_write > 0) ? ErrorCode::kDownloadWriteError : ErrorCode::kSuccess;
187 delegate.expected_data_ = brillo::Blob(data.begin() + 1, data.end());
188 delegate.path_ = output_temp_file.path();
189 ActionProcessor processor;
190 processor.set_delegate(&delegate);
191 processor.EnqueueAction(std::move(feeder_action));
192 processor.EnqueueAction(std::move(download_action));
193
194 loop.PostTask(FROM_HERE,
195 base::Bind(&StartProcessorInRunLoop, &processor, http_fetcher));
196 loop.Run();
197 EXPECT_FALSE(loop.PendingTasks());
198
199 EXPECT_TRUE(fake_system_state.fake_boot_control()->IsSlotBootable(
200 install_plan.source_slot));
201 EXPECT_FALSE(fake_system_state.fake_boot_control()->IsSlotBootable(
202 install_plan.target_slot));
203 }
204 } // namespace
205
TEST(DownloadActionTest,SimpleTest)206 TEST(DownloadActionTest, SimpleTest) {
207 brillo::Blob small;
208 const char* foo = "foo";
209 small.insert(small.end(), foo, foo + strlen(foo));
210 TestWithData(small,
211 0, // fail_write
212 true); // use_download_delegate
213 }
214
TEST(DownloadActionTest,LargeTest)215 TEST(DownloadActionTest, LargeTest) {
216 brillo::Blob big(5 * kMockHttpFetcherChunkSize);
217 char c = '0';
218 for (unsigned int i = 0; i < big.size(); i++) {
219 big[i] = c;
220 c = ('9' == c) ? '0' : c + 1;
221 }
222 TestWithData(big,
223 0, // fail_write
224 true); // use_download_delegate
225 }
226
TEST(DownloadActionTest,FailWriteTest)227 TEST(DownloadActionTest, FailWriteTest) {
228 brillo::Blob big(5 * kMockHttpFetcherChunkSize);
229 char c = '0';
230 for (unsigned int i = 0; i < big.size(); i++) {
231 big[i] = c;
232 c = ('9' == c) ? '0' : c + 1;
233 }
234 TestWithData(big,
235 2, // fail_write
236 true); // use_download_delegate
237 }
238
TEST(DownloadActionTest,NoDownloadDelegateTest)239 TEST(DownloadActionTest, NoDownloadDelegateTest) {
240 brillo::Blob small;
241 const char* foo = "foofoo";
242 small.insert(small.end(), foo, foo + strlen(foo));
243 TestWithData(small,
244 0, // fail_write
245 false); // use_download_delegate
246 }
247
TEST(DownloadActionTest,MultiPayloadProgressTest)248 TEST(DownloadActionTest, MultiPayloadProgressTest) {
249 std::vector<brillo::Blob> payload_datas;
250 // the first payload must be the largest, as it's the actual payload used by
251 // the MockHttpFetcher for all downloaded data.
252 payload_datas.emplace_back(4 * kMockHttpFetcherChunkSize + 256);
253 payload_datas.emplace_back(2 * kMockHttpFetcherChunkSize);
254 brillo::FakeMessageLoop loop(nullptr);
255 loop.SetAsCurrent();
256 FakeSystemState fake_system_state;
257 EXPECT_CALL(*fake_system_state.mock_payload_state(), NextPayload())
258 .WillOnce(Return(true));
259
260 MockFileWriter mock_file_writer;
261 EXPECT_CALL(mock_file_writer, Close()).WillRepeatedly(Return(0));
262 EXPECT_CALL(mock_file_writer, Write(_, _, _))
263 .WillRepeatedly(
264 DoAll(SetArgPointee<2>(ErrorCode::kSuccess), Return(true)));
265
266 InstallPlan install_plan;
267 uint64_t total_expected_download_size{0};
268 for (const auto& data : payload_datas) {
269 uint64_t size = data.size();
270 install_plan.payloads.push_back(
271 {.size = size, .type = InstallPayloadType::kFull});
272 total_expected_download_size += size;
273 }
274 auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
275 feeder_action->set_obj(install_plan);
276 MockPrefs prefs;
277 MockHttpFetcher* http_fetcher = new MockHttpFetcher(
278 payload_datas[0].data(), payload_datas[0].size(), nullptr);
279 // takes ownership of passed in HttpFetcher
280 auto download_action =
281 std::make_unique<DownloadAction>(&prefs,
282 fake_system_state.boot_control(),
283 fake_system_state.hardware(),
284 &fake_system_state,
285 http_fetcher,
286 false /* interactive */);
287 download_action->SetTestFileWriter(&mock_file_writer);
288 BondActions(feeder_action.get(), download_action.get());
289 MockDownloadActionDelegate download_delegate;
290 {
291 InSequence s;
292 download_action->set_delegate(&download_delegate);
293 // these are hand-computed based on the payloads specified above
294 EXPECT_CALL(download_delegate,
295 BytesReceived(kMockHttpFetcherChunkSize,
296 kMockHttpFetcherChunkSize,
297 total_expected_download_size));
298 EXPECT_CALL(download_delegate,
299 BytesReceived(kMockHttpFetcherChunkSize,
300 kMockHttpFetcherChunkSize * 2,
301 total_expected_download_size));
302 EXPECT_CALL(download_delegate,
303 BytesReceived(kMockHttpFetcherChunkSize,
304 kMockHttpFetcherChunkSize * 3,
305 total_expected_download_size));
306 EXPECT_CALL(download_delegate,
307 BytesReceived(kMockHttpFetcherChunkSize,
308 kMockHttpFetcherChunkSize * 4,
309 total_expected_download_size));
310 EXPECT_CALL(download_delegate,
311 BytesReceived(256,
312 kMockHttpFetcherChunkSize * 4 + 256,
313 total_expected_download_size));
314 EXPECT_CALL(download_delegate,
315 BytesReceived(kMockHttpFetcherChunkSize,
316 kMockHttpFetcherChunkSize * 5 + 256,
317 total_expected_download_size));
318 EXPECT_CALL(download_delegate,
319 BytesReceived(kMockHttpFetcherChunkSize,
320 total_expected_download_size,
321 total_expected_download_size));
322 }
323 ActionProcessor processor;
324 processor.EnqueueAction(std::move(feeder_action));
325 processor.EnqueueAction(std::move(download_action));
326
327 loop.PostTask(
328 FROM_HERE,
329 base::Bind(
330 [](ActionProcessor* processor) { processor->StartProcessing(); },
331 base::Unretained(&processor)));
332 loop.Run();
333 EXPECT_FALSE(loop.PendingTasks());
334 }
335
336 namespace {
337 class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
338 public:
ProcessingStopped(const ActionProcessor * processor)339 void ProcessingStopped(const ActionProcessor* processor) {
340 brillo::MessageLoop::current()->BreakLoop();
341 }
342 };
343
TerminateEarlyTestStarter(ActionProcessor * processor)344 void TerminateEarlyTestStarter(ActionProcessor* processor) {
345 processor->StartProcessing();
346 CHECK(processor->IsRunning());
347 processor->StopProcessing();
348 }
349
TestTerminateEarly(bool use_download_delegate)350 void TestTerminateEarly(bool use_download_delegate) {
351 brillo::FakeMessageLoop loop(nullptr);
352 loop.SetAsCurrent();
353
354 brillo::Blob data(kMockHttpFetcherChunkSize + kMockHttpFetcherChunkSize / 2);
355 memset(data.data(), 0, data.size());
356
357 ScopedTempFile temp_file;
358 {
359 DirectFileWriter writer;
360 EXPECT_EQ(0, writer.Open(temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
361
362 // takes ownership of passed in HttpFetcher
363 auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
364 InstallPlan install_plan;
365 install_plan.payloads.resize(1);
366 feeder_action->set_obj(install_plan);
367 FakeSystemState fake_system_state_;
368 MockPrefs prefs;
369 auto download_action = std::make_unique<DownloadAction>(
370 &prefs,
371 fake_system_state_.boot_control(),
372 fake_system_state_.hardware(),
373 &fake_system_state_,
374 new MockHttpFetcher(data.data(), data.size(), nullptr),
375 false /* interactive */);
376 download_action->SetTestFileWriter(&writer);
377 MockDownloadActionDelegate download_delegate;
378 if (use_download_delegate) {
379 download_action->set_delegate(&download_delegate);
380 EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(0);
381 }
382 TerminateEarlyTestProcessorDelegate delegate;
383 ActionProcessor processor;
384 processor.set_delegate(&delegate);
385 BondActions(feeder_action.get(), download_action.get());
386 processor.EnqueueAction(std::move(feeder_action));
387 processor.EnqueueAction(std::move(download_action));
388
389 loop.PostTask(FROM_HERE,
390 base::Bind(&TerminateEarlyTestStarter, &processor));
391 loop.Run();
392 EXPECT_FALSE(loop.PendingTasks());
393 }
394
395 // 1 or 0 chunks should have come through
396 const off_t resulting_file_size(utils::FileSize(temp_file.path()));
397 EXPECT_GE(resulting_file_size, 0);
398 if (resulting_file_size != 0)
399 EXPECT_EQ(kMockHttpFetcherChunkSize,
400 static_cast<size_t>(resulting_file_size));
401 }
402
403 } // namespace
404
TEST(DownloadActionTest,TerminateEarlyTest)405 TEST(DownloadActionTest, TerminateEarlyTest) {
406 TestTerminateEarly(true);
407 }
408
TEST(DownloadActionTest,TerminateEarlyNoDownloadDelegateTest)409 TEST(DownloadActionTest, TerminateEarlyNoDownloadDelegateTest) {
410 TestTerminateEarly(false);
411 }
412
413 class DownloadActionTestAction;
414
415 template <>
416 class ActionTraits<DownloadActionTestAction> {
417 public:
418 typedef InstallPlan OutputObjectType;
419 typedef InstallPlan InputObjectType;
420 };
421
422 // This is a simple Action class for testing.
423 class DownloadActionTestAction : public Action<DownloadActionTestAction> {
424 public:
425 DownloadActionTestAction() = default;
426 typedef InstallPlan InputObjectType;
427 typedef InstallPlan OutputObjectType;
in_pipe()428 ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
out_pipe()429 ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
processor()430 ActionProcessor* processor() { return processor_; }
PerformAction()431 void PerformAction() {
432 ASSERT_TRUE(HasInputObject());
433 EXPECT_TRUE(expected_input_object_ == GetInputObject());
434 ASSERT_TRUE(processor());
435 processor()->ActionComplete(this, ErrorCode::kSuccess);
436 }
StaticType()437 static std::string StaticType() { return "DownloadActionTestAction"; }
Type() const438 string Type() const { return StaticType(); }
439 InstallPlan expected_input_object_;
440 };
441
442 namespace {
443 // This class is an ActionProcessorDelegate that simply terminates the
444 // run loop when the ActionProcessor has completed processing. It's used
445 // only by the test PassObjectOutTest.
446 class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
447 public:
ProcessingDone(const ActionProcessor * processor,ErrorCode code)448 void ProcessingDone(const ActionProcessor* processor,
449 ErrorCode code) override {
450 brillo::MessageLoop::current()->BreakLoop();
451 }
ActionCompleted(ActionProcessor * processor,AbstractAction * action,ErrorCode code)452 void ActionCompleted(ActionProcessor* processor,
453 AbstractAction* action,
454 ErrorCode code) override {
455 if (action->Type() == DownloadActionTestAction::StaticType()) {
456 did_test_action_run_ = true;
457 }
458 }
459
460 bool did_test_action_run_ = false;
461 };
462
463 } // namespace
464
TEST(DownloadActionTest,PassObjectOutTest)465 TEST(DownloadActionTest, PassObjectOutTest) {
466 brillo::FakeMessageLoop loop(nullptr);
467 loop.SetAsCurrent();
468
469 DirectFileWriter writer;
470 EXPECT_EQ(0, writer.Open("/dev/null", O_WRONLY | O_CREAT, 0));
471
472 // takes ownership of passed in HttpFetcher
473 InstallPlan install_plan;
474 install_plan.payloads.push_back({.size = 1});
475 EXPECT_TRUE(
476 HashCalculator::RawHashOfData({'x'}, &install_plan.payloads[0].hash));
477 auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
478 feeder_action->set_obj(install_plan);
479 MockPrefs prefs;
480 FakeSystemState fake_system_state_;
481 auto download_action =
482 std::make_unique<DownloadAction>(&prefs,
483 fake_system_state_.boot_control(),
484 fake_system_state_.hardware(),
485 &fake_system_state_,
486 new MockHttpFetcher("x", 1, nullptr),
487 false /* interactive */);
488 download_action->SetTestFileWriter(&writer);
489
490 auto test_action = std::make_unique<DownloadActionTestAction>();
491 test_action->expected_input_object_ = install_plan;
492 BondActions(feeder_action.get(), download_action.get());
493 BondActions(download_action.get(), test_action.get());
494
495 ActionProcessor processor;
496 PassObjectOutTestProcessorDelegate delegate;
497 processor.set_delegate(&delegate);
498 processor.EnqueueAction(std::move(feeder_action));
499 processor.EnqueueAction(std::move(download_action));
500 processor.EnqueueAction(std::move(test_action));
501
502 loop.PostTask(
503 FROM_HERE,
504 base::Bind(
505 [](ActionProcessor* processor) { processor->StartProcessing(); },
506 base::Unretained(&processor)));
507 loop.Run();
508 EXPECT_FALSE(loop.PendingTasks());
509
510 EXPECT_EQ(true, delegate.did_test_action_run_);
511 }
512
513 // Test fixture for P2P tests.
514 class P2PDownloadActionTest : public testing::Test {
515 protected:
P2PDownloadActionTest()516 P2PDownloadActionTest()
517 : start_at_offset_(0), fake_um_(fake_system_state_.fake_clock()) {}
518
~P2PDownloadActionTest()519 ~P2PDownloadActionTest() override {}
520
521 // Derived from testing::Test.
SetUp()522 void SetUp() override { loop_.SetAsCurrent(); }
523
524 // Derived from testing::Test.
TearDown()525 void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); }
526
527 // To be called by tests to setup the download. The
528 // |starting_offset| parameter is for where to resume.
SetupDownload(off_t starting_offset)529 void SetupDownload(off_t starting_offset) {
530 start_at_offset_ = starting_offset;
531 // Prepare data 10 kB of data.
532 data_.clear();
533 for (unsigned int i = 0; i < 10 * 1000; i++)
534 data_ += 'a' + (i % 25);
535
536 // Setup p2p.
537 FakeP2PManagerConfiguration* test_conf = new FakeP2PManagerConfiguration();
538 p2p_manager_.reset(P2PManager::Construct(test_conf,
539 nullptr,
540 &fake_um_,
541 "cros_au",
542 3,
543 base::TimeDelta::FromDays(5)));
544 fake_system_state_.set_p2p_manager(p2p_manager_.get());
545 }
546
547 // To be called by tests to perform the download. The
548 // |use_p2p_to_share| parameter is used to indicate whether the
549 // payload should be shared via p2p.
StartDownload(bool use_p2p_to_share)550 void StartDownload(bool use_p2p_to_share) {
551 EXPECT_CALL(*fake_system_state_.mock_payload_state(),
552 GetUsingP2PForSharing())
553 .WillRepeatedly(Return(use_p2p_to_share));
554
555 ScopedTempFile output_temp_file;
556 TestDirectFileWriter writer;
557 EXPECT_EQ(
558 0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
559 InstallPlan install_plan;
560 install_plan.payloads.push_back(
561 {.size = data_.length(),
562 .hash = {'1', '2', '3', '4', 'h', 'a', 's', 'h'}});
563 auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
564 feeder_action->set_obj(install_plan);
565 MockPrefs prefs;
566 // Note that DownloadAction takes ownership of the passed in HttpFetcher.
567 auto download_action = std::make_unique<DownloadAction>(
568 &prefs,
569 fake_system_state_.boot_control(),
570 fake_system_state_.hardware(),
571 &fake_system_state_,
572 new MockHttpFetcher(data_.c_str(), data_.length(), nullptr),
573 false /* interactive */);
574 auto http_fetcher = download_action->http_fetcher();
575 download_action->SetTestFileWriter(&writer);
576 BondActions(feeder_action.get(), download_action.get());
577 delegate_.expected_data_ =
578 brillo::Blob(data_.begin() + start_at_offset_, data_.end());
579 delegate_.path_ = output_temp_file.path();
580 processor_.set_delegate(&delegate_);
581 processor_.EnqueueAction(std::move(feeder_action));
582 processor_.EnqueueAction(std::move(download_action));
583
584 loop_.PostTask(
585 FROM_HERE,
586 base::Bind(
587 [](P2PDownloadActionTest* action_test, HttpFetcher* http_fetcher) {
588 action_test->processor_.StartProcessing();
589 http_fetcher->SetOffset(action_test->start_at_offset_);
590 },
591 base::Unretained(this),
592 base::Unretained(http_fetcher)));
593 loop_.Run();
594 }
595
596 // Mainloop used to make StartDownload() synchronous.
597 brillo::FakeMessageLoop loop_{nullptr};
598
599 // Delegate that is passed to the ActionProcessor.
600 DownloadActionTestProcessorDelegate delegate_;
601
602 // The P2PManager used in the test.
603 unique_ptr<P2PManager> p2p_manager_;
604
605 // The ActionProcessor used for running the actions.
606 ActionProcessor processor_;
607
608 // A fake system state.
609 FakeSystemState fake_system_state_;
610
611 // The data being downloaded.
612 string data_;
613
614 private:
615 // The requested starting offset passed to SetupDownload().
616 off_t start_at_offset_;
617
618 chromeos_update_manager::FakeUpdateManager fake_um_;
619 };
620
TEST_F(P2PDownloadActionTest,IsWrittenTo)621 TEST_F(P2PDownloadActionTest, IsWrittenTo) {
622 SetupDownload(0); // starting_offset
623 StartDownload(true); // use_p2p_to_share
624
625 // Check the p2p file and its content matches what was sent.
626 string file_id = delegate_.p2p_file_id_;
627 EXPECT_NE("", file_id);
628 EXPECT_EQ(static_cast<int>(data_.length()),
629 p2p_manager_->FileGetSize(file_id));
630 EXPECT_EQ(static_cast<int>(data_.length()),
631 p2p_manager_->FileGetExpectedSize(file_id));
632 string p2p_file_contents;
633 EXPECT_TRUE(
634 ReadFileToString(p2p_manager_->FileGetPath(file_id), &p2p_file_contents));
635 EXPECT_EQ(data_, p2p_file_contents);
636 }
637
TEST_F(P2PDownloadActionTest,DeleteIfHoleExists)638 TEST_F(P2PDownloadActionTest, DeleteIfHoleExists) {
639 SetupDownload(1000); // starting_offset
640 StartDownload(true); // use_p2p_to_share
641
642 // DownloadAction should convey that the file is not being shared.
643 // and that we don't have any p2p files.
644 EXPECT_EQ(delegate_.p2p_file_id_, "");
645 EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
646 }
647
TEST_F(P2PDownloadActionTest,CanAppend)648 TEST_F(P2PDownloadActionTest, CanAppend) {
649 SetupDownload(1000); // starting_offset
650
651 // Prepare the file with existing data before starting to write to
652 // it via DownloadAction.
653 string file_id = utils::CalculateP2PFileId(
654 {'1', '2', '3', '4', 'h', 'a', 's', 'h'}, data_.length());
655 ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
656 string existing_data;
657 for (unsigned int i = 0; i < 1000; i++)
658 existing_data += '0' + (i % 10);
659 ASSERT_EQ(
660 WriteFile(
661 p2p_manager_->FileGetPath(file_id), existing_data.c_str(), 1000),
662 1000);
663
664 StartDownload(true); // use_p2p_to_share
665
666 // DownloadAction should convey the same file_id and the file should
667 // have the expected size.
668 EXPECT_EQ(delegate_.p2p_file_id_, file_id);
669 EXPECT_EQ(static_cast<ssize_t>(data_.length()),
670 p2p_manager_->FileGetSize(file_id));
671 EXPECT_EQ(static_cast<ssize_t>(data_.length()),
672 p2p_manager_->FileGetExpectedSize(file_id));
673 string p2p_file_contents;
674 // Check that the first 1000 bytes wasn't touched and that we
675 // appended the remaining as appropriate.
676 EXPECT_TRUE(
677 ReadFileToString(p2p_manager_->FileGetPath(file_id), &p2p_file_contents));
678 EXPECT_EQ(existing_data, p2p_file_contents.substr(0, 1000));
679 EXPECT_EQ(data_.substr(1000), p2p_file_contents.substr(1000));
680 }
681
TEST_F(P2PDownloadActionTest,DeletePartialP2PFileIfResumingWithoutP2P)682 TEST_F(P2PDownloadActionTest, DeletePartialP2PFileIfResumingWithoutP2P) {
683 SetupDownload(1000); // starting_offset
684
685 // Prepare the file with all existing data before starting to write
686 // to it via DownloadAction.
687 string file_id = utils::CalculateP2PFileId(
688 {'1', '2', '3', '4', 'h', 'a', 's', 'h'}, data_.length());
689 ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
690 string existing_data;
691 for (unsigned int i = 0; i < 1000; i++)
692 existing_data += '0' + (i % 10);
693 ASSERT_EQ(
694 WriteFile(
695 p2p_manager_->FileGetPath(file_id), existing_data.c_str(), 1000),
696 1000);
697
698 // Check that the file is there.
699 EXPECT_EQ(1000, p2p_manager_->FileGetSize(file_id));
700 EXPECT_EQ(1, p2p_manager_->CountSharedFiles());
701
702 StartDownload(false); // use_p2p_to_share
703
704 // DownloadAction should have deleted the p2p file. Check that it's gone.
705 EXPECT_EQ(-1, p2p_manager_->FileGetSize(file_id));
706 EXPECT_EQ(0, p2p_manager_->CountSharedFiles());
707 }
708
709 } // namespace chromeos_update_engine
710