1 // Copyright 2019 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 <stdint.h>
6
7 #include "base/bind.h"
8 #include "base/run_loop.h"
9 #include "base/test/bind.h"
10 #include "base/test/metrics/histogram_tester.h"
11 #include "base/test/scoped_feature_list.h"
12 #include "base/time/time.h"
13 #include "content/browser/notifications/notification_trigger_constants.h"
14 #include "content/browser/notifications/platform_notification_context_impl.h"
15 #include "content/browser/service_worker/service_worker_context_wrapper.h"
16 #include "content/public/browser/notification_database_data.h"
17 #include "content/public/common/content_client.h"
18 #include "content/public/common/content_features.h"
19 #include "content/public/test/browser_task_environment.h"
20 #include "content/public/test/test_browser_context.h"
21 #include "content/test/mock_platform_notification_service.h"
22 #include "content/test/test_content_browser_client.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "url/gurl.h"
25
26 using base::Time;
27 using base::TimeDelta;
28
29 namespace content {
30
31 namespace {
32
33 // Fake Service Worker registration id to use in tests requiring one.
34 const int64_t kFakeServiceWorkerRegistrationId = 42;
35
36 class NotificationBrowserClient : public TestContentBrowserClient {
37 public:
NotificationBrowserClient(BrowserContext * browser_context)38 explicit NotificationBrowserClient(BrowserContext* browser_context)
39 : platform_notification_service_(
40 std::make_unique<MockPlatformNotificationService>(
41 browser_context)) {}
42
GetPlatformNotificationService(BrowserContext * browser_context)43 PlatformNotificationService* GetPlatformNotificationService(
44 BrowserContext* browser_context) override {
45 return platform_notification_service_.get();
46 }
47
48 private:
49 std::unique_ptr<PlatformNotificationService> platform_notification_service_;
50 };
51
52 } // namespace
53
54 class PlatformNotificationContextTriggerTest : public ::testing::Test {
55 public:
PlatformNotificationContextTriggerTest()56 PlatformNotificationContextTriggerTest()
57 : task_environment_(base::test::TaskEnvironment::MainThreadType::UI,
58 base::test::TaskEnvironment::TimeSource::MOCK_TIME),
59 notification_browser_client_(&browser_context_),
60 success_(false) {
61 SetBrowserClientForTesting(¬ification_browser_client_);
62 }
63
SetUp()64 void SetUp() override {
65 scoped_feature_list_.InitAndEnableFeature(features::kNotificationTriggers);
66 platform_notification_context_ =
67 base::MakeRefCounted<PlatformNotificationContextImpl>(
68 base::FilePath(), &browser_context_, nullptr);
69 platform_notification_context_->SetTaskRunnerForTesting(
70 base::ThreadTaskRunnerHandle::Get());
71 platform_notification_context_->Initialize();
72 base::RunLoop().RunUntilIdle();
73 }
74
TearDown()75 void TearDown() override {
76 // Destroy the context and allow its background tasks to run to close the
77 // database.
78 platform_notification_context_.reset();
79 task_environment_.RunUntilIdle();
80 }
81
82 // Callback to provide when writing a notification to the database.
DidWriteNotificationData(bool success,const std::string & notification_id)83 void DidWriteNotificationData(bool success,
84 const std::string& notification_id) {
85 success_ = success;
86 }
87
88 protected:
WriteNotificationData(const std::string & tag,base::Optional<base::Time> timestamp)89 void WriteNotificationData(const std::string& tag,
90 base::Optional<base::Time> timestamp) {
91 ASSERT_TRUE(
92 TryWriteNotificationData("https://example.com", tag, timestamp));
93 }
94
TryWriteNotificationData(const std::string & url,const std::string & tag,base::Optional<base::Time> timestamp)95 bool TryWriteNotificationData(const std::string& url,
96 const std::string& tag,
97 base::Optional<base::Time> timestamp) {
98 GURL origin(url);
99 NotificationDatabaseData notification_database_data;
100 notification_database_data.origin = origin;
101 notification_database_data.service_worker_registration_id =
102 kFakeServiceWorkerRegistrationId;
103 notification_database_data.notification_data.show_trigger_timestamp =
104 timestamp;
105 notification_database_data.notification_data.tag = tag;
106
107 platform_notification_context_->WriteNotificationData(
108 next_persistent_notification_id(), kFakeServiceWorkerRegistrationId,
109 origin, notification_database_data,
110 base::BindOnce(
111 &PlatformNotificationContextTriggerTest::DidWriteNotificationData,
112 base::Unretained(this)));
113 base::RunLoop().RunUntilIdle();
114 return success_;
115 }
116
117 // Gets the currently displayed notifications from
118 // |notification_browser_client_| synchronously.
GetDisplayedNotifications()119 std::set<std::string> GetDisplayedNotifications() {
120 std::set<std::string> displayed_notification_ids;
121 base::RunLoop run_loop;
122 notification_browser_client_
123 .GetPlatformNotificationService(&browser_context_)
124 ->GetDisplayedNotifications(base::BindLambdaForTesting(
125 [&](std::set<std::string> notification_ids, bool supports_sync) {
126 displayed_notification_ids = std::move(notification_ids);
127 run_loop.Quit();
128 }));
129 run_loop.Run();
130 return displayed_notification_ids;
131 }
132
TriggerNotifications()133 void TriggerNotifications() {
134 platform_notification_context_->TriggerNotifications();
135 base::RunLoop().RunUntilIdle();
136 }
137
138 BrowserTaskEnvironment task_environment_; // Must be first member
139
140 private:
141 base::test::ScopedFeatureList scoped_feature_list_;
142 TestBrowserContext browser_context_;
143 NotificationBrowserClient notification_browser_client_;
144 scoped_refptr<PlatformNotificationContextImpl> platform_notification_context_;
145
146 // Returns the next persistent notification id for tests.
next_persistent_notification_id()147 int64_t next_persistent_notification_id() {
148 return next_persistent_notification_id_++;
149 }
150
151 bool success_;
152 int64_t next_persistent_notification_id_ = 1;
153 };
154
TEST_F(PlatformNotificationContextTriggerTest,TriggerInFuture)155 TEST_F(PlatformNotificationContextTriggerTest, TriggerInFuture) {
156 WriteNotificationData("1", Time::Now() + TimeDelta::FromSeconds(10));
157 ASSERT_EQ(0u, GetDisplayedNotifications().size());
158
159 // Wait until the trigger timestamp is reached.
160 task_environment_.FastForwardBy(TimeDelta::FromSeconds(10));
161
162 // This gets called by the notification scheduling system.
163 TriggerNotifications();
164
165 ASSERT_EQ(1u, GetDisplayedNotifications().size());
166 }
167
TEST_F(PlatformNotificationContextTriggerTest,TriggerInPast)168 TEST_F(PlatformNotificationContextTriggerTest, TriggerInPast) {
169 // Trigger timestamp in the past should immediately trigger.
170 WriteNotificationData("1", Time::Now() - TimeDelta::FromSeconds(10));
171
172 // This gets called by the notification scheduling system.
173 TriggerNotifications();
174
175 ASSERT_EQ(1u, GetDisplayedNotifications().size());
176 }
177
TEST_F(PlatformNotificationContextTriggerTest,OverwriteExistingTriggerInFuture)178 TEST_F(PlatformNotificationContextTriggerTest,
179 OverwriteExistingTriggerInFuture) {
180 WriteNotificationData("1", Time::Now() + TimeDelta::FromSeconds(10));
181 ASSERT_EQ(0u, GetDisplayedNotifications().size());
182
183 task_environment_.FastForwardBy(TimeDelta::FromSeconds(5));
184 ASSERT_EQ(0u, GetDisplayedNotifications().size());
185
186 // Overwrites the scheduled notifications with a new trigger timestamp.
187 WriteNotificationData("1", Time::Now() + TimeDelta::FromSeconds(10));
188
189 task_environment_.FastForwardBy(TimeDelta::FromSeconds(5));
190 ASSERT_EQ(0u, GetDisplayedNotifications().size());
191
192 task_environment_.FastForwardBy(TimeDelta::FromSeconds(5));
193
194 // This gets called by the notification scheduling system.
195 TriggerNotifications();
196
197 ASSERT_EQ(1u, GetDisplayedNotifications().size());
198 }
199
TEST_F(PlatformNotificationContextTriggerTest,OverwriteExistingTriggerToPast)200 TEST_F(PlatformNotificationContextTriggerTest, OverwriteExistingTriggerToPast) {
201 WriteNotificationData("1", Time::Now() + TimeDelta::FromSeconds(10));
202 ASSERT_EQ(0u, GetDisplayedNotifications().size());
203
204 task_environment_.FastForwardBy(TimeDelta::FromSeconds(5));
205
206 // Overwrites the scheduled notifications with a new trigger timestamp.
207 WriteNotificationData("1", Time::Now() - TimeDelta::FromSeconds(10));
208
209 // This gets called by the notification scheduling system.
210 TriggerNotifications();
211
212 ASSERT_EQ(1u, GetDisplayedNotifications().size());
213 }
214
TEST_F(PlatformNotificationContextTriggerTest,OverwriteDisplayedNotificationToPast)215 TEST_F(PlatformNotificationContextTriggerTest,
216 OverwriteDisplayedNotificationToPast) {
217 WriteNotificationData("1", Time::Now() + TimeDelta::FromSeconds(10));
218 task_environment_.FastForwardBy(TimeDelta::FromSeconds(10));
219
220 // Overwrites a displayed notification with a trigger timestamp in the past.
221 WriteNotificationData("1", Time::Now() - TimeDelta::FromSeconds(10));
222
223 // This gets called by the notification scheduling system.
224 TriggerNotifications();
225
226 ASSERT_EQ(1u, GetDisplayedNotifications().size());
227 }
228
TEST_F(PlatformNotificationContextTriggerTest,OverwriteDisplayedNotificationToFuture)229 TEST_F(PlatformNotificationContextTriggerTest,
230 OverwriteDisplayedNotificationToFuture) {
231 WriteNotificationData("1", Time::Now() + TimeDelta::FromSeconds(10));
232 task_environment_.FastForwardBy(TimeDelta::FromSeconds(10));
233
234 // This gets called by the notification scheduling system.
235 TriggerNotifications();
236
237 // Overwrites a displayed notification which hides it until the trigger
238 // timestamp is reached.
239 WriteNotificationData("1", Time::Now() + TimeDelta::FromSeconds(10));
240
241 ASSERT_EQ(0u, GetDisplayedNotifications().size());
242
243 task_environment_.FastForwardBy(TimeDelta::FromSeconds(10));
244
245 // This gets called by the notification scheduling system.
246 TriggerNotifications();
247
248 ASSERT_EQ(1u, GetDisplayedNotifications().size());
249 }
250
TEST_F(PlatformNotificationContextTriggerTest,LimitsNumberOfScheduledNotificationsPerOrigin)251 TEST_F(PlatformNotificationContextTriggerTest,
252 LimitsNumberOfScheduledNotificationsPerOrigin) {
253 for (int i = 1; i <= kMaximumScheduledNotificationsPerOrigin; ++i) {
254 WriteNotificationData(std::to_string(i),
255 Time::Now() + TimeDelta::FromSeconds(i));
256 }
257
258 ASSERT_FALSE(TryWriteNotificationData(
259 "https://example.com",
260 std::to_string(kMaximumScheduledNotificationsPerOrigin + 1),
261 Time::Now() +
262 TimeDelta::FromSeconds(kMaximumScheduledNotificationsPerOrigin + 1)));
263
264 ASSERT_TRUE(TryWriteNotificationData(
265 "https://example2.com",
266 std::to_string(kMaximumScheduledNotificationsPerOrigin + 1),
267 Time::Now() +
268 TimeDelta::FromSeconds(kMaximumScheduledNotificationsPerOrigin + 1)));
269 }
270
TEST_F(PlatformNotificationContextTriggerTest,EnforcesLimitOnUpdate)271 TEST_F(PlatformNotificationContextTriggerTest, EnforcesLimitOnUpdate) {
272 for (int i = 1; i <= kMaximumScheduledNotificationsPerOrigin; ++i) {
273 WriteNotificationData(std::to_string(i),
274 Time::Now() + TimeDelta::FromSeconds(i));
275 }
276
277 ASSERT_TRUE(TryWriteNotificationData(
278 "https://example.com",
279 std::to_string(kMaximumScheduledNotificationsPerOrigin + 1),
280 base::nullopt));
281
282 ASSERT_FALSE(TryWriteNotificationData(
283 "https://example.com",
284 std::to_string(kMaximumScheduledNotificationsPerOrigin + 1),
285 Time::Now() +
286 TimeDelta::FromSeconds(kMaximumScheduledNotificationsPerOrigin + 1)));
287 }
288
TEST_F(PlatformNotificationContextTriggerTest,RecordDisplayDelay)289 TEST_F(PlatformNotificationContextTriggerTest, RecordDisplayDelay) {
290 base::HistogramTester histogram_tester;
291 base::TimeDelta trigger_delay = TimeDelta::FromSeconds(10);
292 base::TimeDelta display_delay = TimeDelta::FromSeconds(8);
293
294 WriteNotificationData("1", Time::Now() + trigger_delay);
295 ASSERT_EQ(0u, GetDisplayedNotifications().size());
296
297 // Forward time until after the expected trigger time.
298 task_environment_.FastForwardBy(trigger_delay + display_delay);
299
300 // Trigger notification |display_delay| after it should have been displayed.
301 TriggerNotifications();
302
303 histogram_tester.ExpectUniqueSample("Notifications.Triggers.DisplayDelay",
304 display_delay.InMilliseconds(), 1);
305 }
306
307 } // namespace content
308