1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/public/common/notifications/notification_mojom_traits.h"
6
7 #include "base/macros.h"
8 #include "base/optional.h"
9 #include "base/stl_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h"
13 #include "mojo/public/cpp/test_support/test_utils.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/blink/public/common/notifications/platform_notification_data.h"
16 #include "third_party/blink/public/mojom/notifications/notification.mojom.h"
17 #include "third_party/skia/include/core/SkBitmap.h"
18 #include "url/gurl.h"
19
20 namespace blink {
21
22 namespace {
23
CreateBitmap(int width,int height,SkColor color)24 SkBitmap CreateBitmap(int width, int height, SkColor color) {
25 SkBitmap bitmap;
26 bitmap.allocN32Pixels(width, height);
27 bitmap.eraseColor(color);
28 return bitmap;
29 }
30
31 // Returns true if |lhs| and |rhs| have the same width and height and the
32 // pixel at position (0, 0) is the same color in both.
ImagesShareDimensionsAndColor(const SkBitmap & lhs,const SkBitmap & rhs)33 bool ImagesShareDimensionsAndColor(const SkBitmap& lhs, const SkBitmap& rhs) {
34 return lhs.width() == rhs.width() && lhs.height() == rhs.height() &&
35 lhs.getColor(0, 0) == rhs.getColor(0, 0);
36 }
37
38 } // namespace
39
TEST(NotificationStructTraitsTest,NotificationDataRoundtrip)40 TEST(NotificationStructTraitsTest, NotificationDataRoundtrip) {
41 PlatformNotificationData notification_data;
42 notification_data.title = base::ASCIIToUTF16("Title of my notification");
43 notification_data.direction = mojom::NotificationDirection::AUTO;
44 notification_data.lang = "test-lang";
45 notification_data.body = base::ASCIIToUTF16("Notification body.");
46 notification_data.tag = "notification-tag";
47 notification_data.image = GURL("https://example.com/image.png");
48 notification_data.icon = GURL("https://example.com/icon.png");
49 notification_data.badge = GURL("https://example.com/badge.png");
50
51 const int vibration_pattern[] = {500, 100, 30};
52 notification_data.vibration_pattern.assign(
53 vibration_pattern, vibration_pattern + base::size(vibration_pattern));
54
55 notification_data.timestamp = base::Time::FromJsTime(1513966159000.);
56 notification_data.renotify = true;
57 notification_data.silent = true;
58 notification_data.require_interaction = true;
59 notification_data.show_trigger_timestamp = base::Time::Now();
60
61 const char data[] = "mock binary notification data";
62 notification_data.data.assign(data, data + base::size(data));
63
64 notification_data.actions.resize(2);
65 notification_data.actions[0].type =
66 blink::mojom::NotificationActionType::BUTTON;
67 notification_data.actions[0].action = "buttonAction";
68 notification_data.actions[0].title = base::ASCIIToUTF16("Button Title!");
69 notification_data.actions[0].icon = GURL("https://example.com/aButton.png");
70 notification_data.actions[0].placeholder = base::nullopt;
71
72 notification_data.actions[1].type =
73 blink::mojom::NotificationActionType::TEXT;
74 notification_data.actions[1].action = "textAction";
75 notification_data.actions[1].title = base::ASCIIToUTF16("Reply Button Title");
76 notification_data.actions[1].icon = GURL("https://example.com/reply.png");
77 notification_data.actions[1].placeholder =
78 base::ASCIIToUTF16("Placeholder Text");
79
80 PlatformNotificationData roundtrip_notification_data;
81
82 ASSERT_TRUE(
83 mojo::test::SerializeAndDeserialize<blink::mojom::NotificationData>(
84 ¬ification_data, &roundtrip_notification_data));
85
86 EXPECT_EQ(roundtrip_notification_data.title, notification_data.title);
87 EXPECT_EQ(roundtrip_notification_data.direction, notification_data.direction);
88 EXPECT_EQ(roundtrip_notification_data.lang, notification_data.lang);
89 EXPECT_EQ(roundtrip_notification_data.body, notification_data.body);
90 EXPECT_EQ(roundtrip_notification_data.tag, notification_data.tag);
91 EXPECT_EQ(roundtrip_notification_data.image, notification_data.image);
92 EXPECT_EQ(roundtrip_notification_data.icon, notification_data.icon);
93 EXPECT_EQ(roundtrip_notification_data.badge, notification_data.badge);
94 EXPECT_EQ(roundtrip_notification_data.vibration_pattern,
95 notification_data.vibration_pattern);
96 EXPECT_EQ(roundtrip_notification_data.timestamp, notification_data.timestamp);
97 EXPECT_EQ(roundtrip_notification_data.renotify, notification_data.renotify);
98 EXPECT_EQ(roundtrip_notification_data.silent, notification_data.silent);
99 EXPECT_EQ(roundtrip_notification_data.require_interaction,
100 notification_data.require_interaction);
101 EXPECT_EQ(roundtrip_notification_data.data, notification_data.data);
102 ASSERT_EQ(notification_data.actions.size(),
103 roundtrip_notification_data.actions.size());
104 for (size_t i = 0; i < notification_data.actions.size(); ++i) {
105 SCOPED_TRACE(base::StringPrintf("Action index: %zd", i));
106 EXPECT_EQ(notification_data.actions[i].type,
107 roundtrip_notification_data.actions[i].type);
108 EXPECT_EQ(notification_data.actions[i].action,
109 roundtrip_notification_data.actions[i].action);
110 EXPECT_EQ(notification_data.actions[i].title,
111 roundtrip_notification_data.actions[i].title);
112 EXPECT_EQ(notification_data.actions[i].icon,
113 roundtrip_notification_data.actions[i].icon);
114 EXPECT_EQ(notification_data.actions[i].placeholder,
115 roundtrip_notification_data.actions[i].placeholder);
116 }
117 EXPECT_EQ(roundtrip_notification_data.show_trigger_timestamp,
118 notification_data.show_trigger_timestamp);
119 }
120
121 // Check upper bound on vibration entries (99).
TEST(NotificationStructTraitsTest,ValidVibrationPattern)122 TEST(NotificationStructTraitsTest, ValidVibrationPattern) {
123 constexpr int kEntries = 99; // valid
124 constexpr int kDurationMs = 999; // valid
125
126 PlatformNotificationData notification_data;
127 notification_data.title =
128 base::ASCIIToUTF16("Notification with 99 x 999ms entries (valid)");
129
130 for (size_t i = 0; i < kEntries; ++i)
131 notification_data.vibration_pattern.push_back(kDurationMs);
132
133 PlatformNotificationData platform_notification_data;
134
135 ASSERT_TRUE(
136 mojo::test::SerializeAndDeserialize<blink::mojom::NotificationData>(
137 ¬ification_data, &platform_notification_data));
138 }
139
140 // Check round-trip fails when there are too many entries in the vibration
141 // pattern.
TEST(NotificationStructTraitsTest,TooManyVibrations)142 TEST(NotificationStructTraitsTest, TooManyVibrations) {
143 constexpr int kEntries = 100; // invalid
144 constexpr int kDurationMs = 1; // valid
145
146 PlatformNotificationData notification_data;
147 notification_data.title =
148 base::ASCIIToUTF16("Notification with 100 x 1ms entries (invalid)");
149
150 for (size_t i = 0; i < kEntries; ++i)
151 notification_data.vibration_pattern.push_back(kDurationMs);
152
153 PlatformNotificationData platform_notification_data;
154
155 ASSERT_FALSE(
156 mojo::test::SerializeAndDeserialize<blink::mojom::NotificationData>(
157 ¬ification_data, &platform_notification_data));
158 }
159
160 // Check round-trip fails when there is a too-long vibration duration.
TEST(NotificationStructTraitsTest,TooLongVibrationDuration)161 TEST(NotificationStructTraitsTest, TooLongVibrationDuration) {
162 constexpr int kEntries = 1; // valid
163 constexpr int kDurationMs = 10001; // invalid (>10 seconds)
164
165 PlatformNotificationData notification_data;
166 notification_data.title =
167 base::ASCIIToUTF16("Notification with 1 x 10001ms entries (invalid)");
168
169 for (size_t i = 0; i < kEntries; ++i)
170 notification_data.vibration_pattern.push_back(kDurationMs);
171
172 PlatformNotificationData platform_notification_data;
173
174 ASSERT_FALSE(
175 mojo::test::SerializeAndDeserialize<blink::mojom::NotificationData>(
176 ¬ification_data, &platform_notification_data));
177 }
178
179 // Check round-trip fails when there are too many actions provided.
TEST(NotificationStructTraitsTest,TooManyActions)180 TEST(NotificationStructTraitsTest, TooManyActions) {
181 constexpr int kActions = 3; // invalid (max is 2)
182
183 PlatformNotificationData notification_data;
184 notification_data.title =
185 base::ASCIIToUTF16("Notification with 3 actions provided (invalid)");
186
187 notification_data.actions.resize(kActions);
188 for (size_t i = 0; i < kActions; ++i) {
189 notification_data.actions[i].title = base::ASCIIToUTF16("action title");
190 }
191
192 PlatformNotificationData platform_notification_data;
193
194 ASSERT_FALSE(
195 mojo::test::SerializeAndDeserialize<blink::mojom::NotificationData>(
196 ¬ification_data, &platform_notification_data));
197 }
198
199 // Check round-trip fails when the data size is too big.
TEST(NotificationStructTraitsTest,DataExceedsMaximumSize)200 TEST(NotificationStructTraitsTest, DataExceedsMaximumSize) {
201 constexpr size_t kDataSize = 1024 * 1024 + 1; // 1 more than max data size.
202
203 PlatformNotificationData notification_data;
204 notification_data.title =
205 base::ASCIIToUTF16("Notification with too much data");
206
207 notification_data.data.resize(kDataSize);
208
209 PlatformNotificationData platform_notification_data;
210
211 ASSERT_FALSE(
212 mojo::test::SerializeAndDeserialize<blink::mojom::NotificationData>(
213 ¬ification_data, &platform_notification_data));
214 }
215
TEST(NotificationStructTraitsTest,NotificationResourcesRoundtrip)216 TEST(NotificationStructTraitsTest, NotificationResourcesRoundtrip) {
217 NotificationResources resources;
218
219 resources.image = CreateBitmap(200, 100, SK_ColorMAGENTA);
220 resources.notification_icon = CreateBitmap(100, 50, SK_ColorGREEN);
221 resources.badge = CreateBitmap(20, 10, SK_ColorBLUE);
222
223 resources.action_icons.resize(2);
224 resources.action_icons[0] = CreateBitmap(10, 10, SK_ColorLTGRAY);
225 resources.action_icons[1] = CreateBitmap(11, 11, SK_ColorDKGRAY);
226
227 NotificationResources roundtrip_resources;
228
229 ASSERT_TRUE(
230 mojo::test::SerializeAndDeserialize<blink::mojom::NotificationResources>(
231 &resources, &roundtrip_resources));
232
233 ASSERT_FALSE(roundtrip_resources.image.empty());
234 EXPECT_TRUE(ImagesShareDimensionsAndColor(resources.image,
235 roundtrip_resources.image));
236
237 ASSERT_FALSE(roundtrip_resources.notification_icon.empty());
238 EXPECT_TRUE(ImagesShareDimensionsAndColor(
239 resources.notification_icon, roundtrip_resources.notification_icon));
240
241 ASSERT_FALSE(roundtrip_resources.badge.empty());
242 EXPECT_TRUE(ImagesShareDimensionsAndColor(resources.badge,
243 roundtrip_resources.badge));
244
245 ASSERT_EQ(resources.action_icons.size(),
246 roundtrip_resources.action_icons.size());
247
248 for (size_t i = 0; i < roundtrip_resources.action_icons.size(); ++i) {
249 SCOPED_TRACE(base::StringPrintf("Action icon index: %zd", i));
250 ASSERT_FALSE(roundtrip_resources.action_icons[i].empty());
251 EXPECT_TRUE(ImagesShareDimensionsAndColor(
252 resources.action_icons[i], roundtrip_resources.action_icons[i]));
253 }
254 }
255
256 } // namespace blink
257