blob: 2db13be7f78705e741004d86ae543bde01ac1c63 [file] [log] [blame]
// 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 <string>
#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<TestingProfile> profile_;
scoped_refptr<DownloadManager> download_manager_;
scoped_refptr<DownloadFileManager> 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/sometimes_prompt.pdf",
"application/pdf",
false,
true,
false, },
{ "http://www.foo.com/always_prompt.jar",
"application/jar",
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<int>(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);
download_manager_->CreateDownloadItem(info);
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();
}
}