Home | History | Annotate | Download | only in payload_consumer
      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::AtLeast;
     56 using testing::InSequence;
     57 using testing::Return;
     58 using testing::SetArgPointee;
     59 using testing::_;
     60 
     61 class DownloadActionTest : public ::testing::Test { };
     62 
     63 namespace {
     64 
     65 class DownloadActionTestProcessorDelegate : public ActionProcessorDelegate {
     66  public:
     67   explicit DownloadActionTestProcessorDelegate(ErrorCode expected_code)
     68       : processing_done_called_(false),
     69         expected_code_(expected_code) {}
     70   ~DownloadActionTestProcessorDelegate() override {
     71     EXPECT_TRUE(processing_done_called_);
     72   }
     73   void ProcessingDone(const ActionProcessor* processor,
     74                       ErrorCode code) override {
     75     brillo::MessageLoop::current()->BreakLoop();
     76     brillo::Blob found_data;
     77     ASSERT_TRUE(utils::ReadFile(path_, &found_data));
     78     if (expected_code_ != ErrorCode::kDownloadWriteError) {
     79       ASSERT_EQ(expected_data_.size(), found_data.size());
     80       for (unsigned i = 0; i < expected_data_.size(); i++) {
     81         EXPECT_EQ(expected_data_[i], found_data[i]);
     82       }
     83     }
     84     processing_done_called_ = true;
     85   }
     86 
     87   void ActionCompleted(ActionProcessor* processor,
     88                        AbstractAction* action,
     89                        ErrorCode code) override {
     90     const string type = action->Type();
     91     if (type == DownloadAction::StaticType()) {
     92       EXPECT_EQ(expected_code_, code);
     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 };
    103 
    104 class TestDirectFileWriter : public DirectFileWriter {
    105  public:
    106   TestDirectFileWriter() : fail_write_(0), current_write_(0) {}
    107   void set_fail_write(int fail_write) { fail_write_ = fail_write; }
    108 
    109   virtual bool Write(const void* bytes, size_t count) {
    110     if (++current_write_ == fail_write_) {
    111       return false;
    112     }
    113     return DirectFileWriter::Write(bytes, count);
    114   }
    115 
    116  private:
    117   // If positive, fail on the |fail_write_| call to Write.
    118   int fail_write_;
    119   int current_write_;
    120 };
    121 
    122 void StartProcessorInRunLoop(ActionProcessor* processor,
    123                              MockHttpFetcher* http_fetcher) {
    124   processor->StartProcessing();
    125   http_fetcher->SetOffset(1);
    126 }
    127 
    128 void TestWithData(const brillo::Blob& data,
    129                   int fail_write,
    130                   bool use_download_delegate) {
    131   brillo::FakeMessageLoop loop(nullptr);
    132   loop.SetAsCurrent();
    133   FakeSystemState fake_system_state;
    134 
    135   // TODO(adlr): see if we need a different file for build bots
    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   fake_system_state.fake_boot_control()->SetSlotBootable(
    154       install_plan.source_slot, true);
    155   fake_system_state.fake_boot_control()->SetSlotBootable(
    156       install_plan.target_slot, true);
    157   ObjectFeederAction<InstallPlan> feeder_action;
    158   feeder_action.set_obj(install_plan);
    159   MockPrefs prefs;
    160   MockHttpFetcher* http_fetcher = new MockHttpFetcher(data.data(),
    161                                                       data.size(),
    162                                                       nullptr);
    163   // takes ownership of passed in HttpFetcher
    164   DownloadAction download_action(&prefs,
    165                                  fake_system_state.boot_control(),
    166                                  fake_system_state.hardware(),
    167                                  &fake_system_state,
    168                                  http_fetcher,
    169                                  false /* is_interactive */);
    170   download_action.SetTestFileWriter(&writer);
    171   BondActions(&feeder_action, &download_action);
    172   MockDownloadActionDelegate download_delegate;
    173   if (use_download_delegate) {
    174     InSequence s;
    175     download_action.set_delegate(&download_delegate);
    176     if (data.size() > kMockHttpFetcherChunkSize)
    177       EXPECT_CALL(download_delegate,
    178                   BytesReceived(_, kMockHttpFetcherChunkSize, _));
    179     EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(AtLeast(1));
    180   }
    181   ErrorCode expected_code = ErrorCode::kSuccess;
    182   if (fail_write > 0)
    183     expected_code = ErrorCode::kDownloadWriteError;
    184   DownloadActionTestProcessorDelegate delegate(expected_code);
    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(&feeder_action);
    190   processor.EnqueueAction(&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(fake_system_state.fake_boot_control()->IsSlotBootable(
    198       install_plan.source_slot));
    199   EXPECT_FALSE(fake_system_state.fake_boot_control()->IsSlotBootable(
    200       install_plan.target_slot));
    201 }
    202 }  // namespace
    203 
    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 
    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 
    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 
    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 
    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   FakeSystemState fake_system_state;
    255   EXPECT_CALL(*fake_system_state.mock_payload_state(), NextPayload())
    256       .WillOnce(Return(true));
    257 
    258   MockFileWriter mock_file_writer;
    259   EXPECT_CALL(mock_file_writer, Close()).WillRepeatedly(Return(0));
    260   EXPECT_CALL(mock_file_writer, Write(_, _, _))
    261       .WillRepeatedly(
    262           DoAll(SetArgPointee<2>(ErrorCode::kSuccess), Return(true)));
    263 
    264   InstallPlan install_plan;
    265   uint64_t total_expected_download_size{0};
    266   for (const auto& data : payload_datas) {
    267     uint64_t size = data.size();
    268     install_plan.payloads.push_back(
    269         {.size = size, .type = InstallPayloadType::kFull});
    270     total_expected_download_size += size;
    271   }
    272   ObjectFeederAction<InstallPlan> feeder_action;
    273   feeder_action.set_obj(install_plan);
    274   MockPrefs prefs;
    275   MockHttpFetcher* http_fetcher = new MockHttpFetcher(
    276       payload_datas[0].data(), payload_datas[0].size(), nullptr);
    277   // takes ownership of passed in HttpFetcher
    278   DownloadAction download_action(&prefs,
    279                                  fake_system_state.boot_control(),
    280                                  fake_system_state.hardware(),
    281                                  &fake_system_state,
    282                                  http_fetcher,
    283                                  false /* is_interactive */);
    284   download_action.SetTestFileWriter(&mock_file_writer);
    285   BondActions(&feeder_action, &download_action);
    286   MockDownloadActionDelegate download_delegate;
    287   {
    288     InSequence s;
    289     download_action.set_delegate(&download_delegate);
    290     // these are hand-computed based on the payloads specified above
    291     EXPECT_CALL(download_delegate,
    292                 BytesReceived(kMockHttpFetcherChunkSize,
    293                               kMockHttpFetcherChunkSize,
    294                               total_expected_download_size));
    295     EXPECT_CALL(download_delegate,
    296                 BytesReceived(kMockHttpFetcherChunkSize,
    297                               kMockHttpFetcherChunkSize * 2,
    298                               total_expected_download_size));
    299     EXPECT_CALL(download_delegate,
    300                 BytesReceived(kMockHttpFetcherChunkSize,
    301                               kMockHttpFetcherChunkSize * 3,
    302                               total_expected_download_size));
    303     EXPECT_CALL(download_delegate,
    304                 BytesReceived(kMockHttpFetcherChunkSize,
    305                               kMockHttpFetcherChunkSize * 4,
    306                               total_expected_download_size));
    307     EXPECT_CALL(download_delegate,
    308                 BytesReceived(256,
    309                               kMockHttpFetcherChunkSize * 4 + 256,
    310                               total_expected_download_size));
    311     EXPECT_CALL(download_delegate,
    312                 BytesReceived(kMockHttpFetcherChunkSize,
    313                               kMockHttpFetcherChunkSize * 5 + 256,
    314                               total_expected_download_size));
    315     EXPECT_CALL(download_delegate,
    316                 BytesReceived(kMockHttpFetcherChunkSize,
    317                               total_expected_download_size,
    318                               total_expected_download_size));
    319   }
    320   ActionProcessor processor;
    321   processor.EnqueueAction(&feeder_action);
    322   processor.EnqueueAction(&download_action);
    323 
    324   loop.PostTask(
    325       FROM_HERE,
    326       base::Bind(
    327           [](ActionProcessor* processor) { processor->StartProcessing(); },
    328           base::Unretained(&processor)));
    329   loop.Run();
    330   EXPECT_FALSE(loop.PendingTasks());
    331 }
    332 
    333 namespace {
    334 class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
    335  public:
    336   void ProcessingStopped(const ActionProcessor* processor) {
    337     brillo::MessageLoop::current()->BreakLoop();
    338   }
    339 };
    340 
    341 void TerminateEarlyTestStarter(ActionProcessor* processor) {
    342   processor->StartProcessing();
    343   CHECK(processor->IsRunning());
    344   processor->StopProcessing();
    345 }
    346 
    347 void TestTerminateEarly(bool use_download_delegate) {
    348   brillo::FakeMessageLoop loop(nullptr);
    349   loop.SetAsCurrent();
    350 
    351   brillo::Blob data(kMockHttpFetcherChunkSize +
    352                       kMockHttpFetcherChunkSize / 2);
    353   memset(data.data(), 0, data.size());
    354 
    355   ScopedTempFile temp_file;
    356   {
    357     DirectFileWriter writer;
    358     EXPECT_EQ(0, writer.Open(temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
    359 
    360     // takes ownership of passed in HttpFetcher
    361     ObjectFeederAction<InstallPlan> feeder_action;
    362     InstallPlan install_plan;
    363     install_plan.payloads.resize(1);
    364     feeder_action.set_obj(install_plan);
    365     FakeSystemState fake_system_state_;
    366     MockPrefs prefs;
    367     DownloadAction download_action(
    368         &prefs,
    369         fake_system_state_.boot_control(),
    370         fake_system_state_.hardware(),
    371         &fake_system_state_,
    372         new MockHttpFetcher(data.data(), data.size(), nullptr),
    373         false /* is_interactive */);
    374     download_action.SetTestFileWriter(&writer);
    375     MockDownloadActionDelegate download_delegate;
    376     if (use_download_delegate) {
    377       download_action.set_delegate(&download_delegate);
    378       EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(0);
    379     }
    380     TerminateEarlyTestProcessorDelegate delegate;
    381     ActionProcessor processor;
    382     processor.set_delegate(&delegate);
    383     processor.EnqueueAction(&feeder_action);
    384     processor.EnqueueAction(&download_action);
    385     BondActions(&feeder_action, &download_action);
    386 
    387     loop.PostTask(FROM_HERE,
    388                   base::Bind(&TerminateEarlyTestStarter, &processor));
    389     loop.Run();
    390     EXPECT_FALSE(loop.PendingTasks());
    391   }
    392 
    393   // 1 or 0 chunks should have come through
    394   const off_t resulting_file_size(utils::FileSize(temp_file.path()));
    395   EXPECT_GE(resulting_file_size, 0);
    396   if (resulting_file_size != 0)
    397     EXPECT_EQ(kMockHttpFetcherChunkSize,
    398               static_cast<size_t>(resulting_file_size));
    399 }
    400 
    401 }  // namespace
    402 
    403 TEST(DownloadActionTest, TerminateEarlyTest) {
    404   TestTerminateEarly(true);
    405 }
    406 
    407 TEST(DownloadActionTest, TerminateEarlyNoDownloadDelegateTest) {
    408   TestTerminateEarly(false);
    409 }
    410 
    411 class DownloadActionTestAction;
    412 
    413 template<>
    414 class ActionTraits<DownloadActionTestAction> {
    415  public:
    416   typedef InstallPlan OutputObjectType;
    417   typedef InstallPlan InputObjectType;
    418 };
    419 
    420 // This is a simple Action class for testing.
    421 class DownloadActionTestAction : public Action<DownloadActionTestAction> {
    422  public:
    423   DownloadActionTestAction() : did_run_(false) {}
    424   typedef InstallPlan InputObjectType;
    425   typedef InstallPlan OutputObjectType;
    426   ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
    427   ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
    428   ActionProcessor* processor() { return processor_; }
    429   void PerformAction() {
    430     did_run_ = true;
    431     ASSERT_TRUE(HasInputObject());
    432     EXPECT_TRUE(expected_input_object_ == GetInputObject());
    433     ASSERT_TRUE(processor());
    434     processor()->ActionComplete(this, ErrorCode::kSuccess);
    435   }
    436   string Type() const { return "DownloadActionTestAction"; }
    437   InstallPlan expected_input_object_;
    438   bool did_run_;
    439 };
    440 
    441 namespace {
    442 // This class is an ActionProcessorDelegate that simply terminates the
    443 // run loop when the ActionProcessor has completed processing. It's used
    444 // only by the test PassObjectOutTest.
    445 class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
    446  public:
    447   void ProcessingDone(const ActionProcessor* processor, ErrorCode code) {
    448     brillo::MessageLoop::current()->BreakLoop();
    449   }
    450 };
    451 
    452 }  // namespace
    453 
    454 TEST(DownloadActionTest, PassObjectOutTest) {
    455   brillo::FakeMessageLoop loop(nullptr);
    456   loop.SetAsCurrent();
    457 
    458   DirectFileWriter writer;
    459   EXPECT_EQ(0, writer.Open("/dev/null", O_WRONLY | O_CREAT, 0));
    460 
    461   // takes ownership of passed in HttpFetcher
    462   InstallPlan install_plan;
    463   install_plan.payloads.push_back({.size = 1});
    464   EXPECT_TRUE(
    465       HashCalculator::RawHashOfData({'x'}, &install_plan.payloads[0].hash));
    466   ObjectFeederAction<InstallPlan> feeder_action;
    467   feeder_action.set_obj(install_plan);
    468   MockPrefs prefs;
    469   FakeSystemState fake_system_state_;
    470   DownloadAction download_action(&prefs,
    471                                  fake_system_state_.boot_control(),
    472                                  fake_system_state_.hardware(),
    473                                  &fake_system_state_,
    474                                  new MockHttpFetcher("x", 1, nullptr),
    475                                  false /* is_interactive */);
    476   download_action.SetTestFileWriter(&writer);
    477 
    478   DownloadActionTestAction test_action;
    479   test_action.expected_input_object_ = install_plan;
    480   BondActions(&feeder_action, &download_action);
    481   BondActions(&download_action, &test_action);
    482 
    483   ActionProcessor processor;
    484   PassObjectOutTestProcessorDelegate delegate;
    485   processor.set_delegate(&delegate);
    486   processor.EnqueueAction(&feeder_action);
    487   processor.EnqueueAction(&download_action);
    488   processor.EnqueueAction(&test_action);
    489 
    490   loop.PostTask(
    491       FROM_HERE,
    492       base::Bind(
    493           [](ActionProcessor* processor) { processor->StartProcessing(); },
    494           base::Unretained(&processor)));
    495   loop.Run();
    496   EXPECT_FALSE(loop.PendingTasks());
    497 
    498   EXPECT_EQ(true, test_action.did_run_);
    499 }
    500 
    501 // Test fixture for P2P tests.
    502 class P2PDownloadActionTest : public testing::Test {
    503  protected:
    504   P2PDownloadActionTest()
    505     : start_at_offset_(0),
    506       fake_um_(fake_system_state_.fake_clock()) {}
    507 
    508   ~P2PDownloadActionTest() override {}
    509 
    510   // Derived from testing::Test.
    511   void SetUp() override {
    512     loop_.SetAsCurrent();
    513   }
    514 
    515   // Derived from testing::Test.
    516   void TearDown() override {
    517     EXPECT_FALSE(loop_.PendingTasks());
    518   }
    519 
    520   // To be called by tests to setup the download. The
    521   // |starting_offset| parameter is for where to resume.
    522   void SetupDownload(off_t starting_offset) {
    523     start_at_offset_ = starting_offset;
    524     // Prepare data 10 kB of data.
    525     data_.clear();
    526     for (unsigned int i = 0; i < 10 * 1000; i++)
    527       data_ += 'a' + (i % 25);
    528 
    529     // Setup p2p.
    530     FakeP2PManagerConfiguration *test_conf = new FakeP2PManagerConfiguration();
    531     p2p_manager_.reset(P2PManager::Construct(
    532         test_conf, nullptr, &fake_um_, "cros_au", 3,
    533         base::TimeDelta::FromDays(5)));
    534     fake_system_state_.set_p2p_manager(p2p_manager_.get());
    535   }
    536 
    537   // To be called by tests to perform the download. The
    538   // |use_p2p_to_share| parameter is used to indicate whether the
    539   // payload should be shared via p2p.
    540   void StartDownload(bool use_p2p_to_share) {
    541     EXPECT_CALL(*fake_system_state_.mock_payload_state(),
    542                 GetUsingP2PForSharing())
    543         .WillRepeatedly(Return(use_p2p_to_share));
    544 
    545     ScopedTempFile output_temp_file;
    546     TestDirectFileWriter writer;
    547     EXPECT_EQ(
    548         0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
    549     InstallPlan install_plan;
    550     install_plan.payloads.push_back(
    551         {.size = data_.length(),
    552          .hash = {'1', '2', '3', '4', 'h', 'a', 's', 'h'}});
    553     ObjectFeederAction<InstallPlan> feeder_action;
    554     feeder_action.set_obj(install_plan);
    555     MockPrefs prefs;
    556     http_fetcher_ = new MockHttpFetcher(data_.c_str(),
    557                                         data_.length(),
    558                                         nullptr);
    559     // Note that DownloadAction takes ownership of the passed in HttpFetcher.
    560     download_action_.reset(new DownloadAction(&prefs,
    561                                               fake_system_state_.boot_control(),
    562                                               fake_system_state_.hardware(),
    563                                               &fake_system_state_,
    564                                               http_fetcher_,
    565                                               false /* is_interactive */));
    566     download_action_->SetTestFileWriter(&writer);
    567     BondActions(&feeder_action, download_action_.get());
    568     DownloadActionTestProcessorDelegate delegate(ErrorCode::kSuccess);
    569     delegate.expected_data_ = brillo::Blob(data_.begin() + start_at_offset_,
    570                                            data_.end());
    571     delegate.path_ = output_temp_file.path();
    572     processor_.set_delegate(&delegate);
    573     processor_.EnqueueAction(&feeder_action);
    574     processor_.EnqueueAction(download_action_.get());
    575 
    576     loop_.PostTask(FROM_HERE, base::Bind(
    577         &P2PDownloadActionTest::StartProcessorInRunLoopForP2P,
    578         base::Unretained(this)));
    579     loop_.Run();
    580   }
    581 
    582   // Mainloop used to make StartDownload() synchronous.
    583   brillo::FakeMessageLoop loop_{nullptr};
    584 
    585   // The DownloadAction instance under test.
    586   unique_ptr<DownloadAction> download_action_;
    587 
    588   // The HttpFetcher used in the test.
    589   MockHttpFetcher* http_fetcher_;
    590 
    591   // The P2PManager used in the test.
    592   unique_ptr<P2PManager> p2p_manager_;
    593 
    594   // The ActionProcessor used for running the actions.
    595   ActionProcessor processor_;
    596 
    597   // A fake system state.
    598   FakeSystemState fake_system_state_;
    599 
    600   // The data being downloaded.
    601   string data_;
    602 
    603  private:
    604   // Callback used in StartDownload() method.
    605   void StartProcessorInRunLoopForP2P() {
    606     processor_.StartProcessing();
    607     download_action_->http_fetcher()->SetOffset(start_at_offset_);
    608   }
    609 
    610   // The requested starting offset passed to SetupDownload().
    611   off_t start_at_offset_;
    612 
    613   chromeos_update_manager::FakeUpdateManager fake_um_;
    614 };
    615 
    616 TEST_F(P2PDownloadActionTest, IsWrittenTo) {
    617   if (!test_utils::IsXAttrSupported(FilePath("/tmp"))) {
    618     LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
    619                  << "Please update your system to support this feature.";
    620     return;
    621   }
    622 
    623   SetupDownload(0);     // starting_offset
    624   StartDownload(true);  // use_p2p_to_share
    625 
    626   // Check the p2p file and its content matches what was sent.
    627   string file_id = download_action_->p2p_file_id();
    628   EXPECT_NE("", file_id);
    629   EXPECT_EQ(static_cast<int>(data_.length()),
    630             p2p_manager_->FileGetSize(file_id));
    631   EXPECT_EQ(static_cast<int>(data_.length()),
    632             p2p_manager_->FileGetExpectedSize(file_id));
    633   string p2p_file_contents;
    634   EXPECT_TRUE(ReadFileToString(p2p_manager_->FileGetPath(file_id),
    635                                &p2p_file_contents));
    636   EXPECT_EQ(data_, p2p_file_contents);
    637 }
    638 
    639 TEST_F(P2PDownloadActionTest, DeleteIfHoleExists) {
    640   if (!test_utils::IsXAttrSupported(FilePath("/tmp"))) {
    641     LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
    642                  << "Please update your system to support this feature.";
    643     return;
    644   }
    645 
    646   SetupDownload(1000);  // starting_offset
    647   StartDownload(true);  // use_p2p_to_share
    648 
    649   // DownloadAction should convey that the file is not being shared.
    650   // and that we don't have any p2p files.
    651   EXPECT_EQ(download_action_->p2p_file_id(), "");
    652   EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
    653 }
    654 
    655 TEST_F(P2PDownloadActionTest, CanAppend) {
    656   if (!test_utils::IsXAttrSupported(FilePath("/tmp"))) {
    657     LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
    658                  << "Please update your system to support this feature.";
    659     return;
    660   }
    661 
    662   SetupDownload(1000);  // starting_offset
    663 
    664   // Prepare the file with existing data before starting to write to
    665   // it via DownloadAction.
    666   string file_id = utils::CalculateP2PFileId(
    667       {'1', '2', '3', '4', 'h', 'a', 's', 'h'}, data_.length());
    668   ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
    669   string existing_data;
    670   for (unsigned int i = 0; i < 1000; i++)
    671     existing_data += '0' + (i % 10);
    672   ASSERT_EQ(WriteFile(p2p_manager_->FileGetPath(file_id), existing_data.c_str(),
    673                       1000), 1000);
    674 
    675   StartDownload(true);  // use_p2p_to_share
    676 
    677   // DownloadAction should convey the same file_id and the file should
    678   // have the expected size.
    679   EXPECT_EQ(download_action_->p2p_file_id(), file_id);
    680   EXPECT_EQ(static_cast<ssize_t>(data_.length()),
    681             p2p_manager_->FileGetSize(file_id));
    682   EXPECT_EQ(static_cast<ssize_t>(data_.length()),
    683             p2p_manager_->FileGetExpectedSize(file_id));
    684   string p2p_file_contents;
    685   // Check that the first 1000 bytes wasn't touched and that we
    686   // appended the remaining as appropriate.
    687   EXPECT_TRUE(ReadFileToString(p2p_manager_->FileGetPath(file_id),
    688                                &p2p_file_contents));
    689   EXPECT_EQ(existing_data, p2p_file_contents.substr(0, 1000));
    690   EXPECT_EQ(data_.substr(1000), p2p_file_contents.substr(1000));
    691 }
    692 
    693 TEST_F(P2PDownloadActionTest, DeletePartialP2PFileIfResumingWithoutP2P) {
    694   if (!test_utils::IsXAttrSupported(FilePath("/tmp"))) {
    695     LOG(WARNING) << "Skipping test because /tmp does not support xattr. "
    696                  << "Please update your system to support this feature.";
    697     return;
    698   }
    699 
    700   SetupDownload(1000);  // starting_offset
    701 
    702   // Prepare the file with all existing data before starting to write
    703   // to it via DownloadAction.
    704   string file_id = utils::CalculateP2PFileId(
    705       {'1', '2', '3', '4', 'h', 'a', 's', 'h'}, data_.length());
    706   ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
    707   string existing_data;
    708   for (unsigned int i = 0; i < 1000; i++)
    709     existing_data += '0' + (i % 10);
    710   ASSERT_EQ(WriteFile(p2p_manager_->FileGetPath(file_id), existing_data.c_str(),
    711                       1000), 1000);
    712 
    713   // Check that the file is there.
    714   EXPECT_EQ(1000, p2p_manager_->FileGetSize(file_id));
    715   EXPECT_EQ(1, p2p_manager_->CountSharedFiles());
    716 
    717   StartDownload(false);  // use_p2p_to_share
    718 
    719   // DownloadAction should have deleted the p2p file. Check that it's gone.
    720   EXPECT_EQ(-1, p2p_manager_->FileGetSize(file_id));
    721   EXPECT_EQ(0, p2p_manager_->CountSharedFiles());
    722 }
    723 
    724 }  // namespace chromeos_update_engine
    725