// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/download/notification/multi_profile_download_notifier.h"
#include <memory>
#include "base/memory/raw_ptr.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/download/content/public/all_download_item_notifier.h"
#include "components/download/public/common/mock_download_item.h"
#include "content/public/test/fake_download_item.h"
#include "content/public/test/mock_download_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AnyNumber;
namespace {
class MockNotifierClient : public MultiProfileDownloadNotifier::Client {
public:
MockNotifierClient() = default;
~MockNotifierClient() override = default;
MockNotifierClient(const MockNotifierClient&) = delete;
MockNotifierClient& operator=(const MockNotifierClient&) = delete;
MOCK_METHOD(void,
OnManagerInitialized,
(content::DownloadManager * manager),
(override));
MOCK_METHOD(void,
OnManagerGoingDown,
(content::DownloadManager * manager),
(override));
MOCK_METHOD(void,
OnDownloadCreated,
(content::DownloadManager * manager,
download::DownloadItem* item),
(override));
MOCK_METHOD(void,
OnDownloadUpdated,
(content::DownloadManager * manager,
download::DownloadItem* item),
(override));
MOCK_METHOD(void,
OnDownloadDestroyed,
(content::DownloadManager * manager,
download::DownloadItem* item),
(override));
MOCK_METHOD(bool, ShouldObserveProfile, (Profile * profile), (override));
};
// A mock `content::DownloadManager` which can notify observers of events.
class MockDownloadManager : public content::MockDownloadManager {
public:
// content::MockDownloadManager:
void AddObserver(Observer* observer) override {
observers_.AddObserver(observer);
}
void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
void Shutdown() override {
for (auto& observer : observers_)
observer.ManagerGoingDown(this);
}
private:
base::ObserverList<content::DownloadManager::Observer>::Unchecked observers_;
};
} // namespace
class MultiProfileDownloadNotifierTest : public BrowserWithTestWindowTest {
public:
// BrowserWithTestWindowTest:
void SetUp() override {
notifier_ = std::make_unique<MultiProfileDownloadNotifier>(
&client(), RequireManagerInitialization());
BrowserWithTestWindowTest::SetUp();
}
TestingProfile* CreateProfile(const std::string& profile_name) override {
auto* profile = BrowserWithTestWindowTest::CreateProfile(profile_name);
SetUpDownloadManager(profile);
return profile;
}
virtual bool ShouldInitializeManager() { return true; }
virtual bool RequireManagerInitialization() { return true; }
MockNotifierClient& client() { return client_; }
MockDownloadManager* manager() { return manager_; }
download::MockDownloadItem* item() { return &item_; }
MultiProfileDownloadNotifier* notifier() { return notifier_.get(); }
download::AllDownloadItemNotifier::Observer*
notifier_as_all_download_item_notifier_observer() const {
return notifier_.get();
}
private:
// Creates a `MockDownloadManager` for `profile` to use.
void SetUpDownloadManager(Profile* profile) {
auto manager = std::make_unique<testing::NiceMock<MockDownloadManager>>();
ON_CALL(*manager, IsManagerInitialized())
.WillByDefault(testing::Return(ShouldInitializeManager()));
manager_ = manager.get();
profile->SetDownloadManagerForTesting(std::move(manager));
}
testing::NiceMock<MockNotifierClient> client_;
raw_ptr<testing::NiceMock<MockDownloadManager>, DanglingUntriaged> manager_;
testing::NiceMock<download::MockDownloadItem> item_;
std::unique_ptr<MultiProfileDownloadNotifier> notifier_;
};
TEST_F(MultiProfileDownloadNotifierTest, ProfileObservation) {
// Make sure that profiles are not observed when they are filtered by
// `ShouldObserveProfile()`.
TestingProfile* profile = GetProfile();
ON_CALL(client(), ShouldObserveProfile(profile))
.WillByDefault(testing::Return(false));
// Since `manager()`'s `IsManagerInitialized()` is mocked to return true,
// `client()`'s `OnManagerInitialized()` would be called if the profile's
// addition were not filtered.
EXPECT_CALL(client(), OnManagerInitialized(manager())).Times(0);
notifier()->AddProfile(profile);
// Make sure that profiles are observed when they are not filtered by
// `ShouldObserveProfile()`.
ON_CALL(client(), ShouldObserveProfile(_))
.WillByDefault(testing::Return(true));
EXPECT_CALL(client(), OnManagerInitialized(manager()));
notifier()->AddProfile(profile);
// Make sure that off-the-record profiles are observed when spawned from an
// observed profile.
TestingProfile::Builder incognito_profile_builder;
incognito_profile_builder.SetProfileName(profile->GetProfileUserName());
EXPECT_CALL(client(), OnManagerInitialized(_));
Profile* const incognito_profile =
incognito_profile_builder.BuildIncognito(profile);
// If `profile` and `incognito_profile` are being observed, `client()`'s
// `OnManagerGoingDown()` should be called with each profile's download
// manager when the profile goes out of scope.
EXPECT_CALL(client(), OnManagerGoingDown(manager()));
EXPECT_CALL(client(),
OnManagerGoingDown(incognito_profile->GetDownloadManager()));
}
class MultiProfileDownloadNotifierManagerInitializationTest
: public MultiProfileDownloadNotifierTest,
public testing::WithParamInterface<
std::tuple<bool /* should_initialize_manager */,
bool /* require_manager_initialization */>> {
public:
bool ShouldInitializeManager() override { return std::get<0>(GetParam()); }
bool RequireManagerInitialization() override {
return std::get<1>(GetParam());
}
};
INSTANTIATE_TEST_SUITE_P(
All,
MultiProfileDownloadNotifierManagerInitializationTest,
testing::Combine(/*should_initialize_manager=*/testing::Bool(),
/*require_manager_initialization=*/testing::Bool()));
TEST_P(MultiProfileDownloadNotifierManagerInitializationTest, ClientCalls) {
TestingProfile* profile = GetProfile();
ON_CALL(client(), ShouldObserveProfile(profile))
.WillByDefault(testing::Return(true));
notifier()->AddProfile(profile);
EXPECT_CALL(client(), OnManagerInitialized(manager()));
notifier_as_all_download_item_notifier_observer()->OnManagerInitialized(
manager());
// Make sure the OnDownload...() functions are not called when we require the
// download manager to be initialized, but it is not.
int num_calls_expected =
!RequireManagerInitialization() || ShouldInitializeManager();
EXPECT_CALL(client(), OnDownloadCreated(manager(), item()))
.Times(num_calls_expected);
notifier_as_all_download_item_notifier_observer()->OnDownloadCreated(
manager(), item());
EXPECT_CALL(client(), OnDownloadUpdated(manager(), item()))
.Times(num_calls_expected);
notifier_as_all_download_item_notifier_observer()->OnDownloadUpdated(
manager(), item());
EXPECT_CALL(client(), OnDownloadDestroyed(manager(), item()))
.Times(num_calls_expected);
notifier_as_all_download_item_notifier_observer()->OnDownloadDestroyed(
manager(), item());
EXPECT_CALL(client(), OnManagerGoingDown(manager()));
notifier_as_all_download_item_notifier_observer()->OnManagerGoingDown(
manager());
}
TEST_P(MultiProfileDownloadNotifierManagerInitializationTest,
DownloadRetrieval) {
TestingProfile* profile = GetProfile();
ON_CALL(client(), ShouldObserveProfile(profile))
.WillByDefault(testing::Return(true));
notifier()->AddProfile(profile);
// For each `MultiProfileDownloadNotifier` download retrieval function, mock
// the `DownloadManager` function to which it ultimately forwards.
std::vector<std::unique_ptr<content::FakeDownloadItem>> downloads;
downloads.emplace_back(std::make_unique<content::FakeDownloadItem>());
downloads.emplace_back(std::make_unique<content::FakeDownloadItem>());
ON_CALL(*manager(), GetDownloadByGuid)
.WillByDefault([&downloads](const std::string& guid) {
return downloads.front().get();
});
auto* item = notifier()->GetDownloadByGuid("");
// `GetDownloadByGuid()` ignores manager initialization status.
EXPECT_EQ(item, downloads.front().get());
ON_CALL(*manager(), GetAllDownloads)
.WillByDefault(
[&downloads](
std::vector<raw_ptr<download::DownloadItem, VectorExperimental>>*
download_ptrs) {
for (auto& download : downloads)
download_ptrs->push_back(download.get());
});
auto retrieved_downloads = notifier()->GetAllDownloads();
// `GetAllDownloads()` honors `client()`'s manager initialization requirement.
EXPECT_EQ(retrieved_downloads.size(),
!RequireManagerInitialization() || ShouldInitializeManager()
? downloads.size()
: 0u);
}