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