text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "base/file_util.h"
  #include "base/message_loop.h"
  #include "base/string_number_conversions.h"
  #include "base/test/test_file_util.h"
  #include "content/browser/browser_thread_impl.h"
  #include "content/browser/byte_stream.h"
  #include "content/browser/download/download_create_info.h"
  #include "content/browser/download/download_file_impl.h"
  #include "content/browser/download/download_request_handle.h"
  #include "content/public/browser/download_destination_observer.h"
  #include "content/public/browser/download_interrupt_reasons.h"
  #include "content/public/browser/download_manager.h"
  #include "content/public/browser/power_save_blocker.h"
  #include "content/public/test/mock_download_manager.h"
  #include "net/base/file_stream.h"
  #include "net/base/mock_file_stream.h"
  #include "net/base/net_errors.h"
  #include "testing/gmock/include/gmock/gmock.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  using ::testing::_;
  using ::testing::AnyNumber;
  using ::testing::DoAll;
  using ::testing::InSequence;
  using ::testing::Return;
  using ::testing::SetArgPointee;
  using ::testing::StrictMock;
  
  namespace content {
  namespace {
  
  class MockByteStreamReader : public ByteStreamReader {
   public:
    MockByteStreamReader() {}
    ~MockByteStreamReader() {}
  
    // ByteStream functions
    MOCK_METHOD2(Read, ByteStreamReader::StreamState(
        scoped_refptr<net::IOBuffer>*, size_t*));
    MOCK_CONST_METHOD0(GetStatus, DownloadInterruptReason());
    MOCK_METHOD1(RegisterCallback, void(const base::Closure&));
  };
  
  class MockDownloadDestinationObserver : public DownloadDestinationObserver {
   public:
    MOCK_METHOD3(DestinationUpdate, void(int64, int64, const std::string&));
    MOCK_METHOD1(DestinationError, void(DownloadInterruptReason));
    MOCK_METHOD1(DestinationCompleted, void(const std::string&));
  
    // Doesn't override any methods in the base class.  Used to make sure
    // that the last DestinationUpdate before a Destination{Completed,Error}
    // had the right values.
    MOCK_METHOD3(CurrentUpdateStatus,
                 void(int64, int64, const std::string&));
  };
  
  MATCHER(IsNullCallback, "") { return (arg.is_null()); }
  
  }  // namespace
  
  DownloadId::Domain kValidIdDomain = "valid DownloadId::Domain";
  
  class DownloadFileTest : public testing::Test {
   public:
  
    static const char* kTestData1;
    static const char* kTestData2;
    static const char* kTestData3;
    static const char* kDataHash;
    static const int32 kDummyDownloadId;
    static const int kDummyChildId;
    static const int kDummyRequestId;
  
    DownloadFileTest() :
        observer_(new StrictMock<MockDownloadDestinationObserver>),
        observer_factory_(observer_.get()),
        bytes_(-1),
        bytes_per_sec_(-1),
        hash_state_("xyzzy"),
        ui_thread_(BrowserThread::UI, &loop_),
        file_thread_(BrowserThread::FILE, &loop_) {
    }
  
    virtual ~DownloadFileTest() {
    }
  
    void SetUpdateDownloadInfo(int64 bytes, int64 bytes_per_sec,
                               const std::string& hash_state) {
      bytes_ = bytes;
      bytes_per_sec_ = bytes_per_sec;
      hash_state_ = hash_state;
    }
  
    void ConfirmUpdateDownloadInfo() {
      observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_, hash_state_);
    }
  
    virtual void SetUp() {
      EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _))
          .Times(AnyNumber())
          .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo));
    }
  
    // Mock calls to this function are forwarded here.
    void RegisterCallback(base::Closure sink_callback) {
      sink_callback_ = sink_callback;
    }
  
    void SetInterruptReasonCallback(bool* was_called,
                                    DownloadInterruptReason* reason_p,
                                    DownloadInterruptReason reason) {
      *was_called = true;
      *reason_p = reason;
    }
  
    virtual bool CreateDownloadFile(int offset, bool calculate_hash) {
      // There can be only one.
      DCHECK(!download_file_.get());
  
      input_stream_ = new StrictMock<MockByteStreamReader>();
  
      // TODO: Need to actually create a function that'll set the variables
      // based on the inputs from the callback.
      EXPECT_CALL(*input_stream_, RegisterCallback(_))
          .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback))
          .RetiresOnSaturation();
  
      scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo());
      download_file_.reset(
          new DownloadFileImpl(
              save_info.Pass(),
              base::FilePath(),
              GURL(),                     // Source
              GURL(),                     // Referrer
              calculate_hash,
              scoped_ptr<ByteStreamReader>(input_stream_),
              net::BoundNetLog(),
              scoped_ptr<PowerSaveBlocker>(NULL).Pass(),
              observer_factory_.GetWeakPtr()));
  
      EXPECT_CALL(*input_stream_, Read(_, _))
          .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
          .RetiresOnSaturation();
  
      base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this);
      bool called = false;
      DownloadInterruptReason result;
      download_file_->Initialize(base::Bind(
          &DownloadFileTest::SetInterruptReasonCallback,
          weak_ptr_factory.GetWeakPtr(), &called, &result));
      loop_.RunUntilIdle();
      EXPECT_TRUE(called);
  
      ::testing::Mock::VerifyAndClearExpectations(input_stream_);
      return result == DOWNLOAD_INTERRUPT_REASON_NONE;
    }
  
    virtual void DestroyDownloadFile(int offset) {
      EXPECT_FALSE(download_file_->InProgress());
      EXPECT_EQ(static_cast<int64>(expected_data_.size()),
                download_file_->BytesSoFar());
  
      // Make sure the data has been properly written to disk.
      std::string disk_data;
      EXPECT_TRUE(file_util::ReadFileToString(download_file_->FullPath(),
                                              &disk_data));
      EXPECT_EQ(expected_data_, disk_data);
  
      // Make sure the Browser and File threads outlive the DownloadFile
      // to satisfy thread checks inside it.
      download_file_.reset();
    }
  
    // Setup the stream to do be a data append; don't actually trigger
    // the callback or do verifications.
    void SetupDataAppend(const char **data_chunks, size_t num_chunks,
                         ::testing::Sequence s) {
      DCHECK(input_stream_);
      for (size_t i = 0; i < num_chunks; i++) {
        const char *source_data = data_chunks[i];
        size_t length = strlen(source_data);
        scoped_refptr<net::IOBuffer> data = new net::IOBuffer(length);
        memcpy(data->data(), source_data, length);
        EXPECT_CALL(*input_stream_, Read(_, _))
            .InSequence(s)
            .WillOnce(DoAll(SetArgPointee<0>(data),
                            SetArgPointee<1>(length),
                            Return(ByteStreamReader::STREAM_HAS_DATA)))
            .RetiresOnSaturation();
        expected_data_ += source_data;
      }
    }
  
    void VerifyStreamAndSize() {
      ::testing::Mock::VerifyAndClearExpectations(input_stream_);
      int64 size;
      EXPECT_TRUE(file_util::GetFileSize(download_file_->FullPath(), &size));
      EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size));
    }
  
    // TODO(rdsmith): Manage full percentage issues properly.
    void AppendDataToFile(const char **data_chunks, size_t num_chunks) {
      ::testing::Sequence s1;
      SetupDataAppend(data_chunks, num_chunks, s1);
      EXPECT_CALL(*input_stream_, Read(_, _))
          .InSequence(s1)
          .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
          .RetiresOnSaturation();
      sink_callback_.Run();
      VerifyStreamAndSize();
    }
  
    void SetupFinishStream(DownloadInterruptReason interrupt_reason,
                         ::testing::Sequence s) {
      EXPECT_CALL(*input_stream_, Read(_, _))
          .InSequence(s)
          .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE))
          .RetiresOnSaturation();
      EXPECT_CALL(*input_stream_, GetStatus())
          .InSequence(s)
          .WillOnce(Return(interrupt_reason))
          .RetiresOnSaturation();
      EXPECT_CALL(*input_stream_, RegisterCallback(_))
          .RetiresOnSaturation();
    }
  
    void FinishStream(DownloadInterruptReason interrupt_reason,
                      bool check_observer) {
      ::testing::Sequence s1;
      SetupFinishStream(interrupt_reason, s1);
      sink_callback_.Run();
      VerifyStreamAndSize();
      if (check_observer) {
        EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
        loop_.RunUntilIdle();
        ::testing::Mock::VerifyAndClearExpectations(observer_.get());
        EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _))
            .Times(AnyNumber())
            .WillRepeatedly(Invoke(this,
                                   &DownloadFileTest::SetUpdateDownloadInfo));
      }
    }
  
    DownloadInterruptReason RenameAndUniquify(
        const base::FilePath& full_path,
        base::FilePath* result_path_p) {
      base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this);
      DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE);
      bool callback_was_called(false);
      base::FilePath result_path;
  
      download_file_->RenameAndUniquify(
          full_path, base::Bind(&DownloadFileTest::SetRenameResult,
                                weak_ptr_factory.GetWeakPtr(),
                                &callback_was_called,
                                &result_reason, result_path_p));
      loop_.RunUntilIdle();
  
      EXPECT_TRUE(callback_was_called);
      return result_reason;
    }
  
    DownloadInterruptReason RenameAndAnnotate(
        const base::FilePath& full_path,
        base::FilePath* result_path_p) {
      base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this);
      DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE);
      bool callback_was_called(false);
      base::FilePath result_path;
  
      download_file_->RenameAndAnnotate(
          full_path, base::Bind(&DownloadFileTest::SetRenameResult,
                                weak_ptr_factory.GetWeakPtr(),
                                &callback_was_called,
                                &result_reason, result_path_p));
      loop_.RunUntilIdle();
  
      EXPECT_TRUE(callback_was_called);
      return result_reason;
    }
  
   protected:
    scoped_ptr<StrictMock<MockDownloadDestinationObserver> > observer_;
    base::WeakPtrFactory<DownloadDestinationObserver> observer_factory_;
  
    // DownloadFile instance we are testing.
    scoped_ptr<DownloadFile> download_file_;
  
    // Stream for sending data into the download file.
    // Owned by download_file_; will be alive for lifetime of download_file_.
    StrictMock<MockByteStreamReader>* input_stream_;
  
    // Sink callback data for stream.
    base::Closure sink_callback_;
  
    // Latest update sent to the observer.
    int64 bytes_;
    int64 bytes_per_sec_;
    std::string hash_state_;
  
    MessageLoop loop_;
  
   private:
    void SetRenameResult(bool* called_p,
                         DownloadInterruptReason* reason_p,
                         base::FilePath* result_path_p,
                         DownloadInterruptReason reason,
                         const base::FilePath& result_path) {
      if (called_p)
        *called_p = true;
      if (reason_p)
        *reason_p = reason;
      if (result_path_p)
        *result_path_p = result_path;
    }
  
    // UI thread.
    BrowserThreadImpl ui_thread_;
    // File thread to satisfy debug checks in DownloadFile.
    BrowserThreadImpl file_thread_;
  
    // Keep track of what data should be saved to the disk file.
    std::string expected_data_;
  };
  
  const char* DownloadFileTest::kTestData1 =
      "Let's write some data to the file!\n";
  const char* DownloadFileTest::kTestData2 = "Writing more data.\n";
  const char* DownloadFileTest::kTestData3 = "Final line.";
  const char* DownloadFileTest::kDataHash =
      "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8";
  
  const int32 DownloadFileTest::kDummyDownloadId = 23;
  const int DownloadFileTest::kDummyChildId = 3;
  const int DownloadFileTest::kDummyRequestId = 67;
  
  // Rename the file before any data is downloaded, after some has, after it all
  // has, and after it's closed.
  TEST_F(DownloadFileTest, RenameFileFinal) {
    ASSERT_TRUE(CreateDownloadFile(0, true));
    base::FilePath initial_path(download_file_->FullPath());
    EXPECT_TRUE(file_util::PathExists(initial_path));
    base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
    base::FilePath path_2(initial_path.InsertBeforeExtensionASCII("_2"));
    base::FilePath path_3(initial_path.InsertBeforeExtensionASCII("_3"));
    base::FilePath path_4(initial_path.InsertBeforeExtensionASCII("_4"));
    base::FilePath path_5(initial_path.InsertBeforeExtensionASCII("_5"));
    base::FilePath output_path;
  
    // Rename the file before downloading any data.
    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
              RenameAndUniquify(path_1, &output_path));
    base::FilePath renamed_path = download_file_->FullPath();
    EXPECT_EQ(path_1, renamed_path);
    EXPECT_EQ(path_1, output_path);
  
    // Check the files.
    EXPECT_FALSE(file_util::PathExists(initial_path));
    EXPECT_TRUE(file_util::PathExists(path_1));
  
    // Download the data.
    const char* chunks1[] = { kTestData1, kTestData2 };
    AppendDataToFile(chunks1, 2);
  
    // Rename the file after downloading some data.
    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
              RenameAndUniquify(path_2, &output_path));
    renamed_path = download_file_->FullPath();
    EXPECT_EQ(path_2, renamed_path);
    EXPECT_EQ(path_2, output_path);
  
    // Check the files.
    EXPECT_FALSE(file_util::PathExists(path_1));
    EXPECT_TRUE(file_util::PathExists(path_2));
  
    const char* chunks2[] = { kTestData3 };
    AppendDataToFile(chunks2, 1);
  
    // Rename the file after downloading all the data.
    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
              RenameAndUniquify(path_3, &output_path));
    renamed_path = download_file_->FullPath();
    EXPECT_EQ(path_3, renamed_path);
    EXPECT_EQ(path_3, output_path);
  
    // Check the files.
    EXPECT_FALSE(file_util::PathExists(path_2));
    EXPECT_TRUE(file_util::PathExists(path_3));
  
    // Should not be able to get the hash until the file is closed.
    std::string hash;
    EXPECT_FALSE(download_file_->GetHash(&hash));
    FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
    loop_.RunUntilIdle();
  
    // Rename the file after downloading all the data and closing the file.
    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
              RenameAndUniquify(path_4, &output_path));
    renamed_path = download_file_->FullPath();
    EXPECT_EQ(path_4, renamed_path);
    EXPECT_EQ(path_4, output_path);
  
    // Check the files.
    EXPECT_FALSE(file_util::PathExists(path_3));
    EXPECT_TRUE(file_util::PathExists(path_4));
  
    // Check the hash.
    EXPECT_TRUE(download_file_->GetHash(&hash));
    EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size()));
  
    // Check that a rename with overwrite to an existing file succeeds.
    std::string file_contents;
    ASSERT_FALSE(file_util::PathExists(path_5));
    static const char file_data[] = "xyzzy";
    ASSERT_EQ(static_cast<int>(sizeof(file_data) - 1),
              file_util::WriteFile(path_5, file_data, sizeof(file_data) - 1));
    ASSERT_TRUE(file_util::PathExists(path_5));
    EXPECT_TRUE(file_util::ReadFileToString(path_5, &file_contents));
    EXPECT_EQ(std::string(file_data), file_contents);
  
    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
              RenameAndAnnotate(path_5, &output_path));
    EXPECT_EQ(path_5, output_path);
  
    file_contents = "";
    EXPECT_TRUE(file_util::ReadFileToString(path_5, &file_contents));
    EXPECT_NE(std::string(file_data), file_contents);
  
    DestroyDownloadFile(0);
  }
  
  // Test to make sure the rename uniquifies if we aren't overwriting
  // and there's a file where we're aiming.
  TEST_F(DownloadFileTest, RenameUniquifies) {
    ASSERT_TRUE(CreateDownloadFile(0, true));
    base::FilePath initial_path(download_file_->FullPath());
    EXPECT_TRUE(file_util::PathExists(initial_path));
    base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
    base::FilePath path_1_suffixed(path_1.InsertBeforeExtensionASCII(" (1)"));
  
    ASSERT_FALSE(file_util::PathExists(path_1));
    static const char file_data[] = "xyzzy";
    ASSERT_EQ(static_cast<int>(sizeof(file_data)),
              file_util::WriteFile(path_1, file_data, sizeof(file_data)));
    ASSERT_TRUE(file_util::PathExists(path_1));
  
    EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL));
    EXPECT_TRUE(file_util::PathExists(path_1_suffixed));
  
    FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
    loop_.RunUntilIdle();
    DestroyDownloadFile(0);
  }
  
  // Test to make sure we get the proper error on failure.
  TEST_F(DownloadFileTest, RenameError) {
    ASSERT_TRUE(CreateDownloadFile(0, true));
    base::FilePath initial_path(download_file_->FullPath());
  
    // Create a subdirectory.
    base::FilePath tempdir(
        initial_path.DirName().Append(FILE_PATH_LITERAL("tempdir")));
    ASSERT_TRUE(file_util::CreateDirectory(tempdir));
    base::FilePath target_path(tempdir.Append(initial_path.BaseName()));
  
    // Targets
    base::FilePath target_path_suffixed(
        target_path.InsertBeforeExtensionASCII(" (1)"));
    ASSERT_FALSE(file_util::PathExists(target_path));
    ASSERT_FALSE(file_util::PathExists(target_path_suffixed));
  
    // Make the directory unwritable and try to rename within it.
    {
      file_util::PermissionRestorer restorer(tempdir);
      ASSERT_TRUE(file_util::MakeFileUnwritable(tempdir));
  
      // Expect nulling out of further processing.
      EXPECT_CALL(*input_stream_, RegisterCallback(IsNullCallback()));
      EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
                RenameAndAnnotate(target_path, NULL));
      EXPECT_FALSE(file_util::PathExists(target_path_suffixed));
    }
  
    FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
    loop_.RunUntilIdle();
    DestroyDownloadFile(0);
  }
  
  // Various tests of the StreamActive method.
  TEST_F(DownloadFileTest, StreamEmptySuccess) {
    ASSERT_TRUE(CreateDownloadFile(0, true));
    base::FilePath initial_path(download_file_->FullPath());
    EXPECT_TRUE(file_util::PathExists(initial_path));
  
    // Test that calling the sink_callback_ on an empty stream shouldn't
    // do anything.
    AppendDataToFile(NULL, 0);
  
    // Finish the download this way and make sure we see it on the
    // observer.
    EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
    FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, false);
    loop_.RunUntilIdle();
  
    DestroyDownloadFile(0);
  }
  
  TEST_F(DownloadFileTest, StreamEmptyError) {
    ASSERT_TRUE(CreateDownloadFile(0, true));
    base::FilePath initial_path(download_file_->FullPath());
    EXPECT_TRUE(file_util::PathExists(initial_path));
  
    // Finish the download in error and make sure we see it on the
    // observer.
    EXPECT_CALL(*(observer_.get()),
                DestinationError(
                    DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED))
        .WillOnce(InvokeWithoutArgs(
            this, &DownloadFileTest::ConfirmUpdateDownloadInfo));
  
    // If this next EXPECT_CALL fails flakily, it's probably a real failure.
    // We'll be getting a stream of UpdateDownload calls from the timer, and
    // the last one may have the correct information even if the failure
    // doesn't produce an update, as the timer update may have triggered at the
    // same time.
    EXPECT_CALL(*(observer_.get()), CurrentUpdateStatus(0, _, _));
  
    FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false);
  
    loop_.RunUntilIdle();
  
    DestroyDownloadFile(0);
  }
  
  TEST_F(DownloadFileTest, StreamNonEmptySuccess) {
    ASSERT_TRUE(CreateDownloadFile(0, true));
    base::FilePath initial_path(download_file_->FullPath());
    EXPECT_TRUE(file_util::PathExists(initial_path));
  
    const char* chunks1[] = { kTestData1, kTestData2 };
    ::testing::Sequence s1;
    SetupDataAppend(chunks1, 2, s1);
    SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, s1);
    EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
    sink_callback_.Run();
    VerifyStreamAndSize();
    loop_.RunUntilIdle();
    DestroyDownloadFile(0);
  }
  
  TEST_F(DownloadFileTest, StreamNonEmptyError) {
    ASSERT_TRUE(CreateDownloadFile(0, true));
    base::FilePath initial_path(download_file_->FullPath());
    EXPECT_TRUE(file_util::PathExists(initial_path));
  
    const char* chunks1[] = { kTestData1, kTestData2 };
    ::testing::Sequence s1;
    SetupDataAppend(chunks1, 2, s1);
    SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1);
  
    EXPECT_CALL(*(observer_.get()),
                DestinationError(
                    DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED))
        .WillOnce(InvokeWithoutArgs(
            this, &DownloadFileTest::ConfirmUpdateDownloadInfo));
  
    // If this next EXPECT_CALL fails flakily, it's probably a real failure.
    // We'll be getting a stream of UpdateDownload calls from the timer, and
    // the last one may have the correct information even if the failure
    // doesn't produce an update, as the timer update may have triggered at the
    // same time.
    EXPECT_CALL(*(observer_.get()),
                CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2),
                                    _, _));
  
    sink_callback_.Run();
    loop_.RunUntilIdle();
    VerifyStreamAndSize();
    DestroyDownloadFile(0);
  }
  
  // Send some data, wait 3/4s of a second, run the message loop, and
  // confirm the values the observer received are correct.
  TEST_F(DownloadFileTest, ConfirmUpdate) {
    CreateDownloadFile(0, true);
  
    const char* chunks1[] = { kTestData1, kTestData2 };
    AppendDataToFile(chunks1, 2);
  
    // Run the message loops for 750ms and check for results.
    loop_.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(),
                           base::TimeDelta::FromMilliseconds(750));
    loop_.Run();
  
    EXPECT_EQ(static_cast<int64>(strlen(kTestData1) + strlen(kTestData2)),
              bytes_);
    EXPECT_EQ(download_file_->GetHashState(), hash_state_);
  
    FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
    DestroyDownloadFile(0);
  }
  
  }  // namespace content
  