summaryrefslogtreecommitdiff
path: root/chromium/content/browser/download/download_file_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/download/download_file_unittest.cc')
-rw-r--r--chromium/content/browser/download/download_file_unittest.cc606
1 files changed, 606 insertions, 0 deletions
diff --git a/chromium/content/browser/download/download_file_unittest.cc b/chromium/content/browser/download/download_file_unittest.cc
new file mode 100644
index 00000000000..49e418c85bc
--- /dev/null
+++ b/chromium/content/browser/download/download_file_unittest.cc
@@ -0,0 +1,606 @@
+// 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/message_loop.h"
+#include "base/strings/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, int());
+ 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
+
+class DownloadFileTest : public testing::Test {
+ public:
+
+ static const char* kTestData1;
+ static const char* kTestData2;
+ static const char* kTestData3;
+ static const char* kDataHash;
+ static const uint32 kDummyDownloadId;
+ static const int kDummyChildId;
+ static const int kDummyRequestId;
+
+ DownloadFileTest() :
+ observer_(new StrictMock<MockDownloadDestinationObserver>),
+ observer_factory_(observer_.get()),
+ input_stream_(NULL),
+ 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>().Pass(),
+ observer_factory_.GetWeakPtr()));
+ download_file_->SetClientGuid(
+ "12345678-ABCD-1234-DCBA-123456789ABC");
+
+ 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());
+
+ // 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_;
+
+ base::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 uint32 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(base::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(base::PathExists(initial_path));
+ EXPECT_TRUE(base::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(base::PathExists(path_1));
+ EXPECT_TRUE(base::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(base::PathExists(path_2));
+ EXPECT_TRUE(base::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(base::PathExists(path_3));
+ EXPECT_TRUE(base::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(base::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(base::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(base::PathExists(initial_path));
+ base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
+ base::FilePath path_1_suffixed(path_1.InsertBeforeExtensionASCII(" (1)"));
+
+ ASSERT_FALSE(base::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(base::PathExists(path_1));
+
+ EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL));
+ EXPECT_TRUE(base::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(base::PathExists(target_path));
+ ASSERT_FALSE(base::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(base::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(base::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(base::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(base::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(base::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,
+ base::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