Commit d25fda1c authored by rdsmith@chromium.org's avatar rdsmith@chromium.org

Rewrite download manager unit to be actual unit tests.

Note that this isn't intended to be complete coverage; it's an attempt to preserve coverage from the old tests while making these "real" unit tests, i.e. with every class except for the main one being tested mocked.  Thorough unit tests are intended for the future after we're more completely done with refactoring.  

BUG=107264
BUG=105200
BUG=110886

Review URL: https://chromiumcodereview.appspot.com/10344024

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@141674 0039d316-1c4b-4281-b951-d872f2087c98
parent aa54218b
...@@ -6,9 +6,11 @@ ...@@ -6,9 +6,11 @@
#include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/dom_storage/dom_storage_context_impl.h" #include "content/browser/dom_storage/dom_storage_context_impl.h"
#include "content/browser/download/download_file_manager.h"
#include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_manager_impl.h"
#include "content/browser/fileapi/browser_file_system_helper.h" #include "content/browser/fileapi/browser_file_system_helper.h"
#include "content/browser/in_process_webkit/indexed_db_context_impl.h" #include "content/browser/in_process_webkit/indexed_db_context_impl.h"
#include "content/browser/renderer_host/resource_dispatcher_host_impl.h"
#include "content/browser/resource_context_impl.h" #include "content/browser/resource_context_impl.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h" #include "content/public/browser/content_browser_client.h"
...@@ -75,7 +77,7 @@ void CreateQuotaManagerAndClients(BrowserContext* context) { ...@@ -75,7 +77,7 @@ void CreateQuotaManagerAndClients(BrowserContext* context) {
context->GetPath(), context->IsOffTheRecord(), context->GetPath(), context->IsOffTheRecord(),
context->GetSpecialStoragePolicy(), quota_manager->proxy(), context->GetSpecialStoragePolicy(), quota_manager->proxy(),
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
context->SetUserData(kDatabaseTrackerKeyName, context->SetUserData(kDatabaseTrackerKeyName,
new UserDataAdapter<DatabaseTracker>(db_tracker)); new UserDataAdapter<DatabaseTracker>(db_tracker));
FilePath path = context->IsOffTheRecord() ? FilePath() : context->GetPath(); FilePath path = context->IsOffTheRecord() ? FilePath() : context->GetPath();
...@@ -142,8 +144,16 @@ DOMStorageContextImpl* GetDOMStorageContextImpl(BrowserContext* context) { ...@@ -142,8 +144,16 @@ DOMStorageContextImpl* GetDOMStorageContextImpl(BrowserContext* context) {
DownloadManager* BrowserContext::GetDownloadManager( DownloadManager* BrowserContext::GetDownloadManager(
BrowserContext* context) { BrowserContext* context) {
if (!context->GetUserData(kDownloadManagerKeyName)) { if (!context->GetUserData(kDownloadManagerKeyName)) {
scoped_refptr<DownloadManager> download_manager = new DownloadManagerImpl( ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get();
GetContentClient()->browser()->GetNetLog()); DCHECK(rdh);
DownloadFileManager* file_manager = rdh->download_file_manager();
DCHECK(file_manager);
scoped_refptr<DownloadManager> download_manager =
new DownloadManagerImpl(
file_manager,
scoped_ptr<DownloadItemFactory>(),
GetContentClient()->browser()->GetNetLog());
context->SetUserData( context->SetUserData(
kDownloadManagerKeyName, kDownloadManagerKeyName,
new UserDataAdapter<DownloadManager>(download_manager)); new UserDataAdapter<DownloadManager>(download_manager));
......
// 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.
//
// The DownloadItemFactory is used to produce different DownloadItems.
// It is separate from the DownloadManager to allow download manager
// unit tests to control the items produced.
#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_ITEM_FACTORY_H_
#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_ITEM_FACTORY_H_
#pragma once
#include "content/browser/download/download_item_impl.h"
#include <string>
struct DownloadCreateInfo;
class DownloadRequestHandleInterface;
class FilePath;
class GURL;
namespace content {
class DownloadId;
class DownloadItem;
struct DownloadPersistentStoreInfo;
}
namespace net {
class BoundNetLog;
}
namespace content {
class DownloadItemFactory {
public:
virtual ~DownloadItemFactory() {}
virtual content::DownloadItem* CreatePersistedItem(
DownloadItemImpl::Delegate* delegate,
content::DownloadId download_id,
const content::DownloadPersistentStoreInfo& info,
const net::BoundNetLog& bound_net_log) = 0;
virtual content::DownloadItem* CreateActiveItem(
DownloadItemImpl::Delegate* delegate,
const DownloadCreateInfo& info,
DownloadRequestHandleInterface* request_handle,
bool is_otr,
const net::BoundNetLog& bound_net_log) = 0;
virtual content::DownloadItem* CreateSavePageItem(
DownloadItemImpl::Delegate* delegate,
const FilePath& path,
const GURL& url,
bool is_otr,
content::DownloadId download_id,
const std::string& mime_type,
const net::BoundNetLog& bound_net_log) = 0;
};
} // namespace content
#endif // CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_ITEM_FACTORY_H_
...@@ -551,6 +551,47 @@ TEST_F(DownloadItemTest, CallbackAfterRenameToIntermediateName) { ...@@ -551,6 +551,47 @@ TEST_F(DownloadItemTest, CallbackAfterRenameToIntermediateName) {
::testing::Mock::VerifyAndClearExpectations(mock_delegate()); ::testing::Mock::VerifyAndClearExpectations(mock_delegate());
} }
TEST_F(DownloadItemTest, Interrupted) {
DownloadItem* item = CreateDownloadItem(DownloadItem::IN_PROGRESS);
int64 size = 1022;
const std::string hash_state("Live beef");
const content::DownloadInterruptReason reason(
content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
// Confirm interrupt sets state properly.
item->Interrupted(size, hash_state, reason);
EXPECT_EQ(size, item->GetReceivedBytes());
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
EXPECT_EQ(hash_state, item->GetHashState());
EXPECT_EQ(reason, item->GetLastReason());
// Cancel should result in no change.
item->Cancel(true);
EXPECT_EQ(size, item->GetReceivedBytes());
EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState());
EXPECT_EQ(hash_state, item->GetHashState());
EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED,
item->GetLastReason());
}
TEST_F(DownloadItemTest, Canceled) {
DownloadItem* item = CreateDownloadItem(DownloadItem::IN_PROGRESS);
// Confirm cancel sets state properly.
EXPECT_CALL(*mock_delegate(), DownloadCancelled(item));
item->Cancel(true);
EXPECT_EQ(DownloadItem::CANCELLED, item->GetState());
}
TEST_F(DownloadItemTest, FileRemoved) {
DownloadItem* item = CreateDownloadItem(DownloadItem::IN_PROGRESS);
EXPECT_EQ(false, item->GetFileExternallyRemoved());
item->OnDownloadedFileRemoved();
EXPECT_EQ(true, item->GetFileExternallyRemoved());
}
TEST(MockDownloadItem, Compiles) { TEST(MockDownloadItem, Compiles) {
MockDownloadItem mock_item; MockDownloadItem mock_item;
} }
...@@ -136,6 +136,42 @@ void EnsureNoPendingDownloadJobsOnIO(bool* result) { ...@@ -136,6 +136,42 @@ void EnsureNoPendingDownloadJobsOnIO(bool* result) {
download_file_manager, result)); download_file_manager, result));
} }
class DownloadItemFactoryImpl : public content::DownloadItemFactory {
public:
DownloadItemFactoryImpl() {}
virtual ~DownloadItemFactoryImpl() {}
virtual content::DownloadItem* CreatePersistedItem(
DownloadItemImpl::Delegate* delegate,
content::DownloadId download_id,
const content::DownloadPersistentStoreInfo& info,
const net::BoundNetLog& bound_net_log) OVERRIDE {
return new DownloadItemImpl(delegate, download_id, info, bound_net_log);
}
virtual content::DownloadItem* CreateActiveItem(
DownloadItemImpl::Delegate* delegate,
const DownloadCreateInfo& info,
DownloadRequestHandleInterface* request_handle,
bool is_otr,
const net::BoundNetLog& bound_net_log) OVERRIDE {
return new DownloadItemImpl(delegate, info, request_handle, is_otr,
bound_net_log);
}
virtual content::DownloadItem* CreateSavePageItem(
DownloadItemImpl::Delegate* delegate,
const FilePath& path,
const GURL& url,
bool is_otr,
content::DownloadId download_id,
const std::string& mime_type,
const net::BoundNetLog& bound_net_log) OVERRIDE {
return new DownloadItemImpl(delegate, path, url, is_otr, download_id,
mime_type, bound_net_log);
}
};
} // namespace } // namespace
namespace content { namespace content {
...@@ -152,12 +188,18 @@ bool DownloadManager::EnsureNoPendingDownloadsForTesting() { ...@@ -152,12 +188,18 @@ bool DownloadManager::EnsureNoPendingDownloadsForTesting() {
} // namespace content } // namespace content
DownloadManagerImpl::DownloadManagerImpl(net::NetLog* net_log) DownloadManagerImpl::DownloadManagerImpl(
: shutdown_needed_(false), DownloadFileManager* file_manager,
scoped_ptr<content::DownloadItemFactory> factory,
net::NetLog* net_log)
: factory_(factory.Pass()),
shutdown_needed_(false),
browser_context_(NULL), browser_context_(NULL),
file_manager_(NULL), file_manager_(file_manager),
delegate_(NULL),
net_log_(net_log) { net_log_(net_log) {
DCHECK(file_manager);
if (!factory_.get())
factory_.reset(new DownloadItemFactoryImpl());
} }
DownloadManagerImpl::~DownloadManagerImpl() { DownloadManagerImpl::~DownloadManagerImpl() {
...@@ -209,11 +251,11 @@ void DownloadManagerImpl::Shutdown() { ...@@ -209,11 +251,11 @@ void DownloadManagerImpl::Shutdown() {
FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown(this)); FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown(this));
// TODO(benjhayden): Consider clearing observers_. // TODO(benjhayden): Consider clearing observers_.
if (file_manager_) { DCHECK(file_manager_);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, BrowserThread::PostTask(
base::Bind(&DownloadFileManager::OnDownloadManagerShutdown, BrowserThread::FILE, FROM_HERE,
file_manager_, make_scoped_refptr(this))); base::Bind(&DownloadFileManager::OnDownloadManagerShutdown,
} file_manager_, make_scoped_refptr(this)));
AssertContainersConsistent(); AssertContainersConsistent();
...@@ -316,7 +358,6 @@ void DownloadManagerImpl::SearchDownloads(const string16& query, ...@@ -316,7 +358,6 @@ void DownloadManagerImpl::SearchDownloads(const string16& query,
} }
} }
// Query the history service for information about all persisted downloads.
bool DownloadManagerImpl::Init(content::BrowserContext* browser_context) { bool DownloadManagerImpl::Init(content::BrowserContext* browser_context) {
DCHECK(browser_context); DCHECK(browser_context);
DCHECK(!shutdown_needed_) << "DownloadManager already initialized."; DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
...@@ -324,15 +365,6 @@ bool DownloadManagerImpl::Init(content::BrowserContext* browser_context) { ...@@ -324,15 +365,6 @@ bool DownloadManagerImpl::Init(content::BrowserContext* browser_context) {
browser_context_ = browser_context; browser_context_ = browser_context;
// In test mode, there may be no ResourceDispatcherHostImpl. In this case
// it's safe to avoid setting |file_manager_| because we only call a small
// set of functions, none of which need it.
ResourceDispatcherHostImpl* rdh = ResourceDispatcherHostImpl::Get();
if (rdh) {
file_manager_ = rdh->download_file_manager();
DCHECK(file_manager_);
}
return true; return true;
} }
...@@ -425,8 +457,9 @@ net::BoundNetLog DownloadManagerImpl::CreateDownloadItem( ...@@ -425,8 +457,9 @@ net::BoundNetLog DownloadManagerImpl::CreateDownloadItem(
net::BoundNetLog bound_net_log = net::BoundNetLog bound_net_log =
net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD); net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD);
info->download_id = GetNextId(); if (!info->download_id.IsValid())
DownloadItem* download = new DownloadItemImpl( info->download_id = GetNextId();
DownloadItem* download = factory_->CreateActiveItem(
this, *info, new DownloadRequestHandle(request_handle), this, *info, new DownloadRequestHandle(request_handle),
browser_context_->IsOffTheRecord(), bound_net_log); browser_context_->IsOffTheRecord(), bound_net_log);
int32 download_id = info->download_id.local(); int32 download_id = info->download_id.local();
...@@ -446,7 +479,7 @@ DownloadItem* DownloadManagerImpl::CreateSavePackageDownloadItem( ...@@ -446,7 +479,7 @@ DownloadItem* DownloadManagerImpl::CreateSavePackageDownloadItem(
DownloadItem::Observer* observer) { DownloadItem::Observer* observer) {
net::BoundNetLog bound_net_log = net::BoundNetLog bound_net_log =
net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD); net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD);
DownloadItem* download = new DownloadItemImpl( DownloadItem* download = factory_->CreateSavePageItem(
this, this,
main_file_path, main_file_path,
page_url, page_url,
...@@ -680,8 +713,8 @@ void DownloadManagerImpl::DownloadCancelled(DownloadItem* download) { ...@@ -680,8 +713,8 @@ void DownloadManagerImpl::DownloadCancelled(DownloadItem* download) {
// should already have been updated. // should already have been updated.
AssertStateConsistent(download); AssertStateConsistent(download);
if (file_manager_) DCHECK(file_manager_);
download->OffThreadCancel(file_manager_); download->OffThreadCancel(file_manager_);
} }
void DownloadManagerImpl::OnDownloadInterrupted( void DownloadManagerImpl::OnDownloadInterrupted(
...@@ -880,7 +913,7 @@ void DownloadManagerImpl::OnPersistentStoreQueryComplete( ...@@ -880,7 +913,7 @@ void DownloadManagerImpl::OnPersistentStoreQueryComplete(
net::BoundNetLog bound_net_log = net::BoundNetLog bound_net_log =
net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD); net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD);
DownloadItem* download = new DownloadItemImpl( DownloadItem* download = factory_->CreatePersistedItem(
this, GetNextId(), entries->at(i), bound_net_log); this, GetNextId(), entries->at(i), bound_net_log);
downloads_.insert(download); downloads_.insert(download);
history_downloads_[download->GetDbHandle()] = download; history_downloads_[download->GetDbHandle()] = download;
...@@ -1150,8 +1183,3 @@ void DownloadManagerImpl::DownloadRenamedToFinalName( ...@@ -1150,8 +1183,3 @@ void DownloadManagerImpl::DownloadRenamedToFinalName(
download, download->GetFullPath()); download, download->GetFullPath());
} }
} }
void DownloadManagerImpl::SetFileManagerForTesting(
DownloadFileManager* file_manager) {
file_manager_ = file_manager;
}
// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
//
#ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_IMPL_H_ #ifndef CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_IMPL_H_
#define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_IMPL_H_ #define CONTENT_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_IMPL_H_
...@@ -17,15 +16,25 @@ ...@@ -17,15 +16,25 @@
#include "base/message_loop_helpers.h" #include "base/message_loop_helpers.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "content/browser/download/download_item_factory.h"
#include "content/browser/download/download_item_impl.h" #include "content/browser/download/download_item_impl.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/download_manager.h" #include "content/public/browser/download_manager.h"
class DownloadFileManager;
class CONTENT_EXPORT DownloadManagerImpl class CONTENT_EXPORT DownloadManagerImpl
: public content::DownloadManager, : public content::DownloadManager,
public DownloadItemImpl::Delegate { public DownloadItemImpl::Delegate {
public: public:
explicit DownloadManagerImpl(net::NetLog* net_log); // Caller guarantees that |file_manager| and |net_log| will remain valid
// for the lifetime of DownloadManagerImpl (until Shutdown() is called).
// |factory| may be a default constructed (null) scoped_ptr; if so,
// the DownloadManagerImpl creates and takes ownership of the
// default DownloadItemFactory.
DownloadManagerImpl(DownloadFileManager* file_manager,
scoped_ptr<content::DownloadItemFactory> factory,
net::NetLog* net_log);
// content::DownloadManager functions. // content::DownloadManager functions.
virtual void SetDelegate(content::DownloadManagerDelegate* delegate) OVERRIDE; virtual void SetDelegate(content::DownloadManagerDelegate* delegate) OVERRIDE;
...@@ -109,9 +118,6 @@ class CONTENT_EXPORT DownloadManagerImpl ...@@ -109,9 +118,6 @@ class CONTENT_EXPORT DownloadManagerImpl
virtual void AssertStateConsistent( virtual void AssertStateConsistent(
content::DownloadItem* download) const OVERRIDE; content::DownloadItem* download) const OVERRIDE;
// For unit tests only.
void SetFileManagerForTesting(DownloadFileManager* file_manager);
private: private:
typedef std::set<content::DownloadItem*> DownloadSet; typedef std::set<content::DownloadItem*> DownloadSet;
typedef base::hash_map<int64, content::DownloadItem*> DownloadMap; typedef base::hash_map<int64, content::DownloadItem*> DownloadMap;
...@@ -179,6 +185,9 @@ class CONTENT_EXPORT DownloadManagerImpl ...@@ -179,6 +185,9 @@ class CONTENT_EXPORT DownloadManagerImpl
// Called when Save Page As entry is committed to the persistent store. // Called when Save Page As entry is committed to the persistent store.
void OnSavePageItemAddedToPersistentStore(int32 download_id, int64 db_handle); void OnSavePageItemAddedToPersistentStore(int32 download_id, int64 db_handle);
// Factory for creation of downloads items.
scoped_ptr<content::DownloadItemFactory> factory_;
// |downloads_| is the owning set for all downloads known to the // |downloads_| is the owning set for all downloads known to the
// DownloadManager. This includes downloads started by the user in // DownloadManager. This includes downloads started by the user in
// this session, downloads initialized from the history system, and // this session, downloads initialized from the history system, and
......
...@@ -8,62 +8,43 @@ ...@@ -8,62 +8,43 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/file_util.h" #include "base/file_util.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h" #include "base/message_loop.h"
#include "base/memory/scoped_ptr.h"
#include "base/scoped_temp_dir.h" #include "base/scoped_temp_dir.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/string16.h" #include "base/string16.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "content/browser/download/byte_stream.h"
#include "content/browser/download/download_create_info.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_file_manager.h"
#include "content/browser/download/download_item_factory.h"
#include "content/browser/download/download_manager_impl.h" #include "content/browser/download/download_manager_impl.h"
#include "content/browser/download/download_request_handle.h" #include "content/browser/download/download_request_handle.h"
#include "content/browser/download/mock_download_file.h" #include "content/browser/download/mock_download_file.h"
#include "content/browser/power_save_blocker.h" #include "content/public/browser/browser_context.h"
#include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_interrupt_reasons.h"
#include "content/public/browser/download_item.h" #include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager_delegate.h" #include "content/public/browser/download_manager_delegate.h"
#include "content/public/test/mock_download_manager.h" #include "content/public/test/mock_download_item.h"
#include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread.h" #include "content/public/test/test_browser_thread.h"
#include "net/base/io_buffer.h"
#include "net/base/net_util.h" #include "net/base/net_util.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gmock_mutant.h" #include "testing/gmock_mutant.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#if defined(USE_AURA) && defined(OS_WIN) using ::testing::DoAll;
// http://crbug.com/105200 using ::testing::Ref;
#define MAYBE_StartDownload DISABLED_StartDownload using ::testing::Return;
#define MAYBE_DownloadOverwriteTest DISABLED_DownloadOverwriteTest using ::testing::ReturnRef;
#define MAYBE_DownloadRemoveTest DISABLED_DownloadRemoveTest using ::testing::SetArgPointee;
#define MAYBE_DownloadFileErrorTest DownloadFileErrorTest using ::testing::StrictMock;
#elif defined(OS_LINUX) using ::testing::_;
// http://crbug.com/110886 for Linux
#define MAYBE_StartDownload DISABLED_StartDownload
#define MAYBE_DownloadOverwriteTest DISABLED_DownloadOverwriteTest
#define MAYBE_DownloadRemoveTest DISABLED_DownloadRemoveTest
#define MAYBE_DownloadFileErrorTest DISABLED_DownloadFileErrorTest
#else
#define MAYBE_StartDownload StartDownload
#define MAYBE_DownloadOverwriteTest DownloadOverwriteTest
#define MAYBE_DownloadRemoveTest DownloadRemoveTest
#define MAYBE_DownloadFileErrorTest DownloadFileErrorTest
#endif
using content::BrowserContext;
using content::BrowserThread;
using content::DownloadFile;
using content::DownloadId;
using content::DownloadItem; using content::DownloadItem;
using content::DownloadManager; using content::DownloadManager;
using content::WebContents; using content::WebContents;
using ::testing::NiceMock;
using ::testing::ReturnRef;
using ::testing::Return;
namespace content { namespace content {
class ByteStreamReader; class ByteStreamReader;
...@@ -71,1191 +52,526 @@ class ByteStreamReader; ...@@ -71,1191 +52,526 @@ class ByteStreamReader;
namespace { namespace {
FilePath GetTempDownloadPath(const FilePath& suggested_path) { class MockDownloadManagerDelegate : public content::DownloadManagerDelegate {
return FilePath(suggested_path.value() + FILE_PATH_LITERAL(".temp"));
}
class MockDownloadFileFactory
: public DownloadFileManager::DownloadFileFactory {
public: public:
MockDownloadFileFactory() {} MockDownloadManagerDelegate();
virtual ~MockDownloadManagerDelegate();
virtual DownloadFile* CreateFile(
DownloadCreateInfo* info, MOCK_METHOD0(Shutdown, void());
scoped_ptr<content::ByteStreamReader> stream, MOCK_METHOD0(GetNextId, content::DownloadId());
const DownloadRequestHandle& request_handle, MOCK_METHOD1(ShouldStartDownload, bool(int32));
DownloadManager* download_manager, MOCK_METHOD1(ChooseDownloadPath, void(DownloadItem*));
bool calculate_hash, MOCK_METHOD2(GetIntermediatePath, FilePath(const DownloadItem&, bool*));
const net::BoundNetLog& bound_net_log) OVERRIDE; MOCK_METHOD0(GetAlternativeWebContentsToNotifyForDownload, WebContents*());
MOCK_METHOD1(ShouldOpenFileBasedOnExtension, bool(const FilePath&));
MOCK_METHOD2(ShouldCompleteDownload, bool(
DownloadItem*, const base::Closure&));
MOCK_METHOD1(ShouldOpenDownload, bool(DownloadItem*));
MOCK_METHOD0(GenerateFileHash, bool());
MOCK_METHOD1(AddItemToPersistentStore, void(DownloadItem*));
MOCK_METHOD1(UpdateItemInPersistentStore, void(DownloadItem*));
MOCK_METHOD2(UpdatePathForItemInPersistentStore,
void(DownloadItem*, const FilePath&));
MOCK_METHOD1(RemoveItemFromPersistentStore, void(DownloadItem*));
MOCK_METHOD2(RemoveItemsFromPersistentStoreBetween, void(
base::Time remove_begin, base::Time remove_end));
MOCK_METHOD4(GetSaveDir, void(WebContents*, FilePath*, FilePath*, bool*));
MOCK_METHOD5(ChooseSavePath, void(
WebContents*, const FilePath&, const FilePath::StringType&,
bool, const content::SavePackagePathPickedCallback&));
}; };
DownloadFile* MockDownloadFileFactory::CreateFile( MockDownloadManagerDelegate::MockDownloadManagerDelegate() { }
DownloadCreateInfo* info,
scoped_ptr<content::ByteStreamReader> stream,
const DownloadRequestHandle& request_handle,
DownloadManager* download_manager,
bool calculate_hash,
const net::BoundNetLog& bound_net_log) {
NOTREACHED();
return NULL;
}
class TestDownloadManagerDelegate : public content::DownloadManagerDelegate { MockDownloadManagerDelegate::~MockDownloadManagerDelegate() { }
public:
explicit TestDownloadManagerDelegate(content::DownloadManager* dm)
: mark_content_dangerous_(false),
prompt_user_for_save_location_(false),
should_complete_download_(true),
download_manager_(dm) {
}
void set_download_directory(const FilePath& path) { class MockDownloadFileManager : public DownloadFileManager {
download_directory_ = path; public:
} MockDownloadFileManager();
void set_prompt_user_for_save_location(bool value) { MOCK_METHOD0(Shutdown, void());
prompt_user_for_save_location_ = value; MOCK_METHOD3(MockStartDownload,
} content::DownloadId(DownloadCreateInfo*,
content::ByteStreamReader* stream,
const DownloadRequestHandle&));
virtual bool ShouldStartDownload(int32 download_id) OVERRIDE { virtual content::DownloadId StartDownload(
DownloadItem* item = download_manager_->GetActiveDownloadItem(download_id); scoped_ptr<DownloadCreateInfo> info,
FilePath path = net::GenerateFileName(item->GetURL(), scoped_ptr<content::ByteStreamReader> stream,
item->GetContentDisposition(), const DownloadRequestHandle& request_handle) OVERRIDE {
item->GetReferrerCharset(), return MockStartDownload(info.get(), stream.get(), request_handle);
item->GetSuggestedFilename(), }
item->GetMimeType(),
std::string()); MOCK_METHOD1(CancelDownload, void(content::DownloadId));
DownloadItem::TargetDisposition disposition = item->GetTargetDisposition(); MOCK_METHOD1(CompleteDownload, void(content::DownloadId));
if (!ShouldOpenFileBasedOnExtension(path) && prompt_user_for_save_location_) MOCK_METHOD1(OnDownloadManagerShutdown, void(content::DownloadManager*));
disposition = DownloadItem::TARGET_DISPOSITION_PROMPT; MOCK_METHOD4(RenameInProgressDownloadFile,
item->OnTargetPathDetermined(download_directory_.Append(path), void(content::DownloadId,
disposition, const FilePath&,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); bool,
return true; const RenameCompletionCallback&));
} MOCK_METHOD4(RenameCompletingDownloadFile,
void(content::DownloadId,
const FilePath&,
bool,
const RenameCompletionCallback&));
MOCK_CONST_METHOD0(NumberOfActiveDownloads, int());
protected:
virtual ~MockDownloadFileManager();
};
virtual FilePath GetIntermediatePath(const DownloadItem& item, MockDownloadFileManager::MockDownloadFileManager()
bool* ok_to_overwrite) OVERRIDE { : DownloadFileManager(NULL) { }
if (intermediate_path_.empty()) {
*ok_to_overwrite = true;
return GetTempDownloadPath(item.GetTargetFilePath());
} else {
*ok_to_overwrite = overwrite_intermediate_path_;
return intermediate_path_;
}
}
virtual void ChooseDownloadPath(DownloadItem* item) OVERRIDE { MockDownloadFileManager::~MockDownloadFileManager() { }
if (!expected_suggested_path_.empty()) {
EXPECT_STREQ(expected_suggested_path_.value().c_str(),
item->GetTargetFilePath().value().c_str());
}
if (file_selection_response_.empty()) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&DownloadManager::FileSelectionCanceled,
download_manager_,
item->GetId()));
} else {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&DownloadManager::FileSelected,
download_manager_,
file_selection_response_,
item->GetId()));
}
expected_suggested_path_.clear();
file_selection_response_.clear();
}
virtual bool ShouldOpenFileBasedOnExtension(const FilePath& path) OVERRIDE { class MockDownloadItemFactory
return path.Extension() == FilePath::StringType(FILE_PATH_LITERAL(".pdf")); : public content::DownloadItemFactory,
} public base::SupportsWeakPtr<MockDownloadItemFactory> {
public:
MockDownloadItemFactory();
virtual ~MockDownloadItemFactory();
virtual void AddItemToPersistentStore(DownloadItem* item) OVERRIDE { // Access to map of created items.
static int64 db_handle = DownloadItem::kUninitializedHandle; // TODO(rdsmith): Could add type (save page, persisted, etc.)
download_manager_->OnItemAddedToPersistentStore(item->GetId(), --db_handle); // functionality if it's ever needed by consumers.
}
void SetFileSelectionExpectation(const FilePath& suggested_path, // Returns NULL if no item of that id is present.
const FilePath& response) { content::MockDownloadItem* GetItem(int id);
expected_suggested_path_ = suggested_path;
file_selection_response_ = response;
}
void SetMarkContentsDangerous(bool dangerous) { // Remove and return an item made by the factory.
mark_content_dangerous_ = dangerous; // Generally used during teardown.
} content::MockDownloadItem* PopItem();
void SetIntermediatePath(const FilePath& intermediate_path, // Should be called when the item of this id is removed so that
bool overwrite_intermediate_path) { // we don't keep dangling pointers.
intermediate_path_ = intermediate_path; void RemoveItem(int id);
overwrite_intermediate_path_ = overwrite_intermediate_path;
}
void SetShouldCompleteDownload(bool value) { // Overridden methods from DownloadItemFactory.
should_complete_download_ = value; virtual content::DownloadItem* CreatePersistedItem(
} DownloadItemImpl::Delegate* delegate,
content::DownloadId download_id,
void InvokeDownloadCompletionCallback() { const content::DownloadPersistentStoreInfo& info,
EXPECT_FALSE(download_completion_callback_.is_null()); const net::BoundNetLog& bound_net_log) OVERRIDE;
download_completion_callback_.Run(); virtual content::DownloadItem* CreateActiveItem(
download_completion_callback_.Reset(); DownloadItemImpl::Delegate* delegate,
} const DownloadCreateInfo& info,
DownloadRequestHandleInterface* request_handle,
virtual bool ShouldCompleteDownload( bool is_otr,
DownloadItem* item, const net::BoundNetLog& bound_net_log) OVERRIDE;
const base::Closure& complete_callback) OVERRIDE { virtual content::DownloadItem* CreateSavePageItem(
download_completion_callback_ = complete_callback; DownloadItemImpl::Delegate* delegate,
if (mark_content_dangerous_) { const FilePath& path,
CHECK(!complete_callback.is_null()); const GURL& url,
BrowserThread::PostTask( bool is_otr,
BrowserThread::UI, FROM_HERE, content::DownloadId download_id,
base::Bind(&TestDownloadManagerDelegate::MarkContentDangerous, const std::string& mime_type,
base::Unretained(this), item->GetId())); const net::BoundNetLog& bound_net_log) OVERRIDE;
mark_content_dangerous_ = false;
return false;
} else {
return should_complete_download_;
}
}
private: private:
void MarkContentDangerous( std::map<int32, content::MockDownloadItem*> items_;
int32 download_id) {
DownloadItem* item = download_manager_->GetActiveDownloadItem(download_id);
if (!item)
return;
item->OnContentCheckCompleted(
content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT);
InvokeDownloadCompletionCallback();
}
FilePath download_directory_; DISALLOW_COPY_AND_ASSIGN(MockDownloadItemFactory);
FilePath expected_suggested_path_;
FilePath file_selection_response_;
FilePath intermediate_path_;
bool overwrite_intermediate_path_;
bool mark_content_dangerous_;
bool prompt_user_for_save_location_;
bool should_complete_download_;
DownloadManager* download_manager_;
base::Closure download_completion_callback_;
DISALLOW_COPY_AND_ASSIGN(TestDownloadManagerDelegate);
}; };
} // namespace MockDownloadItemFactory::MockDownloadItemFactory() {}
class DownloadManagerTest : public testing::Test { MockDownloadItemFactory::~MockDownloadItemFactory() {}
public:
static const char* kTestData;
static const size_t kTestDataLen;
DownloadManagerTest() content::MockDownloadItem* MockDownloadItemFactory::GetItem(int id) {
: browser_context(new content::TestBrowserContext()), if (items_.find(id) == items_.end())
download_manager_(new DownloadManagerImpl(NULL)), return NULL;
ui_thread_(BrowserThread::UI, &message_loop_), return items_[id];
file_thread_(BrowserThread::FILE, &message_loop_) { }
download_manager_delegate_.reset(
new TestDownloadManagerDelegate(download_manager_.get()));
download_manager_->SetDelegate(download_manager_delegate_.get());
download_manager_->Init(browser_context.get());
}
~DownloadManagerTest() { content::MockDownloadItem* MockDownloadItemFactory::PopItem() {
download_manager_->Shutdown(); if (items_.empty())
// browser_context must outlive download_manager_, so we explicitly delete return NULL;
// download_manager_ first.
download_manager_ = NULL;
download_manager_delegate_.reset();
browser_context.reset(NULL);
message_loop_.RunAllPending();
}
// Create a temporary directory as the downloads directory. std::map<int32, content::MockDownloadItem*>::iterator first_item
bool CreateTempDownloadsDirectory() { = items_.begin();
if (!scoped_download_dir_.CreateUniqueTempDir()) content::MockDownloadItem* result = first_item->second;
return false; items_.erase(first_item);
download_manager_delegate_->set_download_directory( return result;
scoped_download_dir_.path()); }
return true;
}
void AddDownloadToFileManager(DownloadId id, DownloadFile* download_file) { void MockDownloadItemFactory::RemoveItem(int id) {
file_manager()->downloads_[id] = DCHECK(items_.find(id) != items_.end());
download_file; items_.erase(id);
} }
void AddMockDownloadToFileManager(DownloadId id, content::DownloadItem* MockDownloadItemFactory::CreatePersistedItem(
MockDownloadFile* download_file) { DownloadItemImpl::Delegate* delegate,
AddDownloadToFileManager(id, download_file); content::DownloadId download_id,
EXPECT_CALL(*download_file, GetDownloadManager()) const content::DownloadPersistentStoreInfo& info,
.WillRepeatedly(Return(download_manager_)); const net::BoundNetLog& bound_net_log) {
} int local_id = download_id.local();
DCHECK(items_.find(local_id) == items_.end());
void OnResponseCompleted(DownloadId download_id, int64 size, content::MockDownloadItem* result =
const std::string& hash) { new StrictMock<content::MockDownloadItem>;
download_manager_->OnResponseCompleted(download_id.local(), size, hash); EXPECT_CALL(*result, GetId())
} .WillRepeatedly(Return(local_id));
items_[local_id] = result;
void ContinueDownloadWithPath(DownloadItem* download, const FilePath& path) { return result;
download->OnTargetPathDetermined( }
path, DownloadItem::TARGET_DISPOSITION_OVERWRITE,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
download_manager_->OnTargetPathAvailable(download);
}
void OnDownloadInterrupted(DownloadId download_id, int64 size, content::DownloadItem* MockDownloadItemFactory::CreateActiveItem(
const std::string& hash_state, DownloadItemImpl::Delegate* delegate,
content::DownloadInterruptReason reason) { const DownloadCreateInfo& info,
download_manager_->OnDownloadInterrupted(download_id.local(), size, DownloadRequestHandleInterface* request_handle,
hash_state, reason); bool is_otr,
} const net::BoundNetLog& bound_net_log) {
int local_id = info.download_id.local();
DCHECK(items_.find(local_id) == items_.end());
// Get the download item with ID |id|. content::MockDownloadItem* result =
DownloadItem* GetActiveDownloadItem(DownloadId id) { new StrictMock<content::MockDownloadItem>;
return download_manager_->GetActiveDownload(id.local()); EXPECT_CALL(*result, GetId())
} .WillRepeatedly(Return(local_id));
items_[local_id] = result;
FilePath GetPathInDownloadsDir(const FilePath::StringType& fragment) { return result;
DCHECK(scoped_download_dir_.IsValid()); }
FilePath full_path(scoped_download_dir_.path().Append(fragment));
return full_path.NormalizePathSeparators();
}
protected: content::DownloadItem* MockDownloadItemFactory::CreateSavePageItem(
scoped_ptr<content::TestBrowserContext> browser_context; DownloadItemImpl::Delegate* delegate,
scoped_ptr<TestDownloadManagerDelegate> download_manager_delegate_; const FilePath& path,
scoped_refptr<DownloadManagerImpl> download_manager_; const GURL& url,
scoped_refptr<DownloadFileManager> file_manager_; bool is_otr,
MessageLoopForUI message_loop_; content::DownloadId download_id,
content::TestBrowserThread ui_thread_; const std::string& mime_type,
content::TestBrowserThread file_thread_; const net::BoundNetLog& bound_net_log) {
ScopedTempDir scoped_download_dir_; int local_id = download_id.local();
DCHECK(items_.find(local_id) == items_.end());
DownloadFileManager* file_manager() { content::MockDownloadItem* result =
if (!file_manager_) { new StrictMock<content::MockDownloadItem>;
file_manager_ = new DownloadFileManager(new MockDownloadFileFactory); EXPECT_CALL(*result, GetId())
download_manager_->SetFileManagerForTesting(file_manager_); .WillRepeatedly(Return(local_id));
} items_[local_id] = result;
return file_manager_;
}
DISALLOW_COPY_AND_ASSIGN(DownloadManagerTest); return result;
}
class MockBrowserContext : public content::BrowserContext {
public:
MockBrowserContext() { }
~MockBrowserContext() { }
MOCK_METHOD0(GetPath, FilePath());
MOCK_CONST_METHOD0(IsOffTheRecord, bool());
MOCK_METHOD0(GetRequestContext, net::URLRequestContextGetter*());
MOCK_METHOD1(GetRequestContextForRenderProcess,
net::URLRequestContextGetter*(int renderer_child_id));
MOCK_METHOD0(GetRequestContextForMedia, net::URLRequestContextGetter*());
MOCK_METHOD0(GetResourceContext, content::ResourceContext*());
MOCK_METHOD0(GetDownloadManagerDelegate, content::DownloadManagerDelegate*());
MOCK_METHOD0(GetGeolocationPermissionContext,
content::GeolocationPermissionContext* ());
MOCK_METHOD0(GetSpeechRecognitionPreferences,
content::SpeechRecognitionPreferences* ());
MOCK_METHOD0(DidLastSessionExitCleanly, bool());
MOCK_METHOD0(GetSpecialStoragePolicy, quota::SpecialStoragePolicy*());
}; };
const char* DownloadManagerTest::kTestData = "a;sdlfalsdfjalsdkfjad"; } // namespace
const size_t DownloadManagerTest::kTestDataLen =
strlen(DownloadManagerTest::kTestData);
// A DownloadFile that we can inject errors into. class DownloadManagerTest : public testing::Test {
class DownloadFileWithErrors : public DownloadFileImpl {
public: public:
DownloadFileWithErrors(DownloadCreateInfo* info, static const char* kTestData;
scoped_ptr<content::ByteStreamReader> stream, static const size_t kTestDataLen;
DownloadManager* manager,
bool calculate_hash);
virtual ~DownloadFileWithErrors() {}
// BaseFile delegated functions.
virtual net::Error Initialize() OVERRIDE;
virtual net::Error AppendDataToFile(const char* data,
size_t data_len) OVERRIDE;
virtual net::Error Rename(const FilePath& full_path) OVERRIDE;
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: DownloadManagerTest()
net::Error ReturnError(net::Error function_error) { : ui_thread_(content::BrowserThread::UI, &message_loop_),
if (forced_error_ != net::OK) { file_thread_(content::BrowserThread::FILE, &message_loop_),
net::Error ret = forced_error_; next_download_id_(0) {
clear_forced_error(); }
return ret;
// We tear down everything in TearDown().
~DownloadManagerTest() {}
// Create a MockDownloadItemFactory, MockDownloadManagerDelegate,
// and MockDownloadFileManager, then create a DownloadManager that points
// at all of those.
virtual void SetUp() {
DCHECK(!download_manager_.get());
mock_download_item_factory_ = (new MockDownloadItemFactory())->AsWeakPtr();
mock_download_manager_delegate_.reset(
new StrictMock<MockDownloadManagerDelegate>);
EXPECT_CALL(*mock_download_manager_delegate_.get(), Shutdown())
.WillOnce(Return());
mock_download_file_manager_ = new StrictMock<MockDownloadFileManager>;
EXPECT_CALL(*mock_download_file_manager_.get(),
OnDownloadManagerShutdown(_));
mock_browser_context_.reset(new StrictMock<MockBrowserContext>);
EXPECT_CALL(*mock_browser_context_.get(), IsOffTheRecord())
.WillRepeatedly(Return(false));
download_manager_ = new DownloadManagerImpl(
mock_download_file_manager_.get(),
scoped_ptr<content::DownloadItemFactory>(
mock_download_item_factory_.get()).Pass(), NULL);
download_manager_->SetDelegate(mock_download_manager_delegate_.get());
download_manager_->Init(mock_browser_context_.get());
}
virtual void TearDown() {
while (content::MockDownloadItem*
item = mock_download_item_factory_->PopItem()) {
EXPECT_CALL(*item, GetSafetyState())
.WillOnce(Return(content::DownloadItem::SAFE));
EXPECT_CALL(*item, IsPartialDownload())
.WillOnce(Return(false));
} }
return function_error; download_manager_->Shutdown();
download_manager_ = NULL;
message_loop_.RunAllPending();
ASSERT_EQ(NULL, mock_download_item_factory_.get());
message_loop_.RunAllPending();
mock_download_manager_delegate_.reset();
mock_download_file_manager_ = NULL;
mock_browser_context_.reset();
} }
net::Error forced_error_; // Returns download id.
}; content::MockDownloadItem& AddItemToManager() {
DownloadCreateInfo info;
DownloadFileWithErrors::DownloadFileWithErrors( DownloadRequestHandle handle;
DownloadCreateInfo* info,
scoped_ptr<content::ByteStreamReader> stream,
DownloadManager* manager,
bool calculate_hash)
: DownloadFileImpl(info,
stream.Pass(),
new DownloadRequestHandle(),
manager,
calculate_hash,
scoped_ptr<content::PowerSaveBlocker>(NULL).Pass(),
net::BoundNetLog()),
forced_error_(net::OK) {
}
net::Error DownloadFileWithErrors::Initialize() { static const char* kDownloadIdDomain = "Test download id domain";
return ReturnError(DownloadFileImpl::Initialize());
}
net::Error DownloadFileWithErrors::AppendDataToFile(const char* data, // Args are ignored except for download id, so everything else can be
size_t data_len) { // null.
return ReturnError(DownloadFileImpl::AppendDataToFile(data, data_len)); int id = next_download_id_;
} ++next_download_id_;
info.download_id = content::DownloadId(kDownloadIdDomain, id);
download_manager_->CreateDownloadItem(&info, handle);
net::Error DownloadFileWithErrors::Rename(const FilePath& full_path) { DCHECK(mock_download_item_factory_->GetItem(id));
return ReturnError(DownloadFileImpl::Rename(full_path)); content::MockDownloadItem& item(*mock_download_item_factory_->GetItem(id));
} ON_CALL(item, GetId())
.WillByDefault(Return(id));
namespace { return item;
}
const struct { content::MockDownloadItem& GetMockDownloadItem(int id) {
const char* url; content::MockDownloadItem* itemp = mock_download_item_factory_->GetItem(id);
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, },
};
// This is an observer that records what download IDs have opened a select DCHECK(itemp);
// file dialog. return *itemp;
class SelectFileObserver : public DownloadManager::Observer {
public:
explicit SelectFileObserver(DownloadManager* download_manager)
: download_manager_(download_manager) {
DCHECK(download_manager_.get());
download_manager_->AddObserver(this);
} }
~SelectFileObserver() { void RemoveMockDownloadItem(int id) {
download_manager_->RemoveObserver(this); // Owned by DownloadManager; should be deleted there.
mock_download_item_factory_->RemoveItem(id);
} }
// Downloadmanager::Observer functions. MockDownloadManagerDelegate& GetMockDownloadManagerDelegate() {
virtual void ModelChanged(DownloadManager* manager) OVERRIDE {} return *mock_download_manager_delegate_;
virtual void ManagerGoingDown(DownloadManager* manager) OVERRIDE {}
virtual void SelectFileDialogDisplayed(
DownloadManager* manager, int32 id) OVERRIDE {
file_dialog_ids_.insert(id);
} }
bool ShowedFileDialogForId(int32 id) { MockDownloadFileManager& GetMockDownloadFileManager() {
return file_dialog_ids_.find(id) != file_dialog_ids_.end(); return *mock_download_file_manager_;
} }
private: // Probe at private internals.
std::set<int32> file_dialog_ids_; DownloadItem* GetActiveDownloadItem(int32 id) {
scoped_refptr<DownloadManager> download_manager_; return download_manager_->GetActiveDownload(id);
};
// 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 { void AddItemToHistory(content::MockDownloadItem& item, int64 db_handle) {
return (1 << state) & states_hit_; // For DCHECK in AddDownloadItemToHistory. Don't want to use
} // WillRepeatedly as it may have to return true after this.
bool was_updated() const { return was_updated_; } if (DCHECK_IS_ON())
bool was_opened() const { return was_opened_; } EXPECT_CALL(item, IsPersisted())
.WillOnce(Return(false));
private: EXPECT_CALL(item, SetDbHandle(db_handle));
// DownloadItem::Observer methods EXPECT_CALL(item, SetIsPersisted());
virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE { EXPECT_CALL(item, GetDbHandle())
DCHECK_EQ(tracked_, download); .WillRepeatedly(Return(db_handle));
states_hit_ |= (1 << download->GetState());
was_updated_ = true;
}
virtual void OnDownloadOpened(DownloadItem* download) OVERRIDE{
DCHECK_EQ(tracked_, download);
states_hit_ |= (1 << download->GetState());
was_opened_ = true;
}
DownloadItem* tracked_; // Null out ShowDownloadInBrowser
int states_hit_; EXPECT_CALL(item, GetWebContents())
bool was_updated_; .WillOnce(Return(static_cast<WebContents*>(NULL)));
bool was_opened_; EXPECT_CALL(GetMockDownloadManagerDelegate(),
}; GetAlternativeWebContentsToNotifyForDownload())
.WillOnce(Return(static_cast<WebContents*>(NULL)));
} // namespace EXPECT_CALL(item, IsInProgress())
.WillOnce(Return(true));
TEST_F(DownloadManagerTest, MAYBE_StartDownload) {
content::TestBrowserThread io_thread(BrowserThread::IO, &message_loop_);
ASSERT_TRUE(CreateTempDownloadsDirectory());
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kStartDownloadCases); ++i) {
DVLOG(1) << "Test case " << i;
download_manager_delegate_->set_prompt_user_for_save_location(
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->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());
scoped_ptr<content::ByteStreamWriter> stream_writer;
scoped_ptr<content::ByteStreamReader> stream_reader;
content::CreateByteStream(message_loop_.message_loop_proxy(),
message_loop_.message_loop_proxy(),
kTestDataLen, &stream_writer, &stream_reader);
DownloadFile* download_file(
new DownloadFileImpl(info.get(),
stream_reader.Pass(),
new DownloadRequestHandle(),
download_manager_, false,
scoped_ptr<content::PowerSaveBlocker>(NULL).Pass(),
net::BoundNetLog()));
AddDownloadToFileManager(info->download_id, download_file);
download_file->Initialize();
download_manager_->StartDownload(info->download_id.local());
message_loop_.RunAllPending();
// SelectFileObserver will have recorded any attempt to open the // Null out MaybeCompleteDownload
// select file dialog. EXPECT_CALL(item, AllDataSaved())
// Note that DownloadManager::FileSelectionCanceled() is never called. .WillOnce(Return(false));
EXPECT_EQ(kStartDownloadCases[i].expected_save_as,
observer.ShowedFileDialogForId(info->download_id.local()));
file_manager()->CancelDownload(info->download_id); download_manager_->OnItemAddedToPersistentStore(item.GetId(), db_handle);
} }
}
namespace {
enum OverwriteDownloadPath { protected:
DONT_OVERWRITE, // Key test variable; we'll keep it available to sub-classes.
OVERWRITE scoped_refptr<DownloadManagerImpl> download_manager_;
};
enum ResponseCompletionTime { private:
COMPLETES_BEFORE_RENAME, MessageLoopForUI message_loop_;
COMPLETES_AFTER_RENAME content::TestBrowserThread ui_thread_;
}; content::TestBrowserThread file_thread_;
base::WeakPtr<MockDownloadItemFactory> mock_download_item_factory_;
scoped_ptr<MockDownloadManagerDelegate> mock_download_manager_delegate_;
scoped_refptr<MockDownloadFileManager> mock_download_file_manager_;
scoped_ptr<MockBrowserContext> mock_browser_context_;
int next_download_id_;
// Test cases to be used with DownloadFilenameTest. The paths are relative to DISALLOW_COPY_AND_ASSIGN(DownloadManagerTest);
// the temporary downloads directory used for testing.
const struct DownloadFilenameTestCase {
// DownloadItem properties prior to calling RestartDownload().
const FilePath::CharType* target_path;
DownloadItem::TargetDisposition target_disposition;
// If we receive a ChooseDownloadPath() call to prompt the user for a download
// location, |expected_prompt_path| is the expected prompt path. The
// TestDownloadManagerDelegate will respond with |chosen_path|. If
// |chosen_path| is empty, then the file choose dialog be cancelled.
const FilePath::CharType* expected_prompt_path;
const FilePath::CharType* chosen_path;
// Response to GetIntermediatePath(). If |intermediate_path| is empty, then a
// temporary path is constructed with
// GetTempDownloadPath(item->GetTargetFilePath()).
const FilePath::CharType* intermediate_path;
OverwriteDownloadPath overwrite_intermediate_path;
// Determines when OnResponseCompleted() is called. If this is
// COMPLETES_BEFORE_RENAME, then the response completes before the filename is
// determines.
ResponseCompletionTime completion;
// The expected intermediate path for the download.
const FilePath::CharType* expected_intermediate_path;
// The expected final path for the download.
const FilePath::CharType* expected_final_path;
} kDownloadFilenameTestCases[] = {
#define TARGET FILE_PATH_LITERAL
#define INTERMEDIATE FILE_PATH_LITERAL
#define EXPECTED_PROMPT FILE_PATH_LITERAL
#define PROMPT_RESPONSE FILE_PATH_LITERAL
#define EXPECTED_INTERMEDIATE FILE_PATH_LITERAL
#define EXPECTED_FINAL FILE_PATH_LITERAL
{
// 0: No prompting.
TARGET("foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECTED_PROMPT(""),
PROMPT_RESPONSE(""),
INTERMEDIATE("foo.txt.intermediate"),
OVERWRITE,
COMPLETES_AFTER_RENAME,
EXPECTED_INTERMEDIATE("foo.txt.intermediate"),
EXPECTED_FINAL("foo.txt"),
},
{
// 1: With prompting. No rename.
TARGET("foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECTED_PROMPT("foo.txt"),
PROMPT_RESPONSE("foo.txt"),
INTERMEDIATE("foo.txt.intermediate"),
OVERWRITE,
COMPLETES_AFTER_RENAME,
EXPECTED_INTERMEDIATE("foo.txt.intermediate"),
EXPECTED_FINAL("foo.txt"),
},
{
// 2: With prompting. Download is renamed.
TARGET("foo.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECTED_PROMPT("foo.txt"),
PROMPT_RESPONSE("bar.txt"),
INTERMEDIATE("bar.txt.intermediate"),
OVERWRITE,
COMPLETES_AFTER_RENAME,
EXPECTED_INTERMEDIATE("bar.txt.intermediate"),
EXPECTED_FINAL("bar.txt"),
},
{
// 3: With prompting. Download path is changed.
TARGET("foo.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECTED_PROMPT("foo.txt"),
PROMPT_RESPONSE("Foo/bar.txt"),
INTERMEDIATE("Foo/bar.txt.intermediate"),
OVERWRITE,
COMPLETES_AFTER_RENAME,
EXPECTED_INTERMEDIATE("Foo/bar.txt.intermediate"),
EXPECTED_FINAL("Foo/bar.txt"),
},
{
// 4: No prompting. Intermediate path exists. Doesn't overwrite
// intermediate path.
TARGET("exists.png"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECTED_PROMPT(""),
PROMPT_RESPONSE(""),
INTERMEDIATE("exists.png.temp"),
DONT_OVERWRITE,
COMPLETES_AFTER_RENAME,
EXPECTED_INTERMEDIATE("exists.png (1).temp"),
EXPECTED_FINAL("exists.png"),
},
{
// 5: No prompting. Intermediate path exists. Overwrites.
TARGET("exists.png"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECTED_PROMPT(""),
PROMPT_RESPONSE(""),
INTERMEDIATE("exists.png.temp"),
OVERWRITE,
COMPLETES_AFTER_RENAME,
EXPECTED_INTERMEDIATE("exists.png.temp"),
EXPECTED_FINAL("exists.png"),
},
{
// 6: No prompting. Final path exists. Doesn't overwrite.
TARGET("exists.txt"),
DownloadItem::TARGET_DISPOSITION_UNIQUIFY,
EXPECTED_PROMPT(""),
PROMPT_RESPONSE(""),
INTERMEDIATE("exists.txt.temp"),
OVERWRITE,
COMPLETES_AFTER_RENAME,
EXPECTED_INTERMEDIATE("exists.txt.temp"),
EXPECTED_FINAL("exists (1).txt"),
},
{
// 7: No prompting. Final path exists. Overwrites.
TARGET("exists.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECTED_PROMPT(""),
PROMPT_RESPONSE(""),
INTERMEDIATE("exists.txt.temp"),
OVERWRITE,
COMPLETES_AFTER_RENAME,
EXPECTED_INTERMEDIATE("exists.txt.temp"),
EXPECTED_FINAL("exists.txt"),
},
{
// 8: No prompting. Response completes before filename determination.
TARGET("foo.txt"),
DownloadItem::TARGET_DISPOSITION_OVERWRITE,
EXPECTED_PROMPT(""),
PROMPT_RESPONSE(""),
INTERMEDIATE("foo.txt.intermediate"),
OVERWRITE,
COMPLETES_BEFORE_RENAME,
EXPECTED_INTERMEDIATE("foo.txt.intermediate"),
EXPECTED_FINAL("foo.txt"),
},
{
// 9: With prompting. Download path is changed. Response completes before
// filename determination.
TARGET("foo.txt"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECTED_PROMPT("foo.txt"),
PROMPT_RESPONSE("Foo/bar.txt"),
INTERMEDIATE("Foo/bar.txt.intermediate"),
OVERWRITE,
COMPLETES_BEFORE_RENAME,
EXPECTED_INTERMEDIATE("Foo/bar.txt.intermediate"),
EXPECTED_FINAL("Foo/bar.txt"),
},
#undef TARGET
#undef INTERMEDIATE
#undef EXPECTED_PROMPT
#undef PROMPT_RESPONSE
#undef EXPECTED_INTERMEDIATE
#undef EXPECTED_FINAL
}; };
} // namespace // Does the DownloadManager prompt when requested?
TEST_F(DownloadManagerTest, RestartDownload) {
// Put a mock we have a handle to on the download manager.
content::MockDownloadItem& item(AddItemToManager());
int download_id = item.GetId();
TEST_F(DownloadManagerTest, DownloadFilenameTest) { // Confirm we're internally consistent.
ASSERT_TRUE(CreateTempDownloadsDirectory()); EXPECT_EQ(&item, GetActiveDownloadItem(download_id));
// We create a known file to test file uniquification.
ASSERT_TRUE(file_util::WriteFile(
GetPathInDownloadsDir(FILE_PATH_LITERAL("exists.txt")), "", 0) == 0);
ASSERT_TRUE(file_util::WriteFile(
GetPathInDownloadsDir(FILE_PATH_LITERAL("exists.png.temp")), "", 0) == 0);
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDownloadFilenameTestCases); ++i) {
const DownloadFilenameTestCase& test_case = kDownloadFilenameTestCases[i];
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo);
SCOPED_TRACE(testing::Message() << "Running test case " << i);
info->url_chain.push_back(GURL());
MockDownloadFile* download_file(
new ::testing::StrictMock<MockDownloadFile>());
FilePath target_path = GetPathInDownloadsDir(test_case.target_path);
FilePath expected_prompt_path =
GetPathInDownloadsDir(test_case.expected_prompt_path);
FilePath chosen_path = GetPathInDownloadsDir(test_case.chosen_path);
FilePath intermediate_path =
GetPathInDownloadsDir(test_case.intermediate_path);
FilePath expected_intermediate_path =
GetPathInDownloadsDir(test_case.expected_intermediate_path);
FilePath expected_final_path =
GetPathInDownloadsDir(test_case.expected_final_path);
EXPECT_CALL(*download_file, Rename(expected_intermediate_path))
.WillOnce(Return(net::OK));
EXPECT_CALL(*download_file, Rename(expected_final_path))
.WillOnce(Return(net::OK));
#if defined(OS_MACOSX)
EXPECT_CALL(*download_file, AnnotateWithSourceInformation());
#endif
EXPECT_CALL(*download_file, Detach());
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
AddMockDownloadToFileManager(info->download_id, download_file);
DownloadItem* download = GetActiveDownloadItem(info->download_id);
ASSERT_TRUE(download != NULL);
if (test_case.completion == COMPLETES_BEFORE_RENAME)
OnResponseCompleted(info->download_id, 1024, std::string("fake_hash"));
download->OnTargetPathDetermined(
target_path, test_case.target_disposition,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
download_manager_delegate_->SetFileSelectionExpectation(
expected_prompt_path, chosen_path);
download_manager_delegate_->SetIntermediatePath(
intermediate_path, test_case.overwrite_intermediate_path);
download_manager_delegate_->SetShouldCompleteDownload(false);
download_manager_->RestartDownload(info->download_id.local());
message_loop_.RunAllPending();
if (test_case.completion == COMPLETES_AFTER_RENAME) { ScopedTempDir download_dir;
OnResponseCompleted(info->download_id, 1024, std::string("fake_hash")); ASSERT_TRUE(download_dir.CreateUniqueTempDir());
message_loop_.RunAllPending(); FilePath expected_path(download_dir.path().Append(
} FILE_PATH_LITERAL("location")));
EXPECT_EQ(expected_intermediate_path.value(), EXPECT_CALL(item, GetTargetDisposition())
download->GetFullPath().value()); .WillOnce(Return(DownloadItem::TARGET_DISPOSITION_PROMPT));
download_manager_delegate_->SetShouldCompleteDownload(true); EXPECT_CALL(GetMockDownloadManagerDelegate(), ChooseDownloadPath(&item));
download_manager_delegate_->InvokeDownloadCompletionCallback(); download_manager_->RestartDownload(download_id);
message_loop_.RunAllPending();
EXPECT_EQ(expected_final_path.value(),
download->GetTargetFilePath().value());
}
}
void WriteCopyToStream(const char *source, // The alternative pathway goes straight to OnTargetPathAvailable,
size_t len, content::ByteStreamWriter* stream) { // so it more naturally belongs below.
scoped_refptr<net::IOBuffer> copy(new net::IOBuffer(len));
memcpy(copy.get()->data(), source, len);
stream->Write(copy, len);
} }
TEST_F(DownloadManagerTest, DownloadInterruptTest) { // Do the results of GetIntermediatePath get passed through to the
using ::testing::_; // download? Note that this path is tested from RestartDownload
using ::testing::CreateFunctor; // to test the non-prompting path in RestartDownload as well.
using ::testing::Invoke; TEST_F(DownloadManagerTest, OnTargetPathAvailable) {
using ::testing::Return; // Put a mock we have a handle to on the download manager.
content::MockDownloadItem& item(AddItemToManager());
// Normally, the download system takes ownership of info, and is
// responsible for deleting it. In these unit tests, however, we ScopedTempDir download_dir;
// don't call the function that deletes it, so we do so ourselves. ASSERT_TRUE(download_dir.CreateUniqueTempDir());
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); FilePath target_path(download_dir.path().Append(
info->prompt_user_for_save_location = false; FILE_PATH_LITERAL("location")));
info->url_chain.push_back(GURL()); FilePath intermediate_path(download_dir.path().Append(
info->total_bytes = static_cast<int64>(kTestDataLen); FILE_PATH_LITERAL("location.crdownload")));
const FilePath new_path(FILE_PATH_LITERAL("foo.zip"));
const FilePath cr_path(GetTempDownloadPath(new_path)); EXPECT_CALL(item, GetTargetDisposition())
.WillOnce(Return(DownloadItem::TARGET_DISPOSITION_UNIQUIFY));
MockDownloadFile* download_file(new NiceMock<MockDownloadFile>()); EXPECT_CALL(GetMockDownloadManagerDelegate(),
GetIntermediatePath(Ref(item), _))
EXPECT_CALL(*download_file, Rename(cr_path)) .WillOnce(DoAll(SetArgPointee<1>(true), Return(intermediate_path)));
.WillOnce(Return(net::OK)); // Finesse DCHECK with WillRepeatedly.
EXPECT_CALL(*download_file, Cancel()); EXPECT_CALL(item, GetTargetFilePath())
.WillRepeatedly(ReturnRef(target_path));
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle()); EXPECT_CALL(item, OnIntermediatePathDetermined(
// |download_file| is owned by DownloadFileManager. &GetMockDownloadFileManager(), intermediate_path, true));
AddMockDownloadToFileManager(info->download_id, download_file); download_manager_->RestartDownload(item.GetId());
DownloadItem* download = GetActiveDownloadItem(info->download_id);
ASSERT_TRUE(download != NULL);
EXPECT_EQ(DownloadItem::IN_PROGRESS, download->GetState());
scoped_ptr<ItemObserver> observer(new ItemObserver(download));
ContinueDownloadWithPath(download, new_path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) != NULL);
int64 error_size = 3;
OnDownloadInterrupted(info->download_id, error_size, "",
content::DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) == 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());
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 // Do the results of an OnDownloadInterrupted get passed through properly
// of a file error while writing the download to disk. // to the DownloadItem? This test tests non-persisted downloads.
TEST_F(DownloadManagerTest, MAYBE_DownloadFileErrorTest) { TEST_F(DownloadManagerTest, OnDownloadInterrupted_NonPersisted) {
// Create a temporary file and a mock stream. // Put a mock we have a handle to on the download manager.
FilePath path; content::MockDownloadItem& item(AddItemToManager());
ASSERT_TRUE(file_util::CreateTemporaryFile(&path)); int download_id = item.GetId();
// This file stream will be used, until the first rename occurs. int64 size = 0xdeadbeef;
net::FileStream* stream = new net::FileStream(NULL); const std::string hash_state("Undead beef");
ASSERT_EQ(0, stream->OpenSync( content::DownloadInterruptReason reason(
path, content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_WRITE));
EXPECT_CALL(item, Interrupted(size, hash_state, reason));
// Make sure the DFM exists; we'll need it later. EXPECT_CALL(item, OffThreadCancel(&GetMockDownloadFileManager()));
// TODO(rdsmith): This is ugly and should be rewritten, but a large EXPECT_CALL(item, IsPersisted())
// rewrite of this test is in progress in a different CL, so doing it .WillOnce(Return(false));
// the hacky way for now. download_manager_->OnDownloadInterrupted(
(void) file_manager(); download_id, size, hash_state, reason);
EXPECT_EQ(&item, GetActiveDownloadItem(download_id));
// 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->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);
scoped_ptr<content::ByteStreamWriter> stream_input;
scoped_ptr<content::ByteStreamReader> stream_output;
content::CreateByteStream(message_loop_.message_loop_proxy(),
message_loop_.message_loop_proxy(),
kTestDataLen, &stream_input, &stream_output);
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
// Create a download file that we can insert errors into.
scoped_ptr<DownloadFileWithErrors> download_file(new DownloadFileWithErrors(
info.get(), stream_output.Pass(), download_manager_, false));
download_file->Initialize();
DownloadItem* download = GetActiveDownloadItem(info->download_id);
ASSERT_TRUE(download != NULL);
EXPECT_EQ(DownloadItem::IN_PROGRESS, download->GetState());
scoped_ptr<ItemObserver> observer(new ItemObserver(download));
// Add some data before finalizing the file name.
WriteCopyToStream(kTestData, kTestDataLen, stream_input.get());
// Finalize the file name.
ContinueDownloadWithPath(download, path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) != NULL);
// Add more data.
WriteCopyToStream(kTestData, kTestDataLen, stream_input.get());
// Add more data, but an error occurs.
download_file->set_forced_error(net::ERR_FAILED);
WriteCopyToStream(kTestData, kTestDataLen, stream_input.get());
message_loop_.RunAllPending();
// Check the state. The download should have been interrupted.
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) == 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());
// Clean up.
download->Cancel(true);
message_loop_.RunAllPending();
} }
TEST_F(DownloadManagerTest, DownloadCancelTest) { // Do the results of an OnDownloadInterrupted get passed through properly
using ::testing::_; // to the DownloadItem? This test tests persisted downloads.
using ::testing::CreateFunctor; TEST_F(DownloadManagerTest, OnDownloadInterrupted_Persisted) {
using ::testing::Invoke; // Put a mock we have a handle to on the download manager.
using ::testing::Return; content::MockDownloadItem& item(AddItemToManager());
int download_id = item.GetId();
// Normally, the download system takes ownership of info, and is int64 db_handle = 0x7;
// responsible for deleting it. In these unit tests, however, we AddItemToHistory(item, db_handle);
// don't call the function that deletes it, so we do so ourselves.
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); int64 size = 0xdeadbeef;
info->prompt_user_for_save_location = false; const std::string hash_state("Undead beef");
info->url_chain.push_back(GURL()); content::DownloadInterruptReason reason(
const FilePath new_path(FILE_PATH_LITERAL("foo.zip")); content::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED);
const FilePath cr_path(GetTempDownloadPath(new_path));
EXPECT_CALL(item, Interrupted(size, hash_state, reason));
MockDownloadFile* download_file(new NiceMock<MockDownloadFile>()); EXPECT_CALL(item, OffThreadCancel(&GetMockDownloadFileManager()));
EXPECT_CALL(item, IsPersisted())
// |download_file| is owned by DownloadFileManager. .WillOnce(Return(true));
EXPECT_CALL(*download_file, Rename(cr_path)) EXPECT_CALL(GetMockDownloadManagerDelegate(),
.WillOnce(Return(net::OK)); UpdateItemInPersistentStore(&item));
EXPECT_CALL(*download_file, Cancel()); download_manager_->OnDownloadInterrupted(
download_id, size, hash_state, reason);
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle()); EXPECT_EQ(NULL, GetActiveDownloadItem(download_id));
AddMockDownloadToFileManager(info->download_id, download_file);
// Remove so we don't get errors on shutdown.
DownloadItem* download = GetActiveDownloadItem(info->download_id); EXPECT_CALL(GetMockDownloadManagerDelegate(),
ASSERT_TRUE(download != NULL); RemoveItemFromPersistentStore(&item));
download_manager_->DownloadRemoved(&item);
EXPECT_EQ(DownloadItem::IN_PROGRESS, download->GetState()); RemoveMockDownloadItem(download_id);
scoped_ptr<ItemObserver> observer(new ItemObserver(download));
ContinueDownloadWithPath(download, new_path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) != NULL);
download->Cancel(false);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) != 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());
file_manager()->CancelDownload(info->download_id);
EXPECT_FALSE(file_util::PathExists(new_path));
EXPECT_FALSE(file_util::PathExists(cr_path));
} }
TEST_F(DownloadManagerTest, MAYBE_DownloadOverwriteTest) { // Does DownloadCancelled remove Download from appropriate queues?
using ::testing::_; // This test tests non-persisted downloads.
using ::testing::CreateFunctor; TEST_F(DownloadManagerTest, OnDownloadCancelled_NonPersisted) {
using ::testing::Invoke; // Put a mock we have a handle to on the download manager.
using ::testing::Return; content::MockDownloadItem& item(AddItemToManager());
ASSERT_TRUE(CreateTempDownloadsDirectory()); EXPECT_CALL(item, IsPersisted())
// File names we're using. .WillRepeatedly(Return(false));
const FilePath new_path(GetPathInDownloadsDir(FILE_PATH_LITERAL("foo.txt"))); EXPECT_CALL(item, GetState())
const FilePath cr_path(GetTempDownloadPath(new_path)); .WillRepeatedly(Return(DownloadItem::CANCELLED));
EXPECT_FALSE(file_util::PathExists(new_path)); EXPECT_CALL(item, GetDbHandle())
.WillRepeatedly(Return(DownloadItem::kUninitializedHandle));
// Create the file that we will overwrite. Will be automatically cleaned
// up when temp_dir_ is destroyed. EXPECT_CALL(item, OffThreadCancel(&GetMockDownloadFileManager()));
FILE* fp = file_util::OpenFile(new_path, "w"); download_manager_->DownloadCancelled(&item);
file_util::CloseFile(fp); // TODO(rdsmith): Confirm that the download item is no longer on the
EXPECT_TRUE(file_util::PathExists(new_path)); // active list by calling GetActiveDownloadItem(id). Currently, the
// item is left on the active list for rendez-vous with the history
// Construct the unique file name that normally would be created, but // system :-{.
// which we will override.
int uniquifier =
file_util::GetUniquePathNumber(new_path, FILE_PATH_LITERAL(""));
FilePath unique_new_path = new_path;
EXPECT_NE(0, uniquifier);
unique_new_path = unique_new_path.InsertBeforeExtensionASCII(
StringPrintf(" (%d)", 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->prompt_user_for_save_location = true;
info->url_chain.push_back(GURL());
scoped_ptr<content::ByteStreamWriter> stream_input;
scoped_ptr<content::ByteStreamReader> stream_output;
content::CreateByteStream(message_loop_.message_loop_proxy(),
message_loop_.message_loop_proxy(),
kTestDataLen, &stream_input, &stream_output);
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadItem* download = GetActiveDownloadItem(info->download_id);
ASSERT_TRUE(download != NULL);
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(), stream_output.Pass(),
new DownloadRequestHandle(),
download_manager_, false,
scoped_ptr<content::PowerSaveBlocker>(NULL).Pass(),
net::BoundNetLog()));
download_file->Rename(cr_path);
// This creates the .temp version of the file.
download_file->Initialize();
// |download_file| is owned by DownloadFileManager.
AddDownloadToFileManager(info->download_id, download_file);
ContinueDownloadWithPath(download, new_path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) != NULL);
WriteCopyToStream(kTestData, kTestDataLen, stream_input.get());
// Finish the download.
OnResponseCompleted(info->download_id, kTestDataLen, "");
message_loop_.RunAllPending();
// Download is complete.
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) == 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_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) { // Does DownloadCancelled remove Download from appropriate queues?
using ::testing::_; // This test tests persisted downloads.
using ::testing::CreateFunctor; TEST_F(DownloadManagerTest, OnDownloadCancelled_Persisted) {
using ::testing::Invoke; // Put a mock we have a handle to on the download manager.
using ::testing::Return; content::MockDownloadItem& item(AddItemToManager());
int download_id = item.GetId();
ASSERT_TRUE(CreateTempDownloadsDirectory()); int64 db_handle = 0x7;
AddItemToHistory(item, db_handle);
// File names we're using.
const FilePath new_path(GetPathInDownloadsDir(FILE_PATH_LITERAL("foo.txt"))); EXPECT_CALL(item, IsPersisted())
const FilePath cr_path(GetTempDownloadPath(new_path)); .WillRepeatedly(Return(true));
EXPECT_FALSE(file_util::PathExists(new_path)); EXPECT_CALL(GetMockDownloadManagerDelegate(),
UpdateItemInPersistentStore(&item));
// Normally, the download system takes ownership of info, and is EXPECT_CALL(item, GetState())
// responsible for deleting it. In these unit tests, however, we .WillRepeatedly(Return(DownloadItem::CANCELLED));
// don't call the function that deletes it, so we do so ourselves. EXPECT_CALL(item, GetDbHandle())
scoped_ptr<DownloadCreateInfo> info(new DownloadCreateInfo); .WillRepeatedly(Return(db_handle));
info->prompt_user_for_save_location = true;
info->url_chain.push_back(GURL()); EXPECT_CALL(item, OffThreadCancel(&GetMockDownloadFileManager()));
scoped_ptr<content::ByteStreamWriter> stream_input; download_manager_->DownloadCancelled(&item);
scoped_ptr<content::ByteStreamReader> stream_output; EXPECT_EQ(NULL, GetActiveDownloadItem(download_id));
content::CreateByteStream(message_loop_.message_loop_proxy(),
message_loop_.message_loop_proxy(),
kTestDataLen, &stream_input, &stream_output);
download_manager_->CreateDownloadItem(info.get(), DownloadRequestHandle());
DownloadItem* download = GetActiveDownloadItem(info->download_id);
ASSERT_TRUE(download != NULL);
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(), stream_output.Pass(),
new DownloadRequestHandle(),
download_manager_, false,
scoped_ptr<content::PowerSaveBlocker>(NULL).Pass(),
net::BoundNetLog()));
download_file->Rename(cr_path);
// This creates the .temp version of the file.
download_file->Initialize();
// |download_file| is owned by DownloadFileManager.
AddDownloadToFileManager(info->download_id, download_file);
ContinueDownloadWithPath(download, new_path);
message_loop_.RunAllPending();
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) != NULL);
WriteCopyToStream(kTestData, kTestDataLen, stream_input.get());
// Finish the download.
OnResponseCompleted(info->download_id, kTestDataLen, "");
message_loop_.RunAllPending();
// Download is complete.
EXPECT_TRUE(GetActiveDownloadItem(info->download_id) == 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_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(info->download_id) == 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_FALSE(file_util::PathExists(new_path));
} }
...@@ -276,6 +276,7 @@ ...@@ -276,6 +276,7 @@
'browser/download/download_file_impl.h', 'browser/download/download_file_impl.h',
'browser/download/download_file_manager.cc', 'browser/download/download_file_manager.cc',
'browser/download/download_file_manager.h', 'browser/download/download_file_manager.h',
'browser/download/download_item_factory.h',
'browser/download/download_item_impl.cc', 'browser/download/download_item_impl.cc',
'browser/download/download_item_impl.h', 'browser/download/download_item_impl.h',
'browser/download/download_manager_impl.cc', 'browser/download/download_manager_impl.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment