普通文本  |  710行  |  25.25 KB

//
// Copyright (C) 2011 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "update_engine/payload_consumer/download_action.h"

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/location.h>
#include <base/strings/stringprintf.h>
#include <brillo/message_loops/fake_message_loop.h>
#include <brillo/message_loops/message_loop.h>

#include "update_engine/common/action_pipe.h"
#include "update_engine/common/hash_calculator.h"
#include "update_engine/common/mock_http_fetcher.h"
#include "update_engine/common/mock_prefs.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
#include "update_engine/fake_p2p_manager_configuration.h"
#include "update_engine/fake_system_state.h"
#include "update_engine/mock_file_writer.h"
#include "update_engine/payload_consumer/mock_download_action.h"
#include "update_engine/update_manager/fake_update_manager.h"

namespace chromeos_update_engine {

using base::FilePath;
using base::ReadFileToString;
using base::WriteFile;
using std::string;
using std::unique_ptr;
using test_utils::ScopedTempFile;
using testing::_;
using testing::AtLeast;
using testing::InSequence;
using testing::Return;
using testing::SetArgPointee;

class DownloadActionTest : public ::testing::Test {};

namespace {

class DownloadActionTestProcessorDelegate : public ActionProcessorDelegate {
 public:
  DownloadActionTestProcessorDelegate()
      : processing_done_called_(false), expected_code_(ErrorCode::kSuccess) {}
  ~DownloadActionTestProcessorDelegate() override {
    EXPECT_TRUE(processing_done_called_);
  }
  void ProcessingDone(const ActionProcessor* processor,
                      ErrorCode code) override {
    brillo::MessageLoop::current()->BreakLoop();
    brillo::Blob found_data;
    ASSERT_TRUE(utils::ReadFile(path_, &found_data));
    if (expected_code_ != ErrorCode::kDownloadWriteError) {
      ASSERT_EQ(expected_data_.size(), found_data.size());
      for (unsigned i = 0; i < expected_data_.size(); i++) {
        EXPECT_EQ(expected_data_[i], found_data[i]);
      }
    }
    processing_done_called_ = true;
  }

  void ActionCompleted(ActionProcessor* processor,
                       AbstractAction* action,
                       ErrorCode code) override {
    const string type = action->Type();
    if (type == DownloadAction::StaticType()) {
      EXPECT_EQ(expected_code_, code);
      p2p_file_id_ = static_cast<DownloadAction*>(action)->p2p_file_id();
    } else {
      EXPECT_EQ(ErrorCode::kSuccess, code);
    }
  }

  string path_;
  brillo::Blob expected_data_;
  bool processing_done_called_;
  ErrorCode expected_code_;
  string p2p_file_id_;
};

class TestDirectFileWriter : public DirectFileWriter {
 public:
  TestDirectFileWriter() : fail_write_(0), current_write_(0) {}
  void set_fail_write(int fail_write) { fail_write_ = fail_write; }

  virtual bool Write(const void* bytes, size_t count) {
    if (++current_write_ == fail_write_) {
      return false;
    }
    return DirectFileWriter::Write(bytes, count);
  }

 private:
  // If positive, fail on the |fail_write_| call to Write.
  int fail_write_;
  int current_write_;
};

void StartProcessorInRunLoop(ActionProcessor* processor,
                             MockHttpFetcher* http_fetcher) {
  processor->StartProcessing();
  http_fetcher->SetOffset(1);
}

void TestWithData(const brillo::Blob& data,
                  int fail_write,
                  bool use_download_delegate) {
  brillo::FakeMessageLoop loop(nullptr);
  loop.SetAsCurrent();
  FakeSystemState fake_system_state;

  // TODO(adlr): see if we need a different file for build bots
  ScopedTempFile output_temp_file;
  TestDirectFileWriter writer;
  EXPECT_EQ(
      0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
  writer.set_fail_write(fail_write);

  uint64_t size = data.size() - 1;
  InstallPlan install_plan;
  install_plan.payloads.push_back(
      {.size = size, .type = InstallPayloadType::kDelta});
  // We pull off the first byte from data and seek past it.
  EXPECT_TRUE(HashCalculator::RawHashOfBytes(
      &data[1], data.size() - 1, &install_plan.payloads[0].hash));
  install_plan.source_slot = 0;
  install_plan.target_slot = 1;
  // We mark both slots as bootable. Only the target slot should be unbootable
  // after the download starts.
  fake_system_state.fake_boot_control()->SetSlotBootable(
      install_plan.source_slot, true);
  fake_system_state.fake_boot_control()->SetSlotBootable(
      install_plan.target_slot, true);
  auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
  feeder_action->set_obj(install_plan);
  MockPrefs prefs;
  MockHttpFetcher* http_fetcher =
      new MockHttpFetcher(data.data(), data.size(), nullptr);
  // takes ownership of passed in HttpFetcher
  auto download_action =
      std::make_unique<DownloadAction>(&prefs,
                                       fake_system_state.boot_control(),
                                       fake_system_state.hardware(),
                                       &fake_system_state,
                                       http_fetcher,
                                       false /* interactive */);
  download_action->SetTestFileWriter(&writer);
  BondActions(feeder_action.get(), download_action.get());
  MockDownloadActionDelegate download_delegate;
  if (use_download_delegate) {
    InSequence s;
    download_action->set_delegate(&download_delegate);
    if (data.size() > kMockHttpFetcherChunkSize)
      EXPECT_CALL(download_delegate,
                  BytesReceived(_, kMockHttpFetcherChunkSize, _));
    EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(AtLeast(1));
    EXPECT_CALL(download_delegate, DownloadComplete())
        .Times(fail_write == 0 ? 1 : 0);
  }
  DownloadActionTestProcessorDelegate delegate;
  delegate.expected_code_ =
      (fail_write > 0) ? ErrorCode::kDownloadWriteError : ErrorCode::kSuccess;
  delegate.expected_data_ = brillo::Blob(data.begin() + 1, data.end());
  delegate.path_ = output_temp_file.path();
  ActionProcessor processor;
  processor.set_delegate(&delegate);
  processor.EnqueueAction(std::move(feeder_action));
  processor.EnqueueAction(std::move(download_action));

  loop.PostTask(FROM_HERE,
                base::Bind(&StartProcessorInRunLoop, &processor, http_fetcher));
  loop.Run();
  EXPECT_FALSE(loop.PendingTasks());

  EXPECT_TRUE(fake_system_state.fake_boot_control()->IsSlotBootable(
      install_plan.source_slot));
  EXPECT_FALSE(fake_system_state.fake_boot_control()->IsSlotBootable(
      install_plan.target_slot));
}
}  // namespace

TEST(DownloadActionTest, SimpleTest) {
  brillo::Blob small;
  const char* foo = "foo";
  small.insert(small.end(), foo, foo + strlen(foo));
  TestWithData(small,
               0,      // fail_write
               true);  // use_download_delegate
}

TEST(DownloadActionTest, LargeTest) {
  brillo::Blob big(5 * kMockHttpFetcherChunkSize);
  char c = '0';
  for (unsigned int i = 0; i < big.size(); i++) {
    big[i] = c;
    c = ('9' == c) ? '0' : c + 1;
  }
  TestWithData(big,
               0,      // fail_write
               true);  // use_download_delegate
}

TEST(DownloadActionTest, FailWriteTest) {
  brillo::Blob big(5 * kMockHttpFetcherChunkSize);
  char c = '0';
  for (unsigned int i = 0; i < big.size(); i++) {
    big[i] = c;
    c = ('9' == c) ? '0' : c + 1;
  }
  TestWithData(big,
               2,      // fail_write
               true);  // use_download_delegate
}

TEST(DownloadActionTest, NoDownloadDelegateTest) {
  brillo::Blob small;
  const char* foo = "foofoo";
  small.insert(small.end(), foo, foo + strlen(foo));
  TestWithData(small,
               0,       // fail_write
               false);  // use_download_delegate
}

TEST(DownloadActionTest, MultiPayloadProgressTest) {
  std::vector<brillo::Blob> payload_datas;
  // the first payload must be the largest, as it's the actual payload used by
  // the MockHttpFetcher for all downloaded data.
  payload_datas.emplace_back(4 * kMockHttpFetcherChunkSize + 256);
  payload_datas.emplace_back(2 * kMockHttpFetcherChunkSize);
  brillo::FakeMessageLoop loop(nullptr);
  loop.SetAsCurrent();
  FakeSystemState fake_system_state;
  EXPECT_CALL(*fake_system_state.mock_payload_state(), NextPayload())
      .WillOnce(Return(true));

  MockFileWriter mock_file_writer;
  EXPECT_CALL(mock_file_writer, Close()).WillRepeatedly(Return(0));
  EXPECT_CALL(mock_file_writer, Write(_, _, _))
      .WillRepeatedly(
          DoAll(SetArgPointee<2>(ErrorCode::kSuccess), Return(true)));

  InstallPlan install_plan;
  uint64_t total_expected_download_size{0};
  for (const auto& data : payload_datas) {
    uint64_t size = data.size();
    install_plan.payloads.push_back(
        {.size = size, .type = InstallPayloadType::kFull});
    total_expected_download_size += size;
  }
  auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
  feeder_action->set_obj(install_plan);
  MockPrefs prefs;
  MockHttpFetcher* http_fetcher = new MockHttpFetcher(
      payload_datas[0].data(), payload_datas[0].size(), nullptr);
  // takes ownership of passed in HttpFetcher
  auto download_action =
      std::make_unique<DownloadAction>(&prefs,
                                       fake_system_state.boot_control(),
                                       fake_system_state.hardware(),
                                       &fake_system_state,
                                       http_fetcher,
                                       false /* interactive */);
  download_action->SetTestFileWriter(&mock_file_writer);
  BondActions(feeder_action.get(), download_action.get());
  MockDownloadActionDelegate download_delegate;
  {
    InSequence s;
    download_action->set_delegate(&download_delegate);
    // these are hand-computed based on the payloads specified above
    EXPECT_CALL(download_delegate,
                BytesReceived(kMockHttpFetcherChunkSize,
                              kMockHttpFetcherChunkSize,
                              total_expected_download_size));
    EXPECT_CALL(download_delegate,
                BytesReceived(kMockHttpFetcherChunkSize,
                              kMockHttpFetcherChunkSize * 2,
                              total_expected_download_size));
    EXPECT_CALL(download_delegate,
                BytesReceived(kMockHttpFetcherChunkSize,
                              kMockHttpFetcherChunkSize * 3,
                              total_expected_download_size));
    EXPECT_CALL(download_delegate,
                BytesReceived(kMockHttpFetcherChunkSize,
                              kMockHttpFetcherChunkSize * 4,
                              total_expected_download_size));
    EXPECT_CALL(download_delegate,
                BytesReceived(256,
                              kMockHttpFetcherChunkSize * 4 + 256,
                              total_expected_download_size));
    EXPECT_CALL(download_delegate,
                BytesReceived(kMockHttpFetcherChunkSize,
                              kMockHttpFetcherChunkSize * 5 + 256,
                              total_expected_download_size));
    EXPECT_CALL(download_delegate,
                BytesReceived(kMockHttpFetcherChunkSize,
                              total_expected_download_size,
                              total_expected_download_size));
  }
  ActionProcessor processor;
  processor.EnqueueAction(std::move(feeder_action));
  processor.EnqueueAction(std::move(download_action));

  loop.PostTask(
      FROM_HERE,
      base::Bind(
          [](ActionProcessor* processor) { processor->StartProcessing(); },
          base::Unretained(&processor)));
  loop.Run();
  EXPECT_FALSE(loop.PendingTasks());
}

namespace {
class TerminateEarlyTestProcessorDelegate : public ActionProcessorDelegate {
 public:
  void ProcessingStopped(const ActionProcessor* processor) {
    brillo::MessageLoop::current()->BreakLoop();
  }
};

void TerminateEarlyTestStarter(ActionProcessor* processor) {
  processor->StartProcessing();
  CHECK(processor->IsRunning());
  processor->StopProcessing();
}

void TestTerminateEarly(bool use_download_delegate) {
  brillo::FakeMessageLoop loop(nullptr);
  loop.SetAsCurrent();

  brillo::Blob data(kMockHttpFetcherChunkSize + kMockHttpFetcherChunkSize / 2);
  memset(data.data(), 0, data.size());

  ScopedTempFile temp_file;
  {
    DirectFileWriter writer;
    EXPECT_EQ(0, writer.Open(temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));

    // takes ownership of passed in HttpFetcher
    auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
    InstallPlan install_plan;
    install_plan.payloads.resize(1);
    feeder_action->set_obj(install_plan);
    FakeSystemState fake_system_state_;
    MockPrefs prefs;
    auto download_action = std::make_unique<DownloadAction>(
        &prefs,
        fake_system_state_.boot_control(),
        fake_system_state_.hardware(),
        &fake_system_state_,
        new MockHttpFetcher(data.data(), data.size(), nullptr),
        false /* interactive */);
    download_action->SetTestFileWriter(&writer);
    MockDownloadActionDelegate download_delegate;
    if (use_download_delegate) {
      download_action->set_delegate(&download_delegate);
      EXPECT_CALL(download_delegate, BytesReceived(_, _, _)).Times(0);
    }
    TerminateEarlyTestProcessorDelegate delegate;
    ActionProcessor processor;
    processor.set_delegate(&delegate);
    BondActions(feeder_action.get(), download_action.get());
    processor.EnqueueAction(std::move(feeder_action));
    processor.EnqueueAction(std::move(download_action));

    loop.PostTask(FROM_HERE,
                  base::Bind(&TerminateEarlyTestStarter, &processor));
    loop.Run();
    EXPECT_FALSE(loop.PendingTasks());
  }

  // 1 or 0 chunks should have come through
  const off_t resulting_file_size(utils::FileSize(temp_file.path()));
  EXPECT_GE(resulting_file_size, 0);
  if (resulting_file_size != 0)
    EXPECT_EQ(kMockHttpFetcherChunkSize,
              static_cast<size_t>(resulting_file_size));
}

}  // namespace

TEST(DownloadActionTest, TerminateEarlyTest) {
  TestTerminateEarly(true);
}

TEST(DownloadActionTest, TerminateEarlyNoDownloadDelegateTest) {
  TestTerminateEarly(false);
}

class DownloadActionTestAction;

template <>
class ActionTraits<DownloadActionTestAction> {
 public:
  typedef InstallPlan OutputObjectType;
  typedef InstallPlan InputObjectType;
};

// This is a simple Action class for testing.
class DownloadActionTestAction : public Action<DownloadActionTestAction> {
 public:
  DownloadActionTestAction() = default;
  typedef InstallPlan InputObjectType;
  typedef InstallPlan OutputObjectType;
  ActionPipe<InstallPlan>* in_pipe() { return in_pipe_.get(); }
  ActionPipe<InstallPlan>* out_pipe() { return out_pipe_.get(); }
  ActionProcessor* processor() { return processor_; }
  void PerformAction() {
    ASSERT_TRUE(HasInputObject());
    EXPECT_TRUE(expected_input_object_ == GetInputObject());
    ASSERT_TRUE(processor());
    processor()->ActionComplete(this, ErrorCode::kSuccess);
  }
  static std::string StaticType() { return "DownloadActionTestAction"; }
  string Type() const { return StaticType(); }
  InstallPlan expected_input_object_;
};

namespace {
// This class is an ActionProcessorDelegate that simply terminates the
// run loop when the ActionProcessor has completed processing. It's used
// only by the test PassObjectOutTest.
class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
 public:
  void ProcessingDone(const ActionProcessor* processor,
                      ErrorCode code) override {
    brillo::MessageLoop::current()->BreakLoop();
  }
  void ActionCompleted(ActionProcessor* processor,
                       AbstractAction* action,
                       ErrorCode code) override {
    if (action->Type() == DownloadActionTestAction::StaticType()) {
      did_test_action_run_ = true;
    }
  }

  bool did_test_action_run_ = false;
};

}  // namespace

TEST(DownloadActionTest, PassObjectOutTest) {
  brillo::FakeMessageLoop loop(nullptr);
  loop.SetAsCurrent();

  DirectFileWriter writer;
  EXPECT_EQ(0, writer.Open("/dev/null", O_WRONLY | O_CREAT, 0));

  // takes ownership of passed in HttpFetcher
  InstallPlan install_plan;
  install_plan.payloads.push_back({.size = 1});
  EXPECT_TRUE(
      HashCalculator::RawHashOfData({'x'}, &install_plan.payloads[0].hash));
  auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
  feeder_action->set_obj(install_plan);
  MockPrefs prefs;
  FakeSystemState fake_system_state_;
  auto download_action =
      std::make_unique<DownloadAction>(&prefs,
                                       fake_system_state_.boot_control(),
                                       fake_system_state_.hardware(),
                                       &fake_system_state_,
                                       new MockHttpFetcher("x", 1, nullptr),
                                       false /* interactive */);
  download_action->SetTestFileWriter(&writer);

  auto test_action = std::make_unique<DownloadActionTestAction>();
  test_action->expected_input_object_ = install_plan;
  BondActions(feeder_action.get(), download_action.get());
  BondActions(download_action.get(), test_action.get());

  ActionProcessor processor;
  PassObjectOutTestProcessorDelegate delegate;
  processor.set_delegate(&delegate);
  processor.EnqueueAction(std::move(feeder_action));
  processor.EnqueueAction(std::move(download_action));
  processor.EnqueueAction(std::move(test_action));

  loop.PostTask(
      FROM_HERE,
      base::Bind(
          [](ActionProcessor* processor) { processor->StartProcessing(); },
          base::Unretained(&processor)));
  loop.Run();
  EXPECT_FALSE(loop.PendingTasks());

  EXPECT_EQ(true, delegate.did_test_action_run_);
}

// Test fixture for P2P tests.
class P2PDownloadActionTest : public testing::Test {
 protected:
  P2PDownloadActionTest()
      : start_at_offset_(0), fake_um_(fake_system_state_.fake_clock()) {}

  ~P2PDownloadActionTest() override {}

  // Derived from testing::Test.
  void SetUp() override { loop_.SetAsCurrent(); }

  // Derived from testing::Test.
  void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); }

  // To be called by tests to setup the download. The
  // |starting_offset| parameter is for where to resume.
  void SetupDownload(off_t starting_offset) {
    start_at_offset_ = starting_offset;
    // Prepare data 10 kB of data.
    data_.clear();
    for (unsigned int i = 0; i < 10 * 1000; i++)
      data_ += 'a' + (i % 25);

    // Setup p2p.
    FakeP2PManagerConfiguration* test_conf = new FakeP2PManagerConfiguration();
    p2p_manager_.reset(P2PManager::Construct(test_conf,
                                             nullptr,
                                             &fake_um_,
                                             "cros_au",
                                             3,
                                             base::TimeDelta::FromDays(5)));
    fake_system_state_.set_p2p_manager(p2p_manager_.get());
  }

  // To be called by tests to perform the download. The
  // |use_p2p_to_share| parameter is used to indicate whether the
  // payload should be shared via p2p.
  void StartDownload(bool use_p2p_to_share) {
    EXPECT_CALL(*fake_system_state_.mock_payload_state(),
                GetUsingP2PForSharing())
        .WillRepeatedly(Return(use_p2p_to_share));

    ScopedTempFile output_temp_file;
    TestDirectFileWriter writer;
    EXPECT_EQ(
        0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
    InstallPlan install_plan;
    install_plan.payloads.push_back(
        {.size = data_.length(),
         .hash = {'1', '2', '3', '4', 'h', 'a', 's', 'h'}});
    auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
    feeder_action->set_obj(install_plan);
    MockPrefs prefs;
    // Note that DownloadAction takes ownership of the passed in HttpFetcher.
    auto download_action = std::make_unique<DownloadAction>(
        &prefs,
        fake_system_state_.boot_control(),
        fake_system_state_.hardware(),
        &fake_system_state_,
        new MockHttpFetcher(data_.c_str(), data_.length(), nullptr),
        false /* interactive */);
    auto http_fetcher = download_action->http_fetcher();
    download_action->SetTestFileWriter(&writer);
    BondActions(feeder_action.get(), download_action.get());
    delegate_.expected_data_ =
        brillo::Blob(data_.begin() + start_at_offset_, data_.end());
    delegate_.path_ = output_temp_file.path();
    processor_.set_delegate(&delegate_);
    processor_.EnqueueAction(std::move(feeder_action));
    processor_.EnqueueAction(std::move(download_action));

    loop_.PostTask(
        FROM_HERE,
        base::Bind(
            [](P2PDownloadActionTest* action_test, HttpFetcher* http_fetcher) {
              action_test->processor_.StartProcessing();
              http_fetcher->SetOffset(action_test->start_at_offset_);
            },
            base::Unretained(this),
            base::Unretained(http_fetcher)));
    loop_.Run();
  }

  // Mainloop used to make StartDownload() synchronous.
  brillo::FakeMessageLoop loop_{nullptr};

  // Delegate that is passed to the ActionProcessor.
  DownloadActionTestProcessorDelegate delegate_;

  // The P2PManager used in the test.
  unique_ptr<P2PManager> p2p_manager_;

  // The ActionProcessor used for running the actions.
  ActionProcessor processor_;

  // A fake system state.
  FakeSystemState fake_system_state_;

  // The data being downloaded.
  string data_;

 private:
  // The requested starting offset passed to SetupDownload().
  off_t start_at_offset_;

  chromeos_update_manager::FakeUpdateManager fake_um_;
};

TEST_F(P2PDownloadActionTest, IsWrittenTo) {
  SetupDownload(0);     // starting_offset
  StartDownload(true);  // use_p2p_to_share

  // Check the p2p file and its content matches what was sent.
  string file_id = delegate_.p2p_file_id_;
  EXPECT_NE("", file_id);
  EXPECT_EQ(static_cast<int>(data_.length()),
            p2p_manager_->FileGetSize(file_id));
  EXPECT_EQ(static_cast<int>(data_.length()),
            p2p_manager_->FileGetExpectedSize(file_id));
  string p2p_file_contents;
  EXPECT_TRUE(
      ReadFileToString(p2p_manager_->FileGetPath(file_id), &p2p_file_contents));
  EXPECT_EQ(data_, p2p_file_contents);
}

TEST_F(P2PDownloadActionTest, DeleteIfHoleExists) {
  SetupDownload(1000);  // starting_offset
  StartDownload(true);  // use_p2p_to_share

  // DownloadAction should convey that the file is not being shared.
  // and that we don't have any p2p files.
  EXPECT_EQ(delegate_.p2p_file_id_, "");
  EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
}

TEST_F(P2PDownloadActionTest, CanAppend) {
  SetupDownload(1000);  // starting_offset

  // Prepare the file with existing data before starting to write to
  // it via DownloadAction.
  string file_id = utils::CalculateP2PFileId(
      {'1', '2', '3', '4', 'h', 'a', 's', 'h'}, data_.length());
  ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
  string existing_data;
  for (unsigned int i = 0; i < 1000; i++)
    existing_data += '0' + (i % 10);
  ASSERT_EQ(
      WriteFile(
          p2p_manager_->FileGetPath(file_id), existing_data.c_str(), 1000),
      1000);

  StartDownload(true);  // use_p2p_to_share

  // DownloadAction should convey the same file_id and the file should
  // have the expected size.
  EXPECT_EQ(delegate_.p2p_file_id_, file_id);
  EXPECT_EQ(static_cast<ssize_t>(data_.length()),
            p2p_manager_->FileGetSize(file_id));
  EXPECT_EQ(static_cast<ssize_t>(data_.length()),
            p2p_manager_->FileGetExpectedSize(file_id));
  string p2p_file_contents;
  // Check that the first 1000 bytes wasn't touched and that we
  // appended the remaining as appropriate.
  EXPECT_TRUE(
      ReadFileToString(p2p_manager_->FileGetPath(file_id), &p2p_file_contents));
  EXPECT_EQ(existing_data, p2p_file_contents.substr(0, 1000));
  EXPECT_EQ(data_.substr(1000), p2p_file_contents.substr(1000));
}

TEST_F(P2PDownloadActionTest, DeletePartialP2PFileIfResumingWithoutP2P) {
  SetupDownload(1000);  // starting_offset

  // Prepare the file with all existing data before starting to write
  // to it via DownloadAction.
  string file_id = utils::CalculateP2PFileId(
      {'1', '2', '3', '4', 'h', 'a', 's', 'h'}, data_.length());
  ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
  string existing_data;
  for (unsigned int i = 0; i < 1000; i++)
    existing_data += '0' + (i % 10);
  ASSERT_EQ(
      WriteFile(
          p2p_manager_->FileGetPath(file_id), existing_data.c_str(), 1000),
      1000);

  // Check that the file is there.
  EXPECT_EQ(1000, p2p_manager_->FileGetSize(file_id));
  EXPECT_EQ(1, p2p_manager_->CountSharedFiles());

  StartDownload(false);  // use_p2p_to_share

  // DownloadAction should have deleted the p2p file. Check that it's gone.
  EXPECT_EQ(-1, p2p_manager_->FileGetSize(file_id));
  EXPECT_EQ(0, p2p_manager_->CountSharedFiles());
}

}  // namespace chromeos_update_engine