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