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 0afad1bfca248fea496ee1801387afca60fc2572..1ea59d3c968e9dc02f90db3193f76ed7e5dfb0a3 100644 |
--- a/content/browser/download/download_item_impl_unittest.cc |
+++ b/content/browser/download/download_item_impl_unittest.cc |
@@ -5,11 +5,15 @@ |
#include "content/browser/download/download_item_impl.h" |
#include <stdint.h> |
+ |
+#include <iterator> |
+#include <queue> |
#include <utility> |
#include "base/callback.h" |
#include "base/feature_list.h" |
#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
#include "base/stl_util.h" |
#include "base/threading/thread.h" |
#include "content/browser/byte_stream.h" |
@@ -18,26 +22,32 @@ |
#include "content/browser/download/download_item_impl_delegate.h" |
#include "content/browser/download/download_request_handle.h" |
#include "content/browser/download/mock_download_file.h" |
+#include "content/public/browser/browser_thread.h" |
#include "content/public/browser/download_destination_observer.h" |
#include "content/public/browser/download_interrupt_reasons.h" |
#include "content/public/browser/download_url_parameters.h" |
#include "content/public/common/content_features.h" |
#include "content/public/test/mock_download_item.h" |
#include "content/public/test/test_browser_context.h" |
-#include "content/public/test/test_browser_thread.h" |
+#include "content/public/test/test_browser_thread_bundle.h" |
#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
-using ::testing::_; |
+using ::testing::DoAll; |
using ::testing::NiceMock; |
using ::testing::Property; |
using ::testing::Return; |
using ::testing::SaveArg; |
using ::testing::StrictMock; |
+using ::testing::WithArg; |
+using ::testing::_; |
const int kDownloadChunkSize = 1000; |
const int kDownloadSpeed = 1000; |
-const base::FilePath::CharType kDummyPath[] = FILE_PATH_LITERAL("/testpath"); |
+const base::FilePath::CharType kDummyTargetPath[] = |
+ FILE_PATH_LITERAL("/testpath"); |
+const base::FilePath::CharType kDummyIntermediatePath[] = |
+ FILE_PATH_LITERAL("/testpathx"); |
namespace content { |
@@ -159,7 +169,7 @@ class TestDownloadItemObserver : public DownloadItem::Observer { |
<< " download = " << download->DebugString(false); |
destroyed_ = true; |
item_->RemoveObserver(this); |
- item_ = NULL; |
+ item_ = nullptr; |
} |
DownloadItem* item_; |
@@ -182,20 +192,28 @@ ACTION_P2(ScheduleRenameCallback, interrupt_reason, new_path) { |
base::Bind(arg1, interrupt_reason, new_path)); |
} |
+// Schedules a task to invoke a callback that's bound to the specified |
+// parameter. |
+// E.g.: |
+// |
+// EXPECT_CALL(foo, Bar(1, _)) |
+// .WithArg<1>(InvokeCallbackWithParam(0)); |
+// |
+// .. will invoke the second argument to Bar with 0 as the parameter. |
+ACTION_P(InvokeCallbackWithParam, param) { |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(arg0, param)); |
+} |
+ |
+ACTION_P(InvokeClosure, closure) { |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, closure); |
+} |
+ |
} // namespace |
class DownloadItemTest : public testing::Test { |
public: |
- DownloadItemTest() |
- : ui_thread_(BrowserThread::UI, &loop_), |
- file_thread_(BrowserThread::FILE, &loop_), |
- delegate_() { |
- } |
- |
- ~DownloadItemTest() { |
- } |
- |
- virtual void SetUp() { |
+ DownloadItemTest() { |
base::FeatureList::ClearInstanceForTesting(); |
scoped_ptr<base::FeatureList> feature_list(new base::FeatureList); |
feature_list->InitializeFromCommandLine(features::kDownloadResumption.name, |
@@ -203,11 +221,19 @@ class DownloadItemTest : public testing::Test { |
base::FeatureList::SetInstance(std::move(feature_list)); |
} |
- virtual void TearDown() { |
- ui_thread_.DeprecatedGetThreadObject()->message_loop()->RunUntilIdle(); |
+ ~DownloadItemTest() { |
+ RunAllPendingInMessageLoops(); |
STLDeleteElements(&allocated_downloads_); |
} |
+ DownloadItemImpl* CreateDownloadItemWithCreateInfo( |
+ scoped_ptr<DownloadCreateInfo> info) { |
+ DownloadItemImpl* download = new DownloadItemImpl( |
+ &delegate_, next_download_id_++, *(info.get()), net::BoundNetLog()); |
+ allocated_downloads_.insert(download); |
+ return download; |
+ } |
+ |
// This class keeps ownership of the created download item; it will |
// be torn down at the end of the test unless DestroyDownloadItem is |
// called. |
@@ -217,24 +243,16 @@ class DownloadItemTest : public testing::Test { |
info.reset(new DownloadCreateInfo()); |
info->save_info = scoped_ptr<DownloadSaveInfo>(new DownloadSaveInfo()); |
info->save_info->prompt_for_save_location = false; |
- info->url_chain.push_back(GURL()); |
+ info->url_chain.push_back(GURL("http://example.com/download")); |
info->etag = "SomethingToSatisfyResumption"; |
return CreateDownloadItemWithCreateInfo(std::move(info)); |
} |
- DownloadItemImpl* CreateDownloadItemWithCreateInfo( |
- scoped_ptr<DownloadCreateInfo> info) { |
- DownloadItemImpl* download = new DownloadItemImpl( |
- &delegate_, next_download_id_++, *(info.get()), net::BoundNetLog()); |
- allocated_downloads_.insert(download); |
- return download; |
- } |
- |
// Add DownloadFile to DownloadItem |
- MockDownloadFile* AddDownloadFileToDownloadItem( |
+ MockDownloadFile* CallDownloadItemStart( |
DownloadItemImpl* item, |
- DownloadItemImplDelegate::DownloadTargetCallback *callback) { |
+ DownloadItemImplDelegate::DownloadTargetCallback* callback) { |
MockDownloadFile* mock_download_file(new StrictMock<MockDownloadFile>); |
scoped_ptr<DownloadFile> download_file(mock_download_file); |
EXPECT_CALL(*mock_download_file, Initialize(_)); |
@@ -250,7 +268,7 @@ class DownloadItemTest : public testing::Test { |
scoped_ptr<DownloadRequestHandleInterface> request_handle( |
new NiceMock<MockRequestHandle>); |
item->Start(std::move(download_file), std::move(request_handle)); |
- loop_.RunUntilIdle(); |
+ RunAllPendingInMessageLoops(); |
// So that we don't have a function writing to a stack variable |
// lying around if the above failed. |
@@ -266,18 +284,17 @@ class DownloadItemTest : public testing::Test { |
} |
// Perform the intermediate rename for |item|. The target path for the |
- // download will be set to kDummyPath. Returns the MockDownloadFile* that was |
+ // download will be set to kDummyTargetPath. Returns the MockDownloadFile* |
+ // that was |
// added to the DownloadItem. |
MockDownloadFile* DoIntermediateRename(DownloadItemImpl* item, |
DownloadDangerType danger_type) { |
EXPECT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); |
EXPECT_TRUE(item->GetTargetFilePath().empty()); |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(item, &callback); |
- base::FilePath target_path(kDummyPath); |
- base::FilePath intermediate_path( |
- target_path.InsertBeforeExtensionASCII("x")); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); |
+ base::FilePath target_path(kDummyTargetPath); |
+ base::FilePath intermediate_path(kDummyIntermediatePath); |
EXPECT_CALL(*download_file, RenameAndUniquify(intermediate_path, _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, |
intermediate_path)); |
@@ -287,6 +304,22 @@ class DownloadItemTest : public testing::Test { |
return download_file; |
} |
+ void DoDestinationComplete(DownloadItemImpl* item, |
+ MockDownloadFile* download_file) { |
+ EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)) |
+ .WillOnce(Return(true)); |
+ base::FilePath final_path(kDummyTargetPath); |
+ EXPECT_CALL(*download_file, RenameAndAnnotate(_, _)) |
+ .WillOnce( |
+ ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, final_path)); |
+ EXPECT_CALL(*download_file, FullPath()) |
+ .WillRepeatedly(Return(base::FilePath(kDummyTargetPath))); |
+ EXPECT_CALL(*download_file, Detach()); |
+ |
+ item->DestinationObserverAsWeakPtr()->DestinationCompleted(""); |
+ RunAllPendingInMessageLoops(); |
+ } |
+ |
// Cleanup a download item (specifically get rid of the DownloadFile on it). |
// The item must be in the expected state. |
void CleanupItem(DownloadItemImpl* item, |
@@ -298,7 +331,7 @@ class DownloadItemTest : public testing::Test { |
if (download_file) |
EXPECT_CALL(*download_file, Cancel()); |
item->Cancel(true); |
- loop_.RunUntilIdle(); |
+ RunAllPendingInMessageLoops(); |
} |
} |
@@ -308,9 +341,7 @@ class DownloadItemTest : public testing::Test { |
delete item; |
} |
- void RunAllPendingInMessageLoops() { |
- loop_.RunUntilIdle(); |
- } |
+ void RunAllPendingInMessageLoops() { base::RunLoop().RunUntilIdle(); } |
MockDelegate* mock_delegate() { |
return &delegate_; |
@@ -323,11 +354,9 @@ class DownloadItemTest : public testing::Test { |
private: |
int next_download_id_ = DownloadItem::kInvalidId + 1; |
- base::MessageLoopForUI loop_; |
- TestBrowserThread ui_thread_; // UI thread |
- TestBrowserThread file_thread_; // FILE thread |
StrictMock<MockDelegate> delegate_; |
std::set<DownloadItem*> allocated_downloads_; |
+ TestBrowserThreadBundle thread_bundle_; |
}; |
// Tests to ensure calls that change a DownloadItem generate an update to |
@@ -349,8 +378,7 @@ TEST_F(DownloadItemTest, NotificationAfterUpdate) { |
TEST_F(DownloadItemTest, NotificationAfterCancel) { |
DownloadItemImpl* user_cancel = CreateDownloadItem(); |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(user_cancel, NULL); |
+ MockDownloadFile* download_file = CallDownloadItemStart(user_cancel, nullptr); |
EXPECT_CALL(*download_file, Cancel()); |
TestDownloadItemObserver observer1(user_cancel); |
@@ -358,7 +386,7 @@ TEST_F(DownloadItemTest, NotificationAfterCancel) { |
ASSERT_TRUE(observer1.CheckAndResetDownloadUpdated()); |
DownloadItemImpl* system_cancel = CreateDownloadItem(); |
- download_file = AddDownloadFileToDownloadItem(system_cancel, NULL); |
+ download_file = CallDownloadItemStart(system_cancel, nullptr); |
EXPECT_CALL(*download_file, Cancel()); |
TestDownloadItemObserver observer2(system_cancel); |
@@ -369,11 +397,10 @@ TEST_F(DownloadItemTest, NotificationAfterCancel) { |
TEST_F(DownloadItemTest, NotificationAfterComplete) { |
DownloadItemImpl* item = CreateDownloadItem(); |
TestDownloadItemObserver observer(item); |
- |
- item->OnAllDataSaved(DownloadItem::kEmptyFileHash); |
+ MockDownloadFile* download_file = |
+ DoIntermediateRename(item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); |
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); |
- |
- item->MarkAsComplete(); |
+ DoDestinationComplete(item, download_file); |
ASSERT_TRUE(observer.CheckAndResetDownloadUpdated()); |
} |
@@ -471,9 +498,10 @@ TEST_F(DownloadItemTest, UnresumableInterrupt) { |
// Fail final rename with unresumable reason. |
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _)) |
.WillOnce(Return(true)); |
- EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) |
+ EXPECT_CALL(*download_file, |
+ RenameAndAnnotate(base::FilePath(kDummyTargetPath), _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_FILE_BLOCKED, |
- base::FilePath(kDummyPath))); |
+ base::FilePath())); |
EXPECT_CALL(*download_file, Cancel()); |
// Complete download to trigger final rename. |
@@ -494,9 +522,9 @@ TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) { |
base::WeakPtr<DownloadDestinationObserver> as_observer( |
item->DestinationObserverAsWeakPtr()); |
TestDownloadItemObserver observer(item); |
- MockDownloadFile* mock_download_file(NULL); |
+ MockDownloadFile* mock_download_file(nullptr); |
scoped_ptr<DownloadFile> download_file; |
- MockRequestHandle* mock_request_handle(NULL); |
+ MockRequestHandle* mock_request_handle(nullptr); |
scoped_ptr<DownloadRequestHandleInterface> request_handle; |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
@@ -517,22 +545,24 @@ TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) { |
ON_CALL(*mock_download_file, FullPath()) |
.WillByDefault(Return(base::FilePath())); |
- // Copied key parts of DoIntermediateRename & AddDownloadFileToDownloadItem |
+ // Copied key parts of DoIntermediateRename & CallDownloadItemStart |
// to allow for holding onto the request handle. |
item->Start(std::move(download_file), std::move(request_handle)); |
RunAllPendingInMessageLoops(); |
+ |
+ base::FilePath target_path(kDummyTargetPath); |
+ base::FilePath intermediate_path(kDummyIntermediatePath); |
if (i == 0) { |
- // Target determination is only done the first time through. |
- base::FilePath target_path(kDummyPath); |
- base::FilePath intermediate_path( |
- target_path.InsertBeforeExtensionASCII("x")); |
+ // RenameAndUniquify is only called the first time. In all the subsequent |
+ // iterations, the intermediate file already has the correct name, hence |
+ // no rename is necessary. |
EXPECT_CALL(*mock_download_file, RenameAndUniquify(intermediate_path, _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, |
intermediate_path)); |
- callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, |
- DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path); |
- RunAllPendingInMessageLoops(); |
} |
+ callback.Run(target_path, DownloadItem::TARGET_DISPOSITION_OVERWRITE, |
+ DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, intermediate_path); |
+ RunAllPendingInMessageLoops(); |
// Use a continuable interrupt. |
item->DestinationObserverAsWeakPtr()->DestinationError( |
@@ -541,6 +571,7 @@ TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) { |
::testing::Mock::VerifyAndClearExpectations(mock_download_file); |
} |
+ EXPECT_EQ(DownloadItem::INTERRUPTED, item->GetState()); |
EXPECT_EQ(1, observer.interrupt_count()); |
CleanupItem(item, nullptr, DownloadItem::INTERRUPTED); |
} |
@@ -588,7 +619,7 @@ TEST_F(DownloadItemTest, ResumeUsingFinalURL) { |
TEST_F(DownloadItemTest, NotificationAfterRemove) { |
DownloadItemImpl* item = CreateDownloadItem(); |
- MockDownloadFile* download_file = AddDownloadFileToDownloadItem(item, NULL); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, nullptr); |
EXPECT_CALL(*download_file, Cancel()); |
EXPECT_CALL(*mock_delegate(), DownloadRemoved(_)); |
TestDownloadItemObserver observer(item); |
@@ -601,16 +632,21 @@ TEST_F(DownloadItemTest, NotificationAfterRemove) { |
TEST_F(DownloadItemTest, NotificationAfterOnContentCheckCompleted) { |
// Setting to NOT_DANGEROUS does not trigger a notification. |
DownloadItemImpl* safe_item = CreateDownloadItem(); |
+ MockDownloadFile* download_file = |
+ DoIntermediateRename(safe_item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); |
TestDownloadItemObserver safe_observer(safe_item); |
safe_item->OnAllDataSaved(std::string()); |
EXPECT_TRUE(safe_observer.CheckAndResetDownloadUpdated()); |
safe_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); |
EXPECT_TRUE(safe_observer.CheckAndResetDownloadUpdated()); |
+ CleanupItem(safe_item, download_file, DownloadItem::IN_PROGRESS); |
// Setting to unsafe url or unsafe file should trigger a notification. |
DownloadItemImpl* unsafeurl_item = |
CreateDownloadItem(); |
+ download_file = |
+ DoIntermediateRename(unsafeurl_item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); |
TestDownloadItemObserver unsafeurl_observer(unsafeurl_item); |
unsafeurl_item->OnAllDataSaved(std::string()); |
@@ -618,11 +654,17 @@ TEST_F(DownloadItemTest, NotificationAfterOnContentCheckCompleted) { |
unsafeurl_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_DANGEROUS_URL); |
EXPECT_TRUE(unsafeurl_observer.CheckAndResetDownloadUpdated()); |
+ EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)) |
+ .WillOnce(Return(true)); |
+ EXPECT_CALL(*download_file, RenameAndAnnotate(_, _)); |
unsafeurl_item->ValidateDangerousDownload(); |
EXPECT_TRUE(unsafeurl_observer.CheckAndResetDownloadUpdated()); |
+ CleanupItem(unsafeurl_item, download_file, DownloadItem::IN_PROGRESS); |
DownloadItemImpl* unsafefile_item = |
CreateDownloadItem(); |
+ download_file = |
+ DoIntermediateRename(unsafefile_item, DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS); |
TestDownloadItemObserver unsafefile_observer(unsafefile_item); |
unsafefile_item->OnAllDataSaved(std::string()); |
@@ -630,8 +672,12 @@ TEST_F(DownloadItemTest, NotificationAfterOnContentCheckCompleted) { |
unsafefile_item->OnContentCheckCompleted(DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE); |
EXPECT_TRUE(unsafefile_observer.CheckAndResetDownloadUpdated()); |
+ EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)) |
+ .WillOnce(Return(true)); |
+ EXPECT_CALL(*download_file, RenameAndAnnotate(_, _)); |
unsafefile_item->ValidateDangerousDownload(); |
EXPECT_TRUE(unsafefile_observer.CheckAndResetDownloadUpdated()); |
+ CleanupItem(unsafefile_item, download_file, DownloadItem::IN_PROGRESS); |
} |
// DownloadItemImpl::OnDownloadTargetDetermined will schedule a task to run |
@@ -642,10 +688,9 @@ TEST_F(DownloadItemTest, NotificationAfterOnContentCheckCompleted) { |
TEST_F(DownloadItemTest, NotificationAfterOnDownloadTargetDetermined) { |
DownloadItemImpl* item = CreateDownloadItem(); |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(item, &callback); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); |
TestDownloadItemObserver observer(item); |
- base::FilePath target_path(kDummyPath); |
+ base::FilePath target_path(kDummyTargetPath); |
base::FilePath intermediate_path(target_path.InsertBeforeExtensionASCII("x")); |
base::FilePath new_intermediate_path( |
target_path.InsertBeforeExtensionASCII("y")); |
@@ -693,9 +738,9 @@ TEST_F(DownloadItemTest, NotificationAfterTogglePause) { |
TEST_F(DownloadItemTest, DisplayName) { |
DownloadItemImpl* item = CreateDownloadItem(); |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(item, &callback); |
- base::FilePath target_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); |
+ base::FilePath target_path( |
+ base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); |
base::FilePath intermediate_path(target_path.InsertBeforeExtensionASCII("x")); |
EXPECT_EQ(FILE_PATH_LITERAL(""), |
item->GetFileNameToReportUser().value()); |
@@ -732,9 +777,9 @@ TEST_F(DownloadItemTest, Start) { |
TEST_F(DownloadItemTest, CallbackAfterRename) { |
DownloadItemImpl* item = CreateDownloadItem(); |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(item, &callback); |
- base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); |
+ base::FilePath final_path( |
+ base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); |
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); |
base::FilePath new_intermediate_path( |
final_path.InsertBeforeExtensionASCII("y")); |
@@ -768,9 +813,9 @@ TEST_F(DownloadItemTest, CallbackAfterRename) { |
TEST_F(DownloadItemTest, CallbackAfterInterruptedRename) { |
DownloadItemImpl* item = CreateDownloadItem(); |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(item, &callback); |
- base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); |
+ base::FilePath final_path( |
+ base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); |
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); |
base::FilePath new_intermediate_path( |
final_path.InsertBeforeExtensionASCII("y")); |
@@ -814,13 +859,13 @@ TEST_F(DownloadItemTest, Interrupted) { |
TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Restart) { |
DownloadItemImpl* item = CreateDownloadItem(); |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(item, &callback); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); |
item->DestinationObserverAsWeakPtr()->DestinationError( |
DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); |
- base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); |
+ base::FilePath final_path( |
+ base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); |
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); |
base::FilePath new_intermediate_path( |
final_path.InsertBeforeExtensionASCII("y")); |
@@ -847,13 +892,13 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Restart) { |
TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Continue) { |
DownloadItemImpl* item = CreateDownloadItem(); |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(item, &callback); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); |
item->DestinationObserverAsWeakPtr()->DestinationError( |
DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); |
ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); |
- base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); |
+ base::FilePath final_path( |
+ base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); |
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); |
base::FilePath new_intermediate_path( |
final_path.InsertBeforeExtensionASCII("y")); |
@@ -880,13 +925,13 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Continue) { |
TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Failed) { |
DownloadItemImpl* item = CreateDownloadItem(); |
DownloadItemImplDelegate::DownloadTargetCallback callback; |
- MockDownloadFile* download_file = |
- AddDownloadFileToDownloadItem(item, &callback); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, &callback); |
item->DestinationObserverAsWeakPtr()->DestinationError( |
DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED); |
ASSERT_EQ(DownloadItem::IN_PROGRESS, item->GetState()); |
- base::FilePath final_path(base::FilePath(kDummyPath).AppendASCII("foo.bar")); |
+ base::FilePath final_path( |
+ base::FilePath(kDummyTargetPath).AppendASCII("foo.bar")); |
base::FilePath intermediate_path(final_path.InsertBeforeExtensionASCII("x")); |
base::FilePath new_intermediate_path( |
final_path.InsertBeforeExtensionASCII("y")); |
@@ -910,7 +955,7 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Failed) { |
TEST_F(DownloadItemTest, Canceled) { |
DownloadItemImpl* item = CreateDownloadItem(); |
- MockDownloadFile* download_file = AddDownloadFileToDownloadItem(item, NULL); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, nullptr); |
// Confirm cancel sets state properly. |
EXPECT_CALL(*download_file, Cancel()); |
@@ -979,6 +1024,7 @@ TEST_F(DownloadItemTest, DestinationError) { |
TEST_F(DownloadItemTest, DestinationCompleted) { |
DownloadItemImpl* item = CreateDownloadItem(); |
+ MockDownloadFile* download_file = CallDownloadItemStart(item, nullptr); |
base::WeakPtr<DownloadDestinationObserver> as_observer( |
item->DestinationObserverAsWeakPtr()); |
TestDownloadItemObserver observer(item); |
@@ -1004,6 +1050,11 @@ TEST_F(DownloadItemTest, DestinationCompleted) { |
EXPECT_EQ("livebeef", item->GetHash()); |
EXPECT_EQ("", item->GetHashState()); |
EXPECT_TRUE(item->AllDataSaved()); |
+ |
+ // Even though the DownloadItem receives a DestinationCompleted() event, |
+ // target determination hasn't completed, hence the download item is stuck in |
+ // TARGET_PENDING. |
+ CleanupItem(item, download_file, DownloadItem::IN_PROGRESS); |
} |
TEST_F(DownloadItemTest, EnabledActionsForNormalDownload) { |
@@ -1020,7 +1071,7 @@ TEST_F(DownloadItemTest, EnabledActionsForNormalDownload) { |
// Complete |
EXPECT_CALL(*download_file, RenameAndAnnotate(_, _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, |
- base::FilePath(kDummyPath))); |
+ base::FilePath(kDummyTargetPath))); |
EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(item, _)) |
.WillOnce(Return(true)); |
EXPECT_CALL(*download_file, FullPath()) |
@@ -1052,7 +1103,7 @@ TEST_F(DownloadItemTest, EnabledActionsForTemporaryDownload) { |
.WillOnce(Return(true)); |
EXPECT_CALL(*download_file, RenameAndAnnotate(_, _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, |
- base::FilePath(kDummyPath))); |
+ base::FilePath(kDummyTargetPath))); |
EXPECT_CALL(*download_file, FullPath()) |
.WillOnce(Return(base::FilePath())); |
EXPECT_CALL(*download_file, Detach()); |
@@ -1077,7 +1128,7 @@ TEST_F(DownloadItemTest, EnabledActionsForInterruptedDownload) { |
ASSERT_EQ(DownloadItem::INTERRUPTED, item->GetState()); |
ASSERT_FALSE(item->GetTargetFilePath().empty()); |
EXPECT_FALSE(item->CanShowInFolder()); |
- EXPECT_FALSE(item->CanOpenDownload()); |
+ EXPECT_TRUE(item->CanOpenDownload()); |
} |
TEST_F(DownloadItemTest, EnabledActionsForCancelledDownload) { |
@@ -1112,9 +1163,10 @@ TEST_F(DownloadItemTest, CompleteDelegate_ReturnTrue) { |
EXPECT_FALSE(item->IsDangerous()); |
// Make sure the download can complete. |
- EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) |
+ EXPECT_CALL(*download_file, |
+ RenameAndAnnotate(base::FilePath(kDummyTargetPath), _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, |
- base::FilePath(kDummyPath))); |
+ base::FilePath(kDummyTargetPath))); |
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _)) |
.WillOnce(Return(true)); |
EXPECT_CALL(*download_file, FullPath()) |
@@ -1150,9 +1202,10 @@ TEST_F(DownloadItemTest, CompleteDelegate_BlockOnce) { |
EXPECT_FALSE(item->IsDangerous()); |
// Make sure the download can complete. |
- EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) |
+ EXPECT_CALL(*download_file, |
+ RenameAndAnnotate(base::FilePath(kDummyTargetPath), _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, |
- base::FilePath(kDummyPath))); |
+ base::FilePath(kDummyTargetPath))); |
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _)) |
.WillOnce(Return(true)); |
EXPECT_CALL(*download_file, FullPath()) |
@@ -1191,9 +1244,10 @@ TEST_F(DownloadItemTest, CompleteDelegate_SetDanger) { |
EXPECT_TRUE(item->IsDangerous()); |
// Make sure the download doesn't complete until we've validated it. |
- EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) |
+ EXPECT_CALL(*download_file, |
+ RenameAndAnnotate(base::FilePath(kDummyTargetPath), _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, |
- base::FilePath(kDummyPath))); |
+ base::FilePath(kDummyTargetPath))); |
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _)) |
.WillOnce(Return(true)); |
EXPECT_CALL(*download_file, FullPath()) |
@@ -1242,9 +1296,10 @@ TEST_F(DownloadItemTest, CompleteDelegate_BlockTwice) { |
EXPECT_FALSE(item->IsDangerous()); |
// Make sure the download can complete. |
- EXPECT_CALL(*download_file, RenameAndAnnotate(base::FilePath(kDummyPath), _)) |
+ EXPECT_CALL(*download_file, |
+ RenameAndAnnotate(base::FilePath(kDummyTargetPath), _)) |
.WillOnce(ScheduleRenameCallback(DOWNLOAD_INTERRUPT_REASON_NONE, |
- base::FilePath(kDummyPath))); |
+ base::FilePath(kDummyTargetPath))); |
EXPECT_CALL(*mock_delegate(), ShouldOpenDownload(item, _)) |
.WillOnce(Return(true)); |
EXPECT_CALL(*download_file, FullPath()) |
@@ -1319,6 +1374,412 @@ TEST_F(DownloadItemTest, StealInterruptedNonResumableDangerousDownload) { |
EXPECT_TRUE(returned_path.empty()); |
} |
+namespace { |
+ |
+// A call to a DownloadDestinationObserver method that's missing the |
+// DownloadDestinationObserver object. Currying this way allows us to bind a |
+// call prior to constructing the object on which the method would be invoked. |
+// This is necessary since we are going to construct various permutations of |
+// observer calls that will then be applied to a DownloadItem in a state as yet |
+// undetermined. |
+using CurriedObservation = |
+ base::Callback<void(base::WeakPtr<DownloadDestinationObserver>)>; |
+ |
+// A set of observations that are to be made during some event in the |
+// DownloadItemImpl control flow. Ordering of the observations is significant. |
+using ObservationSet = std::deque<CurriedObservation>; |
+ |
+// An ordered list of events. |
+// |
+// An "event" in this context refers to some stage in the DownloadItemImpl's |
+// workflow. For example: immediately after the initialization of the |
+// DownloadFile. |
+using EventList = std::deque<ObservationSet>; |
+ |
+// The following functions help us with currying the calls to |
+// DownloadDestinationObserver. If std::bind was allowed along with |
+// std::placeholders, it is possible to avoid these functions, but currently |
+// Chromium doesn't allow using std::bind for good reasons. |
+void DestinationUpdateInvoker( |
+ int64_t bytes_so_far, |
+ int64_t bytes_per_sec, |
+ const std::string& hash_state, |
+ base::WeakPtr<DownloadDestinationObserver> observer) { |
+ if (observer) |
+ observer->DestinationUpdate(bytes_so_far, bytes_per_sec, hash_state); |
+} |
+ |
+void DestinationErrorInvoker( |
+ DownloadInterruptReason reason, |
+ base::WeakPtr<DownloadDestinationObserver> observer) { |
+ if (observer) |
+ observer->DestinationError(reason); |
+} |
+ |
+void DestinationCompletedInvoker( |
+ const std::string& final_hash, |
+ base::WeakPtr<DownloadDestinationObserver> observer) { |
+ if (observer) |
+ observer->DestinationCompleted(final_hash); |
+} |
+ |
+// Given a set of observations (via the range |begin|..|end|), constructs a list |
+// of EventLists such that: |
+// |
+// * There are exactly |event_count| ObservationSets in each EventList. |
+// |
+// * Each ObservationSet in each EventList contains a subrange of observations |
+// from the input range, in the same order as the input range. |
+// |
+// * The ordering of the ObservationSet in each EventList is such that all |
+// observations in one ObservationSet occur earlier than all observations in |
+// an ObservationSet that follows it. |
+// |
+// * The list of EventLists together describe all the possible ways in which the |
+// list of observations can be distributed into |event_count| events. |
+std::vector<EventList> DistributeObservationsIntoEvents( |
+ const std::vector<CurriedObservation>::iterator begin, |
+ const std::vector<CurriedObservation>::iterator end, |
+ int event_count) { |
+ std::vector<EventList> all_observation_sets; |
+ for (auto partition = begin;; ++partition) { |
+ ObservationSet first_group_of_observations(begin, partition); |
+ if (event_count > 1) { |
+ std::vector<EventList> subsequent_bins = |
+ DistributeObservationsIntoEvents(partition, end, event_count - 1); |
+ for (const auto other_bins : subsequent_bins) { |
+ EventList observation_set; |
+ observation_set = other_bins; |
+ observation_set.push_front(first_group_of_observations); |
+ all_observation_sets.push_back(observation_set); |
+ } |
+ } else { |
+ EventList observation_set; |
+ observation_set.push_front(first_group_of_observations); |
+ all_observation_sets.push_back(observation_set); |
+ } |
+ if (partition == end) |
+ break; |
+ } |
+ return all_observation_sets; |
+} |
+ |
+// For the purpose of this tests, we are only concerned with 3 events: |
+// |
+// 1. Immediately after the DownloadFile is initialized. |
+// 2. Immediately after the DownloadTargetCallback is invoked. |
+// 3. Immediately after the intermediate file is renamed. |
+// |
+// We are going to take a couple of sets of DownloadDestinationObserver events |
+// and distribute them into the three events described above. And then we are |
+// going to invoke the observations while a DownloadItemImpl is carefully |
+// stepped through its stages. |
+ |
+std::vector<EventList> GenerateSuccessfulEventLists() { |
+ std::vector<CurriedObservation> all_observations; |
+ all_observations.push_back( |
+ base::Bind(&DestinationUpdateInvoker, 100, 100, "abc")); |
+ all_observations.push_back( |
+ base::Bind(&DestinationUpdateInvoker, 200, 100, "def")); |
+ all_observations.push_back( |
+ base::Bind(&DestinationCompletedInvoker, "final-hash-1")); |
+ return DistributeObservationsIntoEvents(all_observations.begin(), |
+ all_observations.end(), 3); |
+} |
+ |
+std::vector<EventList> GenerateFailingEventLists() { |
+ std::vector<CurriedObservation> all_observations; |
+ all_observations.push_back( |
+ base::Bind(&DestinationUpdateInvoker, 100, 100, "abc")); |
+ all_observations.push_back(base::Bind( |
+ &DestinationErrorInvoker, DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED)); |
+ return DistributeObservationsIntoEvents(all_observations.begin(), |
+ all_observations.end(), 3); |
+} |
+ |
+class DownloadItemDestinationUpdateRaceTest |
+ : public DownloadItemTest, |
+ public ::testing::WithParamInterface<EventList> { |
+ public: |
+ DownloadItemDestinationUpdateRaceTest() |
+ : DownloadItemTest(), |
+ item_(CreateDownloadItem()), |
+ file_(new ::testing::StrictMock<MockDownloadFile>()), |
+ request_handle_(new ::testing::StrictMock<MockRequestHandle>()) { |
+ DCHECK_EQ(GetParam().size(), 3u); |
+ EXPECT_CALL(*request_handle_, GetWebContents()) |
+ .WillRepeatedly(Return(nullptr)); |
+ } |
+ |
+ protected: |
+ const ObservationSet& PostInitializeFileObservations() { |
+ return GetParam().front(); |
+ } |
+ const ObservationSet& PostTargetDeterminationObservations() { |
+ return *(GetParam().begin() + 1); |
+ } |
+ const ObservationSet& PostIntermediateRenameObservations() { |
+ return *(GetParam().begin() + 2); |
+ } |
+ |
+ // Apply all the observations in |observations| to |observer|, but do so |
+ // asynchronously so that the events are applied in order behind any tasks |
+ // that are already scheduled. |
+ void ScheduleObservations( |
+ const ObservationSet& observations, |
+ base::WeakPtr<DownloadDestinationObserver> observer) { |
+ for (const auto action : observations) |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ base::Bind(action, observer)); |
+ } |
+ |
+ DownloadItemImpl* item_; |
+ scoped_ptr<MockDownloadFile> file_; |
+ scoped_ptr<MockRequestHandle> request_handle_; |
+ |
+ std::queue<base::Closure> successful_update_events_; |
+ std::queue<base::Closure> failing_update_events_; |
+}; |
+ |
+INSTANTIATE_TEST_CASE_P(Success, |
+ DownloadItemDestinationUpdateRaceTest, |
+ ::testing::ValuesIn(GenerateSuccessfulEventLists())); |
+ |
+INSTANTIATE_TEST_CASE_P(Failure, |
+ DownloadItemDestinationUpdateRaceTest, |
+ ::testing::ValuesIn(GenerateFailingEventLists())); |
+ |
+} // namespace |
+ |
+TEST_P(DownloadItemDestinationUpdateRaceTest, DumpParameters) { |
+ const auto parameter = GetParam(); |
+ class Dump : public DownloadDestinationObserver { |
+ public: |
+ void DestinationUpdate(int64_t bytes_so_far, |
+ int64_t bytes_per_sec, |
+ const std::string& hash_state) override { |
+ DVLOG(20) << " DestinationUpdate(bytes_so_far:" << bytes_so_far |
+ << " bytes_per_sec:" << bytes_per_sec |
+ << " hash_state:" << hash_state << ")"; |
+ }; |
+ |
+ void DestinationError(DownloadInterruptReason reason) override { |
+ DVLOG(20) << " DestinationError(reason:" |
+ << DownloadInterruptReasonToString(reason) << ")"; |
+ } |
+ |
+ void DestinationCompleted(const std::string& final_hash) override { |
+ DVLOG(20) << " DestinationCompleted(final_hash:" << final_hash << ")"; |
+ } |
+ } dumper; |
+ base::WeakPtrFactory<DownloadDestinationObserver> factory(&dumper); |
+ base::WeakPtr<DownloadDestinationObserver> dumper_ptr = factory.GetWeakPtr(); |
+ |
+ for (const auto bin : parameter) { |
+ DVLOG(20) << "New bin: " << bin.size() << " elements"; |
+ for (const auto invoker : bin) |
+ invoker.Run(dumper_ptr); |
+ } |
+} |
+ |
+TEST_P(DownloadItemDestinationUpdateRaceTest, InitDownloadFileFails) { |
+ // Expect that the download file and the request will be cancelled as a |
+ // result. |
+ EXPECT_CALL(*file_, Cancel()); |
+ EXPECT_CALL(*request_handle_, CancelRequest()); |
+ |
+ EXPECT_CALL(*file_, Initialize(_)) |
+ .WillOnce(InvokeCallbackWithParam( |
+ DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED)); |
+ item_->Start(std::move(file_), std::move(request_handle_)); |
+ |
+ RunAllPendingInMessageLoops(); |
+ |
+ // Since the download file initialization failed, no destination updates are |
+ // expected. No need to drain any action buckets. |
+ EXPECT_EQ(DownloadItem::INTERRUPTED, item_->GetState()); |
+ EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, |
+ item_->GetLastReason()); |
+} |
+ |
+TEST_P(DownloadItemDestinationUpdateRaceTest, DownloadCancelledByUser) { |
+ // Expect that the download file and the request will be cancelled as a |
+ // result. |
+ EXPECT_CALL(*file_, Cancel()); |
+ EXPECT_CALL(*request_handle_, CancelRequest()); |
+ |
+ base::RunLoop initialize_loop; |
+ DownloadFile::InitializeCallback initialize_callback; |
+ EXPECT_CALL(*file_, Initialize(_)) |
+ .WillOnce(DoAll(SaveArg<0>(&initialize_callback), |
+ InvokeClosure(initialize_loop.QuitClosure()))); |
+ |
+ item_->Start(std::move(file_), std::move(request_handle_)); |
+ initialize_loop.Run(); |
+ base::WeakPtr<DownloadDestinationObserver> destination_observer = |
+ item_->DestinationObserverAsWeakPtr(); |
+ |
+ base::RunLoop target_determination_loop; |
+ DownloadItemImplDelegate::DownloadTargetCallback target_callback; |
+ EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _)) |
+ .WillOnce(DoAll(SaveArg<1>(&target_callback), |
+ InvokeClosure(target_determination_loop.QuitClosure()))); |
+ ScheduleObservations(PostInitializeFileObservations(), destination_observer); |
+ initialize_callback.Run(DOWNLOAD_INTERRUPT_REASON_NONE); |
+ target_determination_loop.Run(); |
+ |
+ RunAllPendingInMessageLoops(); |
+ |
+ ASSERT_FALSE(target_callback.is_null()); |
+ ScheduleObservations(PostTargetDeterminationObservations(), |
+ destination_observer); |
+ target_callback.Run(base::FilePath(), |
+ DownloadItem::TARGET_DISPOSITION_OVERWRITE, |
+ DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, base::FilePath()); |
+ RunAllPendingInMessageLoops(); |
+ |
+ EXPECT_EQ(DownloadItem::CANCELLED, item_->GetState()); |
+} |
+ |
+TEST_P(DownloadItemDestinationUpdateRaceTest, IntermediateRenameFails) { |
+ // Expect that the download file and the request will be cancelled as a |
+ // result. |
+ EXPECT_CALL(*file_, Cancel()); |
+ EXPECT_CALL(*request_handle_, CancelRequest()); |
+ |
+ // Intermediate rename loop is not used immediately, but let's set up the |
+ // DownloadFile expectations since we are about to transfer its ownership to |
+ // the DownloadItem. |
+ base::RunLoop intermediate_rename_loop; |
+ DownloadFile::RenameCompletionCallback intermediate_rename_callback; |
+ EXPECT_CALL(*file_, RenameAndUniquify(_, _)) |
+ .WillOnce(DoAll(SaveArg<1>(&intermediate_rename_callback), |
+ InvokeClosure(intermediate_rename_loop.QuitClosure()))); |
+ |
+ base::RunLoop initialize_loop; |
+ DownloadFile::InitializeCallback initialize_callback; |
+ EXPECT_CALL(*file_, Initialize(_)) |
+ .WillOnce(DoAll(SaveArg<0>(&initialize_callback), |
+ InvokeClosure(initialize_loop.QuitClosure()))); |
+ |
+ item_->Start(std::move(file_), std::move(request_handle_)); |
+ initialize_loop.Run(); |
+ base::WeakPtr<DownloadDestinationObserver> destination_observer = |
+ item_->DestinationObserverAsWeakPtr(); |
+ |
+ base::RunLoop target_determination_loop; |
+ DownloadItemImplDelegate::DownloadTargetCallback target_callback; |
+ EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _)) |
+ .WillOnce(DoAll(SaveArg<1>(&target_callback), |
+ InvokeClosure(target_determination_loop.QuitClosure()))); |
+ ScheduleObservations(PostInitializeFileObservations(), destination_observer); |
+ initialize_callback.Run(DOWNLOAD_INTERRUPT_REASON_NONE); |
+ target_determination_loop.Run(); |
+ |
+ RunAllPendingInMessageLoops(); |
+ ASSERT_FALSE(target_callback.is_null()); |
+ |
+ ScheduleObservations(PostTargetDeterminationObservations(), |
+ destination_observer); |
+ target_callback.Run(base::FilePath(kDummyTargetPath), |
+ DownloadItem::TARGET_DISPOSITION_OVERWRITE, |
+ DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, |
+ base::FilePath(kDummyIntermediatePath)); |
+ |
+ intermediate_rename_loop.Run(); |
+ ASSERT_FALSE(intermediate_rename_callback.is_null()); |
+ |
+ ScheduleObservations(PostIntermediateRenameObservations(), |
+ destination_observer); |
+ intermediate_rename_callback.Run(DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, |
+ base::FilePath()); |
+ RunAllPendingInMessageLoops(); |
+ |
+ EXPECT_EQ(DownloadItem::INTERRUPTED, item_->GetState()); |
+} |
+ |
+TEST_P(DownloadItemDestinationUpdateRaceTest, IntermediateRenameSucceeds) { |
+ // We expect either that the download will fail (in which case the request and |
+ // the download file will be cancelled), or it will succeed (in which case the |
+ // DownloadFile will Detach()). It depends on the list of observations that |
+ // are given to us. |
+ EXPECT_CALL(*file_, Cancel()).Times(::testing::AnyNumber()); |
+ EXPECT_CALL(*request_handle_, CancelRequest()).Times(::testing::AnyNumber()); |
+ EXPECT_CALL(*file_, Detach()).Times(::testing::AnyNumber()); |
+ |
+ EXPECT_CALL(*file_, FullPath()) |
+ .WillRepeatedly(Return(base::FilePath(kDummyIntermediatePath))); |
+ |
+ // Intermediate rename loop is not used immediately, but let's set up the |
+ // DownloadFile expectations since we are about to transfer its ownership to |
+ // the DownloadItem. |
+ base::RunLoop intermediate_rename_loop; |
+ DownloadFile::RenameCompletionCallback intermediate_rename_callback; |
+ EXPECT_CALL(*file_, RenameAndUniquify(_, _)) |
+ .WillOnce(DoAll(SaveArg<1>(&intermediate_rename_callback), |
+ InvokeClosure(intermediate_rename_loop.QuitClosure()))); |
+ |
+ base::RunLoop initialize_loop; |
+ DownloadFile::InitializeCallback initialize_callback; |
+ EXPECT_CALL(*file_, Initialize(_)) |
+ .WillOnce(DoAll(SaveArg<0>(&initialize_callback), |
+ InvokeClosure(initialize_loop.QuitClosure()))); |
+ |
+ item_->Start(std::move(file_), std::move(request_handle_)); |
+ initialize_loop.Run(); |
+ base::WeakPtr<DownloadDestinationObserver> destination_observer = |
+ item_->DestinationObserverAsWeakPtr(); |
+ |
+ base::RunLoop target_determination_loop; |
+ DownloadItemImplDelegate::DownloadTargetCallback target_callback; |
+ EXPECT_CALL(*mock_delegate(), DetermineDownloadTarget(_, _)) |
+ .WillOnce(DoAll(SaveArg<1>(&target_callback), |
+ InvokeClosure(target_determination_loop.QuitClosure()))); |
+ ScheduleObservations(PostInitializeFileObservations(), destination_observer); |
+ initialize_callback.Run(DOWNLOAD_INTERRUPT_REASON_NONE); |
+ target_determination_loop.Run(); |
+ |
+ RunAllPendingInMessageLoops(); |
+ ASSERT_FALSE(target_callback.is_null()); |
+ |
+ ScheduleObservations(PostTargetDeterminationObservations(), |
+ destination_observer); |
+ target_callback.Run(base::FilePath(kDummyTargetPath), |
+ DownloadItem::TARGET_DISPOSITION_OVERWRITE, |
+ DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, |
+ base::FilePath(kDummyIntermediatePath)); |
+ |
+ intermediate_rename_loop.Run(); |
+ ASSERT_FALSE(intermediate_rename_callback.is_null()); |
+ |
+ // This may or may not be called, depending on whether there are any errors in |
+ // our action list. |
+ EXPECT_CALL(*mock_delegate(), ShouldCompleteDownload(_, _)) |
+ .Times(::testing::AnyNumber()); |
+ |
+ ScheduleObservations(PostIntermediateRenameObservations(), |
+ destination_observer); |
+ intermediate_rename_callback.Run(DOWNLOAD_INTERRUPT_REASON_NONE, |
+ base::FilePath(kDummyIntermediatePath)); |
+ RunAllPendingInMessageLoops(); |
+ |
+ // The state of the download depends on the observer events that were played |
+ // back to the DownloadItemImpl. Hence we can't establish a single expectation |
+ // here. On Debug builds, the DCHECKs will verify that the state transitions |
+ // were correct, and on Release builds, the tests should run to completion |
+ // without crashing. |
+ EXPECT_TRUE(item_->GetState() == DownloadItem::IN_PROGRESS || |
+ item_->GetState() == DownloadItem::INTERRUPTED); |
+ if (item_->GetState() == DownloadItem::INTERRUPTED) |
+ EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, item_->GetLastReason()); |
+ |
+ item_->Cancel(true); |
+ RunAllPendingInMessageLoops(); |
+} |
+ |
+// TODO(asanka): Write tests for resuming and auto-resuming downloads which have |
+// their own brand of race conditions. |
+ |
TEST(MockDownloadItem, Compiles) { |
MockDownloadItem mock_item; |
} |