Chromium Code Reviews| Index: content/browser/download/download_item_impl_unittest.cc |
| diff --git a/content/browser/download/download_item_impl_unittest.cc b/content/browser/download/download_item_impl_unittest.cc |
| index 74d84c42db58d2027c67a790ade3960f001f2880..4461d64e8d1994289becbc63474e0883b4fedf47 100644 |
| --- a/content/browser/download/download_item_impl_unittest.cc |
| +++ b/content/browser/download/download_item_impl_unittest.cc |
| @@ -2,6 +2,7 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include "base/command_line.h" |
| #include "base/message_loop.h" |
| #include "base/stl_util.h" |
| #include "base/threading/thread.h" |
| @@ -15,6 +16,8 @@ |
| #include "content/public/browser/download_id.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_switches.h" |
| #include "content/public/test/mock_download_item.h" |
| #include "content/public/test/test_browser_thread.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| @@ -22,6 +25,7 @@ |
| using ::testing::_; |
| using ::testing::AllOf; |
| +using ::testing::NiceMock; |
| using ::testing::Property; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| @@ -50,6 +54,14 @@ class MockDelegate : public DownloadItemImplDelegate { |
| bool(DownloadItemImpl*, const ShouldOpenDownloadCallback&)); |
| MOCK_METHOD1(ShouldOpenFileBasedOnExtension, bool(const FilePath&)); |
| MOCK_METHOD1(CheckForFileRemoval, void(DownloadItemImpl*)); |
| + |
| + virtual void ResumeInterruptedDownload( |
| + scoped_ptr<DownloadUrlParameters> params, DownloadId id) OVERRIDE { |
| + MockResumeInterruptedDownload(params.get(), id); |
| + } |
| + MOCK_METHOD2(MockResumeInterruptedDownload, |
| + void(DownloadUrlParameters* params, DownloadId id)); |
| + |
| MOCK_CONST_METHOD0(GetBrowserContext, BrowserContext*()); |
| MOCK_METHOD1(UpdatePersistence, void(DownloadItemImpl*)); |
| MOCK_METHOD1(DownloadOpened, void(DownloadItemImpl*)); |
| @@ -80,6 +92,11 @@ ACTION_P2(ScheduleRenameCallback, interrupt_reason, new_path) { |
| base::Bind(arg1, interrupt_reason, new_path)); |
| } |
| +const int kDownloadChunkSize = 1000; |
| +const int kDownloadSpeed = 1000; |
| +const int kDummyDBHandle = 10; |
| +const FilePath::CharType kDummyPath[] = FILE_PATH_LITERAL("/testpath"); |
|
asanka
2013/01/04 22:54:16
Duplicate?
Randy Smith (Not in Mondays)
2013/01/07 20:54:10
Sure looks it. Removed.
|
| + |
| } // namespace |
| class DownloadItemTest : public testing::Test { |
| @@ -88,9 +105,12 @@ class DownloadItemTest : public testing::Test { |
| public: |
| explicit MockObserver(DownloadItem* item) |
| : item_(item), |
| + last_state_(item->GetState()), |
| removed_(false), |
| destroyed_(false), |
| - updated_(false) { |
| + updated_(false), |
| + interrupt_count_(0), |
| + resume_count_(0) { |
| item_->AddObserver(this); |
| } |
| @@ -99,17 +119,35 @@ class DownloadItemTest : public testing::Test { |
| } |
| virtual void OnDownloadRemoved(DownloadItem* download) { |
| + DVLOG(20) << " " << __FUNCTION__ |
| + << " download = " << download->DebugString(false); |
| removed_ = true; |
| } |
| virtual void OnDownloadUpdated(DownloadItem* download) { |
| + 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; |
| } |
| virtual void OnDownloadOpened(DownloadItem* download) { |
| + DVLOG(20) << " " << __FUNCTION__ |
| + << " download = " << download->DebugString(false); |
| } |
| virtual void OnDownloadDestroyed(DownloadItem* download) { |
| + DVLOG(20) << " " << __FUNCTION__ |
| + << " download = " << download->DebugString(false); |
| destroyed_ = true; |
| item_->RemoveObserver(this); |
| item_ = NULL; |
| @@ -129,11 +167,22 @@ class DownloadItemTest : public testing::Test { |
| return was_updated; |
| } |
| + int CheckInterrupted() { |
|
asanka
2013/01/04 22:54:16
Nit: Why not GetInterruptCount()?
Randy Smith (Not in Mondays)
2013/01/07 20:54:10
Done.
|
| + return interrupt_count_; |
| + } |
| + |
| + int CheckResumed() { |
|
asanka
2013/01/04 22:54:16
Nit: Ditto.
Randy Smith (Not in Mondays)
2013/01/07 20:54:10
Done.
|
| + return resume_count_; |
| + } |
| + |
| private: |
| DownloadItem* item_; |
| + DownloadItem::DownloadState last_state_; |
| bool removed_; |
| bool destroyed_; |
| bool updated_; |
| + int interrupt_count_; |
| + int resume_count_; |
| }; |
| DownloadItemTest() |
| @@ -170,11 +219,8 @@ class DownloadItemTest : public testing::Test { |
| info_->save_info->prompt_for_save_location = false; |
| info_->url_chain.push_back(GURL()); |
| - scoped_ptr<DownloadRequestHandleInterface> request_handle( |
| - new testing::NiceMock<MockRequestHandle>); |
| DownloadItemImpl* download = |
| - new DownloadItemImpl(&delegate_, *(info_.get()), |
| - request_handle.Pass(), net::BoundNetLog()); |
| + new DownloadItemImpl(&delegate_, *(info_.get()), net::BoundNetLog()); |
| allocated_downloads_.insert(download); |
| return download; |
| } |
| @@ -195,7 +241,9 @@ class DownloadItemTest : public testing::Test { |
| EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(item, _)); |
| } |
| - item->Start(download_file.Pass()); |
| + 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 |
| @@ -226,13 +274,17 @@ class DownloadItemTest : public testing::Test { |
| } |
| // Cleanup a download item (specifically get rid of the DownloadFile on it). |
| - // The item must be in the IN_PROGRESS state. |
| - void CleanupItem(DownloadItemImpl* item, MockDownloadFile* download_file) { |
| - EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); |
| - |
| - EXPECT_CALL(*download_file, Cancel()); |
| - item->Cancel(true); |
| - loop_.RunUntilIdle(); |
| + // 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) { |
| + EXPECT_CALL(*download_file, Cancel()); |
| + item->Cancel(true); |
| + loop_.RunUntilIdle(); |
| + } |
| } |
| // Destroy a previously created download item. |
| @@ -318,8 +370,11 @@ TEST_F(DownloadItemTest, NotificationAfterInterrupted) { |
| EXPECT_CALL(*download_file, Cancel()); |
| MockObserver observer(item); |
| + EXPECT_CALL(*mock_delegate(), MockResumeInterruptedDownload(_,_)) |
| + .Times(0); |
| + |
| item->DestinationObserverAsWeakPtr()->DestinationError( |
| - DOWNLOAD_INTERRUPT_REASON_NONE); |
| + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
| ASSERT_TRUE(observer.CheckUpdated()); |
| } |
| @@ -341,6 +396,91 @@ TEST_F(DownloadItemTest, NotificationAfterDestroyed) { |
| ASSERT_TRUE(observer.CheckDestroyed()); |
| } |
| +TEST_F(DownloadItemTest, ContinueAfterInterrupted) { |
| + DownloadItemImpl* item = CreateDownloadItem(); |
| + MockObserver observer(item); |
| + DownloadItemImplDelegate::DownloadTargetCallback callback; |
| + MockDownloadFile* download_file = DoIntermediateRename(item); |
| + |
| + // Interrupt the download, using a continuable interrupt. |
| + EXPECT_CALL(*download_file, Detach()); |
| + item->DestinationObserverAsWeakPtr()->DestinationError( |
| + DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); |
| + ASSERT_TRUE(observer.CheckUpdated()); |
| + // Should attempt to auto-resume. Because we don't have a mock WebContents, |
| + // ResumeInterruptedDownload() will abort early, with another interrupt, |
| + // which will be ignored. |
| + ASSERT_EQ(1, observer.CheckInterrupted()); |
| + ASSERT_EQ(0, observer.CheckResumed()); |
| + RunAllPendingInMessageLoops(); |
| + |
| + CleanupItem(item, download_file, DownloadItem::INTERRUPTED); |
| +} |
| + |
| +// Same as above, but with a non-continuable interrupt. |
| +TEST_F(DownloadItemTest, RestartAfterInterrupted) { |
| + DownloadItemImpl* item = CreateDownloadItem(); |
| + MockObserver observer(item); |
| + DownloadItemImplDelegate::DownloadTargetCallback callback; |
| + MockDownloadFile* download_file = DoIntermediateRename(item); |
| + |
| + // Interrupt the download, using a restartable interrupt. |
| + EXPECT_CALL(*download_file, Cancel()); |
| + item->DestinationObserverAsWeakPtr()->DestinationError( |
| + DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
| + ASSERT_TRUE(observer.CheckUpdated()); |
| + // Should not try to auto-resume. |
| + ASSERT_EQ(1, observer.CheckInterrupted()); |
| + ASSERT_EQ(0, observer.CheckResumed()); |
| + RunAllPendingInMessageLoops(); |
| + |
| + CleanupItem(item, download_file, DownloadItem::INTERRUPTED); |
| +} |
| + |
| +TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) { |
| + CommandLine::ForCurrentProcess()->AppendSwitch( |
| + switches::kEnableDownloadResumption); |
| + |
| + DownloadItemImpl* item = CreateDownloadItem(); |
| + base::WeakPtr<DownloadDestinationObserver> as_observer( |
| + item->DestinationObserverAsWeakPtr()); |
| + MockObserver observer(item); |
| + MockDownloadFile* mock_download_file(new NiceMock<MockDownloadFile>); |
| + scoped_ptr<DownloadFile> download_file(mock_download_file); |
|
asanka
2013/01/04 22:54:16
Nit: You can use PassAs<DownloadFile>() and just k
Randy Smith (Not in Mondays)
2013/01/07 20:54:10
I don't *think* that will work because of ownershi
|
| + MockRequestHandle* mock_request_handle = new NiceMock<MockRequestHandle>; |
| + scoped_ptr<DownloadRequestHandleInterface> request_handle( |
| + mock_request_handle); |
| + |
| + for (int i = 0; i < (DownloadItemImpl::kMaxAutoResumeAttempts + 2); ++i) { |
|
asanka
2013/01/04 22:54:16
Can you explain the +2 here? You have an ASSERT_EQ
Randy Smith (Not in Mondays)
2013/01/07 20:54:10
I think it was to make sure the test (to avoid aut
|
| + DVLOG(20) << "Loop iteration " << i; |
| + |
| + // It's too complicated to set up a WebContents instance that would cause |
| + // the MockDownloadItemDelegate's ResumeInterruptedDownload() function |
| + // to be callled, so we simply verify that GetWebContents() is called. |
| + if (i < (DownloadItemImpl::kMaxAutoResumeAttempts - 1)) { |
| + EXPECT_CALL(*mock_request_handle, GetWebContents()) |
| + .WillOnce(Return(static_cast<WebContents*>(NULL))); |
|
asanka
2013/01/04 22:54:16
Since mock_request_handle is a NiceMock, would it
Randy Smith (Not in Mondays)
2013/01/07 20:54:10
The original code had Times(0)/Times(1), but I've
|
| + } |
| + |
| + item->Start(download_file.Pass(), request_handle.Pass()); |
| + |
| + ASSERT_EQ(i, observer.CheckResumed()); |
| + |
| + // Use a continuable interrupt. |
| + item->DestinationObserverAsWeakPtr()->DestinationError( |
| + DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); |
| + |
| + mock_download_file = new NiceMock<MockDownloadFile>; |
| + download_file.reset(mock_download_file); |
| + mock_request_handle = new NiceMock<MockRequestHandle>; |
| + request_handle.reset(mock_request_handle); |
| + |
| + ASSERT_EQ(i + 1, observer.CheckInterrupted()); |
| + } |
| + |
| + CleanupItem(item, mock_download_file, DownloadItem::INTERRUPTED); |
| +} |
| + |
| TEST_F(DownloadItemTest, NotificationAfterRemove) { |
| DownloadItemImpl* item = CreateDownloadItem(); |
| MockDownloadFile* download_file = AddDownloadFileToDownloadItem(item, NULL); |
| @@ -415,18 +555,28 @@ TEST_F(DownloadItemTest, NotificationAfterOnDownloadTargetDetermined) { |
| EXPECT_TRUE(observer.CheckUpdated()); |
| EXPECT_EQ(new_intermediate_path, item->GetFullPath()); |
| - CleanupItem(item, download_file); |
| + CleanupItem(item, download_file, DownloadItem::IN_PROGRESS); |
| } |
| TEST_F(DownloadItemTest, NotificationAfterTogglePause) { |
| DownloadItemImpl* item = CreateDownloadItem(); |
| MockObserver 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(_)); |
| + item->Start(download_file.Pass(), request_handle.Pass()); |
| item->TogglePause(); |
| ASSERT_TRUE(observer.CheckUpdated()); |
| item->TogglePause(); |
| ASSERT_TRUE(observer.CheckUpdated()); |
| + RunAllPendingInMessageLoops(); |
| + |
| + CleanupItem(item, mock_download_file, DownloadItem::IN_PROGRESS); |
| } |
| TEST_F(DownloadItemTest, DisplayName) { |
| @@ -449,7 +599,7 @@ TEST_F(DownloadItemTest, DisplayName) { |
| item->SetDisplayName(FilePath(FILE_PATH_LITERAL("new.name"))); |
| EXPECT_EQ(FILE_PATH_LITERAL("new.name"), |
| item->GetFileNameToReportUser().value()); |
| - CleanupItem(item, download_file); |
| + CleanupItem(item, download_file, DownloadItem::IN_PROGRESS); |
| } |
| // Test to make sure that Start method calls DF initialize properly. |
| @@ -458,9 +608,11 @@ TEST_F(DownloadItemTest, Start) { |
| scoped_ptr<DownloadFile> download_file(mock_download_file); |
| DownloadItemImpl* item = CreateDownloadItem(); |
| EXPECT_CALL(*mock_download_file, Initialize(_)); |
| - item->Start(download_file.Pass()); |
| + scoped_ptr<DownloadRequestHandleInterface> request_handle( |
| + new NiceMock<MockRequestHandle>); |
| + item->Start(download_file.Pass(), request_handle.Pass()); |
| - CleanupItem(item, mock_download_file); |
| + CleanupItem(item, mock_download_file, DownloadItem::IN_PROGRESS); |
| } |
| // Test that the delegate is invoked after the download file is renamed. |
| @@ -537,9 +689,9 @@ TEST_F(DownloadItemTest, Interrupted) { |
| EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); |
| EXPECT_EQ(reason, item->GetLastReason()); |
| - // Cancel should result in no change. |
| + // Cancel should kill it. |
| item->Cancel(true); |
| - EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); |
| + EXPECT_EQ(DownloadItem::CANCELLED, item->GetState()); |
| EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_USER_CANCELED, item->GetLastReason()); |
| } |