Index: content/browser/download/download_file_unittest.cc |
diff --git a/content/browser/download/download_file_unittest.cc b/content/browser/download/download_file_unittest.cc |
index 2e8df324f0ebb164fae83acc2af352d829d123cc..31497e8f87de6ba8fa705de3f4a0615f75f68a9c 100644 |
--- a/content/browser/download/download_file_unittest.cc |
+++ b/content/browser/download/download_file_unittest.cc |
@@ -6,14 +6,17 @@ |
#include "base/message_loop.h" |
#include "base/string_number_conversions.h" |
#include "content/browser/browser_thread_impl.h" |
+#include "content/browser/download/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/browser/power_save_blocker.h" |
+#include "content/public/browser/download_interrupt_reasons.h" |
#include "content/public/browser/download_manager.h" |
#include "content/test/mock_download_manager.h" |
#include "net/base/file_stream.h" |
#include "net/base/net_errors.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
using content::BrowserThread; |
@@ -21,9 +24,45 @@ using content::BrowserThreadImpl; |
using content::DownloadFile; |
using content::DownloadId; |
using content::DownloadManager; |
-using testing::_; |
-using testing::AnyNumber; |
-using testing::StrictMock; |
+using ::testing::_; |
+using ::testing::AnyNumber; |
+using ::testing::DoAll; |
+using ::testing::Return; |
+using ::testing::SetArgPointee; |
+using ::testing::StrictMock; |
+ |
+namespace { |
+ |
+class MockByteStream : public content::ByteStream { |
+ public: |
+ MockByteStream() {} |
+ ~MockByteStream() {} |
+ |
+ // ByteStream functions |
+ MOCK_METHOD1(SetBufferSize, void(size_t)); |
+ MOCK_METHOD1(SetSourceCallbackHysteresis, void(int)); |
+ MOCK_METHOD2(AddData, bool(scoped_refptr<net::IOBuffer>, size_t)); |
+ MOCK_METHOD1(SourceComplete, void(content::DownloadInterruptReason)); |
+ MOCK_CONST_METHOD0(IsFull, bool()); |
+ MOCK_METHOD3(RegisterSourceCallback, |
+ void(scoped_refptr<base::TaskRunner>, |
+ content::ByteStream::ByteStreamCallback, int)); |
+ MOCK_METHOD2(GetData, |
+ content::ByteStream::StreamState( |
+ scoped_refptr<net::IOBuffer>*, size_t*)); |
+ MOCK_CONST_METHOD0(GetSourceResult, content::DownloadInterruptReason()); |
+ MOCK_METHOD3(RegisterSinkCallback, |
+ void(scoped_refptr<base::TaskRunner>, |
+ content::ByteStream::ByteStreamCallback, int)); |
+ MOCK_CONST_METHOD0(num_source_callbacks, size_t()); |
+ MOCK_CONST_METHOD0(num_sink_callbacks, size_t()); |
+ MOCK_CONST_METHOD0(bytes_read, size_t()); |
+ MOCK_CONST_METHOD0(buffers_read, size_t()); |
+ MOCK_CONST_METHOD0(source_trigger_wait_time, base::TimeDelta()); |
+ MOCK_CONST_METHOD0(sink_trigger_wait_time, base::TimeDelta()); |
+}; |
+ |
+} // namespace |
DownloadId::Domain kValidIdDomain = "valid DownloadId::Domain"; |
@@ -43,6 +82,7 @@ class DownloadFileTest : public testing::Test { |
// calling Release() on |download_manager_| won't ever result in its |
// destructor being called and we get a leak. |
DownloadFileTest() : |
+ full_percentage_(0), |
ui_thread_(BrowserThread::UI, &loop_), |
file_thread_(BrowserThread::FILE, &loop_) { |
} |
@@ -77,45 +117,127 @@ class DownloadFileTest : public testing::Test { |
ui_thread_.message_loop()->RunAllPending(); |
} |
- virtual void CreateDownloadFile(scoped_ptr<DownloadFile>* file, |
- int offset, |
- bool calculate_hash) { |
+ // Mock calls to this function are forwarded here. |
+ void RegisterSinkCallback( |
+ scoped_refptr<base::TaskRunner> sink_task_runner, |
+ content::ByteStream::ByteStreamCallback sink_callback, |
+ int full_percentage) { |
+ sink_task_runner_ = sink_task_runner; |
+ sink_callback_ = sink_callback; |
+ full_percentage_ = full_percentage; |
+ } |
+ |
+ virtual bool CreateDownloadFile(int offset, bool calculate_hash) { |
+ // There can be only one. |
+ DCHECK(!download_file_.get()); |
+ |
+ input_pipe_ = new StrictMock<MockByteStream>(); |
+ // TODO: Need to actually create a function that'll set the variables |
+ // based on the inputs from the callback. |
+ EXPECT_CALL(*input_pipe_.get(), RegisterSinkCallback(_, _, 33)) |
+ .WillOnce(Invoke(this, &DownloadFileTest::RegisterSinkCallback)) |
+ .RetiresOnSaturation(); |
+ |
DownloadCreateInfo info; |
info.download_id = DownloadId(kValidIdDomain, kDummyDownloadId + offset); |
+ info.pipe = input_pipe_; |
// info.request_handle default constructed to null. |
info.save_info.file_stream = file_stream_; |
- file->reset( |
+ download_file_.reset( |
new DownloadFileImpl(&info, new DownloadRequestHandle(), |
download_manager_, calculate_hash, |
scoped_ptr<PowerSaveBlocker>(NULL).Pass(), |
net::BoundNetLog())); |
+ |
+ EXPECT_CALL(*input_pipe_.get(), GetData(_, _)) |
+ .WillOnce(Return(content::ByteStream::STREAM_EMPTY)) |
+ .RetiresOnSaturation(); |
+ net::Error result = download_file_->Initialize(); |
+ ::testing::Mock::VerifyAndClearExpectations(input_pipe_.get()); |
+ return result == net::OK; |
} |
- virtual void DestroyDownloadFile(scoped_ptr<DownloadFile>* file, int offset) { |
- EXPECT_EQ(kDummyDownloadId + offset, (*file)->Id()); |
- EXPECT_EQ(download_manager_, (*file)->GetDownloadManager()); |
- EXPECT_FALSE((*file)->InProgress()); |
+ virtual void DestroyDownloadFile(int offset) { |
+ EXPECT_EQ(kDummyDownloadId + offset, download_file_->Id()); |
+ EXPECT_EQ(download_manager_, download_file_->GetDownloadManager()); |
+ EXPECT_FALSE(download_file_->InProgress()); |
EXPECT_EQ(static_cast<int64>(expected_data_.size()), |
- (*file)->BytesSoFar()); |
+ download_file_->BytesSoFar()); |
// Make sure the data has been properly written to disk. |
std::string disk_data; |
- EXPECT_TRUE(file_util::ReadFileToString((*file)->FullPath(), |
+ 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. |
- file->reset(); |
+ EXPECT_CALL(*input_pipe_.get(), RegisterSinkCallback(_, _, 0)) |
+ .WillOnce(Invoke(this, &DownloadFileTest::RegisterSinkCallback)) |
+ .RetiresOnSaturation(); |
+ download_file_.reset(); |
} |
- void AppendDataToFile(scoped_ptr<DownloadFile>* file, |
- const std::string& data) { |
- EXPECT_TRUE((*file)->InProgress()); |
- (*file)->AppendDataToFile(data.data(), data.size()); |
- expected_data_ += data; |
- EXPECT_EQ(static_cast<int64>(expected_data_.size()), |
- (*file)->BytesSoFar()); |
+ // Setup the pipe 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_pipe_.get()); |
+ 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_pipe_.get(), GetData(_, _)) |
+ .InSequence(s) |
+ .WillOnce(DoAll(SetArgPointee<0>(data), |
+ SetArgPointee<1>(length), |
+ Return(content::ByteStream::STREAM_HAS_DATA))) |
+ .RetiresOnSaturation(); |
+ expected_data_ += source_data; |
+ } |
+ } |
+ |
+ void VerifyPipeAndSize() { |
+ ::testing::Mock::VerifyAndClearExpectations(input_pipe_.get()); |
+ 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_pipe_.get(), GetData(_, _)) |
+ .InSequence(s1) |
+ .WillOnce(Return(content::ByteStream::STREAM_EMPTY)) |
+ .RetiresOnSaturation(); |
+ sink_callback_.Run(); |
+ VerifyPipeAndSize(); |
+ } |
+ |
+ void SetupFinishPipe(content::DownloadInterruptReason interrupt_reason, |
+ ::testing::Sequence s) { |
+ EXPECT_CALL(*input_pipe_.get(), GetData(_, _)) |
+ .InSequence(s) |
+ .WillOnce(Return(content::ByteStream::STREAM_COMPLETE)) |
+ .RetiresOnSaturation(); |
+ EXPECT_CALL(*input_pipe_.get(), GetSourceResult()) |
+ .InSequence(s) |
+ .WillOnce(Return(interrupt_reason)) |
+ .RetiresOnSaturation(); |
+ EXPECT_CALL(*input_pipe_.get(), RegisterSinkCallback(_, _, 0)) |
+ .InSequence(s) |
+ .WillOnce(Invoke(this, &DownloadFileTest::RegisterSinkCallback)) |
+ .RetiresOnSaturation(); |
+ } |
+ |
+ void FinishPipe(content::DownloadInterruptReason interrupt_reason) { |
+ ::testing::Sequence s1; |
+ SetupFinishPipe(interrupt_reason, s1); |
+ sink_callback_.Run(); |
+ VerifyPipeAndSize(); |
} |
protected: |
@@ -126,6 +248,14 @@ class DownloadFileTest : public testing::Test { |
// DownloadFile instance we are testing. |
scoped_ptr<DownloadFile> download_file_; |
+ // Pipe for sending data into the download file. |
+ scoped_refptr<StrictMock<MockByteStream> > input_pipe_; |
+ |
+ // Sink callback data for pipe. |
+ scoped_refptr<base::TaskRunner> sink_task_runner_; |
+ content::ByteStream::ByteStreamCallback sink_callback_; |
+ int full_percentage_; |
+ |
// Latest update sent to the download manager. |
int64 bytes_; |
int64 bytes_per_sec_; |
@@ -141,6 +271,7 @@ class DownloadFileTest : public testing::Test { |
// Keep track of what data should be saved to the disk file. |
std::string expected_data_; |
+ |
}; |
const char* DownloadFileTest::kTestData1 = |
@@ -157,7 +288,7 @@ 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) { |
- CreateDownloadFile(&download_file_, 0, true); |
+ CreateDownloadFile(0, true); |
ASSERT_EQ(net::OK, download_file_->Initialize()); |
FilePath initial_path(download_file_->FullPath()); |
EXPECT_TRUE(file_util::PathExists(initial_path)); |
@@ -176,8 +307,8 @@ TEST_F(DownloadFileTest, RenameFileFinal) { |
EXPECT_TRUE(file_util::PathExists(path_1)); |
// Download the data. |
- AppendDataToFile(&download_file_, kTestData1); |
- AppendDataToFile(&download_file_, kTestData2); |
+ const char* chunks1[] = { kTestData1, kTestData2 }; |
+ AppendDataToFile(chunks1, 2); |
// Rename the file after downloading some data. |
EXPECT_EQ(net::OK, download_file_->Rename(path_2)); |
@@ -188,7 +319,8 @@ TEST_F(DownloadFileTest, RenameFileFinal) { |
EXPECT_FALSE(file_util::PathExists(path_1)); |
EXPECT_TRUE(file_util::PathExists(path_2)); |
- AppendDataToFile(&download_file_, kTestData3); |
+ const char* chunks2[] = { kTestData3 }; |
+ AppendDataToFile(chunks2, 1); |
// Rename the file after downloading all the data. |
EXPECT_EQ(net::OK, download_file_->Rename(path_3)); |
@@ -202,8 +334,7 @@ TEST_F(DownloadFileTest, RenameFileFinal) { |
// Should not be able to get the hash until the file is closed. |
std::string hash; |
EXPECT_FALSE(download_file_->GetHash(&hash)); |
- |
- download_file_->Finish(); |
+ FinishPipe(content::DOWNLOAD_INTERRUPT_REASON_NONE); |
// Rename the file after downloading all the data and closing the file. |
EXPECT_EQ(net::OK, download_file_->Rename(path_4)); |
@@ -218,17 +349,94 @@ TEST_F(DownloadFileTest, RenameFileFinal) { |
EXPECT_TRUE(download_file_->GetHash(&hash)); |
EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size())); |
- DestroyDownloadFile(&download_file_, 0); |
+ DestroyDownloadFile(0); |
+} |
+ |
+// Various tests of the PipeActive method. |
+TEST_F(DownloadFileTest, PipeEmptySuccess) { |
+ ASSERT_TRUE(CreateDownloadFile(0, true)); |
+ FilePath initial_path(download_file_->FullPath()); |
+ EXPECT_TRUE(file_util::PathExists(initial_path)); |
+ |
+ // Test that calling the sink_callback_ on an empty pipe shouldn't |
+ // do anything. |
+ AppendDataToFile(NULL, 0); |
+ ::testing::Mock::VerifyAndClearExpectations(download_manager_.get()); |
+ |
+ // Finish the download this way and make sure we see it on the |
+ // DownloadManager. |
+ EXPECT_CALL(*(download_manager_.get()), |
+ OnResponseCompleted(DownloadId(kValidIdDomain, |
+ kDummyDownloadId + 0).local(), |
+ 0, _)); |
+ FinishPipe(content::DOWNLOAD_INTERRUPT_REASON_NONE); |
+ |
+ DestroyDownloadFile(0); |
+} |
+ |
+TEST_F(DownloadFileTest, PipeEmptyError) { |
+ ASSERT_TRUE(CreateDownloadFile(0, true)); |
+ 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 |
+ // DownloadManager. |
+ EXPECT_CALL(*(download_manager_.get()), |
+ OnDownloadInterrupted( |
+ DownloadId(kValidIdDomain, kDummyDownloadId + 0).local(), |
+ 0, _, |
+ content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)); |
+ FinishPipe(content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED); |
+ |
+ DestroyDownloadFile(0); |
+} |
+ |
+TEST_F(DownloadFileTest, PipeNonEmptySuccess) { |
+ ASSERT_TRUE(CreateDownloadFile(0, true)); |
+ 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); |
+ SetupFinishPipe(content::DOWNLOAD_INTERRUPT_REASON_NONE, s1); |
+ EXPECT_CALL(*(download_manager_.get()), |
+ OnResponseCompleted(DownloadId(kValidIdDomain, |
+ kDummyDownloadId + 0).local(), |
+ strlen(kTestData1) + strlen(kTestData2), |
+ _)); |
+ sink_callback_.Run(); |
+ VerifyPipeAndSize(); |
+ DestroyDownloadFile(0); |
+} |
+ |
+TEST_F(DownloadFileTest, PipeNonEmptyError) { |
+ ASSERT_TRUE(CreateDownloadFile(0, true)); |
+ 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); |
+ SetupFinishPipe(content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1); |
+ EXPECT_CALL(*(download_manager_.get()), |
+ OnDownloadInterrupted( |
+ DownloadId(kValidIdDomain, kDummyDownloadId + 0).local(), |
+ strlen(kTestData1) + strlen(kTestData2), _, |
+ content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)); |
+ sink_callback_.Run(); |
+ VerifyPipeAndSize(); |
+ DestroyDownloadFile(0); |
} |
// Send some data, wait 3/4s of a second, run the message loop, and |
// confirm the values the DownloadManager received are correct. |
TEST_F(DownloadFileTest, ConfirmUpdate) { |
- CreateDownloadFile(&download_file_, 0, true); |
+ CreateDownloadFile(0, true); |
ASSERT_EQ(net::OK, download_file_->Initialize()); |
- AppendDataToFile(&download_file_, kTestData1); |
- AppendDataToFile(&download_file_, kTestData2); |
+ const char* chunks1[] = { kTestData1, kTestData2 }; |
+ AppendDataToFile(chunks1, 2); |
// Run the message loops for 750ms and check for results. |
loop_.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), |
@@ -239,6 +447,6 @@ TEST_F(DownloadFileTest, ConfirmUpdate) { |
bytes_); |
EXPECT_EQ(download_file_->GetHashState(), hash_state_); |
- download_file_->Finish(); |
- DestroyDownloadFile(&download_file_, 0); |
+ FinishPipe(content::DOWNLOAD_INTERRUPT_REASON_NONE); |
+ DestroyDownloadFile(0); |
} |