blob: 0e821bb0383858eec950b9e8ea7848fe6cf89fba [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/notifications/scheduler/internal/notification_scheduler.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/task_environment.h"
#include "chrome/browser/notifications/scheduler/internal/notification_entry.h"
#include "chrome/browser/notifications/scheduler/internal/notification_scheduler_context.h"
#include "chrome/browser/notifications/scheduler/internal/scheduler_config.h"
#include "chrome/browser/notifications/scheduler/public/notification_scheduler_client_registrar.h"
#include "chrome/browser/notifications/scheduler/public/notification_scheduler_types.h"
#include "chrome/browser/notifications/scheduler/test/mock_background_task_coordinator.h"
#include "chrome/browser/notifications/scheduler/test/mock_display_agent.h"
#include "chrome/browser/notifications/scheduler/test/mock_display_decider.h"
#include "chrome/browser/notifications/scheduler/test/mock_impression_history_tracker.h"
#include "chrome/browser/notifications/scheduler/test/mock_notification_background_task_scheduler.h"
#include "chrome/browser/notifications/scheduler/test/mock_notification_scheduler_client.h"
#include "chrome/browser/notifications/scheduler/test/mock_scheduled_notification_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::NiceMock;
using ::testing::SetArgPointee;
namespace notifications {
namespace {
const char kGuid[] = "guid";
const char kTitle[] = "title";
class NotificationSchedulerTest : public testing::Test {
public:
NotificationSchedulerTest()
: registrar_(nullptr),
impression_tracker_(nullptr),
notification_manager_(nullptr),
client_(nullptr),
task_coordinator_(nullptr),
display_agent_(nullptr),
display_decider_(nullptr) {}
NotificationSchedulerTest(const NotificationSchedulerTest&) = delete;
NotificationSchedulerTest& operator=(const NotificationSchedulerTest&) =
delete;
~NotificationSchedulerTest() override = default;
void SetUp() override {
auto registrar = std::make_unique<NotificationSchedulerClientRegistrar>();
auto task_coordinator =
std::make_unique<test::MockBackgroundTaskCoordinator>();
auto impression_tracker =
std::make_unique<NiceMock<test::MockImpressionHistoryTracker>>();
auto notification_manager =
std::make_unique<NiceMock<test::MockScheduledNotificationManager>>();
auto display_agent = std::make_unique<test::MockDisplayAgent>();
auto display_decider = std::make_unique<test::MockDisplayDecider>();
auto config = SchedulerConfig::Create();
registrar_ = registrar.get();
impression_tracker_ = impression_tracker.get();
notification_manager_ = notification_manager.get();
task_coordinator_ = task_coordinator.get();
display_agent_ = display_agent.get();
display_decider_ = display_decider.get();
// Register mock clients.
auto client = std::make_unique<test::MockNotificationSchedulerClient>();
client_ = client.get();
registrar_->RegisterClient(SchedulerClientType::kTest1, std::move(client));
auto context = std::make_unique<NotificationSchedulerContext>(
std::move(registrar), std::move(task_coordinator),
std::move(impression_tracker), std::move(notification_manager),
std::move(display_agent), std::move(display_decider),
std::move(config));
notification_scheduler_ = NotificationScheduler::Create(std::move(context));
}
protected:
void Init() {
EXPECT_CALL(*impression_tracker(), Init(_, _))
.WillOnce(Invoke([&](ImpressionHistoryTracker::Delegate* delegate,
ImpressionHistoryTracker::InitCallback callback) {
std::move(callback).Run(true);
}));
EXPECT_CALL(*notification_manager(), Init(_))
.WillOnce(
Invoke([&](ScheduledNotificationManager::InitCallback callback) {
std::move(callback).Run(true);
}));
base::RunLoop run_loop;
scheduler()->Init(
base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
EXPECT_CALL(*client(), OnSchedulerInitialized(true, _))
.WillOnce(InvokeWithoutArgs([&]() { run_loop.Quit(); }));
run_loop.Run();
}
// Starts the background task and wait for task finished callback to invoke.
void OnStartTask() {
base::RunLoop loop;
auto task_finish_callback =
base::BindOnce([](base::RepeatingClosure quit_closure,
bool needs_reschedule) { quit_closure.Run(); },
loop.QuitClosure());
scheduler()->OnStartTask(std::move(task_finish_callback));
loop.Run();
}
NotificationScheduler* scheduler() { return notification_scheduler_.get(); }
test::MockImpressionHistoryTracker* impression_tracker() {
return impression_tracker_;
}
test::MockScheduledNotificationManager* notification_manager() {
return notification_manager_;
}
test::MockNotificationSchedulerClient* client() { return client_; }
test::MockBackgroundTaskCoordinator* task_coordinator() {
return task_coordinator_;
}
test::MockDisplayAgent* display_agent() { return display_agent_; }
test::MockDisplayDecider* display_decider() { return display_decider_; }
private:
base::test::TaskEnvironment task_environment_;
NotificationSchedulerClientRegistrar* registrar_;
test::MockImpressionHistoryTracker* impression_tracker_;
test::MockScheduledNotificationManager* notification_manager_;
test::MockNotificationSchedulerClient* client_;
test::MockBackgroundTaskCoordinator* task_coordinator_;
test::MockDisplayAgent* display_agent_;
test::MockDisplayDecider* display_decider_;
std::unique_ptr<NotificationScheduler> notification_scheduler_;
};
// Tests successful initialization flow.
TEST_F(NotificationSchedulerTest, InitSuccess) {
Init();
}
// Tests the case when impression tracker failed to initialize.
TEST_F(NotificationSchedulerTest, InitImpressionTrackerFailed) {
EXPECT_CALL(*impression_tracker(), Init(_, _))
.WillOnce(Invoke([](ImpressionHistoryTracker::Delegate* delegate,
ImpressionHistoryTracker::InitCallback callback) {
// Impression tracker failed to load.
std::move(callback).Run(false);
}));
EXPECT_CALL(*notification_manager(), Init(_)).Times(0);
base::RunLoop run_loop;
scheduler()->Init(
base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
EXPECT_CALL(*client(), OnSchedulerInitialized(false, _))
.WillOnce(InvokeWithoutArgs([&]() { run_loop.Quit(); }));
run_loop.Run();
}
// Tests the case when scheduled notification manager failed to initialize.
TEST_F(NotificationSchedulerTest, InitScheduledNotificationManagerFailed) {
EXPECT_CALL(*impression_tracker(), Init(_, _))
.WillOnce(Invoke([](ImpressionHistoryTracker::Delegate* delegate,
ImpressionHistoryTracker::InitCallback callback) {
std::move(callback).Run(true);
}));
EXPECT_CALL(*notification_manager(), Init(_))
.WillOnce(Invoke([](ScheduledNotificationManager::InitCallback callback) {
// Scheduled notification manager failed to load.
std::move(callback).Run(false);
}));
base::RunLoop run_loop;
scheduler()->Init(
base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
EXPECT_CALL(*client(), OnSchedulerInitialized(false, _))
.WillOnce(InvokeWithoutArgs([&]() { run_loop.Quit(); }));
run_loop.Run();
}
// Test to schedule a notification.
TEST_F(NotificationSchedulerTest, Schedule) {
Init();
auto param = std::unique_ptr<NotificationParams>();
EXPECT_CALL(*notification_manager(), ScheduleNotification(_, _))
.WillOnce(
Invoke([](std::unique_ptr<NotificationParams>,
ScheduledNotificationManager::ScheduleCallback callback) {
std::move(callback).Run(true);
}));
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _));
scheduler()->Schedule(std::move(param));
}
// When failed to add to the scheduled notification manager, no background task
// is triggered.
TEST_F(NotificationSchedulerTest, ScheduleFailed) {
Init();
auto param = std::unique_ptr<NotificationParams>();
EXPECT_CALL(*notification_manager(), ScheduleNotification(_, _))
.WillOnce(
Invoke([](std::unique_ptr<NotificationParams>,
ScheduledNotificationManager::ScheduleCallback callback) {
std::move(callback).Run(false);
}));
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _)).Times(0);
scheduler()->Schedule(std::move(param));
}
// Test to delete notifications.
TEST_F(NotificationSchedulerTest, DeleteAllNotifications) {
Init();
// Currently we don't reschedule background task even if all the notifications
// are deleted.
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _)).Times(0);
EXPECT_CALL(*notification_manager(),
DeleteNotifications(SchedulerClientType::kTest1));
scheduler()->DeleteAllNotifications(SchedulerClientType::kTest1);
}
// Test to get client overview.
TEST_F(NotificationSchedulerTest, GetClientOverview) {
Init();
EXPECT_CALL(*impression_tracker(),
GetImpressionDetail(SchedulerClientType::kTest1, _))
.WillOnce(Invoke([](SchedulerClientType type,
ImpressionDetail::ImpressionDetailCallback callback) {
std::move(callback).Run(ImpressionDetail());
}));
EXPECT_CALL(*notification_manager(), GetNotifications(_, _));
scheduler()->GetClientOverview(SchedulerClientType::kTest1,
base::DoNothing());
}
// Test to verify user actions are propagated through correctly.
TEST_F(NotificationSchedulerTest, OnUserAction) {
Init();
base::RunLoop loop;
UserActionData action_data(SchedulerClientType::kTest1,
UserActionType::kButtonClick, kGuid);
EXPECT_CALL(*impression_tracker(), OnUserAction(action_data));
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _)).Times(1);
EXPECT_CALL(*client(), OnUserAction(_)).WillOnce(InvokeWithoutArgs([&]() {
loop.Quit();
}));
scheduler()->OnUserAction(action_data);
loop.Run();
}
// Test to simulate a background task flow without any notification shown.
TEST_F(NotificationSchedulerTest, BackgroundTaskStartShowNothing) {
Init();
// No notification picked to show.
DisplayDecider::Results result;
EXPECT_CALL(*display_decider(), FindNotificationsToShow(_, _, _))
.WillOnce(SetArgPointee<2>(result));
EXPECT_CALL(*display_agent(), ShowNotification(_, _)).Times(0);
EXPECT_CALL(*notification_manager(), DisplayNotification(_, _)).Times(0);
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _));
OnStartTask();
}
MATCHER_P(NotifcationDataEq, title, "Verify notification data.") {
EXPECT_EQ(arg->title, base::UTF8ToUTF16(title));
return true;
}
MATCHER_P2(SystemDataEq, type, guid, "Verify system data.") {
EXPECT_EQ(arg->type, type);
EXPECT_EQ(arg->guid, guid);
return true;
}
// Test to simulate a background task flow with some notifications shown.
TEST_F(NotificationSchedulerTest, BackgroundTaskStartShowNotification) {
Init();
// Mock the notification to show.
auto entry =
std::make_unique<NotificationEntry>(SchedulerClientType::kTest1, kGuid);
EXPECT_CALL(
*display_agent(),
ShowNotification(NotifcationDataEq(kTitle),
SystemDataEq(SchedulerClientType::kTest1, kGuid)));
DisplayDecider::Results result({kGuid});
EXPECT_CALL(*display_decider(), FindNotificationsToShow(_, _, _))
.WillOnce(SetArgPointee<2>(result));
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _));
EXPECT_CALL(*impression_tracker(), AddImpression(_, _, _, _, _));
EXPECT_CALL(*notification_manager(), DisplayNotification(_, _))
.WillOnce(
Invoke([&](const std::string& guid,
ScheduledNotificationManager::DisplayCallback callback) {
std::move(callback).Run(std::move(entry));
}));
EXPECT_CALL(*client(), BeforeShowNotification(_, _))
.WillOnce(Invoke(
[&](std::unique_ptr<NotificationData> notification_data,
NotificationSchedulerClient::NotificationDataCallback callback) {
// The client updates the notification data here.
notification_data->title = base::UTF8ToUTF16(kTitle);
std::move(callback).Run(std::move(notification_data));
}));
OnStartTask();
}
// Verifies if the entry is failed to load, the background task flow can still
// be finished.
TEST_F(NotificationSchedulerTest, BackgroundTaskStartNoEntry) {
Init();
DisplayDecider::Results result({kGuid});
EXPECT_CALL(*display_decider(), FindNotificationsToShow(_, _, _))
.WillOnce(SetArgPointee<2>(result));
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _));
EXPECT_CALL(*impression_tracker(), AddImpression(_, _, _, _, _)).Times(0);
EXPECT_CALL(*display_agent(), ShowNotification(_, _)).Times(0);
EXPECT_CALL(*client(), BeforeShowNotification(_, _)).Times(0);
EXPECT_CALL(*notification_manager(), DisplayNotification(_, _))
.WillOnce(
Invoke([&](const std::string& guid,
ScheduledNotificationManager::DisplayCallback callback) {
std::move(callback).Run(nullptr /*entry*/);
}));
OnStartTask();
}
// Verifies if the client is not found during display flow, the background task
// flow can still be finished.
TEST_F(NotificationSchedulerTest, BackgroundTaskStartNoClient) {
Init();
// Creates entry without corresponding client.
auto entry_no_client =
std::make_unique<NotificationEntry>(SchedulerClientType::kTest2, kGuid);
DisplayDecider::Results result({kGuid});
EXPECT_CALL(*display_decider(), FindNotificationsToShow(_, _, _))
.WillOnce(SetArgPointee<2>(result));
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _));
EXPECT_CALL(*impression_tracker(), AddImpression(_, _, _, _, _)).Times(0);
EXPECT_CALL(*display_agent(), ShowNotification(_, _)).Times(0);
EXPECT_CALL(*client(), BeforeShowNotification(_, _)).Times(0);
EXPECT_CALL(*notification_manager(), DisplayNotification(_, _))
.WillOnce(
Invoke([&](const std::string& guid,
ScheduledNotificationManager::DisplayCallback callback) {
std::move(callback).Run(std::move(entry_no_client));
}));
OnStartTask();
}
// Verifies the case that the client dropped the notification data.
TEST_F(NotificationSchedulerTest, ClientDropNotification) {
Init();
// Mock the notification to show.
auto entry =
std::make_unique<NotificationEntry>(SchedulerClientType::kTest1, kGuid);
DisplayDecider::Results result({kGuid});
EXPECT_CALL(*display_decider(), FindNotificationsToShow(_, _, _))
.WillOnce(SetArgPointee<2>(result));
EXPECT_CALL(*notification_manager(), DisplayNotification(_, _))
.WillOnce(
Invoke([&](const std::string& guid,
ScheduledNotificationManager::DisplayCallback callback) {
std::move(callback).Run(std::move(entry));
}));
// The client drops the notification data before showing the notification.
EXPECT_CALL(*client(), BeforeShowNotification(_, _))
.WillOnce(Invoke(
[&](std::unique_ptr<NotificationData> notification_data,
NotificationSchedulerClient::NotificationDataCallback callback) {
std::move(callback).Run(nullptr);
}));
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _));
EXPECT_CALL(*impression_tracker(), AddImpression(_, _, _, _, _)).Times(0);
EXPECT_CALL(*display_agent(), ShowNotification(_, _)).Times(0);
OnStartTask();
}
// Test to simulate a background task stopped by the OS.
TEST_F(NotificationSchedulerTest, BackgroundTaskStop) {
Init();
EXPECT_CALL(*task_coordinator(), ScheduleBackgroundTask(_, _));
scheduler()->OnStopTask();
}
} // namespace
} // namespace notifications