blob: 9ba2d1777ec8f60544a0afb20856d0b1ca56264f [file] [log] [blame]
// 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 "base/callback.h"
#include "base/feature_list.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/threading/thread.h"
#include "content/browser/byte_stream.h"
#include "content/browser/download/download_create_info.h"
#include "content/browser/download/download_file_factory.h"
#include "content/browser/download/download_item_impl.h"
#include "content/browser/download/download_item_impl_delegate.h"
#include "content/browser/download/download_request_handle.h"
#include "content/browser/download/mock_download_file.h"
#include "content/public/browser/download_destination_observer.h"
#include "content/public/browser/download_interrupt_reasons.h"
#include "content/public/browser/download_url_parameters.h"
#include "content/public/common/content_features.h"
#include "content/public/test/mock_download_item.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Property;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
const int kDownloadChunkSize = 1000;
const int kDownloadSpeed = 1000;
const base::FilePath::CharType kDummyPath[] = FILE_PATH_LITERAL("/testpath");
namespace content {
namespace {
class MockDelegate : public DownloadItemImplDelegate {
public:
MockDelegate() : DownloadItemImplDelegate() {
SetDefaultExpectations();
}
MOCK_METHOD2(DetermineDownloadTarget, void(
DownloadItemImpl*, const DownloadTargetCallback&));
MOCK_METHOD2(ShouldCompleteDownload,
bool(DownloadItemImpl*, const base::Closure&));
MOCK_METHOD2(ShouldOpenDownload,
bool(DownloadItemImpl*, const ShouldOpenDownloadCallback&));
MOCK_METHOD1(ShouldOpenFileBasedOnExtension, bool(const base::FilePath&));
MOCK_METHOD1(CheckForFileRemoval, void(DownloadItemImpl*));
void ResumeInterruptedDownload(
scoped_ptr<DownloadUrlParameters> params, uint32 id) override {
MockResumeInterruptedDownload(params.get(), id);
}
MOCK_METHOD2(MockResumeInterruptedDownload,
void(DownloadUrlParameters* params, uint32 id));
MOCK_CONST_METHOD0(GetBrowserContext, BrowserContext*());
MOCK_METHOD1(UpdatePersistence, void(DownloadItemImpl*));
MOCK_METHOD1(DownloadOpened, void(DownloadItemImpl*));
MOCK_METHOD1(DownloadRemoved, void(DownloadItemImpl*));
MOCK_CONST_METHOD1(AssertStateConsistent, void(DownloadItemImpl*));
void VerifyAndClearExpectations() {
::testing::Mock::VerifyAndClearExpectations(this);
SetDefaultExpectations();
}
private:
void SetDefaultExpectations() {
EXPECT_CALL(*this, AssertStateConsistent(_))
.WillRepeatedly(Return());
EXPECT_CALL(*this, ShouldOpenFileBasedOnExtension(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*this, ShouldOpenDownload(_, _))
.WillRepeatedly(Return(true));
}
};
class MockRequestHandle : public DownloadRequestHandleInterface {
public:
MOCK_CONST_METHOD0(GetWebContents, WebContents*());
MOCK_CONST_METHOD0(GetDownloadManager, DownloadManager*());
MOCK_CONST_METHOD0(PauseRequest, void());
MOCK_CONST_METHOD0(ResumeRequest, void());
MOCK_CONST_METHOD0(CancelRequest, void());
MOCK_CONST_METHOD0(DebugString, std::string());
};
class TestDownloadItemObserver : public DownloadItem::Observer {
public:
explicit TestDownloadItemObserver(DownloadItem* item)
: item_(item),
last_state_(item->GetState()),
removed_(false),
destroyed_(false),
updated_(false),
interrupt_count_(0),
resume_count_(0) {
item_->AddObserver(this);
}
~TestDownloadItemObserver() override {
if (item_)
item_->RemoveObserver(this);
}
bool download_removed() const { return removed_; }
bool download_destroyed() const { return destroyed_; }
int interrupt_count() const { return interrupt_count_; }
int resume_count() const { return resume_count_; }
bool CheckAndResetDownloadUpdated() {
bool was_updated = updated_;
updated_ = false;
return was_updated;
}
private:
void OnDownloadRemoved(DownloadItem* download) override {
DVLOG(20) << " " << __FUNCTION__
<< " download = " << download->DebugString(false);
removed_ = true;
}
void OnDownloadUpdated(DownloadItem* download) override {
DVLOG(20) << " " << __FUNCTION__
<< " download = " << download->DebugString(false);
updated_ = true;
DownloadItem::DownloadState new_state = download->GetState();
if (last_state_ == DownloadItem::IN_PROGRESS &&
new_state == DownloadItem::INTERRUPTED) {
interrupt_count_++;
}
if (last_state_ == DownloadItem::INTERRUPTED &&
new_state == DownloadItem::IN_PROGRESS) {
resume_count_++;
}
last_state_ = new_state;
}
void OnDownloadOpened(DownloadItem* download) override {
DVLOG(20) << " " << __FUNCTION__
<< " download = " << download->DebugString(false);
}
void OnDownloadDestroyed(DownloadItem* download) override {
DVLOG(20) << " " << __FUNCTION__
<< " download = " << download->DebugString(false);
destroyed_ = true;
item_->RemoveObserver(this);
item_ = NULL;
}
DownloadItem* item_;
DownloadItem::DownloadState last_state_;
bool removed_;
bool destroyed_;
bool updated_;
int interrupt_count_;
int resume_count_;
};
// Schedules a task to invoke the RenameCompletionCallback with |new_path| on
// the UI thread. Should only be used as the action for
// MockDownloadFile::Rename as follows:
// EXPECT_CALL(download_file, Rename*(_,_))
// .WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
// new_path));
ACTION_P2(ScheduleRenameCallback, interrupt_reason, new_path) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(arg1, interrupt_reason, new_path));
}
} // namespace
class DownloadItemTest : public testing::Test {
public:
DownloadItemTest()
: ui_thread_(BrowserThread::UI, &loop_),
file_thread_(BrowserThread::FILE, &loop_),
delegate_() {
}
~DownloadItemTest() {
}
virtual void SetUp() {
base::FeatureList::ClearInstanceForTesting();
scoped_ptr<base::FeatureList> feature_list(new base::FeatureList);
feature_list->InitializeFromCommandLine(features::kDownloadResumption.name,
std::string());
base::FeatureList::SetInstance(std::move(feature_list));
}
virtual void TearDown() {
ui_thread_.DeprecatedGetThreadObject()->message_loop()->RunUntilIdle();
STLDeleteElements(&allocated_downloads_);
}
// This class keeps ownership of the created download item; it will
// be torn down at the end of the test unless DestroyDownloadItem is
// called.
DownloadItemImpl* CreateDownloadItem() {
scoped_ptr<DownloadCreateInfo> info;
info.reset(new DownloadCreateInfo());
info->save_info = scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo());
info->save_info->prompt_for_save_location = false;
info->url_chain.push_back(GURL());
info->etag = "SomethingToSatisfyResumption";
return CreateDownloadItemWithCreateInfo(info.Pass());
}
DownloadItemImpl* CreateDownloadItemWithCreateInfo(
scoped_ptr<DownloadCreateInfo> info) {
DownloadItemImpl* download = new DownloadItemImpl(
&delegate_, next_download_id_++, *(info.get()), net::BoundNetLog());
allocated_downloads_.insert(download);
return download;
}
// Add DownloadFile to DownloadItem
MockDownloadFile* AddDownloadFileToDownloadItem(
DownloadItemImpl* item,
DownloadItemImplDelegate::DownloadTargetCallback *callback) {
MockDownloadFile* mock_download_file(new StrictMock<MockDownloadFile>);
scoped_ptr<DownloadFile> download_file(mock_download_file);
EXPECT_CALL(*mock_download_file, Initialize(_));
if (callback) {
// Save the callback.
EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _))
.WillOnce(SaveArg<1>(callback));
} else {
// Drop it on the floor.
EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _));
}
scoped_ptr<DownloadRequestHandleInterface> request_handle(
new NiceMock<MockRequestHandle>);
item->Start(download_file.Pass(), request_handle.Pass());
loop_.RunUntilIdle();
// So that we don't have a function writing to a stack variable
// lying around if the above failed.
mock_delegate()->VerifyAndClearExpectations();
EXPECT_CALL(*mock_delegate(), AssertStateConsistent(_))
.WillRepeatedly(Return());
EXPECT_CALL(*mock_delegate(), ShouldOpenFileBasedOnExtension(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(_, _))
.WillRepeatedly(Return(true));
return mock_download_file;
}
// Perform the intermediate rename for |item|. The target path for the
// download will be set to kDummyPath. Returns the MockDownloadFile* that was
// added to the DownloadItem.
MockDownloadFile* DoIntermediateRename(DownloadItemImpl* item,
DownloadDangerType danger_type) {
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_TRUE(item->GetTargetFilePath().empty());
DownloadItemImplDelegate::DownloadTargetCallback callback;
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(item, &callback);
base::FilePath target_path(kDummyPath);
base::FilePath intermediate_path(
target_path.InsertBeforeExtensionASCII("x"));
EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
intermediate_path));
callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
danger_type, intermediate_path);
RunAllPendingInMessageLoops();
return download_file;
}
// Cleanup a download item (specifically get rid of the DownloadFile on it).
// The item must be in the expected state.
void CleanupItem(DownloadItemImpl* item,
MockDownloadFile* download_file,
DownloadItem::DownloadState expected_state) {
EXPECT_EQ(expected_state, item->GetState());
if (expected_state == DownloadItem::IN_PROGRESS) {
if (download_file)
EXPECT_CALL(*download_file, Cancel());
item->Cancel(true);
loop_.RunUntilIdle();
}
}
// Destroy a previously created download item.
void DestroyDownloadItem(DownloadItem* item) {
allocated_downloads_.erase(item);
delete item;
}
void RunAllPendingInMessageLoops() {
loop_.RunUntilIdle();
}
MockDelegate* mock_delegate() {
return &delegate_;
}
void OnDownloadFileAcquired(base::FilePath* return_path,
const base::FilePath& path) {
*return_path = path;
}
private:
int next_download_id_ = DownloadItem::kInvalidId + 1;
base::MessageLoopForUI loop_;
TestBrowserThread ui_thread_; // UI thread
TestBrowserThread file_thread_; // FILE thread
StrictMock<MockDelegate> delegate_;
std::set<DownloadItem*> allocated_downloads_;
};
// Tests to ensure calls that change a DownloadItem generate an update to
// observers.
// State changing functions not tested:
// void OpenDownload();
// void ShowDownloadInShell();
// void CompleteDelayedDownload();
// set_* mutators
TEST_F(DownloadItemTest, NotificationAfterUpdate) {
DownloadItemImpl* item = CreateDownloadItem();
TestDownloadItemObserver observer(item);
item->DestinationUpdate(kDownloadChunkSize, kDownloadSpeed, std::string());
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
EXPECT_EQ(kDownloadSpeed, item->CurrentSpeed());
}
TEST_F(DownloadItemTest, NotificationAfterCancel) {
DownloadItemImpl* user_cancel = CreateDownloadItem();
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(user_cancel, NULL);
EXPECT_CALL(*download_file, Cancel());
TestDownloadItemObserver observer1(user_cancel);
user_cancel->Cancel(true);
ASSERT_TRUE(observer1.CheckAndResetDownloadUpdated());
DownloadItemImpl* system_cancel = CreateDownloadItem();
download_file = AddDownloadFileToDownloadItem(system_cancel, NULL);
EXPECT_CALL(*download_file, Cancel());
TestDownloadItemObserver observer2(system_cancel);
system_cancel->Cancel(false);
ASSERT_TRUE(observer2.CheckAndResetDownloadUpdated());
}
TEST_F(DownloadItemTest, NotificationAfterComplete) {
DownloadItemImpl* item = CreateDownloadItem();
TestDownloadItemObserver observer(item);
item->OnAllDataSaved(DownloadItem::kEmptyFileHash);
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
item->MarkAsComplete();
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
}
TEST_F(DownloadItemTest, NotificationAfterDownloadedFileRemoved) {
DownloadItemImpl* item = CreateDownloadItem();
TestDownloadItemObserver observer(item);
item->OnDownloadedFileRemoved();
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
}
TEST_F(DownloadItemTest, NotificationAfterInterrupted) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
EXPECT_CALL(*download_file, Cancel());
TestDownloadItemObserver observer(item);
EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_,_))
.Times(0);
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
}
TEST_F(DownloadItemTest, NotificationAfterDestroyed) {
DownloadItemImpl* item = CreateDownloadItem();
TestDownloadItemObserver observer(item);
DestroyDownloadItem(item);
ASSERT_TRUE(observer.download_destroyed());
}
TEST_F(DownloadItemTest, ContinueAfterInterrupted) {
TestBrowserContext test_browser_context;
DownloadItemImpl* item = CreateDownloadItem();
TestDownloadItemObserver observer(item);
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// Interrupt the download, using a continuable interrupt.
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
EXPECT_CALL(*mock_delegate(), GetBrowserContext())
.WillRepeatedly(Return(&test_browser_context));
EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _)).Times(1);
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR);
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
// Since the download is resumed automatically, the interrupt count doesn't
// increase.
ASSERT_EQ(0, observer.interrupt_count());
// Test expectations verify that ResumeInterruptedDownload() is called (by way
// of MockResumeInterruptedDownload) after the download is interrupted. But
// the mock doesn't follow through with the resumption.
// ResumeInterruptedDownload() being called is sufficient for verifying that
// the automatic resumption was triggered.
RunAllPendingInMessageLoops();
// The download item is currently in RESUMING_INTERNAL state, which maps to
// IN_PROGRESS.
CleanupItem(item, nullptr, DownloadItem::IN_PROGRESS);
}
// Same as above, but with a non-continuable interrupt.
TEST_F(DownloadItemTest, RestartAfterInterrupted) {
DownloadItemImpl* item = CreateDownloadItem();
TestDownloadItemObserver observer(item);
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// Interrupt the download, using a restartable interrupt.
EXPECT_CALL(*download_file, Cancel());
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
// Should not try to auto-resume.
ASSERT_EQ(1, observer.interrupt_count());
ASSERT_EQ(0, observer.resume_count());
RunAllPendingInMessageLoops();
CleanupItem(item, nullptr, DownloadItem::INTERRUPTED);
}
// Check we do correct cleanup for RESUME_MODE_INVALID interrupts.
TEST_F(DownloadItemTest, UnresumableInterrupt) {
DownloadItemImpl* item = CreateDownloadItem();
TestDownloadItemObserver observer(item);
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// Fail final rename with unresumable reason.
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _))
.WillOnce(Return(true));
EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED,
base::FilePath(kDummyPath)));
EXPECT_CALL(*download_file, Cancel());
// Complete download to trigger final rename.
item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
RunAllPendingInMessageLoops();
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
// Should not try to auto-resume.
ASSERT_EQ(1, observer.interrupt_count());
ASSERT_EQ(0, observer.resume_count());
CleanupItem(item, nullptr, DownloadItem::INTERRUPTED);
}
TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) {
TestBrowserContext test_browser_context;
DownloadItemImpl* item = CreateDownloadItem();
base::WeakPtr<DownloadDestinationObserver> as_observer(
item->DestinationObserverAsWeakPtr());
TestDownloadItemObserver observer(item);
MockDownloadFile* mock_download_file(NULL);
scoped_ptr<DownloadFile> download_file;
MockRequestHandle* mock_request_handle(NULL);
scoped_ptr<DownloadRequestHandleInterface> request_handle;
DownloadItemImplDelegate::DownloadTargetCallback callback;
EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _))
.WillRepeatedly(SaveArg<1>(&callback));
EXPECT_CALL(*mock_delegate(), GetBrowserContext())
.WillRepeatedly(Return(&test_browser_context));
EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_, _))
.Times(DownloadItemImpl::kMaxAutoResumeAttempts);
for (int i = 0; i < (DownloadItemImpl::kMaxAutoResumeAttempts + 1); ++i) {
DVLOG(20) << "Loop iteration " << i;
mock_download_file = new NiceMock<MockDownloadFile>;
download_file.reset(mock_download_file);
mock_request_handle = new NiceMock<MockRequestHandle>;
request_handle.reset(mock_request_handle);
ON_CALL(*mock_download_file, FullPath())
.WillByDefault(Return(base::FilePath()));
// Copied key parts of DoIntermediateRename & AddDownloadFileToDownloadItem
// to allow for holding onto the request handle.
item->Start(download_file.Pass(), request_handle.Pass());
RunAllPendingInMessageLoops();
if (i == 0) {
// Target determination is only done the first time through.
base::FilePath target_path(kDummyPath);
base::FilePath intermediate_path(
target_path.InsertBeforeExtensionASCII("x"));
EXPECT_CALL(*mock_download_file, RenameAndUniquify(intermediate_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
intermediate_path));
callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path);
RunAllPendingInMessageLoops();
}
// Use a continuable interrupt.
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR);
::testing::Mock::VerifyAndClearExpectations(mock_download_file);
}
EXPECT_EQ(1, observer.interrupt_count());
CleanupItem(item, nullptr, DownloadItem::INTERRUPTED);
}
// Test that resumption uses the final URL in a URL chain when resuming.
TEST_F(DownloadItemTest, ResumeUsingFinalURL) {
TestBrowserContext test_browser_context;
scoped_ptr<DownloadCreateInfo> create_info(new DownloadCreateInfo);
create_info->save_info = scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo());
create_info->save_info->prompt_for_save_location = false;
create_info->etag = "SomethingToSatisfyResumption";
create_info->url_chain.push_back(GURL("http://example.com/a"));
create_info->url_chain.push_back(GURL("http://example.com/b"));
create_info->url_chain.push_back(GURL("http://example.com/c"));
DownloadItemImpl* item = CreateDownloadItemWithCreateInfo(create_info.Pass());
TestDownloadItemObserver observer(item);
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// Interrupt the download, using a continuable interrupt.
EXPECT_CALL(*download_file, FullPath()).WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
EXPECT_CALL(*mock_delegate(), GetBrowserContext())
.WillRepeatedly(Return(&test_browser_context));
EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(
Property(&DownloadUrlParameters::url,
GURL("http://example.com/c")),
_))
.Times(1);
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR);
// Test expectations verify that ResumeInterruptedDownload() is called (by way
// of MockResumeInterruptedDownload) after the download is interrupted. But
// the mock doesn't follow through with the resumption.
// ResumeInterruptedDownload() being called is sufficient for verifying that
// the resumption was triggered.
RunAllPendingInMessageLoops();
// The download is currently in RESUMING_INTERNAL, which maps to IN_PROGRESS.
CleanupItem(item, nullptr, DownloadItem::IN_PROGRESS);
}
TEST_F(DownloadItemTest, NotificationAfterRemove) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file = AddDownloadFileToDownloadItem(item, NULL);
EXPECT_CALL(*download_file, Cancel());
EXPECT_CALL(*mock_delegate(), DownloadRemoved(_));
TestDownloadItemObserver observer(item);
item->Remove();
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
ASSERT_TRUE(observer.download_removed());
}
TEST_F(DownloadItemTest, NotificationAfterOnContentCheckCompleted) {
// Setting to NOT_DANGEROUS does not trigger a notification.
DownloadItemImpl* safe_item = CreateDownloadItem();
TestDownloadItemObserver safe_observer(safe_item);
safe_item->OnAllDataSaved(std::string());
EXPECT_TRUE(safe_observer.CheckAndResetDownloadUpdated());
safe_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
EXPECT_TRUE(safe_observer.CheckAndResetDownloadUpdated());
// Setting to unsafe url or unsafe file should trigger a notification.
DownloadItemImpl* unsafeurl_item =
CreateDownloadItem();
TestDownloadItemObserver unsafeurl_observer(unsafeurl_item);
unsafeurl_item->OnAllDataSaved(std::string());
EXPECT_TRUE(unsafeurl_observer.CheckAndResetDownloadUpdated());
unsafeurl_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_DANGEROUS_URL);
EXPECT_TRUE(unsafeurl_observer.CheckAndResetDownloadUpdated());
unsafeurl_item->ValidateDangerousDownload();
EXPECT_TRUE(unsafeurl_observer.CheckAndResetDownloadUpdated());
DownloadItemImpl* unsafefile_item =
CreateDownloadItem();
TestDownloadItemObserver unsafefile_observer(unsafefile_item);
unsafefile_item->OnAllDataSaved(std::string());
EXPECT_TRUE(unsafefile_observer.CheckAndResetDownloadUpdated());
unsafefile_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE);
EXPECT_TRUE(unsafefile_observer.CheckAndResetDownloadUpdated());
unsafefile_item->ValidateDangerousDownload();
EXPECT_TRUE(unsafefile_observer.CheckAndResetDownloadUpdated());
}
// DownloadItemImpl::OnDownloadTargetDetermined will schedule a task to run
// DownloadFile::Rename(). Once the rename
// completes, DownloadItemImpl receives a notification with the new file
// name. Check that observers are updated when the new filename is available and
// not before.
TEST_F(DownloadItemTest, NotificationAfterOnDownloadTargetDetermined) {
DownloadItemImpl* item = CreateDownloadItem();
DownloadItemImplDelegate::DownloadTargetCallback callback;
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(item, &callback);
TestDownloadItemObserver observer(item);
base::FilePath target_path(kDummyPath);
base::FilePath intermediate_path(target_path.InsertBeforeExtensionASCII("x"));
base::FilePath new_intermediate_path(
target_path.InsertBeforeExtensionASCII("y"));
EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
new_intermediate_path));
// Currently, a notification would be generated if the danger type is anything
// other than NOT_DANGEROUS.
callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path);
EXPECT_FALSE(observer.CheckAndResetDownloadUpdated());
RunAllPendingInMessageLoops();
EXPECT_TRUE(observer.CheckAndResetDownloadUpdated());
EXPECT_EQ(new_intermediate_path, item->GetFullPath());
CleanupItem(item, download_file, DownloadItem::IN_PROGRESS);
}
TEST_F(DownloadItemTest, NotificationAfterTogglePause) {
DownloadItemImpl* item = CreateDownloadItem();
TestDownloadItemObserver observer(item);
MockDownloadFile* mock_download_file(new MockDownloadFile);
scoped_ptr<DownloadFile> download_file(mock_download_file);
scoped_ptr<DownloadRequestHandleInterface> request_handle(
new NiceMock<MockRequestHandle>);
EXPECT_CALL(*mock_download_file, Initialize(_));
EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _));
item->Start(download_file.Pass(), request_handle.Pass());
item->Pause();
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
ASSERT_TRUE(item->IsPaused());
item->Resume();
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated());
RunAllPendingInMessageLoops();
CleanupItem(item, mock_download_file, DownloadItem::IN_PROGRESS);
}
TEST_F(DownloadItemTest, DisplayName) {
DownloadItemImpl* item = CreateDownloadItem();
DownloadItemImplDelegate::DownloadTargetCallback callback;
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(item, &callback);
base::FilePath target_path(base::FilePath(kDummyPath).AppendASCII("foo.bar"));
base::FilePath intermediate_path(target_path.InsertBeforeExtensionASCII("x"));
EXPECT_EQ(FILE_PATH_LITERAL(""),
item->GetFileNameToReportUser().value());
EXPECT_CALL(*download_file, RenameAndUniquify(_, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
intermediate_path));
callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path);
RunAllPendingInMessageLoops();
EXPECT_EQ(FILE_PATH_LITERAL("foo.bar"),
item->GetFileNameToReportUser().value());
item->SetDisplayName(base::FilePath(FILE_PATH_LITERAL("new.name")));
EXPECT_EQ(FILE_PATH_LITERAL("new.name"),
item->GetFileNameToReportUser().value());
CleanupItem(item, download_file, DownloadItem::IN_PROGRESS);
}
// Test to make sure that Start method calls DF initialize properly.
TEST_F(DownloadItemTest, Start) {
MockDownloadFile* mock_download_file(new MockDownloadFile);
scoped_ptr<DownloadFile> download_file(mock_download_file);
DownloadItemImpl* item = CreateDownloadItem();
EXPECT_CALL(*mock_download_file, Initialize(_));
scoped_ptr<DownloadRequestHandleInterface> request_handle(
new NiceMock<MockRequestHandle>);
EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _));
item->Start(download_file.Pass(), request_handle.Pass());
RunAllPendingInMessageLoops();
CleanupItem(item, mock_download_file, DownloadItem::IN_PROGRESS);
}
// Test that the delegate is invoked after the download file is renamed.
TEST_F(DownloadItemTest, CallbackAfterRename) {
DownloadItemImpl* item = CreateDownloadItem();
DownloadItemImplDelegate::DownloadTargetCallback callback;
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(item, &callback);
base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar"));
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x"));
base::FilePath new_intermediate_path(
final_path.InsertBeforeExtensionASCII("y"));
EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
new_intermediate_path));
callback.Run(final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path);
RunAllPendingInMessageLoops();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
mock_delegate()->VerifyAndClearExpectations();
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _))
.WillOnce(Return(true));
EXPECT_CALL(*download_file, RenameAndAnnotate(final_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
final_path));
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
RunAllPendingInMessageLoops();
::testing::Mock::VerifyAndClearExpectations(download_file);
mock_delegate()->VerifyAndClearExpectations();
}
// Test that the delegate is invoked after the download file is renamed and the
// download item is in an interrupted state.
TEST_F(DownloadItemTest, CallbackAfterInterruptedRename) {
DownloadItemImpl* item = CreateDownloadItem();
DownloadItemImplDelegate::DownloadTargetCallback callback;
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(item, &callback);
base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar"));
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x"));
base::FilePath new_intermediate_path(
final_path.InsertBeforeExtensionASCII("y"));
EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED,
new_intermediate_path));
EXPECT_CALL(*download_file, Cancel())
.Times(1);
callback.Run(final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path);
RunAllPendingInMessageLoops();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
mock_delegate()->VerifyAndClearExpectations();
}
TEST_F(DownloadItemTest, Interrupted) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
const DownloadInterruptReason reason(
DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
// Confirm interrupt sets state properly.
EXPECT_CALL(*download_file, Cancel());
item->DestinationObserverAsWeakPtr()->DestinationError(reason);
RunAllPendingInMessageLoops();
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
EXPECT_EQ(reason, item->GetLastReason());
// Cancel should kill it.
item->Cancel(true);
EXPECT_EQ(DownloadItem::CANCELLED, item->GetState());
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_USER_CANCELED, item->GetLastReason());
}
// Destination errors that occur before the intermediate rename shouldn't cause
// the download to be marked as interrupted until after the intermediate rename.
TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Restart) {
DownloadItemImpl* item = CreateDownloadItem();
DownloadItemImplDelegate::DownloadTargetCallback callback;
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(item, &callback);
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar"));
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x"));
base::FilePath new_intermediate_path(
final_path.InsertBeforeExtensionASCII("y"));
EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
new_intermediate_path));
EXPECT_CALL(*download_file, Cancel())
.Times(1);
callback.Run(final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path);
RunAllPendingInMessageLoops();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
mock_delegate()->VerifyAndClearExpectations();
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
EXPECT_TRUE(item->GetFullPath().empty());
EXPECT_EQ(final_path, item->GetTargetFilePath());
}
// As above. But if the download can be resumed by continuing, then the
// intermediate path should be retained when the download is interrupted after
// the intermediate rename succeeds.
TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Continue) {
DownloadItemImpl* item = CreateDownloadItem();
DownloadItemImplDelegate::DownloadTargetCallback callback;
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(item, &callback);
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED);
ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar"));
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x"));
base::FilePath new_intermediate_path(
final_path.InsertBeforeExtensionASCII("y"));
EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
new_intermediate_path));
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath(new_intermediate_path)));
EXPECT_CALL(*download_file, Detach());
callback.Run(final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path);
RunAllPendingInMessageLoops();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
mock_delegate()->VerifyAndClearExpectations();
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
EXPECT_EQ(new_intermediate_path, item->GetFullPath());
EXPECT_EQ(final_path, item->GetTargetFilePath());
}
// As above. If the intermediate rename fails, then the interrupt reason should
// be set to the destination error and the intermediate path should be empty.
TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Failed) {
DownloadItemImpl* item = CreateDownloadItem();
DownloadItemImplDelegate::DownloadTargetCallback callback;
MockDownloadFile* download_file =
AddDownloadFileToDownloadItem(item, &callback);
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED);
ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar"));
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x"));
base::FilePath new_intermediate_path(
final_path.InsertBeforeExtensionASCII("y"));
EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED,
new_intermediate_path));
EXPECT_CALL(*download_file, Cancel())
.Times(1);
callback.Run(final_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path);
RunAllPendingInMessageLoops();
// All the callbacks should have happened by now.
::testing::Mock::VerifyAndClearExpectations(download_file);
mock_delegate()->VerifyAndClearExpectations();
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, item->GetLastReason());
EXPECT_TRUE(item->GetFullPath().empty());
EXPECT_EQ(final_path, item->GetTargetFilePath());
}
TEST_F(DownloadItemTest, Canceled) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file = AddDownloadFileToDownloadItem(item, NULL);
// Confirm cancel sets state properly.
EXPECT_CALL(*download_file, Cancel());
item->Cancel(true);
EXPECT_EQ(DownloadItem::CANCELLED, item->GetState());
}
TEST_F(DownloadItemTest, FileRemoved) {
DownloadItemImpl* item = CreateDownloadItem();
EXPECT_FALSE(item->GetFileExternallyRemoved());
item->OnDownloadedFileRemoved();
EXPECT_TRUE(item->GetFileExternallyRemoved());
}
TEST_F(DownloadItemTest, DestinationUpdate) {
DownloadItemImpl* item = CreateDownloadItem();
base::WeakPtr<DownloadDestinationObserver> as_observer(
item->DestinationObserverAsWeakPtr());
TestDownloadItemObserver observer(item);
EXPECT_EQ(0l, item->CurrentSpeed());
EXPECT_EQ("", item->GetHashState());
EXPECT_EQ(0l, item->GetReceivedBytes());
EXPECT_EQ(0l, item->GetTotalBytes());
EXPECT_FALSE(observer.CheckAndResetDownloadUpdated());
item->SetTotalBytes(100l);
EXPECT_EQ(100l, item->GetTotalBytes());
as_observer->DestinationUpdate(10, 20, "deadbeef");
EXPECT_EQ(20l, item->CurrentSpeed());
EXPECT_EQ("deadbeef", item->GetHashState());
EXPECT_EQ(10l, item->GetReceivedBytes());
EXPECT_EQ(100l, item->GetTotalBytes());
EXPECT_TRUE(observer.CheckAndResetDownloadUpdated());
as_observer->DestinationUpdate(200, 20, "livebeef");
EXPECT_EQ(20l, item->CurrentSpeed());
EXPECT_EQ("livebeef", item->GetHashState());
EXPECT_EQ(200l, item->GetReceivedBytes());
EXPECT_EQ(0l, item->GetTotalBytes());
EXPECT_TRUE(observer.CheckAndResetDownloadUpdated());
}
TEST_F(DownloadItemTest, DestinationError) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
base::WeakPtr<DownloadDestinationObserver> as_observer(
item->DestinationObserverAsWeakPtr());
TestDownloadItemObserver observer(item);
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, item->GetLastReason());
EXPECT_FALSE(observer.CheckAndResetDownloadUpdated());
EXPECT_CALL(*download_file, Cancel());
as_observer->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
mock_delegate()->VerifyAndClearExpectations();
EXPECT_TRUE(observer.CheckAndResetDownloadUpdated());
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED,
item->GetLastReason());
}
TEST_F(DownloadItemTest, DestinationCompleted) {
DownloadItemImpl* item = CreateDownloadItem();
base::WeakPtr<DownloadDestinationObserver> as_observer(
item->DestinationObserverAsWeakPtr());
TestDownloadItemObserver observer(item);
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_EQ("", item->GetHash());
EXPECT_EQ("", item->GetHashState());
EXPECT_FALSE(item->AllDataSaved());
EXPECT_FALSE(observer.CheckAndResetDownloadUpdated());
as_observer->DestinationUpdate(10, 20, "deadbeef");
EXPECT_TRUE(observer.CheckAndResetDownloadUpdated());
EXPECT_FALSE(observer.CheckAndResetDownloadUpdated()); // Confirm reset.
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_EQ("", item->GetHash());
EXPECT_EQ("deadbeef", item->GetHashState());
EXPECT_FALSE(item->AllDataSaved());
as_observer->DestinationCompleted("livebeef");
mock_delegate()->VerifyAndClearExpectations();
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_TRUE(observer.CheckAndResetDownloadUpdated());
EXPECT_EQ("livebeef", item->GetHash());
EXPECT_EQ("", item->GetHashState());
EXPECT_TRUE(item->AllDataSaved());
}
TEST_F(DownloadItemTest, EnabledActionsForNormalDownload) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// InProgress
ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
ASSERT_FALSE(item->GetTargetFilePath().empty());
EXPECT_TRUE(item->CanShowInFolder());
EXPECT_TRUE(item->CanOpenDownload());
// Complete
EXPECT_CALL(*download_file, RenameAndAnnotate(_, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
base::FilePath(kDummyPath)));
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _))
.WillOnce(Return(true));
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
RunAllPendingInMessageLoops();
ASSERT_EQ(DownloadItem::COMPLETE, item->GetState());
EXPECT_TRUE(item->CanShowInFolder());
EXPECT_TRUE(item->CanOpenDownload());
}
TEST_F(DownloadItemTest, EnabledActionsForTemporaryDownload) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
item->SetIsTemporary(true);
// InProgress Temporary
ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
ASSERT_FALSE(item->GetTargetFilePath().empty());
ASSERT_TRUE(item->IsTemporary());
EXPECT_FALSE(item->CanShowInFolder());
EXPECT_FALSE(item->CanOpenDownload());
// Complete Temporary
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _))
.WillOnce(Return(true));
EXPECT_CALL(*download_file, RenameAndAnnotate(_, _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
base::FilePath(kDummyPath)));
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
RunAllPendingInMessageLoops();
ASSERT_EQ(DownloadItem::COMPLETE, item->GetState());
EXPECT_FALSE(item->CanShowInFolder());
EXPECT_FALSE(item->CanOpenDownload());
}
TEST_F(DownloadItemTest, EnabledActionsForInterruptedDownload) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
EXPECT_CALL(*download_file, Cancel());
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
RunAllPendingInMessageLoops();
ASSERT_EQ(DownloadItem::INTERRUPTED, item->GetState());
ASSERT_FALSE(item->GetTargetFilePath().empty());
EXPECT_FALSE(item->CanShowInFolder());
EXPECT_FALSE(item->CanOpenDownload());
}
TEST_F(DownloadItemTest, EnabledActionsForCancelledDownload) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
EXPECT_CALL(*download_file, Cancel());
item->Cancel(true);
RunAllPendingInMessageLoops();
ASSERT_EQ(DownloadItem::CANCELLED, item->GetState());
EXPECT_FALSE(item->CanShowInFolder());
EXPECT_FALSE(item->CanOpenDownload());
}
// Test various aspects of the delegate completion blocker.
// Just allowing completion.
TEST_F(DownloadItemTest, CompleteDelegate_ReturnTrue) {
// Test to confirm that if we have a callback that returns true,
// we complete immediately.
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// Drive the delegate interaction.
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _))
.WillOnce(Return(true));
item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_FALSE(item->IsDangerous());
// Make sure the download can complete.
EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
base::FilePath(kDummyPath)));
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _))
.WillOnce(Return(true));
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
RunAllPendingInMessageLoops();
EXPECT_EQ(DownloadItem::COMPLETE, item->GetState());
}
// Just delaying completion.
TEST_F(DownloadItemTest, CompleteDelegate_BlockOnce) {
// Test to confirm that if we have a callback that returns true,
// we complete immediately.
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// Drive the delegate interaction.
base::Closure delegate_callback;
base::Closure copy_delegate_callback;
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _))
.WillOnce(DoAll(SaveArg<1>(&delegate_callback),
Return(false)))
.WillOnce(Return(true));
item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
ASSERT_FALSE(delegate_callback.is_null());
copy_delegate_callback = delegate_callback;
delegate_callback.Reset();
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
copy_delegate_callback.Run();
ASSERT_TRUE(delegate_callback.is_null());
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_FALSE(item->IsDangerous());
// Make sure the download can complete.
EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
base::FilePath(kDummyPath)));
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _))
.WillOnce(Return(true));
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
RunAllPendingInMessageLoops();
EXPECT_EQ(DownloadItem::COMPLETE, item->GetState());
}
// Delay and set danger.
TEST_F(DownloadItemTest, CompleteDelegate_SetDanger) {
// Test to confirm that if we have a callback that returns true,
// we complete immediately.
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// Drive the delegate interaction.
base::Closure delegate_callback;
base::Closure copy_delegate_callback;
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _))
.WillOnce(DoAll(SaveArg<1>(&delegate_callback),
Return(false)))
.WillOnce(Return(true));
item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
ASSERT_FALSE(delegate_callback.is_null());
copy_delegate_callback = delegate_callback;
delegate_callback.Reset();
EXPECT_FALSE(item->IsDangerous());
item->OnContentCheckCompleted(
content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE);
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
copy_delegate_callback.Run();
ASSERT_TRUE(delegate_callback.is_null());
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_TRUE(item->IsDangerous());
// Make sure the download doesn't complete until we've validated it.
EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
base::FilePath(kDummyPath)));
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _))
.WillOnce(Return(true));
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
RunAllPendingInMessageLoops();
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_TRUE(item->IsDangerous());
item->ValidateDangerousDownload();
EXPECT_EQ(DOWNLOAD_DANGER_TYPE_USER_VALIDATED, item->GetDangerType());
RunAllPendingInMessageLoops();
EXPECT_EQ(DownloadItem::COMPLETE, item->GetState());
}
// Just delaying completion twice.
TEST_F(DownloadItemTest, CompleteDelegate_BlockTwice) {
// Test to confirm that if we have a callback that returns true,
// we complete immediately.
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
// Drive the delegate interaction.
base::Closure delegate_callback;
base::Closure copy_delegate_callback;
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _))
.WillOnce(DoAll(SaveArg<1>(&delegate_callback),
Return(false)))
.WillOnce(DoAll(SaveArg<1>(&delegate_callback),
Return(false)))
.WillOnce(Return(true));
item->DestinationObserverAsWeakPtr()->DestinationCompleted(std::string());
ASSERT_FALSE(delegate_callback.is_null());
copy_delegate_callback = delegate_callback;
delegate_callback.Reset();
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
copy_delegate_callback.Run();
ASSERT_FALSE(delegate_callback.is_null());
copy_delegate_callback = delegate_callback;
delegate_callback.Reset();
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
copy_delegate_callback.Run();
ASSERT_TRUE(delegate_callback.is_null());
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState());
EXPECT_FALSE(item->IsDangerous());
// Make sure the download can complete.
EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _))
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE,
base::FilePath(kDummyPath)));
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _))
.WillOnce(Return(true));
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(base::FilePath()));
EXPECT_CALL(*download_file, Detach());
RunAllPendingInMessageLoops();
EXPECT_EQ(DownloadItem::COMPLETE, item->GetState());
}
TEST_F(DownloadItemTest, StealDangerousDownload) {
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE);
ASSERT_TRUE(item->IsDangerous());
base::FilePath full_path(FILE_PATH_LITERAL("foo.txt"));
base::FilePath returned_path;
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(full_path));
EXPECT_CALL(*download_file, Detach());
EXPECT_CALL(*mock_delegate(), DownloadRemoved(_));
base::WeakPtrFactory<DownloadItemTest> weak_ptr_factory(this);
item->StealDangerousDownload(
base::Bind(&DownloadItemTest::OnDownloadFileAcquired,
weak_ptr_factory.GetWeakPtr(),
base::Unretained(&returned_path)));
RunAllPendingInMessageLoops();
EXPECT_EQ(full_path, returned_path);
}
TEST_F(DownloadItemTest, StealInterruptedDangerousDownload) {
base::FilePath returned_path;
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE);
base::FilePath full_path = item->GetFullPath();
EXPECT_FALSE(full_path.empty());
EXPECT_CALL(*download_file, FullPath())
.WillOnce(Return(full_path));
EXPECT_CALL(*download_file, Detach());
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED);
ASSERT_TRUE(item->IsDangerous());
EXPECT_CALL(*mock_delegate(), DownloadRemoved(_));
base::WeakPtrFactory<DownloadItemTest> weak_ptr_factory(this);
item->StealDangerousDownload(
base::Bind(&DownloadItemTest::OnDownloadFileAcquired,
weak_ptr_factory.GetWeakPtr(),
base::Unretained(&returned_path)));
RunAllPendingInMessageLoops();
EXPECT_EQ(full_path, returned_path);
}
TEST_F(DownloadItemTest, StealInterruptedNonResumableDangerousDownload) {
base::FilePath returned_path;
DownloadItemImpl* item = CreateDownloadItem();
MockDownloadFile* download_file =
DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE);
EXPECT_CALL(*download_file, Cancel());
item->DestinationObserverAsWeakPtr()->DestinationError(
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
ASSERT_TRUE(item->IsDangerous());
EXPECT_CALL(*mock_delegate(), DownloadRemoved(_));
base::WeakPtrFactory<DownloadItemTest> weak_ptr_factory(this);
item->StealDangerousDownload(
base::Bind(&DownloadItemTest::OnDownloadFileAcquired,
weak_ptr_factory.GetWeakPtr(),
base::Unretained(&returned_path)));
RunAllPendingInMessageLoops();
EXPECT_TRUE(returned_path.empty());
}
TEST(MockDownloadItem, Compiles) {
MockDownloadItem mock_item;
}
} // namespace content