blob: c5f74a8daea2c30f45879fb9782d14ff38552e98 [file] [log] [blame]
// Copyright 2021 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 <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/mac/scoped_nsobject.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#import "chrome/browser/notifications/notification_alert_service_bridge.h"
#include "chrome/services/mac_notifications/public/cpp/notification_constants_mac.h"
#include "chrome/services/mac_notifications/public/cpp/notification_operation.h"
#include "chrome/services/mac_notifications/public/mojom/mac_notifications.mojom.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
namespace {
class MockNotificationService
: public mac_notifications::mojom::MacNotificationService {
public:
MOCK_METHOD(void,
DisplayNotification,
(mac_notifications::mojom::NotificationPtr),
(override));
MOCK_METHOD(void,
GetDisplayedNotifications,
(mac_notifications::mojom::ProfileIdentifierPtr,
GetDisplayedNotificationsCallback),
(override));
MOCK_METHOD(void,
CloseNotification,
(mac_notifications::mojom::NotificationIdentifierPtr),
(override));
MOCK_METHOD(void,
CloseNotificationsForProfile,
(mac_notifications::mojom::ProfileIdentifierPtr),
(override));
MOCK_METHOD(void, CloseAllNotifications, (), (override));
};
class MockNotificationProvider
: public mac_notifications::mojom::MacNotificationProvider {
public:
MOCK_METHOD(
void,
BindNotificationService,
(mojo::PendingReceiver<mac_notifications::mojom::MacNotificationService>,
mojo::PendingRemote<
mac_notifications::mojom::MacNotificationActionHandler>),
(override));
};
} // namespace
class NotificationAlertServiceBridgeTest : public testing::Test {
public:
NotificationAlertServiceBridgeTest() {
base::RunLoop run_loop;
EXPECT_CALL(mock_provider_, BindNotificationService)
.WillOnce(
[&](mojo::PendingReceiver<
mac_notifications::mojom::MacNotificationService>
service_receiver,
mojo::PendingRemote<
mac_notifications::mojom::MacNotificationActionHandler>
handler_remote) {
service_receiver_.Bind(std::move(service_receiver));
handler_remote_.Bind(std::move(handler_remote));
run_loop.Quit();
});
mojo::Remote<mac_notifications::mojom::MacNotificationProvider>
provider_remote;
provider_receiver_.Bind(provider_remote.BindNewPipeAndPassReceiver());
bridge_.reset([[NotificationAlertServiceBridge alloc]
initWithDisconnectHandler:on_disconnect_.Get()
actionHandler:on_action_.Get()
provider:std::move(provider_remote)]);
run_loop.Run();
}
~NotificationAlertServiceBridgeTest() override {
// Make sure we run all remaining posted tasks that may use
// |profile_manager_| before we destroy it.
task_environment_.RunUntilIdle();
}
// testing::Test:
void SetUp() override {
profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(profile_manager_->SetUp());
}
protected:
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfileManager> profile_manager_;
base::MockOnceClosure on_disconnect_;
base::MockRepeatingClosure on_action_;
MockNotificationService mock_service_;
mojo::Receiver<mac_notifications::mojom::MacNotificationService>
service_receiver_{&mock_service_};
mojo::Remote<mac_notifications::mojom::MacNotificationActionHandler>
handler_remote_;
MockNotificationProvider mock_provider_;
mojo::Receiver<mac_notifications::mojom::MacNotificationProvider>
provider_receiver_{&mock_provider_};
base::scoped_nsobject<NotificationAlertServiceBridge> bridge_;
};
TEST_F(NotificationAlertServiceBridgeTest, DisconnectHandler) {
base::RunLoop run_loop;
EXPECT_CALL(on_disconnect_, Run).WillOnce([&]() { run_loop.Quit(); });
provider_receiver_.reset();
run_loop.Run();
}
TEST_F(NotificationAlertServiceBridgeTest, DeliverNotification) {
base::RunLoop run_loop;
EXPECT_CALL(mock_service_, DisplayNotification)
.WillOnce([&](mac_notifications::mojom::NotificationPtr notification) {
const mac_notifications::mojom::NotificationIdentifierPtr& identifier =
notification->meta->id;
EXPECT_EQ("notificationId", identifier->id);
EXPECT_EQ("profileId", identifier->profile->id);
EXPECT_TRUE(identifier->profile->incognito);
EXPECT_EQ(u"title", notification->title);
EXPECT_EQ(u"subtitle", notification->subtitle);
EXPECT_EQ(u"body", notification->body);
EXPECT_FALSE(notification->renotify);
EXPECT_TRUE(notification->show_settings_button);
ASSERT_EQ(2u, notification->buttons.size());
EXPECT_EQ(u"button1", notification->buttons[0]->title);
EXPECT_EQ(u"button2", notification->buttons[1]->title);
run_loop.Quit();
});
[bridge_ deliverNotification:@{
notification_constants::kNotificationId : @"notificationId",
notification_constants::kNotificationProfileId : @"profileId",
notification_constants::kNotificationIncognito : @YES,
notification_constants::kNotificationTitle : @"title",
notification_constants::kNotificationSubTitle : @"subtitle",
notification_constants::kNotificationInformativeText : @"body",
notification_constants::kNotificationRenotify : @NO,
notification_constants::kNotificationHasSettingsButton : @YES,
notification_constants::kNotificationButtonOne : @"button1",
notification_constants::kNotificationButtonTwo : @"button2",
}];
run_loop.Run();
}
TEST_F(NotificationAlertServiceBridgeTest, GetDisplayedAlertsForProfile) {
EXPECT_CALL(mock_service_, GetDisplayedNotifications)
.WillOnce([&](mac_notifications::mojom::ProfileIdentifierPtr profile,
MockNotificationService::GetDisplayedNotificationsCallback
callback) {
ASSERT_TRUE(profile);
EXPECT_EQ("profileId", profile->id);
EXPECT_TRUE(profile->incognito);
std::vector<mac_notifications::mojom::NotificationIdentifierPtr> alerts;
alerts.push_back(mac_notifications::mojom::NotificationIdentifier::New(
"notificationId", std::move(profile)));
std::move(callback).Run(std::move(alerts));
});
base::RunLoop run_loop;
base::RepeatingClosure run_loop_closure = run_loop.QuitClosure();
[bridge_ getDisplayedAlertsForProfileId:@"profileId"
incognito:YES
reply:^(NSArray* alerts) {
ASSERT_EQ(1ul, [alerts count]);
EXPECT_NSEQ(@"notificationId", alerts[0]);
run_loop_closure.Run();
}];
run_loop.Run();
}
TEST_F(NotificationAlertServiceBridgeTest, GetAllDisplayedAlerts) {
EXPECT_CALL(mock_service_, GetDisplayedNotifications)
.WillOnce([&](mac_notifications::mojom::ProfileIdentifierPtr profile,
MockNotificationService::GetDisplayedNotificationsCallback
callback) {
ASSERT_FALSE(profile);
std::vector<mac_notifications::mojom::NotificationIdentifierPtr> alerts;
alerts.push_back(mac_notifications::mojom::NotificationIdentifier::New(
"notificationId", mac_notifications::mojom::ProfileIdentifier::New(
"profileId", /*incognito=*/true)));
std::move(callback).Run(std::move(alerts));
});
NSDictionary* expected = @{
notification_constants::kNotificationId : @"notificationId",
notification_constants::kNotificationProfileId : @"profileId",
notification_constants::kNotificationIncognito : @YES,
};
base::RunLoop run_loop;
base::RepeatingClosure run_loop_closure = run_loop.QuitClosure();
[bridge_ getAllDisplayedAlertsWithReply:^(NSArray* alerts) {
ASSERT_EQ(1ul, [alerts count]);
EXPECT_NSEQ(expected, alerts[0]);
run_loop_closure.Run();
}];
run_loop.Run();
}
TEST_F(NotificationAlertServiceBridgeTest, CloseNotification) {
base::RunLoop run_loop;
EXPECT_CALL(mock_service_, CloseNotification)
.WillOnce(
[&](mac_notifications::mojom::NotificationIdentifierPtr identifier) {
ASSERT_TRUE(identifier);
EXPECT_EQ("notificationId", identifier->id);
ASSERT_TRUE(identifier->profile);
EXPECT_EQ("profileId", identifier->profile->id);
EXPECT_TRUE(identifier->profile->incognito);
run_loop.Quit();
});
[bridge_ closeNotificationWithId:@"notificationId"
profileId:@"profileId"
incognito:YES];
run_loop.Run();
}
TEST_F(NotificationAlertServiceBridgeTest, CloseProfileNotifications) {
base::RunLoop run_loop;
EXPECT_CALL(mock_service_, CloseNotificationsForProfile)
.WillOnce([&](mac_notifications::mojom::ProfileIdentifierPtr profile) {
ASSERT_TRUE(profile);
ASSERT_EQ("profileId", profile->id);
EXPECT_TRUE(profile->incognito);
run_loop.Quit();
});
[bridge_ closeNotificationsWithProfileId:@"profileId" incognito:YES];
run_loop.Run();
}
TEST_F(NotificationAlertServiceBridgeTest, CloseAllNotifications) {
base::RunLoop run_loop;
EXPECT_CALL(mock_service_, CloseAllNotifications).WillOnce([&]() {
run_loop.Quit();
});
[bridge_ closeAllNotifications];
run_loop.Run();
}
TEST_F(NotificationAlertServiceBridgeTest, OnNotificationAction) {
base::HistogramTester histogram_tester;
auto profile_identifier = mac_notifications::mojom::ProfileIdentifier::New(
"profileId", /*incognito=*/true);
auto notification_identifier =
mac_notifications::mojom::NotificationIdentifier::New(
"notificationId", std::move(profile_identifier));
auto meta = mac_notifications::mojom::NotificationMetadata::New(
std::move(notification_identifier), /*type=*/0, /*origin_url=*/GURL(),
base::GetCurrentProcId());
base::RunLoop run_loop;
EXPECT_CALL(on_action_, Run).WillOnce([&]() { run_loop.Quit(); });
auto action_info = mac_notifications::mojom::NotificationActionInfo::New(
std::move(meta), NotificationOperation::NOTIFICATION_CLICK,
/*button_index=*/-1, /*reply=*/base::nullopt);
handler_remote_->OnNotificationAction(std::move(action_info));
// TODO(knollr): verify expected notification action data.
// Wait until the action has been handled.
run_loop.Run();
histogram_tester.ExpectUniqueSample(
"Notifications.macOS.ActionReceived.Alert", /*sample=*/true,
/*expected_count=*/1);
}