// Copyright (c) 2010 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 #include "base/string_util.h" #include "build/build_config.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/download/download_file.h" #include "chrome/browser/download/download_file_manager.h" #include "chrome/browser/download/download_manager.h" #include "chrome/browser/download/download_prefs.h" #include "chrome/browser/download/download_status_updater.h" #include "chrome/browser/download/download_util.h" #include "chrome/browser/history/download_create_info.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_profile.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock_mutant.h" #include "testing/gtest/include/gtest/gtest.h" class MockDownloadManager : public DownloadManager { public: explicit MockDownloadManager(DownloadStatusUpdater* updater) : DownloadManager(updater) { } // Override some functions. virtual void UpdateHistoryForDownload(DownloadItem*) { } virtual void ContinueDownloadFinished(DownloadItem*) { } }; class DownloadManagerTest : public testing::Test { public: DownloadManagerTest() : profile_(new TestingProfile()), download_manager_(new MockDownloadManager(&download_status_updater_)), ui_thread_(BrowserThread::UI, &message_loop_) { download_manager_->Init(profile_.get()); } ~DownloadManagerTest() { download_manager_->Shutdown(); // profile_ must outlive download_manager_, so we explicitly delete // download_manager_ first. download_manager_ = NULL; profile_.reset(NULL); message_loop_.RunAllPending(); } void AddDownloadToFileManager(int id, DownloadFile* download) { file_manager()->downloads_[id] = download; } protected: DownloadStatusUpdater download_status_updater_; scoped_ptr profile_; scoped_refptr download_manager_; scoped_refptr file_manager_; MessageLoopForUI message_loop_; BrowserThread ui_thread_; DownloadFileManager* file_manager() { if (!file_manager_) { file_manager_ = new DownloadFileManager(NULL); download_manager_->file_manager_ = file_manager_; } return file_manager_; } DISALLOW_COPY_AND_ASSIGN(DownloadManagerTest); }; namespace { const struct { const char* url; const char* mime_type; bool save_as; bool prompt_for_download; bool expected_save_as; } kStartDownloadCases[] = { { "http://www.foo.com/dont-open.html", "text/html", false, false, false, }, { "http://www.foo.com/save-as.html", "text/html", true, false, true, }, { "http://www.foo.com/always-prompt.html", "text/html", false, true, true, }, { "http://www.foo.com/wrong_mime_extension.user.js", "text/html", false, true, false, }, { "http://www.foo.com/extensionless-extension", "application/x-chrome-extension", true, false, true, }, { "http://www.foo.com/save-as.pdf", "application/pdf", true, false, true, }, { "http://www.foo.com/always_prompt.pdf", "application/pdf", false, true, true, }, }; } // namespace TEST_F(DownloadManagerTest, StartDownload) { PrefService* prefs = profile_->GetPrefs(); prefs->SetFilePath(prefs::kDownloadDefaultDirectory, FilePath()); download_manager_->download_prefs()->EnableAutoOpenBasedOnExtension( FilePath(FILE_PATH_LITERAL("example.pdf"))); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kStartDownloadCases); ++i) { prefs->SetBoolean(prefs::kPromptForDownload, kStartDownloadCases[i].prompt_for_download); DownloadCreateInfo info; info.prompt_user_for_save_location = kStartDownloadCases[i].save_as; info.url = GURL(kStartDownloadCases[i].url); info.mime_type = kStartDownloadCases[i].mime_type; download_manager_->StartDownload(&info); EXPECT_EQ(kStartDownloadCases[i].expected_save_as, info.prompt_user_for_save_location); } } namespace { const struct { FilePath::StringType suggested_path; bool is_dangerous; bool finish_before_rename; bool will_delete_crdownload; int expected_rename_count; } kDownloadRenameCases[] = { // Safe download, download finishes BEFORE rename. // Needs to be renamed only once. Crdownload file needs to be deleted. { FILE_PATH_LITERAL("foo.zip"), false, true, true, 1, }, // Dangerous download, download finishes BEFORE rename. // Needs to be renamed only once. { FILE_PATH_LITERAL("unconfirmed xxx.crdownload"), true, true, false, 1, }, // Safe download, download finishes AFTER rename. // Needs to be renamed twice. { FILE_PATH_LITERAL("foo.zip"), false, false, false, 2, }, // Dangerous download, download finishes AFTER rename. // Needs to be renamed only once. { FILE_PATH_LITERAL("unconfirmed xxx.crdownload"), true, false, false, 1, }, }; class MockDownloadFile : public DownloadFile { public: explicit MockDownloadFile(DownloadCreateInfo* info) : DownloadFile(info, NULL), renamed_count_(0) { } virtual ~MockDownloadFile() { Destructed(); } MOCK_METHOD2(Rename, bool(const FilePath&, bool)); MOCK_METHOD0(DeleteCrDownload, void()); MOCK_METHOD0(Destructed, void()); bool TestMultipleRename( int expected_count, bool expected_final, const FilePath& expected, const FilePath& path, bool is_final_rename) { ++renamed_count_; EXPECT_EQ(expected_count, renamed_count_); EXPECT_EQ(expected_final, is_final_rename); EXPECT_EQ(expected.value(), path.value()); return true; } private: int renamed_count_; }; } // namespace TEST_F(DownloadManagerTest, DownloadRenameTest) { using ::testing::_; using ::testing::CreateFunctor; using ::testing::Invoke; using ::testing::Return; BrowserThread file_thread(BrowserThread::FILE, &message_loop_); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDownloadRenameCases); ++i) { // |info| will be destroyed in download_manager_. DownloadCreateInfo* info(new DownloadCreateInfo); info->download_id = static_cast(i); info->prompt_user_for_save_location = false; info->is_dangerous = kDownloadRenameCases[i].is_dangerous; FilePath new_path(kDownloadRenameCases[i].suggested_path); MockDownloadFile* download(new MockDownloadFile(info)); AddDownloadToFileManager(info->download_id, download); // |download| is owned by DownloadFileManager. ::testing::Mock::AllowLeak(download); EXPECT_CALL(*download, Destructed()).Times(1); if (kDownloadRenameCases[i].expected_rename_count == 1) { EXPECT_CALL(*download, Rename(new_path, true)).WillOnce(Return(true)); } else { ASSERT_EQ(2, kDownloadRenameCases[i].expected_rename_count); FilePath crdownload(download_util::GetCrDownloadPath(new_path)); EXPECT_CALL(*download, Rename(_, _)) .WillOnce(testing::WithArgs<0, 1>(Invoke(CreateFunctor( download, &MockDownloadFile::TestMultipleRename, 1, false, crdownload)))) .WillOnce(testing::WithArgs<0, 1>(Invoke(CreateFunctor( download, &MockDownloadFile::TestMultipleRename, 2, true, new_path)))); } if (kDownloadRenameCases[i].will_delete_crdownload) EXPECT_CALL(*download, DeleteCrDownload()).Times(1); if (kDownloadRenameCases[i].finish_before_rename) { download_manager_->OnAllDataSaved(i, 1024); download_manager_->FileSelected(new_path, i, info); } else { download_manager_->FileSelected(new_path, i, info); download_manager_->OnAllDataSaved(i, 1024); } message_loop_.RunAllPending(); } }