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           &notification_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           &notification_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           &notification_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           &notification_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           &notification_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           &notification_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