blob: 42e6a2c4d6c2c5fab5ca83d4f490a915ed726b22 [file] [log] [blame]
// Copyright (c) 2011 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 <set>
#include <string>
#include "base/bind.h"
#include "base/file_util.h"
#include "base/i18n/number_formatting.h"
#include "base/i18n/rtl.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/scoped_temp_dir.h"
#include "base/stl_util.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "content/browser/download/download_buffer.h"
#include "content/browser/download/download_create_info.h"
#include "content/browser/download/download_file_impl.h"
#include "content/browser/download/download_file_manager.h"
#include "content/browser/download/download_id_factory.h"
#include "content/browser/download/download_manager_impl.h"
#include "content/browser/download/download_request_handle.h"
#include "content/browser/download/download_status_updater.h"
#include "content/browser/download/interrupt_reasons.h"
#include "content/browser/download/mock_download_file.h"
#include "content/browser/download/mock_download_manager.h"
#include "content/public/browser/download_item.h"
#include "content/test/test_browser_thread.h"
#include "grit/generated_resources.h"
#include "net/base/io_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/text/bytes_formatting.h"
#if defined(USE_AURA) && defined(OS_WIN)
// http://crbug.com/105200
#define MAYBE_StartDownload DISABLED_StartDownload
#define MAYBE_DownloadOverwriteTest DISABLED_DownloadOverwriteTest
#define MAYBE_DownloadRemoveTest DISABLED_DownloadRemoveTest
#else
#define MAYBE_StartDownload StartDownload
#define MAYBE_DownloadOverwriteTest DownloadOverwriteTest
#define MAYBE_DownloadRemoveTest DownloadRemoveTest
#endif
using content::BrowserThread;
using content::DownloadFile;
using content::DownloadItem;
using content::DownloadManager;
using content::WebContents;
namespace {
class MockDownloadFileFactory
: public DownloadFileManager::DownloadFileFactory {
public:
MockDownloadFileFactory() {}
virtual DownloadFile* CreateFile(DownloadCreateInfo* info,
const DownloadRequestHandle& request_handle,
DownloadManager* download_manager,
bool calculate_hash) OVERRIDE;
};
DownloadFile* MockDownloadFileFactory::CreateFile(
DownloadCreateInfo* info,
const DownloadRequestHandle& request_handle,
DownloadManager* download_manager,
bool calculate_hash) {
NOTREACHED();
return NULL;
}
DownloadId::Domain kValidIdDomain = "valid DownloadId::Domain";
class TestDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
public:
explicit TestDownloadManagerDelegate(Profile* profile)
: ChromeDownloadManagerDelegate(profile),
mark_content_dangerous_(false) {
}
virtual void ChooseDownloadPath(WebContents* web_contents,
const FilePath& suggested_path,
void* data) OVERRIDE {
if (!expected_suggested_path_.empty()) {
EXPECT_STREQ(expected_suggested_path_.value().c_str(),
suggested_path.value().c_str());
}
if (file_selection_response_.empty()) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&DownloadManager::FileSelectionCanceled,
download_manager_.get(),
base::Unretained(data)));
} else {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&DownloadManager::FileSelected,
download_manager_.get(),
file_selection_response_,
base::Unretained(data)));
}
expected_suggested_path_.clear();
file_selection_response_.clear();
}
void SetFileSelectionExpectation(const FilePath& suggested_path,
const FilePath& response) {
expected_suggested_path_ = suggested_path;
file_selection_response_ = response;
}
void SetMarkContentsDangerous(bool dangerous) {
mark_content_dangerous_ = dangerous;
}
virtual bool ShouldCompleteDownload(DownloadItem* item) {
if (mark_content_dangerous_) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TestDownloadManagerDelegate::MarkContentDangerous,
this, item->GetId()));
mark_content_dangerous_ = false;
return false;
} else {
return true;
}
}
private:
void MarkContentDangerous(int32 download_id) {
DownloadItem* item = download_manager_->GetActiveDownloadItem(download_id);
if (!item)
return;
item->MarkContentDangerous();
item->MaybeCompleteDownload();
}
FilePath expected_suggested_path_;
FilePath file_selection_response_;
bool mark_content_dangerous_;
};
} // namespace
class DownloadManagerTest : public testing::Test {
public:
static const char* kTestData;
static const size_t kTestDataLen;
DownloadManagerTest()
: profile_(new TestingProfile()),
download_manager_delegate_(new TestDownloadManagerDelegate(
profile_.get())),
id_factory_(new DownloadIdFactory(kValidIdDomain)),
download_manager_(new DownloadManagerImpl(
download_manager_delegate_,
id_factory_,
&download_status_updater_)),
ui_thread_(BrowserThread::UI, &message_loop_),
file_thread_(BrowserThread::FILE, &message_loop_),
download_buffer_(new content::DownloadBuffer) {
download_manager_->Init(profile_.get());
download_manager_delegate_->SetDownloadManager(download_manager_);
}
~DownloadManagerTest() {
download_manager_->Shutdown();
// profile_ must outlive download_manager_, so we explicitly delete
// download_manager_ first.
download_manager_ = NULL;
download_manager_delegate_ = NULL;
profile_.reset(NULL);
message_loop_.RunAllPending();
}
void AddDownloadToFileManager(int id, DownloadFile* download_file) {
file_manager()->downloads_[DownloadId(kValidIdDomain, id)] =
download_file;
}
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) {
download_manager_->FileSelected(path, params);
}
void ContinueDownloadWithPath(DownloadItem* download, const FilePath& path) {
download_manager_->ContinueDownloadWithPath(download, path);
}
void UpdateData(int32 id, const char* data, size_t length) {
// We are passing ownership of this buffer to the download file manager.
net::IOBuffer* io_buffer = new net::IOBuffer(length);
// We need |AddRef()| because we do a |Release()| in |UpdateDownload()|.
io_buffer->AddRef();
memcpy(io_buffer->data(), data, length);
download_buffer_->AddData(io_buffer, length);
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&DownloadFileManager::UpdateDownload, file_manager_.get(),
DownloadId(kValidIdDomain, id), download_buffer_));
message_loop_.RunAllPending();
}
void OnDownloadInterrupted(int32 download_id, int64 size,
const std::string& hash_state,
InterruptReason reason) {
download_manager_->OnDownloadInterrupted(download_id, size,
hash_state, reason);
}
// Get the download item with ID |id|.
DownloadItem* GetActiveDownloadItem(int32 id) {
return download_manager_->GetActiveDownload(id);
}
protected:
DownloadStatusUpdater download_status_updater_;
scoped_ptr<TestingProfile> profile_;
scoped_refptr<TestDownloadManagerDelegate> download_manager_delegate_;
scoped_refptr<DownloadIdFactory> id_factory_;
scoped_refptr<DownloadManager> download_manager_;
scoped_refptr<DownloadFileManager> file_manager_;
MessageLoopForUI message_loop_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread file_thread_;
scoped_refptr<content::DownloadBuffer> download_buffer_;
DownloadFileManager* file_manager() {
if (!file_manager_) {
file_manager_ = new DownloadFileManager(NULL,
new MockDownloadFileFactory);
download_manager_->SetFileManager(file_manager_);
}
return file_manager_;
}
DISALLOW_COPY_AND_ASSIGN(DownloadManagerTest);
};
const char* DownloadManagerTest::kTestData = "a;sdlfalsdfjalsdkfjad";
const size_t DownloadManagerTest::kTestDataLen =
strlen(DownloadManagerTest::kTestData);
// A DownloadFile that we can inject errors into.
class DownloadFileWithErrors : public DownloadFileImpl {
public:
DownloadFileWithErrors(DownloadCreateInfo* info,
DownloadManager* manager,
bool calculate_hash);
virtual ~DownloadFileWithErrors() {}
// BaseFile delegated functions.
virtual net::Error Initialize();
virtual net::Error AppendDataToFile(const char* data, size_t data_len);
virtual net::Error Rename(const FilePath& full_path);
void set_forced_error(net::Error error) { forced_error_ = error; }
void clear_forced_error() { forced_error_ = net::OK; }
net::Error forced_error() const { return forced_error_; }
private:
net::Error ReturnError(net::Error function_error) {
if (forced_error_ != net::OK) {
net::Error ret = forced_error_;
clear_forced_error();
return ret;
}
return function_error;
}
net::Error forced_error_;
};
DownloadFileWithErrors::DownloadFileWithErrors(DownloadCreateInfo* info,
DownloadManager* manager,
bool calculate_hash)
: DownloadFileImpl(info,
new DownloadRequestHandle(),
manager,
calculate_hash),
forced_error_(net::OK) {
}
net::Error DownloadFileWithErrors::Initialize() {
return ReturnError(DownloadFileImpl::Initialize());
}
net::Error DownloadFileWithErrors::AppendDataToFile(const char* data,
size_t data_len) {
return ReturnError(DownloadFileImpl::AppendDataToFile(data, data_len));
}
net::Error DownloadFileWithErrors::Rename(const FilePath& full_path) {
return ReturnError(DownloadFileImpl::Rename(full_path));
}
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/user-script-text-html-mimetype.user.js",
"text/html",
false,
false,
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, },
};
const struct {
FilePath::StringType suggested_path;
DownloadStateInfo::DangerType danger;
bool finish_before_rename;
int expected_rename_count;
} kDownloadRenameCases[] = {
// Safe download, download finishes BEFORE file name determined.
// Renamed twice (linear path through UI). Crdownload file does not need
// to be deleted.
{ FILE_PATH_LITERAL("foo.zip"), DownloadStateInfo::NOT_DANGEROUS, true, 2, },
// Potentially dangerous download (e.g., file is dangerous), download finishes
// BEFORE file name determined. Needs to be renamed only once.
{ FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
DownloadStateInfo::MAYBE_DANGEROUS_CONTENT, true, 1, },
{ FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
DownloadStateInfo::DANGEROUS_FILE, true, 1, },
// Safe download, download finishes AFTER file name determined.
// Needs to be renamed twice.
{ FILE_PATH_LITERAL("foo.zip"), DownloadStateInfo::NOT_DANGEROUS, false, 2, },
// Potentially dangerous download, download finishes AFTER file name
// determined. Needs to be renamed only once.
{ FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
DownloadStateInfo::MAYBE_DANGEROUS_CONTENT, false, 1, },
{ FILE_PATH_LITERAL("Unconfirmed xxx.crdownload"),
DownloadStateInfo::DANGEROUS_FILE, false, 1, },
};
// This is an observer that records what download IDs have opened a select
// file dialog.
class SelectFileObserver : public content::DownloadManager::Observer {
public:
explicit SelectFileObserver(DownloadManager* download_manager)
: download_manager_(download_manager) {
DCHECK(download_manager_.get());
download_manager_->AddObserver(this);
}
~SelectFileObserver() {
download_manager_->RemoveObserver(this);
}
// Downloadmanager::Observer functions.
virtual void ModelChanged() {}
virtual void ManagerGoingDown() {}
virtual void SelectFileDialogDisplayed(int32 id) {
file_dialog_ids_.insert(id);
}
bool ShowedFileDialogForId(int32 id) {
return file_dialog_ids_.find(id) != file_dialog_ids_.end();
}
private:
std::set<int32> file_dialog_ids_;
scoped_refptr<DownloadManager> download_manager_;
};
// This observer tracks the progress of |DownloadItem|s.
class ItemObserver : public DownloadItem::Observer {
public:
explicit ItemObserver(DownloadItem* tracked)
: tracked_(tracked), states_hit_(0),
was_updated_(false), was_opened_(false) {
DCHECK(tracked_);
tracked_->AddObserver(this);
// Record the initial state.
OnDownloadUpdated(tracked_);
}
~ItemObserver() {
tracked_->RemoveObserver(this);
}
bool hit_state(int state) const {
return (1 << state) & states_hit_;
}
bool was_updated() const { return was_updated_; }
bool was_opened() const { return was_opened_; }
private:
// DownloadItem::Observer methods
virtual void OnDownloadUpdated(DownloadItem* download) {
DCHECK_EQ(tracked_, download);
states_hit_ |= (1 << download->GetState());
was_updated_ = true;
}
virtual void OnDownloadOpened(DownloadItem* download) {
DCHECK_EQ(tracked_, download);
states_hit_ |= (1 << download->GetState());
was_opened_ = true;
}
DownloadItem* tracked_;
int states_hit_;
bool was_updated_;
bool was_opened_;
};
} // namespace
TEST_F(DownloadManagerTest, MAYBE_StartDownload) {
content::TestBrowserThread io_thread(BrowserThread::IO, &message_loop_);
PrefService* prefs = profile_->GetPrefs();
prefs->SetFilePath(prefs::kDownloadDefaultDirectory, FilePath());
DownloadPrefs* download_prefs =
DownloadPrefs::FromDownloadManager(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);
SelectFileObserver observer(download_manager_);
// 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 do so ourselves.
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
info->download_id = DownloadId(kValidIdDomain, static_cast<int>(i));
info->prompt_user_for_save_location = kStartDownloadCases[i].save_as;
info->url_chain.push_back(GURL(kStartDownloadCases[i].url));
info->mime_type = kStartDownloadCases[i].mime_type;
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadFile* download_file(
new DownloadFileImpl(info.get(), new DownloadRequestHandle(),
download_manager_, false));
AddDownloadToFileManager(info->download_id.local(), download_file);
download_file->Initialize();
download_manager_->StartDownload(info->download_id.local());
message_loop_.RunAllPending();
// SelectFileObserver will have recorded any attempt to open the
// select file dialog.
// Note that DownloadManager::FileSelectionCanceled() is never called.
EXPECT_EQ(kStartDownloadCases[i].expected_save_as,
observer.ShowedFileDialogForId(i));
}
}
namespace {
enum PromptForSaveLocation {
DONT_PROMPT,
PROMPT
};
enum ValidateDangerousDownload {
DONT_VALIDATE,
VALIDATE
};
// Test cases to be used with DownloadFilenameTest. The paths that are used in
// test cases can contain "$dl" and "$alt" tokens which are replaced by a
// default download path, and an alternate download path in
// ExpandFilenameTestPath() below.
const struct DownloadFilenameTestCase {
// Fields to be set in DownloadStateInfo when calling SetFileCheckResults().
const FilePath::CharType* suggested_path;
const FilePath::CharType* target_name;
PromptForSaveLocation prompt_user_for_save_location;
DownloadStateInfo::DangerType danger_type;
// If we receive a ChooseDownloadPath() call to prompt the user for a download
// location, |prompt_path| is the expected prompt path. The
// TestDownloadManagerDelegate will respond with |final_path|. If |final_path|
// is empty, then the file choose dialog be cancelled.
const FilePath::CharType* prompt_path;
// The expected intermediate path for the download.
const FilePath::CharType* intermediate_path;
// The expected final path for the download.
const FilePath::CharType* final_path;
// If this is a dangerous download, then we will either validate the download
// or delete it depending on the value of |validate_dangerous_download|.
ValidateDangerousDownload validate_dangerous_download;
} kDownloadFilenameTestCases[] = {
{
// 0: A safe file is downloaded with no prompting.
FILE_PATH_LITERAL("$dl/foo.txt"),
FILE_PATH_LITERAL(""),
DONT_PROMPT,
DownloadStateInfo::NOT_DANGEROUS,
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("$dl/foo.txt.crdownload"),
FILE_PATH_LITERAL("$dl/foo.txt"),
DONT_VALIDATE
},
{
// 1: A safe file is downloaded with prompting.
FILE_PATH_LITERAL("$dl/foo.txt"),
FILE_PATH_LITERAL(""),
PROMPT,
DownloadStateInfo::NOT_DANGEROUS,
FILE_PATH_LITERAL("$dl/foo.txt"),
FILE_PATH_LITERAL("$dl/foo.txt.crdownload"),
FILE_PATH_LITERAL("$dl/foo.txt"),
DONT_VALIDATE
},
{
// 2: A safe file is downloaded. The filename is changed before the dialog
// completes.
FILE_PATH_LITERAL("$dl/foo.txt"),
FILE_PATH_LITERAL(""),
PROMPT,
DownloadStateInfo::NOT_DANGEROUS,
FILE_PATH_LITERAL("$dl/foo.txt"),
FILE_PATH_LITERAL("$dl/bar.txt.crdownload"),
FILE_PATH_LITERAL("$dl/bar.txt"),
DONT_VALIDATE
},
{
// 3: A safe file is downloaded. The download path is changed before the
// dialog completes.
FILE_PATH_LITERAL("$dl/foo.txt"),
FILE_PATH_LITERAL(""),
PROMPT,
DownloadStateInfo::NOT_DANGEROUS,
FILE_PATH_LITERAL("$dl/foo.txt"),
FILE_PATH_LITERAL("$alt/bar.txt.crdownload"),
FILE_PATH_LITERAL("$alt/bar.txt"),
DONT_VALIDATE
},
{
// 4: Potentially dangerous content.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("foo.exe"),
DONT_PROMPT,
DownloadStateInfo::MAYBE_DANGEROUS_CONTENT,
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$dl/foo.exe"),
DONT_VALIDATE
},
{
// 5: Potentially dangerous content. Uses "Save as."
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("foo.exe"),
PROMPT,
DownloadStateInfo::MAYBE_DANGEROUS_CONTENT,
FILE_PATH_LITERAL("$dl/foo.exe"),
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$dl/foo.exe"),
DONT_VALIDATE
},
{
// 6: Potentially dangerous content. Uses "Save as." The download filename
// is changed before the dialog completes.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("foo.exe"),
PROMPT,
DownloadStateInfo::MAYBE_DANGEROUS_CONTENT,
FILE_PATH_LITERAL("$dl/foo.exe"),
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$dl/bar.exe"),
DONT_VALIDATE
},
{
// 7: Potentially dangerous content. Uses "Save as." The download directory
// is changed before the dialog completes.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("foo.exe"),
PROMPT,
DownloadStateInfo::MAYBE_DANGEROUS_CONTENT,
FILE_PATH_LITERAL("$dl/foo.exe"),
FILE_PATH_LITERAL("$alt/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$alt/bar.exe"),
DONT_VALIDATE
},
{
// 8: Dangerous content. Saved directly.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("foo.exe"),
PROMPT,
DownloadStateInfo::DANGEROUS_URL,
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$dl/foo.exe"),
VALIDATE
},
{
// 9: Dangerous content. Saved directly. Not validated.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("foo.exe"),
DONT_PROMPT,
DownloadStateInfo::DANGEROUS_URL,
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL(""),
DONT_VALIDATE
},
{
// 10: Dangerous content. Uses "Save as." The download directory is changed
// before the dialog completes.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("foo.exe"),
PROMPT,
DownloadStateInfo::DANGEROUS_URL,
FILE_PATH_LITERAL("$dl/foo.exe"),
FILE_PATH_LITERAL("$alt/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$alt/bar.exe"),
VALIDATE
},
{
// 11: A safe file is download. The target file exists, but we don't
// uniquify. Safe downloads are uniquified in ChromeDownloadManagerDelegate
// instead of DownloadManagerImpl.
FILE_PATH_LITERAL("$dl/exists.txt"),
FILE_PATH_LITERAL(""),
DONT_PROMPT,
DownloadStateInfo::NOT_DANGEROUS,
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("$dl/exists.txt.crdownload"),
FILE_PATH_LITERAL("$dl/exists.txt"),
DONT_VALIDATE
},
{
// 12: A potentially dangerous file is download. The target file exists. The
// target path is uniquified.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("exists.exe"),
DONT_PROMPT,
DownloadStateInfo::MAYBE_DANGEROUS_CONTENT,
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$dl/exists (1).exe"),
DONT_VALIDATE
},
{
// 13: A dangerous file is download. The target file exists. The target path
// is uniquified.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("exists.exe"),
DONT_PROMPT,
DownloadStateInfo::DANGEROUS_CONTENT,
FILE_PATH_LITERAL(""),
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$dl/exists (1).exe"),
VALIDATE
},
{
// 14: A potentially dangerous file is download with prompting. The target
// file exists. The target path is not uniquified because the filename was
// given to us by the user.
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("exists.exe"),
PROMPT,
DownloadStateInfo::MAYBE_DANGEROUS_CONTENT,
FILE_PATH_LITERAL("$dl/exists.exe"),
FILE_PATH_LITERAL("$dl/Unconfirmed xxx.download"),
FILE_PATH_LITERAL("$dl/exists.exe"),
DONT_VALIDATE
},
};
FilePath ExpandFilenameTestPath(const FilePath::CharType* template_path,
const FilePath& downloads_dir,
const FilePath& alternate_dir) {
FilePath::StringType path(template_path);
ReplaceSubstringsAfterOffset(&path, 0, FILE_PATH_LITERAL("$dl"),
downloads_dir.value());
ReplaceSubstringsAfterOffset(&path, 0, FILE_PATH_LITERAL("$alt"),
alternate_dir.value());
FilePath file_path(path);
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
file_path = file_path.NormalizeWindowsPathSeparators();
#endif
return file_path;
}
} // namespace
TEST_F(DownloadManagerTest, DownloadFilenameTest) {
ScopedTempDir scoped_dl_dir;
ASSERT_TRUE(scoped_dl_dir.CreateUniqueTempDir());
FilePath downloads_dir(scoped_dl_dir.path());
FilePath alternate_dir(downloads_dir.Append(FILE_PATH_LITERAL("Foo")));
// We create a known file to test file uniquification.
file_util::WriteFile(downloads_dir.Append(FILE_PATH_LITERAL("exists.txt")),
"", 0);
file_util::WriteFile(downloads_dir.Append(FILE_PATH_LITERAL("exists.exe")),
"", 0);
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDownloadFilenameTestCases); ++i) {
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
info->download_id = DownloadId(kValidIdDomain, i);
info->url_chain.push_back(GURL());
MockDownloadFile::StatisticsRecorder recorder;
MockDownloadFile* download_file(new MockDownloadFile(
info.get(), DownloadRequestHandle(), download_manager_, &recorder));
FilePath suggested_path(ExpandFilenameTestPath(
kDownloadFilenameTestCases[i].suggested_path,
downloads_dir, alternate_dir));
FilePath prompt_path(ExpandFilenameTestPath(
kDownloadFilenameTestCases[i].prompt_path,
downloads_dir, alternate_dir));
FilePath intermediate_path(ExpandFilenameTestPath(
kDownloadFilenameTestCases[i].intermediate_path,
downloads_dir, alternate_dir));
FilePath final_path(ExpandFilenameTestPath(
kDownloadFilenameTestCases[i].final_path,
downloads_dir, alternate_dir));
// If |final_path| is empty, its a signal that the download doesn't
// complete. Therefore it will only go through a single rename.
int expected_rename_count = (final_path.empty() ? 1 : 2);
AddDownloadToFileManager(info->download_id.local(), download_file);
download_file->SetExpectedPath(0, intermediate_path);
if (!final_path.empty())
download_file->SetExpectedPath(1, final_path);
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadItem* download = GetActiveDownloadItem(i);
ASSERT_TRUE(download != NULL);
DownloadStateInfo state = download->GetStateInfo();
state.suggested_path = suggested_path;
state.danger = kDownloadFilenameTestCases[i].danger_type;
state.prompt_user_for_save_location =
(kDownloadFilenameTestCases[i].prompt_user_for_save_location == PROMPT);
state.target_name = FilePath(kDownloadFilenameTestCases[i].target_name);
if (state.danger == DownloadStateInfo::DANGEROUS_CONTENT) {
// DANGEROUS_CONTENT will only be known once we have all the data. We let
// our TestDownloadManagerDelegate handle it.
state.danger = DownloadStateInfo::MAYBE_DANGEROUS_CONTENT;
download_manager_delegate_->SetMarkContentsDangerous(true);
}
download->SetFileCheckResults(state);
download_manager_delegate_->SetFileSelectionExpectation(
prompt_path, final_path);
download_manager_->RestartDownload(i);
message_loop_.RunAllPending();
OnResponseCompleted(i, 1024, std::string("fake_hash"));
message_loop_.RunAllPending();
if (download->GetSafetyState() == DownloadItem::DANGEROUS) {
if (kDownloadFilenameTestCases[i].validate_dangerous_download == VALIDATE)
download->DangerousDownloadValidated();
else
download->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD);
message_loop_.RunAllPending();
// |download| might be deleted when we get here.
}
EXPECT_EQ(
expected_rename_count,
recorder.Count(MockDownloadFile::StatisticsRecorder::STAT_RENAME))
<< "For test run " << i;
}
}
TEST_F(DownloadManagerTest, DownloadRenameTest) {
using ::testing::_;
using ::testing::CreateFunctor;
using ::testing::Invoke;
using ::testing::Return;
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDownloadRenameCases); ++i) {
// 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 do so ourselves.
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
info->download_id = DownloadId(kValidIdDomain, static_cast<int>(i));
info->prompt_user_for_save_location = false;
info->url_chain.push_back(GURL());
const FilePath new_path(kDownloadRenameCases[i].suggested_path);
MockDownloadFile::StatisticsRecorder recorder;
MockDownloadFile* download_file(
new MockDownloadFile(info.get(),
DownloadRequestHandle(),
download_manager_,
&recorder));
AddDownloadToFileManager(info->download_id.local(), download_file);
// |download_file| is owned by DownloadFileManager.
if (kDownloadRenameCases[i].expected_rename_count == 1) {
download_file->SetExpectedPath(0, new_path);
} else {
ASSERT_EQ(2, kDownloadRenameCases[i].expected_rename_count);
FilePath crdownload(download_util::GetCrDownloadPath(new_path));
download_file->SetExpectedPath(0, crdownload);
download_file->SetExpectedPath(1, new_path);
}
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadItem* download = GetActiveDownloadItem(i);
ASSERT_TRUE(download != NULL);
DownloadStateInfo state = download->GetStateInfo();
state.danger = kDownloadRenameCases[i].danger;
download->SetFileCheckResults(state);
int32* id_ptr = new int32;
*id_ptr = i; // Deleted in FileSelected().
if (kDownloadRenameCases[i].finish_before_rename) {
OnResponseCompleted(i, 1024, std::string("fake_hash"));
message_loop_.RunAllPending();
FileSelected(new_path, id_ptr);
} else {
FileSelected(new_path, id_ptr);
message_loop_.RunAllPending();
OnResponseCompleted(i, 1024, std::string("fake_hash"));
}
message_loop_.RunAllPending();
EXPECT_EQ(
kDownloadRenameCases[i].expected_rename_count,
recorder.Count(MockDownloadFile::StatisticsRecorder::STAT_RENAME));
}
}
TEST_F(DownloadManagerTest, DownloadInterruptTest) {
using ::testing::_;
using ::testing::CreateFunctor;
using ::testing::Invoke;
using ::testing::Return;
// 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 do so ourselves.
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
info->download_id = DownloadId(kValidIdDomain, 0);
info->prompt_user_for_save_location = false;
info->url_chain.push_back(GURL());
info->total_bytes = static_cast<int64>(kTestDataLen);
const FilePath new_path(FILE_PATH_LITERAL("foo.zip"));
const FilePath cr_path(download_util::GetCrDownloadPath(new_path));
MockDownloadFile::StatisticsRecorder recorder;
MockDownloadFile* download_file(
new MockDownloadFile(info.get(),
DownloadRequestHandle(),
download_manager_,
&recorder));
AddDownloadToFileManager(info->download_id.local(), download_file);
// |download_file| is owned by DownloadFileManager.
download_file->SetExpectedPath(0, cr_path);
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadItem* download = GetActiveDownloadItem(0);
ASSERT_TRUE(download != NULL);
scoped_ptr<DownloadItemModel> download_item_model(
new DownloadItemModel(download));
EXPECT_EQ(DownloadItem::IN_PROGRESS, download->GetState());
scoped_ptr<ItemObserver> observer(new ItemObserver(download));
download_file->AppendDataToFile(kTestData, kTestDataLen);
ContinueDownloadWithPath(download, new_path);
message_loop_.RunAllPending();
EXPECT_EQ(1,
recorder.Count(MockDownloadFile::StatisticsRecorder::STAT_RENAME));
EXPECT_TRUE(GetActiveDownloadItem(0) != NULL);
int64 error_size = 3;
OnDownloadInterrupted(0, error_size, "",
DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
message_loop_.RunAllPending();
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->GetFileExternallyRemoved());
EXPECT_EQ(DownloadItem::INTERRUPTED, download->GetState());
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(kTestDataLen, amount_units, true));
EXPECT_EQ(download_item_model->GetStatusText(),
l10n_util::GetStringFUTF16(IDS_DOWNLOAD_STATUS_INTERRUPTED,
simple_size,
simple_total));
download->Cancel(true);
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->GetFileExternallyRemoved());
EXPECT_EQ(DownloadItem::INTERRUPTED, download->GetState());
EXPECT_EQ(download->GetReceivedBytes(), error_size);
EXPECT_EQ(download->GetTotalBytes(), 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.
net::FileStream* stream = new net::FileStream;
ASSERT_EQ(0, 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 do so ourselves.
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
static const int32 local_id = 0;
info->download_id = DownloadId(kValidIdDomain, local_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;
info->save_info.file_stream.reset(stream);
// Create a download file that we can insert errors into.
DownloadFileWithErrors* download_file(new DownloadFileWithErrors(
info.get(), download_manager_, false));
download_file->Initialize();
AddDownloadToFileManager(local_id, download_file);
// |download_file| is owned by DownloadFileManager.
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
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->GetState());
scoped_ptr<ItemObserver> observer(new ItemObserver(download));
// Add some data before finalizing the file name.
UpdateData(local_id, kTestData, kTestDataLen);
// Finalize the file name.
ContinueDownloadWithPath(download, path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(0) != NULL);
// Add more data.
UpdateData(local_id, kTestData, kTestDataLen);
// Add more data, but an error occurs.
download_file->set_forced_error(net::ERR_FAILED);
UpdateData(local_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->GetFileExternallyRemoved());
EXPECT_EQ(DownloadItem::INTERRUPTED, download->GetState());
// Check the download shelf's information.
size_t error_size = kTestDataLen * 3;
size_t total_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(total_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);
message_loop_.RunAllPending();
}
TEST_F(DownloadManagerTest, DownloadCancelTest) {
using ::testing::_;
using ::testing::CreateFunctor;
using ::testing::Invoke;
using ::testing::Return;
// 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 do so ourselves.
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
info->download_id = DownloadId(kValidIdDomain, 0);
info->prompt_user_for_save_location = false;
info->url_chain.push_back(GURL());
const FilePath new_path(FILE_PATH_LITERAL("foo.zip"));
const FilePath cr_path(download_util::GetCrDownloadPath(new_path));
MockDownloadFile* download_file(
new MockDownloadFile(info.get(),
DownloadRequestHandle(),
download_manager_,
NULL));
AddDownloadToFileManager(info->download_id.local(), download_file);
// |download_file| is owned by DownloadFileManager.
download_file->SetExpectedPath(0, cr_path);
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadItem* download = GetActiveDownloadItem(0);
ASSERT_TRUE(download != NULL);
scoped_ptr<DownloadItemModel> download_item_model(
new DownloadItemModel(download));
EXPECT_EQ(DownloadItem::IN_PROGRESS, download->GetState());
scoped_ptr<ItemObserver> observer(new ItemObserver(download));
ContinueDownloadWithPath(download, new_path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(0) != NULL);
download_file->AppendDataToFile(kTestData, kTestDataLen);
download->Cancel(false);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(0) != NULL);
EXPECT_TRUE(observer->hit_state(DownloadItem::IN_PROGRESS));
EXPECT_TRUE(observer->hit_state(DownloadItem::CANCELLED));
EXPECT_FALSE(observer->hit_state(DownloadItem::INTERRUPTED));
EXPECT_FALSE(observer->hit_state(DownloadItem::COMPLETE));
EXPECT_FALSE(observer->hit_state(DownloadItem::REMOVING));
EXPECT_TRUE(observer->was_updated());
EXPECT_FALSE(observer->was_opened());
EXPECT_FALSE(download->GetFileExternallyRemoved());
EXPECT_EQ(DownloadItem::CANCELLED, download->GetState());
EXPECT_EQ(download_item_model->GetStatusText(),
l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_CANCELED));
EXPECT_FALSE(file_util::PathExists(new_path));
EXPECT_FALSE(file_util::PathExists(cr_path));
}
TEST_F(DownloadManagerTest, MAYBE_DownloadOverwriteTest) {
using ::testing::_;
using ::testing::CreateFunctor;
using ::testing::Invoke;
using ::testing::Return;
// Create a temporary directory.
ScopedTempDir temp_dir_;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
// File names we're using.
const FilePath new_path(temp_dir_.path().AppendASCII("foo.txt"));
const FilePath cr_path(download_util::GetCrDownloadPath(new_path));
EXPECT_FALSE(file_util::PathExists(new_path));
// Create the file that we will overwrite. Will be automatically cleaned
// up when temp_dir_ is destroyed.
FILE* fp = file_util::OpenFile(new_path, "w");
file_util::CloseFile(fp);
EXPECT_TRUE(file_util::PathExists(new_path));
// Construct the unique file name that normally would be created, but
// which we will override.
int uniquifier = DownloadFile::GetUniquePathNumber(new_path);
FilePath unique_new_path = new_path;
EXPECT_NE(0, uniquifier);
DownloadFile::AppendNumberToPath(&unique_new_path, uniquifier);
// 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 do so ourselves.
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
info->download_id = DownloadId(kValidIdDomain, 0);
info->prompt_user_for_save_location = true;
info->url_chain.push_back(GURL());
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadItem* download = GetActiveDownloadItem(0);
ASSERT_TRUE(download != NULL);
scoped_ptr<DownloadItemModel> download_item_model(
new DownloadItemModel(download));
EXPECT_EQ(DownloadItem::IN_PROGRESS, download->GetState());
scoped_ptr<ItemObserver> observer(new ItemObserver(download));
// Create and initialize the download file. We're bypassing the first part
// of the download process and skipping to the part after the final file
// name has been chosen, so we need to initialize the download file
// properly.
DownloadFile* download_file(
new DownloadFileImpl(info.get(), new DownloadRequestHandle(),
download_manager_, false));
download_file->Rename(cr_path);
// This creates the .crdownload version of the file.
download_file->Initialize();
// |download_file| is owned by DownloadFileManager.
AddDownloadToFileManager(info->download_id.local(), download_file);
ContinueDownloadWithPath(download, new_path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(0) != NULL);
download_file->AppendDataToFile(kTestData, kTestDataLen);
// Finish the download.
OnResponseCompleted(0, kTestDataLen, "");
message_loop_.RunAllPending();
// Download is complete.
EXPECT_TRUE(GetActiveDownloadItem(0) == NULL);
EXPECT_TRUE(observer->hit_state(DownloadItem::IN_PROGRESS));
EXPECT_FALSE(observer->hit_state(DownloadItem::CANCELLED));
EXPECT_FALSE(observer->hit_state(DownloadItem::INTERRUPTED));
EXPECT_TRUE(observer->hit_state(DownloadItem::COMPLETE));
EXPECT_FALSE(observer->hit_state(DownloadItem::REMOVING));
EXPECT_TRUE(observer->was_updated());
EXPECT_FALSE(observer->was_opened());
EXPECT_FALSE(download->GetFileExternallyRemoved());
EXPECT_EQ(DownloadItem::COMPLETE, download->GetState());
EXPECT_EQ(download_item_model->GetStatusText(), string16());
EXPECT_TRUE(file_util::PathExists(new_path));
EXPECT_FALSE(file_util::PathExists(cr_path));
EXPECT_FALSE(file_util::PathExists(unique_new_path));
std::string file_contents;
EXPECT_TRUE(file_util::ReadFileToString(new_path, &file_contents));
EXPECT_EQ(std::string(kTestData), file_contents);
}
TEST_F(DownloadManagerTest, MAYBE_DownloadRemoveTest) {
using ::testing::_;
using ::testing::CreateFunctor;
using ::testing::Invoke;
using ::testing::Return;
// Create a temporary directory.
ScopedTempDir temp_dir_;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
// File names we're using.
const FilePath new_path(temp_dir_.path().AppendASCII("foo.txt"));
const FilePath cr_path(download_util::GetCrDownloadPath(new_path));
EXPECT_FALSE(file_util::PathExists(new_path));
// 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 do so ourselves.
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
info->download_id = DownloadId(kValidIdDomain, 0);
info->prompt_user_for_save_location = true;
info->url_chain.push_back(GURL());
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadItem* download = GetActiveDownloadItem(0);
ASSERT_TRUE(download != NULL);
scoped_ptr<DownloadItemModel> download_item_model(
new DownloadItemModel(download));
EXPECT_EQ(DownloadItem::IN_PROGRESS, download->GetState());
scoped_ptr<ItemObserver> observer(new ItemObserver(download));
// Create and initialize the download file. We're bypassing the first part
// of the download process and skipping to the part after the final file
// name has been chosen, so we need to initialize the download file
// properly.
DownloadFile* download_file(
new DownloadFileImpl(info.get(), new DownloadRequestHandle(),
download_manager_, false));
download_file->Rename(cr_path);
// This creates the .crdownload version of the file.
download_file->Initialize();
// |download_file| is owned by DownloadFileManager.
AddDownloadToFileManager(info->download_id.local(), download_file);
ContinueDownloadWithPath(download, new_path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(0) != NULL);
download_file->AppendDataToFile(kTestData, kTestDataLen);
// Finish the download.
OnResponseCompleted(0, kTestDataLen, "");
message_loop_.RunAllPending();
// Download is complete.
EXPECT_TRUE(GetActiveDownloadItem(0) == NULL);
EXPECT_TRUE(observer->hit_state(DownloadItem::IN_PROGRESS));
EXPECT_FALSE(observer->hit_state(DownloadItem::CANCELLED));
EXPECT_FALSE(observer->hit_state(DownloadItem::INTERRUPTED));
EXPECT_TRUE(observer->hit_state(DownloadItem::COMPLETE));
EXPECT_FALSE(observer->hit_state(DownloadItem::REMOVING));
EXPECT_TRUE(observer->was_updated());
EXPECT_FALSE(observer->was_opened());
EXPECT_FALSE(download->GetFileExternallyRemoved());
EXPECT_EQ(DownloadItem::COMPLETE, download->GetState());
EXPECT_EQ(download_item_model->GetStatusText(), string16());
EXPECT_TRUE(file_util::PathExists(new_path));
EXPECT_FALSE(file_util::PathExists(cr_path));
// Remove the downloaded file.
ASSERT_TRUE(file_util::Delete(new_path, false));
download->OnDownloadedFileRemoved();
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(0) == NULL);
EXPECT_TRUE(observer->hit_state(DownloadItem::IN_PROGRESS));
EXPECT_FALSE(observer->hit_state(DownloadItem::CANCELLED));
EXPECT_FALSE(observer->hit_state(DownloadItem::INTERRUPTED));
EXPECT_TRUE(observer->hit_state(DownloadItem::COMPLETE));
EXPECT_FALSE(observer->hit_state(DownloadItem::REMOVING));
EXPECT_TRUE(observer->was_updated());
EXPECT_FALSE(observer->was_opened());
EXPECT_TRUE(download->GetFileExternallyRemoved());
EXPECT_EQ(DownloadItem::COMPLETE, download->GetState());
EXPECT_EQ(download_item_model->GetStatusText(),
l10n_util::GetStringUTF16(IDS_DOWNLOAD_STATUS_REMOVED));
EXPECT_FALSE(file_util::PathExists(new_path));
}