Chromium Code Reviews| Index: chrome/browser/download/download_manager_unittest.cc |
| diff --git a/chrome/browser/download/download_manager_unittest.cc b/chrome/browser/download/download_manager_unittest.cc |
| index 58c39fa36df05fc6b64bca79419515ae2768b082..4938db0ea1e76fca82df45f77fb588f22c1ffa26 100644 |
| --- a/chrome/browser/download/download_manager_unittest.cc |
| +++ b/chrome/browser/download/download_manager_unittest.cc |
| @@ -30,6 +30,8 @@ |
| #include "content/browser/download/download_status_updater.h" |
| #include "content/browser/download/mock_download_manager.h" |
| #include "grit/generated_resources.h" |
| +#include "net/base/io_buffer.h" |
| +#include "net/base/mock_file_stream.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gmock_mutant.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| @@ -67,8 +69,9 @@ class DownloadManagerTest : public testing::Test { |
| file_manager()->downloads_[id] = download_file; |
| } |
| - void OnAllDataSaved(int32 download_id, int64 size, const std::string& hash) { |
| - download_manager_->OnAllDataSaved(download_id, size, hash); |
| + void OnResponseCompleted(int32 download_id, int64 size, |
| + const std::string& hash) { |
| + download_manager_->OnResponseCompleted(download_id, size, hash); |
| } |
| void FileSelected(const FilePath& path, void* params) { |
| @@ -79,6 +82,31 @@ class DownloadManagerTest : public testing::Test { |
| download_manager_->ContinueDownloadWithPath(download, path); |
| } |
| + void UpdateData(int32 id, const void* data, size_t length) { |
|
cbentzel
2011/08/29 15:05:27
Should |data| be const char* to be consistent with
ahendrickson
2011/08/29 16:30:53
I don't have a strong opinion about this.
Changed
|
| + // We are passing ownership of this buffer to the download file manager. |
| + net::IOBuffer* io_buffer = new net::IOBuffer(length); |
| + // We need |AddRef()| because io_buffer is not a |scoped_refptr|, and we |
|
cbentzel
2011/08/29 15:05:27
This comment isn't quite correct. If io_buffer wer
ahendrickson
2011/08/29 16:30:53
Yeah, it's funky.
Changed.
|
| + // will do a |Release()| in |UpdateDownload()|. |
| + io_buffer->AddRef(); |
| + memcpy(io_buffer->data(), data, length); |
| + |
| + { |
| + base::AutoLock auto_lock(download_buffer_.lock); |
|
cbentzel
2011/08/29 15:05:27
This is kinda gross, although it already existed.
ahendrickson
2011/08/29 16:30:53
I think that's outside the scope of this CL.
Randy Smith (Not in Mondays)
2011/08/29 16:57:57
Agreed with both points (suggested refactor, and t
|
| + |
| + download_buffer_.contents.push_back( |
| + std::make_pair(io_buffer, length)); |
| + } |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::FILE, FROM_HERE, |
| + NewRunnableMethod(file_manager_.get(), |
| + &DownloadFileManager::UpdateDownload, |
| + id, |
| + &download_buffer_)); |
| + |
| + message_loop_.RunAllPending(); |
|
cbentzel
2011/08/29 15:05:27
Why do you need to do this inside of UpdateData?
ahendrickson
2011/08/29 16:30:53
For other operations. Most tests don't need to us
|
| + } |
| + |
| void OnDownloadError(int32 download_id, int64 size, int os_error) { |
| download_manager_->OnDownloadError(download_id, size, os_error); |
| } |
| @@ -99,6 +127,7 @@ class DownloadManagerTest : public testing::Test { |
| MessageLoopForUI message_loop_; |
| BrowserThread ui_thread_; |
| BrowserThread file_thread_; |
| + DownloadBuffer download_buffer_; |
| DownloadFileManager* file_manager() { |
| if (!file_manager_) { |
| @@ -126,6 +155,35 @@ const char* DownloadManagerTest::kTestData = "a;sdlfalsdfjalsdkfjad"; |
| const size_t DownloadManagerTest::kTestDataLen = |
| strlen(DownloadManagerTest::kTestData); |
| +// A DownloadFile that we can inject errors into. Uses MockFileStream. |
| +// Note: This can't be in an anonymous namespace because it must be declared |
| +// as a friend of |DownloadFile| in order to access its private members. |
|
cbentzel
2011/08/29 15:05:27
Could you just add a DownloadFile constructor whic
ahendrickson
2011/08/29 16:30:53
It needs more information than just the file strea
|
| +class DownloadFileWithMockStream : public DownloadFile { |
| + public: |
| + DownloadFileWithMockStream(DownloadCreateInfo* info, |
| + DownloadManager* manager, |
| + testing::MockFileStream* stream) |
| + : DownloadFile(info, manager) { |
| + DCHECK(file_stream_ == NULL); |
| + file_stream_.reset(stream); |
| + } |
| + virtual ~DownloadFileWithMockStream() { |
| + } |
| + |
| + void SetForcedError(int error) |
| + { |
| + testing::MockFileStream* mock_stream = |
|
Randy Smith (Not in Mondays)
2011/08/28 22:14:03
Please put in a comment as to why the upcast is ok
ahendrickson
2011/08/29 16:30:53
Done.
|
| + static_cast<testing::MockFileStream *>(file_stream_.get()); |
| + mock_stream->set_forced_error(error); |
| + } |
| + |
| + protected: |
| + // This version creates a |MockFileStream| instead of a |FileStream|. |
| + virtual void CreateFileStream() { |
|
cbentzel
2011/08/29 15:05:27
OVERRIDE
cbentzel
2011/08/29 15:05:27
Is this expected to be called?
ahendrickson
2011/08/29 16:30:53
Yes, from Open().
ahendrickson
2011/08/29 16:30:53
Done.
cbentzel
2011/08/29 17:27:55
Well, it could be called from Open(), but that onl
ahendrickson
2011/08/29 17:51:37
No, the rename operation does a Close() (which res
|
| + file_stream_.reset(new testing::MockFileStream); |
| + } |
| +}; |
| + |
| namespace { |
| const struct { |
| @@ -384,13 +442,13 @@ TEST_F(DownloadManagerTest, DownloadRenameTest) { |
| int32* id_ptr = new int32; |
| *id_ptr = i; // Deleted in FileSelected(). |
| if (kDownloadRenameCases[i].finish_before_rename) { |
| - OnAllDataSaved(i, 1024, std::string("fake_hash")); |
| + OnResponseCompleted(i, 1024, std::string("fake_hash")); |
| message_loop_.RunAllPending(); |
| FileSelected(new_path, id_ptr); |
| } else { |
| FileSelected(new_path, id_ptr); |
| message_loop_.RunAllPending(); |
| - OnAllDataSaved(i, 1024, std::string("fake_hash")); |
| + OnResponseCompleted(i, 1024, std::string("fake_hash")); |
| } |
| message_loop_.RunAllPending(); |
| @@ -482,6 +540,91 @@ TEST_F(DownloadManagerTest, DownloadInterruptTest) { |
| EXPECT_EQ(download->total_bytes(), static_cast<int64>(kTestDataLen)); |
| } |
| +// Test the behavior of DownloadFileManager and DownloadManager in the event |
| +// of a file error while writing the download to disk. |
| +TEST_F(DownloadManagerTest, DownloadFileErrorTest) { |
| + // Create a temporary file and a mock stream. |
| + FilePath path; |
| + ASSERT_TRUE(file_util::CreateTemporaryFile(&path)); |
| + |
| + // This file stream will be used, until the first rename occurs. |
| + testing::MockFileStream* mock_stream = new testing::MockFileStream; |
| + ASSERT_EQ(0, mock_stream->Open( |
| + path, |
| + base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE)); |
| + |
| + // Normally, the download system takes ownership of info, and is |
| + // responsible for deleting it. In these unit tests, however, we |
| + // don't call the function that deletes it, so we can use a stack variable. |
|
cbentzel
2011/08/29 15:05:27
Looks like other tests use scoped_ptr. I'd either
ahendrickson
2011/08/29 16:30:53
DownloadFileManager::CreateDownloadFile() normally
Randy Smith (Not in Mondays)
2011/08/29 16:57:57
I'm cool with shifting back to scoped_ptr; I agree
ahendrickson
2011/08/29 17:51:37
Done.
|
| + DownloadCreateInfo info; |
| + int32 id = 0; |
| + info.download_id = id; |
| + info.prompt_user_for_save_location = false; |
| + info.url_chain.push_back(GURL()); |
| + info.total_bytes = static_cast<int64>(kTestDataLen * 3); |
| + info.save_info.file_path = path; |
| + |
| + // Create a download file that we can insert errors into. |
| + DownloadFileWithMockStream* download_file(new DownloadFileWithMockStream( |
| + &info, download_manager_, mock_stream)); |
| + AddDownloadToFileManager(id, download_file); |
| + |
| + // |download_file| is owned by DownloadFileManager. |
| + ::testing::Mock::AllowLeak(download_file); |
|
Randy Smith (Not in Mondays)
2011/08/28 22:14:03
Why does the leak occur? Shouldn't it be cleaned
ahendrickson
2011/08/29 16:30:53
Hmm, thinking about it, due to recent refactorings
|
| + download_manager_->CreateDownloadItem(&info); |
| + |
| + DownloadItem* download = GetActiveDownloadItem(0); |
| + ASSERT_TRUE(download != NULL); |
| + // This will keep track of what should be displayed on the shelf. |
| + scoped_ptr<DownloadItemModel> download_item_model( |
| + new DownloadItemModel(download)); |
| + |
| + EXPECT_EQ(DownloadItem::IN_PROGRESS, download->state()); |
| + scoped_ptr<ItemObserver> observer(new ItemObserver(download)); |
| + |
| + // Add some data before finalizing the file name. |
| + UpdateData(id, kTestData, kTestDataLen); |
| + |
| + // Finalize the file name. |
| + ContinueDownloadWithPath(download, path); |
| + message_loop_.RunAllPending(); |
| + EXPECT_TRUE(GetActiveDownloadItem(0) != NULL); |
| + |
| + // Add more data. |
| + UpdateData(id, kTestData, kTestDataLen); |
| + |
| + // Add more data, but an error occurs. |
| + download_file->SetForcedError(net::ERR_FAILED); |
| + UpdateData(id, kTestData, kTestDataLen); |
| + |
| + // Check the state. The download should have been interrupted. |
| + EXPECT_TRUE(GetActiveDownloadItem(0) == NULL); |
| + EXPECT_TRUE(observer->hit_state(DownloadItem::IN_PROGRESS)); |
| + EXPECT_TRUE(observer->hit_state(DownloadItem::INTERRUPTED)); |
| + EXPECT_FALSE(observer->hit_state(DownloadItem::COMPLETE)); |
| + EXPECT_FALSE(observer->hit_state(DownloadItem::CANCELLED)); |
| + EXPECT_FALSE(observer->hit_state(DownloadItem::REMOVING)); |
| + EXPECT_TRUE(observer->was_updated()); |
| + EXPECT_FALSE(observer->was_opened()); |
| + EXPECT_FALSE(download->file_externally_removed()); |
| + EXPECT_EQ(DownloadItem::INTERRUPTED, download->state()); |
| + |
| + // Check the download shelf's information. |
| + size_t error_size = kTestDataLen * 3; |
| + ui::DataUnits amount_units = ui::GetByteDisplayUnits(kTestDataLen); |
| + string16 simple_size = |
| + ui::FormatBytesWithUnits(error_size, amount_units, false); |
| + string16 simple_total = base::i18n::GetDisplayStringInLTRDirectionality( |
| + ui::FormatBytesWithUnits(error_size, amount_units, true)); |
| + EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_INTERRUPTED, |
| + simple_size, |
| + simple_total), |
| + download_item_model->GetStatusText()); |
| + |
| + // Clean up. |
| + download->Cancel(true); |
| +} |
| + |
| TEST_F(DownloadManagerTest, DownloadCancelTest) { |
| using ::testing::_; |
| using ::testing::CreateFunctor; |
| @@ -609,7 +752,7 @@ TEST_F(DownloadManagerTest, DownloadOverwriteTest) { |
| download_file->AppendDataToFile(kTestData, kTestDataLen); |
| // Finish the download. |
| - OnAllDataSaved(0, kTestDataLen, ""); |
| + OnResponseCompleted(0, kTestDataLen, ""); |
| message_loop_.RunAllPending(); |
| // Download is complete. |
| @@ -685,7 +828,7 @@ TEST_F(DownloadManagerTest, DownloadRemoveTest) { |
| download_file->AppendDataToFile(kTestData, kTestDataLen); |
| // Finish the download. |
| - OnAllDataSaved(0, kTestDataLen, ""); |
| + OnResponseCompleted(0, kTestDataLen, ""); |
| message_loop_.RunAllPending(); |
| // Download is complete. |