diff options
Diffstat (limited to 'content/browser/download/download_file_manager_unittest.cc')
-rw-r--r-- | content/browser/download/download_file_manager_unittest.cc | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/content/browser/download/download_file_manager_unittest.cc b/content/browser/download/download_file_manager_unittest.cc new file mode 100644 index 0000000..52ce433 --- /dev/null +++ b/content/browser/download/download_file_manager_unittest.cc @@ -0,0 +1,413 @@ +// 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 "content/browser/download/download_file_manager.h" + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/scoped_temp_dir.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_interrupt_reasons_impl.h" +#include "content/browser/download/download_request_handle.h" +#include "content/browser/download/mock_download_file.h" +#include "content/public/browser/download_id.h" +#include "content/public/test/mock_download_manager.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::BrowserThread; +using content::BrowserThreadImpl; +using content::DownloadId; +using content::MockDownloadManager; + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Mock; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::StrictMock; +using ::testing::StrEq; + +namespace { + +// MockDownloadManager with the addition of a mock callback used for testing. +class TestDownloadManager : public MockDownloadManager { + public: + MOCK_METHOD3(OnDownloadRenamed, + void(int download_id, + content::DownloadInterruptReason reason, + const FilePath& full_path)); + private: + ~TestDownloadManager() {} +}; + +class MockDownloadFileFactory : + public DownloadFileManager::DownloadFileFactory { + + public: + MockDownloadFileFactory() {} + virtual ~MockDownloadFileFactory() {} + + virtual content::DownloadFile* CreateFile( + DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, + content::DownloadManager* download_manager, + bool calculate_hash, + const net::BoundNetLog& bound_net_log) OVERRIDE; + + MockDownloadFile* GetExistingFile(const DownloadId& id); + + private: + std::map<DownloadId, MockDownloadFile*> files_; +}; + +content::DownloadFile* MockDownloadFileFactory::CreateFile( + DownloadCreateInfo* info, + scoped_ptr<content::ByteStreamReader> stream, + content::DownloadManager* download_manager, + bool calculate_hash, + const net::BoundNetLog& bound_net_log) { + DCHECK(files_.end() == files_.find(info->download_id)); + MockDownloadFile* created_file = new StrictMock<MockDownloadFile>(); + files_[info->download_id] = created_file; + + ON_CALL(*created_file, GetDownloadManager()) + .WillByDefault(Return(download_manager)); + EXPECT_CALL(*created_file, Initialize()); + + return created_file; +} + +MockDownloadFile* MockDownloadFileFactory::GetExistingFile( + const DownloadId& id) { + DCHECK(files_.find(id) != files_.end()); + return files_[id]; +} + +class MockDownloadRequestHandle : public DownloadRequestHandle { + public: + MockDownloadRequestHandle(content::DownloadManager* manager) + : manager_(manager) {} + + virtual content::DownloadManager* GetDownloadManager() const OVERRIDE; + + private: + + content::DownloadManager* manager_; +}; + +content::DownloadManager* MockDownloadRequestHandle::GetDownloadManager() + const { + return manager_; +} + +void NullCallback() { } + +} // namespace + +class DownloadFileManagerTest : public testing::Test { + public: + // State of renamed file. Used with RenameFile(). + enum RenameFileState { + IN_PROGRESS, + COMPLETE + }; + + // Whether to overwrite the target filename in RenameFile(). + enum RenameFileOverwrite { + OVERWRITE, + DONT_OVERWRITE + }; + + static const char* kTestData1; + static const char* kTestData2; + static const char* kTestData3; + static const char* kTestData4; + static const char* kTestData5; + static const char* kTestData6; + static const int32 kDummyDownloadId; + static const int32 kDummyDownloadId2; + static const int kDummyChildId; + static const int kDummyRequestId; + + // We need a UI |BrowserThread| in order to destruct |download_manager_|, + // which has trait |BrowserThread::DeleteOnUIThread|. Without this, + // calling Release() on |download_manager_| won't ever result in its + // destructor being called and we get a leak. + DownloadFileManagerTest() + : last_reason_(content::DOWNLOAD_INTERRUPT_REASON_NONE), + ui_thread_(BrowserThread::UI, &loop_), + file_thread_(BrowserThread::FILE, &loop_) { + } + + ~DownloadFileManagerTest() { + } + + virtual void SetUp() { + download_manager_ = new TestDownloadManager(); + request_handle_.reset(new MockDownloadRequestHandle(download_manager_)); + download_file_factory_ = new MockDownloadFileFactory; + download_file_manager_ = new DownloadFileManager(download_file_factory_); + } + + virtual void TearDown() { + // When a DownloadManager's reference count drops to 0, it is not + // deleted immediately. Instead, a task is posted to the UI thread's + // message loop to delete it. + // So, drop the reference count to 0 and run the message loop once + // to ensure that all resources are cleaned up before the test exits. + download_manager_ = NULL; + ui_thread_.message_loop()->RunAllPending(); + } + + void ProcessAllPendingMessages() { + loop_.RunAllPending(); + } + + // Clears all gmock expectations for the download file |id| and the manager. + void ClearExpectations(DownloadId id) { + MockDownloadFile* file = download_file_factory_->GetExistingFile(id); + Mock::VerifyAndClearExpectations(file); + Mock::VerifyAndClearExpectations(download_manager_); + } + + void OnDownloadFileCreated(content::DownloadInterruptReason reason) { + last_reason_ = reason; + } + + // Create a download item on the DFM. + // |info| is the information needed to create a new download file. + // |id| is the download ID of the new download file. + void CreateDownloadFile(scoped_ptr<DownloadCreateInfo> info) { + // Mostly null out args; they'll be passed to MockDownloadFileFactory + // to be ignored anyway. + download_file_manager_->CreateDownloadFile( + info.Pass(), scoped_ptr<content::ByteStreamReader>(), + download_manager_, true, net::BoundNetLog(), + base::Bind(&DownloadFileManagerTest::OnDownloadFileCreated, + // The test jig will outlive all download files. + base::Unretained(this))); + + // Anything that isn't DOWNLOAD_INTERRUPT_REASON_NONE. + last_reason_ = content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; + ProcessAllPendingMessages(); + EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, last_reason_); + } + + // Renames the download file. + // |id| is the download ID of the download file. + // |new_path| is the new file path. + // |unique_path| is the actual path that the download file will be + // renamed to. If there is an existing file at + // |new_path| and |replace| is false, then |new_path| + // will be uniquified. + // |rename_error| is the error to inject. For no error, + // use content::DOWNLOAD_INTERRUPT_REASON_NONE. + // |state| whether we are renaming an in-progress download or a + // completed download. + // |should_overwrite| indicates whether to replace or uniquify the file. + void RenameFile(const DownloadId& id, + const FilePath& new_path, + bool should_overwrite) { + MockDownloadFile* file = download_file_factory_->GetExistingFile(id); + ASSERT_TRUE(file != NULL); + content::DownloadFile::RenameCompletionCallback rename_callback; + + EXPECT_CALL(*file, Rename(new_path, should_overwrite, _)) + .WillOnce(SaveArg<2>(&rename_callback)); + + content::DownloadFile::RenameCompletionCallback passed_callback( + base::Bind(&TestDownloadManager::OnDownloadRenamed, + download_manager_, id.local())); + + download_file_manager_->RenameDownloadFile( + id, new_path, should_overwrite, passed_callback); + + EXPECT_TRUE(rename_callback.Equals(passed_callback)); + } + + void Complete(DownloadId id) { + MockDownloadFile* file = download_file_factory_->GetExistingFile(id); + ASSERT_TRUE(file != NULL); + + EXPECT_CALL(*file, AnnotateWithSourceInformation()) + .WillOnce(Return()); + EXPECT_CALL(*file, Detach()) + .WillOnce(Return()); + int num_downloads = download_file_manager_->NumberOfActiveDownloads(); + EXPECT_LT(0, num_downloads); + download_file_manager_->CompleteDownload(id, base::Bind(NullCallback)); + EXPECT_EQ(num_downloads - 1, + download_file_manager_->NumberOfActiveDownloads()); + EXPECT_EQ(NULL, download_file_manager_->GetDownloadFile(id)); + } + + void CleanUp(DownloadId id) { + // Expected calls: + // DownloadFileManager::CancelDownload + // DownloadFile::Cancel + // DownloadFileManager::EraseDownload + // if no more downloads: + // DownloadFileManager::StopUpdateTimer + MockDownloadFile* file = download_file_factory_->GetExistingFile(id); + ASSERT_TRUE(file != NULL); + + EXPECT_CALL(*file, Cancel()); + + download_file_manager_->CancelDownload(id); + + EXPECT_EQ(NULL, download_file_manager_->GetDownloadFile(id)); + } + + protected: + scoped_refptr<TestDownloadManager> download_manager_; + scoped_ptr<MockDownloadRequestHandle> request_handle_; + MockDownloadFileFactory* download_file_factory_; + scoped_refptr<DownloadFileManager> download_file_manager_; + + // Error from creating download file. + content::DownloadInterruptReason last_reason_; + + // Per-download statistics. + std::map<DownloadId, int64> byte_count_; + std::map<DownloadId, int> error_count_; + + private: + MessageLoop loop_; + + // UI thread. + BrowserThreadImpl ui_thread_; + + // File thread to satisfy debug checks in DownloadFile. + BrowserThreadImpl file_thread_; +}; + +const char* DownloadFileManagerTest::kTestData1 = + "Let's write some data to the file!\n"; +const char* DownloadFileManagerTest::kTestData2 = "Writing more data.\n"; +const char* DownloadFileManagerTest::kTestData3 = "Final line."; +const char* DownloadFileManagerTest::kTestData4 = "Writing, writing, writing\n"; +const char* DownloadFileManagerTest::kTestData5 = "All we do is writing,\n"; +const char* DownloadFileManagerTest::kTestData6 = "Rawhide!"; + +const int32 DownloadFileManagerTest::kDummyDownloadId = 23; +const int32 DownloadFileManagerTest::kDummyDownloadId2 = 77; +const int DownloadFileManagerTest::kDummyChildId = 3; +const int DownloadFileManagerTest::kDummyRequestId = 67; + +TEST_F(DownloadFileManagerTest, Cancel) { + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); + DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); + info->download_id = dummy_id; + + CreateDownloadFile(info.Pass()); + + CleanUp(dummy_id); +} + +TEST_F(DownloadFileManagerTest, Complete) { + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); + DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); + info->download_id = dummy_id; + + CreateDownloadFile(info.Pass()); + + Complete(dummy_id); +} + +TEST_F(DownloadFileManagerTest, Rename) { + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); + DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); + info->download_id = dummy_id; + ScopedTempDir download_dir; + ASSERT_TRUE(download_dir.CreateUniqueTempDir()); + + CreateDownloadFile(info.Pass()); + + FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); + RenameFile(dummy_id, foo, true); + + CleanUp(dummy_id); +} + +TEST_F(DownloadFileManagerTest, RenameNoOverwrite) { + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); + DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); + info->download_id = dummy_id; + ScopedTempDir download_dir; + ASSERT_TRUE(download_dir.CreateUniqueTempDir()); + + CreateDownloadFile(info.Pass()); + + FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); + RenameFile(dummy_id, foo, false); + + CleanUp(dummy_id); +} + +TEST_F(DownloadFileManagerTest, RenameTwice) { + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); + DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); + info->download_id = dummy_id; + ScopedTempDir download_dir; + ASSERT_TRUE(download_dir.CreateUniqueTempDir()); + + CreateDownloadFile(info.Pass()); + + FilePath crfoo(download_dir.path().Append( + FILE_PATH_LITERAL("foo.txt.crdownload"))); + RenameFile(dummy_id, crfoo, true); + + FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); + RenameFile(dummy_id, foo, true); + + CleanUp(dummy_id); +} + +TEST_F(DownloadFileManagerTest, TwoDownloads) { + // Same as StartDownload, at first. + scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); + DownloadId dummy_id(download_manager_.get(), kDummyDownloadId); + info->download_id = dummy_id; + scoped_ptr<DownloadCreateInfo> info2(new DownloadCreateInfo); + DownloadId dummy_id2(download_manager_.get(), kDummyDownloadId2); + info2->download_id = dummy_id2; + ScopedTempDir download_dir; + ASSERT_TRUE(download_dir.CreateUniqueTempDir()); + + CreateDownloadFile(info.Pass()); + CreateDownloadFile(info2.Pass()); + + FilePath crbar(download_dir.path().Append( + FILE_PATH_LITERAL("bar.txt.crdownload"))); + RenameFile(dummy_id2, crbar, true); + + FilePath crfoo(download_dir.path().Append( + FILE_PATH_LITERAL("foo.txt.crdownload"))); + RenameFile(dummy_id, crfoo, true); + + + FilePath bar(download_dir.path().Append(FILE_PATH_LITERAL("bar.txt"))); + RenameFile(dummy_id2, bar, true); + + CleanUp(dummy_id2); + + FilePath foo(download_dir.path().Append(FILE_PATH_LITERAL("foo.txt"))); + RenameFile(dummy_id, foo, true); + + CleanUp(dummy_id); +} + +// TODO(ahendrickson) -- A test for download manager shutdown. +// Expected call sequence: +// OnDownloadManagerShutdown +// DownloadFile::GetDownloadManager +// DownloadFile::CancelDownloadRequest +// DownloadFile::~DownloadFile |