blob: a102eb3ab8d95e53e68513a096b3a68511bd95c7 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/notifications/notification_database_conversions.h"
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <optional>
#include "base/compiler_specific.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "content/browser/notifications/notification_database_data.pb.h"
#include "content/browser/notifications/notification_database_resources.pb.h"
#include "content/public/browser/notification_database_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/notifications/notification_constants.h"
#include "third_party/blink/public/common/notifications/notification_resources.h"
#include "third_party/blink/public/mojom/notifications/notification.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image_unittest_util.h"
namespace content {
const blink::mojom::NotificationActionType kNotificationActionType =
blink::mojom::NotificationActionType::TEXT;
const int kNotificationVibrationPattern[] = {100, 200, 300};
TEST(NotificationDatabaseConversionsTest, SerializeAndDeserializeData) {
std::vector<int> vibration_pattern(std::begin(kNotificationVibrationPattern),
std::end(kNotificationVibrationPattern));
std::vector<char> developer_data({
'\xdf',
'\xff',
'\x00',
'\x00',
'\xff',
'\xdf',
});
blink::PlatformNotificationData notification_data;
notification_data.title = u"My Notification";
notification_data.direction =
blink::mojom::NotificationDirection::RIGHT_TO_LEFT;
notification_data.lang = "nl";
notification_data.body = u"Hello, world!";
notification_data.tag = "my_tag";
notification_data.image = GURL("https://example.com/image.jpg");
notification_data.icon = GURL("https://example.com/icon.png");
notification_data.badge = GURL("https://example.com/badge.png");
notification_data.vibration_pattern = vibration_pattern;
notification_data.timestamp =
base::Time::FromMillisecondsSinceUnixEpoch(621046800.);
notification_data.renotify = true;
notification_data.silent = true;
notification_data.require_interaction = true;
notification_data.show_trigger_timestamp =
base::Time::FromMillisecondsSinceUnixEpoch(621086800.);
notification_data.data = developer_data;
for (size_t i = 0; i < blink::kNotificationMaxActions; i++) {
auto notification_action = blink::mojom::NotificationAction::New();
notification_action->type = kNotificationActionType;
notification_action->action = base::NumberToString(i);
notification_action->title = base::NumberToString16(i);
notification_action->icon = GURL("https://example.com/action_icon.png");
notification_action->placeholder = base::NumberToString16(i);
notification_data.actions.push_back(std::move(notification_action));
}
NotificationDatabaseData database_data;
database_data.notification_id = "my-notification";
database_data.origin = GURL("https://example.com/");
database_data.service_worker_registration_id = 9001;
database_data.notification_data = notification_data;
database_data.replaced_existing_notification = true;
database_data.num_clicks = 8;
database_data.num_action_button_clicks = 9;
database_data.creation_time_millis =
base::Time::FromSecondsSinceUnixEpoch(12345);
database_data.time_until_first_click_millis = base::Milliseconds(11111);
database_data.time_until_last_click_millis = base::Milliseconds(22222);
database_data.time_until_close_millis = base::Milliseconds(33333);
database_data.closed_reason = NotificationDatabaseData::ClosedReason::USER;
database_data.has_triggered = true;
database_data.is_shown_by_browser = true;
database_data.serialized_metadata = {
{"content-detection", "{\"dummy\":\"value\"}"},
};
std::string serialized_data;
// Serialize the data in |notification_data| to the string |serialized_data|.
ASSERT_TRUE(
SerializeNotificationDatabaseData(database_data, &serialized_data));
NotificationDatabaseData copied_data;
// Deserialize the data in |serialized_data| to |copied_data|.
ASSERT_TRUE(
DeserializeNotificationDatabaseData(serialized_data, &copied_data));
EXPECT_EQ(database_data.notification_id, copied_data.notification_id);
EXPECT_EQ(database_data.origin, copied_data.origin);
EXPECT_EQ(database_data.service_worker_registration_id,
copied_data.service_worker_registration_id);
EXPECT_EQ(database_data.num_clicks, copied_data.num_clicks);
EXPECT_EQ(database_data.num_action_button_clicks,
copied_data.num_action_button_clicks);
EXPECT_EQ(database_data.replaced_existing_notification,
copied_data.replaced_existing_notification);
EXPECT_EQ(database_data.creation_time_millis,
copied_data.creation_time_millis);
EXPECT_EQ(database_data.time_until_first_click_millis,
copied_data.time_until_first_click_millis);
EXPECT_EQ(database_data.time_until_last_click_millis,
copied_data.time_until_last_click_millis);
EXPECT_EQ(database_data.time_until_close_millis,
copied_data.time_until_close_millis);
EXPECT_EQ(database_data.closed_reason, copied_data.closed_reason);
EXPECT_EQ(database_data.has_triggered, copied_data.has_triggered);
EXPECT_EQ(database_data.is_shown_by_browser, copied_data.is_shown_by_browser);
EXPECT_EQ(database_data.serialized_metadata, copied_data.serialized_metadata);
const blink::PlatformNotificationData& copied_notification_data =
copied_data.notification_data;
EXPECT_EQ(notification_data.title, copied_notification_data.title);
EXPECT_EQ(notification_data.direction, copied_notification_data.direction);
EXPECT_EQ(notification_data.lang, copied_notification_data.lang);
EXPECT_EQ(notification_data.body, copied_notification_data.body);
EXPECT_EQ(notification_data.tag, copied_notification_data.tag);
EXPECT_EQ(notification_data.image, copied_notification_data.image);
EXPECT_EQ(notification_data.icon, copied_notification_data.icon);
EXPECT_EQ(notification_data.badge, copied_notification_data.badge);
EXPECT_THAT(copied_notification_data.vibration_pattern,
testing::ElementsAreArray(kNotificationVibrationPattern));
EXPECT_EQ(notification_data.timestamp, copied_notification_data.timestamp);
EXPECT_EQ(notification_data.renotify, copied_notification_data.renotify);
EXPECT_EQ(notification_data.silent, copied_notification_data.silent);
EXPECT_EQ(notification_data.require_interaction,
copied_notification_data.require_interaction);
EXPECT_EQ(notification_data.show_trigger_timestamp,
copied_notification_data.show_trigger_timestamp);
ASSERT_EQ(developer_data.size(), copied_notification_data.data.size());
for (size_t i = 0; i < developer_data.size(); ++i)
EXPECT_EQ(developer_data[i], copied_notification_data.data[i]);
ASSERT_EQ(notification_data.actions.size(),
copied_notification_data.actions.size());
for (size_t i = 0; i < notification_data.actions.size(); ++i) {
EXPECT_EQ(notification_data.actions[i]->type,
copied_notification_data.actions[i]->type);
EXPECT_EQ(notification_data.actions[i]->action,
copied_notification_data.actions[i]->action);
EXPECT_EQ(notification_data.actions[i]->title,
copied_notification_data.actions[i]->title);
EXPECT_EQ(notification_data.actions[i]->icon,
copied_notification_data.actions[i]->icon);
EXPECT_EQ(notification_data.actions[i]->placeholder,
copied_notification_data.actions[i]->placeholder);
EXPECT_TRUE(copied_notification_data.actions[i]->placeholder);
}
}
TEST(NotificationDatabaseConversionsTest, ActionDeserializationIsNotAdditive) {
NotificationDatabaseData database_data;
for (size_t i = 0; i < blink::kNotificationMaxActions; ++i) {
database_data.notification_data.actions.emplace_back(
blink::mojom::NotificationAction::New());
}
std::string serialized_data;
NotificationDatabaseData copied_database_data;
// Serialize the data in |notification_data| to the string |serialized_data|,
// and then deserialize it again immediately to |copied_database_data|.
ASSERT_TRUE(
SerializeNotificationDatabaseData(database_data, &serialized_data));
ASSERT_TRUE(DeserializeNotificationDatabaseData(serialized_data,
&copied_database_data));
EXPECT_EQ(copied_database_data.notification_data.actions.size(),
blink::kNotificationMaxActions);
// Deserialize it again in the same |copied_database_data|. The number of
// actions in the structure should not be affected.
ASSERT_TRUE(DeserializeNotificationDatabaseData(serialized_data,
&copied_database_data));
EXPECT_EQ(copied_database_data.notification_data.actions.size(),
blink::kNotificationMaxActions);
}
TEST(NotificationDatabaseConversionsTest, SerializeAndDeserializeActionTypes) {
for (blink::mojom::NotificationActionType action_type : {
blink::mojom::NotificationActionType::BUTTON,
blink::mojom::NotificationActionType::TEXT,
}) {
blink::PlatformNotificationData notification_data;
auto action = blink::mojom::NotificationAction::New();
action->type = action_type;
notification_data.actions.push_back(std::move(action));
NotificationDatabaseData database_data;
database_data.notification_data = notification_data;
std::string serialized_data;
ASSERT_TRUE(
SerializeNotificationDatabaseData(database_data, &serialized_data));
NotificationDatabaseData copied_data;
ASSERT_TRUE(
DeserializeNotificationDatabaseData(serialized_data, &copied_data));
EXPECT_EQ(action_type, copied_data.notification_data.actions[0]->type);
}
}
TEST(NotificationDatabaseConversionsTest, SerializeAndDeserializeDirections) {
auto directions = std::to_array<blink::mojom::NotificationDirection>({
blink::mojom::NotificationDirection::LEFT_TO_RIGHT,
blink::mojom::NotificationDirection::RIGHT_TO_LEFT,
blink::mojom::NotificationDirection::AUTO,
});
for (size_t i = 0; i < std::size(directions); ++i) {
blink::PlatformNotificationData notification_data;
notification_data.direction = directions[i];
NotificationDatabaseData database_data;
database_data.notification_data = notification_data;
std::string serialized_data;
ASSERT_TRUE(
SerializeNotificationDatabaseData(database_data, &serialized_data));
NotificationDatabaseData copied_data;
ASSERT_TRUE(
DeserializeNotificationDatabaseData(serialized_data, &copied_data));
EXPECT_EQ(directions[i], copied_data.notification_data.direction);
}
}
TEST(NotificationDatabaseConversionsTest,
SerializeAndDeserializeClosedReasons) {
auto closed_reasons = std::to_array<NotificationDatabaseData::ClosedReason>({
NotificationDatabaseData::ClosedReason::USER,
NotificationDatabaseData::ClosedReason::DEVELOPER,
NotificationDatabaseData::ClosedReason::UNKNOWN,
});
for (size_t i = 0; i < std::size(closed_reasons); ++i) {
NotificationDatabaseData database_data;
database_data.closed_reason = closed_reasons[i];
std::string serialized_data;
ASSERT_TRUE(
SerializeNotificationDatabaseData(database_data, &serialized_data));
NotificationDatabaseData copied_data;
ASSERT_TRUE(
DeserializeNotificationDatabaseData(serialized_data, &copied_data));
EXPECT_EQ(closed_reasons[i], copied_data.closed_reason);
}
}
TEST(NotificationDatabaseConversionsTest,
SerializeAndDeserializeNullPlaceholder) {
auto action = blink::mojom::NotificationAction::New();
action->type = kNotificationActionType;
action->placeholder = std::nullopt; // null string.
blink::PlatformNotificationData notification_data;
notification_data.actions.push_back(std::move(action));
NotificationDatabaseData database_data;
database_data.notification_data = notification_data;
std::string serialized_data;
ASSERT_TRUE(
SerializeNotificationDatabaseData(database_data, &serialized_data));
NotificationDatabaseData copied_data;
ASSERT_TRUE(
DeserializeNotificationDatabaseData(serialized_data, &copied_data));
EXPECT_FALSE(copied_data.notification_data.actions[0]->placeholder);
}
TEST(NotificationDatabaseConversionsTest,
SerializeAndDeserializeNullShowTriggerTimestamp) {
blink::PlatformNotificationData notification_data;
// explicitly empty timestamp
notification_data.show_trigger_timestamp = std::nullopt;
NotificationDatabaseData database_data;
database_data.notification_data = notification_data;
std::string serialized_data;
ASSERT_TRUE(
SerializeNotificationDatabaseData(database_data, &serialized_data));
NotificationDatabaseData copied_data;
ASSERT_TRUE(
DeserializeNotificationDatabaseData(serialized_data, &copied_data));
EXPECT_FALSE(
copied_data.notification_data.show_trigger_timestamp.has_value());
}
TEST(NotificationDatabaseConversionsTest, OptionalFieldsGetCleared) {
NotificationDatabaseData data_without_fields;
NotificationDatabaseData data_with_fields;
data_with_fields.time_until_close_millis = base::Seconds(1);
data_with_fields.time_until_first_click_millis = base::Seconds(2);
data_with_fields.time_until_last_click_millis = base::Seconds(3);
data_with_fields.notification_resources = blink::NotificationResources();
std::string serialized_data;
NotificationDatabaseData copied_database_data;
// Serialize the |data_with_fields| to the string |serialized_data|,
// and then deserialize it again immediately to |copied_database_data|.
ASSERT_TRUE(
SerializeNotificationDatabaseData(data_with_fields, &serialized_data));
ASSERT_TRUE(DeserializeNotificationDatabaseData(serialized_data,
&copied_database_data));
EXPECT_EQ(base::Seconds(1), copied_database_data.time_until_close_millis);
EXPECT_EQ(base::Seconds(2),
copied_database_data.time_until_first_click_millis);
EXPECT_EQ(base::Seconds(3),
copied_database_data.time_until_last_click_millis);
EXPECT_FALSE(copied_database_data.notification_resources.has_value());
// Deserialize the |data_without_fields| in the same |copied_database_data|.
// The optional fields should now be gone.
ASSERT_TRUE(
SerializeNotificationDatabaseData(data_without_fields, &serialized_data));
ASSERT_TRUE(DeserializeNotificationDatabaseData(serialized_data,
&copied_database_data));
EXPECT_FALSE(copied_database_data.time_until_close_millis.has_value());
EXPECT_FALSE(copied_database_data.time_until_first_click_millis.has_value());
EXPECT_FALSE(copied_database_data.time_until_last_click_millis.has_value());
EXPECT_FALSE(copied_database_data.notification_resources.has_value());
EXPECT_EQ(0u, copied_database_data.serialized_metadata.size());
}
TEST(NotificationDatabaseConversionsTest,
SerializeAndDeserializeNotificationResources) {
blink::NotificationResources notification_resources;
notification_resources.notification_icon =
gfx::test::CreateBitmap(/*size=*/10, SK_ColorBLUE);
notification_resources.image =
gfx::test::CreateBitmap(/*size=*/20, SK_ColorGREEN);
notification_resources.badge =
gfx::test::CreateBitmap(/*size=*/30, SK_ColorRED);
notification_resources.action_icons.push_back(
gfx::test::CreateBitmap(/*size=*/40, SK_ColorYELLOW));
notification_resources.action_icons.push_back(
gfx::test::CreateBitmap(/*size=*/41, SK_ColorCYAN));
notification_resources.action_icons.push_back(
gfx::test::CreateBitmap(/*size=*/42, SK_ColorMAGENTA));
std::string serialized_resources;
ASSERT_TRUE(SerializeNotificationDatabaseResources(notification_resources,
&serialized_resources));
blink::NotificationResources copied_resources;
ASSERT_TRUE(DeserializeNotificationDatabaseResources(serialized_resources,
&copied_resources));
EXPECT_EQ(10, copied_resources.notification_icon.width());
EXPECT_EQ(20, copied_resources.image.width());
EXPECT_EQ(30, copied_resources.badge.width());
EXPECT_EQ(3u, copied_resources.action_icons.size());
EXPECT_EQ(40, copied_resources.action_icons[0].width());
EXPECT_EQ(41, copied_resources.action_icons[1].width());
EXPECT_EQ(42, copied_resources.action_icons[2].width());
}
} // namespace content