[go: nahoru, domu]

blob: 86eaba1e58e8aa71ca58c89e7336818fc3669b1f [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/download/download_manager_coordinator.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import <StoreKit/StoreKit.h>
#import <UIKit/UIKit.h>
#import "base/apple/foundation_util.h"
#import "base/files/file_util.h"
#import "base/run_loop.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "base/test/metrics/histogram_tester.h"
#import "base/test/metrics/user_action_tester.h"
#import "ios/chrome/browser/download/confirm_download_closing_overlay.h"
#import "ios/chrome/browser/download/confirm_download_replacing_overlay.h"
#import "ios/chrome/browser/download/download_directory_util.h"
#import "ios/chrome/browser/download/download_manager_metric_names.h"
#import "ios/chrome/browser/download/download_manager_tab_helper.h"
#import "ios/chrome/browser/download/external_app_util.h"
#import "ios/chrome/browser/download/installation_notifier.h"
#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
#import "ios/chrome/browser/overlays/public/web_content_area/alert_overlay.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/browser_state/test_chrome_browser_state.h"
#import "ios/chrome/browser/shared/model/web_state_list/test/fake_web_state_list_delegate.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
#import "ios/chrome/browser/shared/model/web_state_list/web_state_opener.h"
#import "ios/chrome/browser/shared/public/commands/browser_coordinator_commands.h"
#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
#import "ios/chrome/browser/ui/download/download_manager_view_controller.h"
#import "ios/chrome/test/fakes/fake_contained_presenter.h"
#import "ios/chrome/test/scoped_key_window.h"
#import "ios/web/public/test/fakes/fake_download_task.h"
#import "ios/web/public/test/fakes/fake_web_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "net/base/net_errors.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
using base::test::ios::WaitUntilConditionOrTimeout;
using base::test::ios::kWaitForUIElementTimeout;
namespace {
// Constants for configuring a fake download task.
const char kTestUrl[] = "https://chromium.test/download.txt";
const char kTestMimeType[] = "text/html";
const int64_t kTestTotalBytes = 10;
const int64_t kTestReceivedBytes = 0;
const base::FilePath::CharType kTestSuggestedFileName[] =
FILE_PATH_LITERAL("file.zip");
} // namespace
// Test fixture for testing DownloadManagerCoordinator class.
class DownloadManagerCoordinatorTest : public PlatformTest {
protected:
DownloadManagerCoordinatorTest() {
browser_state_ = TestChromeBrowserState::Builder().Build();
browser_ = std::make_unique<TestBrowser>(browser_state_.get());
presenter_ = [[FakeContainedPresenter alloc] init];
base_view_controller_ = [[UIViewController alloc] init];
activity_view_controller_class_ =
OCMClassMock([UIActivityViewController class]);
DownloadManagerTabHelper::CreateForWebState(&web_state_);
coordinator_ = [[DownloadManagerCoordinator alloc]
initWithBaseViewController:base_view_controller_
browser:browser_.get()];
[scoped_key_window_.Get() setRootViewController:base_view_controller_];
coordinator_.presenter = presenter_;
}
~DownloadManagerCoordinatorTest() override {
// Stop to avoid holding a dangling pointer to destroyed task.
@autoreleasepool {
// Calling -stop will retain and autorelease coordinator_.
// task_environment_ has to outlive the coordinator, so wrapping -stop
// call in @autorelease will ensure that coordinator_ is deallocated.
[coordinator_ stop];
}
[activity_view_controller_class_ stopMocking];
[application_ stopMocking];
[[InstallationNotifier sharedInstance] stopPolling];
}
DownloadManagerTabHelper* tab_helper() {
return DownloadManagerTabHelper::FromWebState(&web_state_);
}
// Creates a fake download task for testing.
std::unique_ptr<web::FakeDownloadTask> CreateTestTask() {
auto task =
std::make_unique<web::FakeDownloadTask>(GURL(kTestUrl), kTestMimeType);
task->SetTotalBytes(kTestTotalBytes);
task->SetReceivedBytes(kTestReceivedBytes);
task->SetGeneratedFileName(base::FilePath(kTestSuggestedFileName));
task->SetWebState(&web_state_);
return task;
}
web::WebTaskEnvironment task_environment_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
std::unique_ptr<TestBrowser> browser_;
FakeContainedPresenter* presenter_;
UIViewController* base_view_controller_;
ScopedKeyWindow scoped_key_window_;
id activity_view_controller_class_;
web::FakeWebState web_state_;
// Application can be lazily created by tests, but it has to be OCMock.
// Destructor will call -stopMocking on this object to make sure that
// UIApplication is not mocked after these test finish running.
id application_;
DownloadManagerCoordinator* coordinator_;
base::UserActionTester user_action_tester_;
base::HistogramTester histogram_tester_;
};
// Tests starting the coordinator. Verifies that view controller is presented
// without animation (default configuration) and that
// DownloadManagerViewController is propertly configured and presented.
TEST_F(DownloadManagerCoordinatorTest, Start) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
// By default coordinator presents without animation.
EXPECT_FALSE(presenter_.lastPresentationWasAnimated);
// Verify that presented view controller is DownloadManagerViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Verify that DownloadManagerViewController configuration matches download
// task.
EXPECT_FALSE(viewController.actionButton.hidden);
EXPECT_NSEQ(@"file.zip - 10 bytes", viewController.statusLabel.text);
EXPECT_NSEQ(@"Download",
[viewController.actionButton titleForState:UIControlStateNormal]);
}
// Tests stopping coordinator. Verifies that hiding web states dismisses the
// presented view controller and download task is reset to null (to prevent a
// stale raw pointer).
TEST_F(DownloadManagerCoordinatorTest, Stop) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
@autoreleasepool {
// Calling -stop will retain and autorelease coordinator_. task_environment_
// has to outlive the coordinator, so wrapping -stop call in @autorelease
// will ensure that coordinator_ is deallocated.
[coordinator_ stop];
}
// Verify that child view controller is removed and download task is set to
// null.
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
EXPECT_FALSE(coordinator_.downloadTask);
}
// Tests destroying coordinator during the download.
TEST_F(DownloadManagerCoordinatorTest, DestructionDuringDownload) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Start the download.
base::FilePath path;
ASSERT_TRUE(base::GetTempDir(&path));
task->Start(path.Append(task->GenerateFileName()));
@autoreleasepool {
// Calling -downloadManagerViewControllerDidStartDownload will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidStartDownload
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidStartDownload:viewController];
[coordinator_ stop];
// Destroy coordinator before destroying the download task.
coordinator_ = nil;
}
// Verify that child view controller is removed.
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileResult",
static_cast<base::HistogramBase::Sample>(DownloadFileResult::Other), 1);
}
// Tests downloadManagerTabHelper:didCreateDownload:webStateIsVisible: callback
// for visible web state. Verifies that coordinator's properties are set up and
// that DownloadManagerViewController is properly configured and presented with
// animation.
TEST_F(DownloadManagerCoordinatorTest, DelegateCreatedDownload) {
auto task = CreateTestTask();
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileUI", 0);
[coordinator_ downloadManagerTabHelper:tab_helper()
didCreateDownload:task.get()
webStateIsVisible:YES];
// Verify that coordinator's properties are set up.
EXPECT_EQ(task.get(), coordinator_.downloadTask);
EXPECT_TRUE(coordinator_.animatesPresentation);
// First presentation of Download Manager UI should be animated.
EXPECT_TRUE(presenter_.lastPresentationWasAnimated);
// Verify that presented view controller is DownloadManagerViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Verify that DownloadManagerViewController configuration matches download
// task.
EXPECT_NSEQ(@"file.zip - 10 bytes", viewController.statusLabel.text);
EXPECT_FALSE(viewController.actionButton.hidden);
EXPECT_NSEQ(@"Download",
[viewController.actionButton titleForState:UIControlStateNormal]);
// Verify that UMA action was logged.
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileUI", 1);
}
// Tests calling downloadManagerTabHelper:didCreateDownload:webStateIsVisible:
// callback twice. Second call should replace the old download task with the new
// one.
TEST_F(DownloadManagerCoordinatorTest, DelegateReplacedDownload) {
auto task = CreateTestTask();
task->Start(base::FilePath());
task->SetDone(true);
[coordinator_ downloadManagerTabHelper:tab_helper()
didCreateDownload:task.get()
webStateIsVisible:YES];
// Verify that presented view controller is DownloadManagerViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Verify that DownloadManagerViewController configuration matches download
// task.
EXPECT_NSEQ(@"file.zip", viewController.statusLabel.text);
EXPECT_FALSE(viewController.actionButton.hidden);
EXPECT_NSEQ(@"Open in…",
[viewController.actionButton titleForState:UIControlStateNormal]);
// Replace download task with a new one.
auto new_task = CreateTestTask();
[coordinator_ downloadManagerTabHelper:tab_helper()
didCreateDownload:new_task.get()
webStateIsVisible:YES];
// Verify that DownloadManagerViewController configuration matches new
// download task.
EXPECT_NSEQ(@"file.zip - 10 bytes", viewController.statusLabel.text);
EXPECT_FALSE(viewController.actionButton.hidden);
EXPECT_NSEQ(@"Download",
[viewController.actionButton titleForState:UIControlStateNormal]);
}
// Tests downloadManagerTabHelper:didCreateDownload:webStateIsVisible: callback
// for hidden web state. Verifies that coordinator ignores callback from
// a background tab.
TEST_F(DownloadManagerCoordinatorTest,
DelegateCreatedDownloadForHiddenWebState) {
auto task = CreateTestTask();
[coordinator_ downloadManagerTabHelper:tab_helper()
didCreateDownload:task.get()
webStateIsVisible:NO];
// Background tab should not present Download Manager UI.
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
}
// Tests downloadManagerTabHelper:didHideDownload: callback. Verifies that
// hiding web states dismisses the presented view controller and download task
// is reset to null (to prevent a stale raw pointer).
TEST_F(DownloadManagerCoordinatorTest, DelegateHideDownload) {
auto task = CreateTestTask();
[coordinator_ downloadManagerTabHelper:tab_helper()
didCreateDownload:task.get()
webStateIsVisible:YES];
@autoreleasepool {
// Calling -downloadManagerTabHelper:didHideDownload: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerTabHelper:didHideDownload:
// call in @autorelease will ensure that coordinator_ is deallocated.
[coordinator_ downloadManagerTabHelper:tab_helper()
didHideDownload:task.get()];
}
// Verify that child view controller is removed and download task is set to
// null.
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
EXPECT_FALSE(coordinator_.downloadTask);
}
// Tests downloadManagerTabHelper:didShowDownload: callback. Verifies that
// showing web state presents download manager UI for that web state.
TEST_F(DownloadManagerCoordinatorTest, DelegateShowDownload) {
auto task = CreateTestTask();
[coordinator_ downloadManagerTabHelper:tab_helper()
didShowDownload:task.get()];
// Only first presentation is animated. Switching between tab should create
// the impression that UI was never dismissed.
EXPECT_FALSE(presenter_.lastPresentationWasAnimated);
// Verify that presented view controller is DownloadManagerViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Verify that DownloadManagerViewController configuration matches download
// task for shown web state.
EXPECT_NSEQ(@"file.zip - 10 bytes", viewController.statusLabel.text);
EXPECT_FALSE(viewController.actionButton.hidden);
}
// Tests closing view controller. Coordinator should be stopped and task
// cancelled.
TEST_F(DownloadManagerCoordinatorTest, Close) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
ASSERT_EQ(0, user_action_tester_.GetActionCount("IOSDownloadClose"));
@autoreleasepool {
// Calling -downloadManagerViewControllerDidClose: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidClose:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidClose:viewController];
}
// Verify that child view controller is removed, download task is set to null
// and download task is cancelled.
EXPECT_EQ(0U, base_view_controller_.childViewControllers.count);
EXPECT_FALSE(coordinator_.downloadTask);
EXPECT_EQ(web::DownloadTask::State::kCancelled, task->GetState());
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileResult",
static_cast<base::HistogramBase::Sample>(DownloadFileResult::NotStarted),
1);
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileUIGoogleDrive",
0);
EXPECT_EQ(1, user_action_tester_.GetActionCount("IOSDownloadClose"));
}
// Tests presenting Install Google Drive dialog. Coordinator presents StoreKit
// dialog and hides Install Google Drive button.
TEST_F(DownloadManagerCoordinatorTest, InstallDrive) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Make Install Google Drive UI visible.
[viewController setInstallDriveButtonVisible:YES animated:NO];
// The button itself is never hidden, but the superview which contains the
// button changes it's alpha.
ASSERT_EQ(1.0f, viewController.installDriveButton.superview.alpha);
ASSERT_EQ(
0, user_action_tester_.GetActionCount("IOSDownloadInstallGoogleDrive"));
@autoreleasepool {
// Calling -installDriveForDownloadManagerViewController: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -installDriveForDownloadManagerViewController:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
installDriveForDownloadManagerViewController:viewController];
}
// Verify that Store Kit dialog was presented.
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
return [base_view_controller_.presentedViewController class] ==
[SKStoreProductViewController class];
}));
// Verify that Install Google Drive UI is hidden.
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, ^{
return viewController.installDriveButton.superview.alpha == 0.0f;
}));
// Simulate Google Drive app installation and verify that expected histograms
// have been recorded.
EXPECT_EQ(
1, user_action_tester_.GetActionCount("IOSDownloadInstallGoogleDrive"));
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileUIGoogleDrive",
0);
// SKStoreProductViewController uses UIApplication, so it's not possible to
// install the mock before the test run.
application_ = OCMClassMock([UIApplication class]);
OCMStub([application_ sharedApplication]).andReturn(application_);
OCMStub([application_ canOpenURL:GetGoogleDriveAppUrl()]).andReturn(YES);
}
// Tests presenting Open In... menu without actually opening the download.
TEST_F(DownloadManagerCoordinatorTest, OpenIn) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* view_controller =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [view_controller class]);
id download_view_controller_mock = OCMPartialMock(view_controller);
id dispatcher_mock = OCMProtocolMock(@protocol(BrowserCoordinatorCommands));
[browser_->GetCommandDispatcher()
startDispatchingToTarget:dispatcher_mock
forProtocol:@protocol(BrowserCoordinatorCommands)];
// Start the download.
base::FilePath path;
ASSERT_TRUE(base::GetTempDir(&path));
task->Start(path.Append(task->GenerateFileName()));
// Stub UIActivityViewController.
OCMStub([download_view_controller_mock presentViewController:[OCMArg any]
animated:YES
completion:[OCMArg any]])
.andDo(^(NSInvocation* invocation) {
__weak id object;
[invocation getArgument:&object atIndex:2];
EXPECT_EQ([UIActivityViewController class], [object class]);
UIActivityViewController* open_in_controller =
base::apple::ObjCCastStrict<UIActivityViewController>(object);
EXPECT_EQ(open_in_controller.excludedActivityTypes.count, 2.0);
});
ASSERT_EQ(0, user_action_tester_.GetActionCount("IOSDownloadOpenIn"));
// Present Open In... menu.
@autoreleasepool {
// Calling -installDriveForDownloadManagerViewController: and
// presentOpenInForDownloadManagerViewController will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping calls in @autorelease will ensure that
// coordinator_ is deallocated.
[view_controller.delegate
downloadManagerViewControllerDidStartDownload:view_controller];
// Complete the download before presenting Open In... menu.
task->SetDone(true);
[view_controller.delegate
presentOpenInForDownloadManagerViewController:view_controller];
}
// Download task is destroyed without opening the file.
task = nullptr;
histogram_tester_.ExpectTotalCount("Download.IOSDownloadedFileNetError", 0);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileResult",
static_cast<base::HistogramBase::Sample>(DownloadFileResult::Completed),
1);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadedFileAction",
static_cast<base::HistogramBase::Sample>(
DownloadedFileAction::NoActionOrOpenedViaExtension),
1);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadedFileAction",
static_cast<base::HistogramBase::Sample>(
DownloadFileInBackground::SucceededWithoutBackgrounding),
1);
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileUIGoogleDrive",
1);
EXPECT_EQ(1, user_action_tester_.GetActionCount("IOSDownloadOpenIn"));
}
// Tests destroying download task for in progress download.
TEST_F(DownloadManagerCoordinatorTest, DestroyInProgressDownload) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Start and the download.
@autoreleasepool {
// Calling -downloadManagerViewControllerDidStartDownload: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidStartDownload:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidStartDownload:viewController];
}
// Starting download is async for model.
web::DownloadTask* task_ptr = task.get();
ASSERT_TRUE(
WaitUntilConditionOrTimeout(base::test::ios::kWaitForDownloadTimeout, ^{
base::RunLoop().RunUntilIdle();
return task_ptr->GetState() == web::DownloadTask::State::kInProgress;
}));
// Download task is destroyed before the download is complete.
task = nullptr;
histogram_tester_.ExpectTotalCount("Download.IOSDownloadedFileNetError", 0);
histogram_tester_.ExpectTotalCount("Download.IOSDownloadedFileAction", 0);
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileInBackground", 0);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileResult",
static_cast<base::HistogramBase::Sample>(DownloadFileResult::Other), 1);
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileUIGoogleDrive",
0);
}
// Tests quitting the app during in-progress download.
TEST_F(DownloadManagerCoordinatorTest, QuitDuringInProgressDownload) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
auto web_state = std::make_unique<web::FakeWebState>();
browser_->GetWebStateList()->InsertWebState(
0, std::move(web_state), WebStateList::INSERT_NO_FLAGS, WebStateOpener());
[coordinator_ start];
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Start and the download.
@autoreleasepool {
// Calling -downloadManagerViewControllerDidStartDownload: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidStartDownload:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidStartDownload:viewController];
}
// Starting download is async for model.
web::DownloadTask* task_ptr = task.get();
ASSERT_TRUE(
WaitUntilConditionOrTimeout(base::test::ios::kWaitForDownloadTimeout, ^{
base::RunLoop().RunUntilIdle();
return task_ptr->GetState() == web::DownloadTask::State::kInProgress;
}));
// Web States are closed without user action only during app termination.
browser_->GetWebStateList()->CloseAllWebStates(WebStateList::CLOSE_NO_FLAGS);
// Download task is destroyed before the download is complete.
task = nullptr;
histogram_tester_.ExpectTotalCount("Download.IOSDownloadedFileNetError", 0);
histogram_tester_.ExpectTotalCount("Download.IOSDownloadedFileAction", 0);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileInBackground",
static_cast<base::HistogramBase::Sample>(
DownloadFileInBackground::CanceledAfterAppQuit),
1);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileResult",
static_cast<base::HistogramBase::Sample>(DownloadFileResult::Other), 1);
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileUIGoogleDrive",
0);
}
// Tests closing view controller while the download is in progress. Coordinator
// should present the confirmation dialog.
TEST_F(DownloadManagerCoordinatorTest, CloseInProgressDownload) {
auto task = CreateTestTask();
task->Start(base::FilePath());
coordinator_.downloadTask = task.get();
[coordinator_ start];
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
ASSERT_EQ(0, user_action_tester_.GetActionCount(
"IOSDownloadTryCloseWhenInProgress"));
OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
&web_state_, OverlayModality::kWebContentArea);
ASSERT_EQ(0U, queue->size());
@autoreleasepool {
// Calling -downloadManagerViewControllerDidClose: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidClose:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidClose:viewController];
}
// Verify that confirm request was sent.
ASSERT_EQ(1U, queue->size());
alert_overlays::AlertRequest* config =
queue->front_request()->GetConfig<alert_overlays::AlertRequest>();
ASSERT_TRUE(config);
EXPECT_NSEQ(@"Stop Download?", config->title());
EXPECT_FALSE(config->message());
ASSERT_EQ(2U, config->button_configs().size());
alert_overlays::ButtonConfig stop_button = config->button_configs()[0][0];
EXPECT_NSEQ(@"Stop", stop_button.title);
EXPECT_EQ(kDownloadCloseActionName, stop_button.user_action_name);
alert_overlays::ButtonConfig continue_button = config->button_configs()[1][0];
EXPECT_NSEQ(@"Continue", continue_button.title);
EXPECT_EQ(kDownloadDoNotCloseActionName, continue_button.user_action_name);
// Stop to avoid holding a dangling pointer to destroyed task.
queue->CancelAllRequests();
@autoreleasepool {
// Calling -stop will retain and autorelease coordinator_. task_environment_
// has to outlive the coordinator, so wrapping -stop call in @autorelease
// will ensure that coordinator_ is deallocated.
[coordinator_ stop];
}
EXPECT_EQ(0U, queue->size());
EXPECT_EQ(1, user_action_tester_.GetActionCount(
"IOSDownloadTryCloseWhenInProgress"));
EXPECT_EQ(0, user_action_tester_.GetActionCount(kDownloadCloseActionName));
EXPECT_EQ(0,
user_action_tester_.GetActionCount(kDownloadDoNotCloseActionName));
}
// Tests downloadManagerTabHelper:decidePolicyForDownload:completionHandler:.
// Coordinator should present the confirmation dialog.
TEST_F(DownloadManagerCoordinatorTest, DecidePolicyForDownload) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
&web_state_, OverlayModality::kWebContentArea);
ASSERT_EQ(0U, queue->size());
[coordinator_ downloadManagerTabHelper:tab_helper()
decidePolicyForDownload:task.get()
completionHandler:^(NewDownloadPolicy){
}];
// Verify that confirm request was sent.
ASSERT_EQ(1U, queue->size());
alert_overlays::AlertRequest* config =
queue->front_request()->GetConfig<alert_overlays::AlertRequest>();
ASSERT_TRUE(config);
EXPECT_NSEQ(@"Start New Download?", config->title());
EXPECT_NSEQ(@"This will stop all progress for your current download.",
config->message());
ASSERT_EQ(2U, config->button_configs().size());
alert_overlays::ButtonConfig ok_button = config->button_configs()[0][0];
EXPECT_NSEQ(@"OK", ok_button.title);
EXPECT_EQ(kDownloadReplaceActionName, ok_button.user_action_name);
alert_overlays::ButtonConfig cancel_button = config->button_configs()[1][0];
EXPECT_NSEQ(@"Cancel", cancel_button.title);
EXPECT_EQ(kDownloadDoNotReplaceActionName, cancel_button.user_action_name);
queue->CancelAllRequests();
@autoreleasepool {
// Calling -stop will retain and autorelease coordinator_. task_environment_
// has to outlive the coordinator, so wrapping -stop call in @autorelease
// will ensure that coordinator_ is deallocated.
[coordinator_ stop];
}
EXPECT_EQ(0U, queue->size());
}
// Tests downloadManagerTabHelper:decidePolicyForDownload:completionHandler:.
// Coordinator should present the confirmation dialog.
TEST_F(DownloadManagerCoordinatorTest,
DecidePolicyForDownloadFromBackgroundTab) {
auto task = CreateTestTask();
coordinator_.downloadTask = nullptr; // Current Tab does not have task.
OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
&web_state_, OverlayModality::kWebContentArea);
ASSERT_EQ(0U, queue->size());
[coordinator_ downloadManagerTabHelper:tab_helper()
decidePolicyForDownload:task.get()
completionHandler:^(NewDownloadPolicy){
}];
// Verify that confirm request was sent.
ASSERT_EQ(1U, queue->size());
alert_overlays::AlertRequest* config =
queue->front_request()->GetConfig<alert_overlays::AlertRequest>();
ASSERT_TRUE(config);
EXPECT_NSEQ(@"Start New Download?", config->title());
EXPECT_NSEQ(@"This will stop all progress for your current download.",
config->message());
ASSERT_EQ(2U, config->button_configs().size());
alert_overlays::ButtonConfig ok_button = config->button_configs()[0][0];
EXPECT_NSEQ(@"OK", ok_button.title);
EXPECT_EQ(kDownloadReplaceActionName, ok_button.user_action_name);
alert_overlays::ButtonConfig cancel_button = config->button_configs()[1][0];
EXPECT_NSEQ(@"Cancel", cancel_button.title);
EXPECT_EQ(kDownloadDoNotReplaceActionName, cancel_button.user_action_name);
queue->CancelAllRequests();
@autoreleasepool {
// Calling -stop will retain and autorelease coordinator_. task_environment_
// has to outlive the coordinator, so wrapping -stop call in @autorelease
// will ensure that coordinator_ is deallocated.
[coordinator_ stop];
}
EXPECT_EQ(0U, queue->size());
}
// Tests starting the download. Verifies that download task is started and its
// file writer is configured to write into download directory.
TEST_F(DownloadManagerCoordinatorTest, StartDownload) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
@autoreleasepool {
// Calling -downloadManagerViewControllerDidStartDownload: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidStartDownload:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidStartDownload:viewController];
}
// Starting download is async for model.
web::DownloadTask* task_ptr = task.get();
ASSERT_TRUE(
WaitUntilConditionOrTimeout(base::test::ios::kWaitForDownloadTimeout, ^{
base::RunLoop().RunUntilIdle();
return task_ptr->GetState() == web::DownloadTask::State::kInProgress;
}));
// Download file should be located in download directory.
base::FilePath file = task->GetResponsePath();
base::FilePath download_dir;
ASSERT_TRUE(GetTempDownloadsDirectory(&download_dir));
EXPECT_TRUE(download_dir.IsParent(file));
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileInBackground", 0);
EXPECT_EQ(0,
user_action_tester_.GetActionCount("MobileDownloadRetryDownload"));
EXPECT_EQ(1, user_action_tester_.GetActionCount("IOSDownloadStartDownload"));
}
// Tests retrying the download. Verifies that kDownloadManagerRetryDownload UMA
// metric is logged.
TEST_F(DownloadManagerCoordinatorTest, RetryingDownload) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
// First download is a failure.
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
ASSERT_EQ(0, user_action_tester_.GetActionCount("IOSDownloadStartDownload"));
@autoreleasepool {
// Calling -downloadManagerViewControllerDidStartDownload: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidStartDownload:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidStartDownload:viewController];
}
task->SetErrorCode(net::ERR_INTERNET_DISCONNECTED);
task->SetDone(true);
ASSERT_EQ(1, user_action_tester_.GetActionCount("IOSDownloadStartDownload"));
@autoreleasepool {
// Calling -downloadManagerViewControllerDidStartDownload: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidStartDownload:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidStartDownload:viewController];
}
// Starting download is async for model.
web::DownloadTask* task_ptr = task.get();
ASSERT_TRUE(
WaitUntilConditionOrTimeout(base::test::ios::kWaitForDownloadTimeout, ^{
base::RunLoop().RunUntilIdle();
return task_ptr->GetState() == web::DownloadTask::State::kInProgress;
}));
histogram_tester_.ExpectUniqueSample("Download.IOSDownloadedFileNetError",
-net::ERR_INTERNET_DISCONNECTED, 1);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileResult",
static_cast<base::HistogramBase::Sample>(DownloadFileResult::Failure), 1);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileInBackground",
static_cast<base::HistogramBase::Sample>(
DownloadFileInBackground::FailedWithoutBackgrounding),
1);
EXPECT_EQ(1,
user_action_tester_.GetActionCount("MobileDownloadRetryDownload"));
}
// Tests download failure in background.
TEST_F(DownloadManagerCoordinatorTest, FailingInBackground) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
// Start and immediately fail the download.
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
@autoreleasepool {
// Calling -downloadManagerViewControllerDidStartDownload: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidStartDownload:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidStartDownload:viewController];
}
task->SetPerformedBackgroundDownload(true);
task->SetErrorCode(net::ERR_INTERNET_DISCONNECTED);
task->SetDone(true);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileResult",
static_cast<base::HistogramBase::Sample>(DownloadFileResult::Failure), 1);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileInBackground",
static_cast<base::HistogramBase::Sample>(
DownloadFileInBackground::FailedWithBackgrounding),
1);
histogram_tester_.ExpectTotalCount("Download.IOSDownloadFileUIGoogleDrive",
0);
}
// Tests successful download in background.
TEST_F(DownloadManagerCoordinatorTest, SucceedingInBackground) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
[coordinator_ start];
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Start the download.
base::FilePath path;
ASSERT_TRUE(base::GetTempDir(&path));
task->Start(path.Append(task->GenerateFileName()));
// Start the download.
@autoreleasepool {
// Calling -downloadManagerViewControllerDidStartDownload: will retain and
// autorelease coordinator_. task_environment_ has to outlive the
// coordinator, so wrapping -downloadManagerViewControllerDidStartDownload:
// call in @autorelease will ensure that coordinator_ is deallocated.
[viewController.delegate
downloadManagerViewControllerDidStartDownload:viewController];
}
// Complete the download to log UMA.
task->SetPerformedBackgroundDownload(true);
task->SetDone(true);
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadFileInBackground",
static_cast<base::HistogramBase::Sample>(
DownloadFileInBackground::SucceededWithBackgrounding),
1);
}
// Tests that viewController returns correct view controller if coordinator is
// started and nil when stopped.
TEST_F(DownloadManagerCoordinatorTest, ViewController) {
auto task = CreateTestTask();
coordinator_.downloadTask = task.get();
ASSERT_FALSE(coordinator_.viewController);
[coordinator_ start];
// Verify that presented view controller is DownloadManagerViewController.
EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
DownloadManagerViewController* viewController =
base_view_controller_.childViewControllers.firstObject;
ASSERT_EQ([DownloadManagerViewController class], [viewController class]);
// Verify view controller property.
EXPECT_NSEQ(viewController, coordinator_.viewController);
@autoreleasepool {
// Calling -stop will retain and autorelease coordinator_. task_environment_
// has to outlive the coordinator, so wrapping -stop call in @autorelease
// will ensure that coordinator_ is deallocated.
[coordinator_ stop];
}
EXPECT_FALSE(coordinator_.viewController);
}