Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Side by Side Diff: content/browser/notifications/platform_notification_context_unittest.cc

Issue 2878443002: Address a race condition in displaying notifications
Patch Set: Address a race condition in displaying notifications Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <stdint.h> 5 #include <stdint.h>
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/files/file_util.h" 8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h" 9 #include "base/files/scoped_temp_dir.h"
10 #include "base/run_loop.h" 10 #include "base/run_loop.h"
11 #include "base/strings/utf_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h"
12 #include "base/test/simple_test_clock.h"
12 #include "base/threading/thread_task_runner_handle.h" 13 #include "base/threading/thread_task_runner_handle.h"
13 #include "content/browser/notifications/platform_notification_context_impl.h" 14 #include "content/browser/notifications/platform_notification_context_impl.h"
14 #include "content/browser/service_worker/embedded_worker_test_helper.h" 15 #include "content/browser/service_worker/embedded_worker_test_helper.h"
15 #include "content/browser/service_worker/service_worker_context_wrapper.h" 16 #include "content/browser/service_worker/service_worker_context_wrapper.h"
16 #include "content/common/service_worker/service_worker_types.h" 17 #include "content/common/service_worker/service_worker_types.h"
17 #include "content/public/browser/notification_database_data.h" 18 #include "content/public/browser/notification_database_data.h"
18 #include "content/public/common/notification_resources.h" 19 #include "content/public/common/notification_resources.h"
19 #include "content/public/test/test_browser_context.h" 20 #include "content/public/test/test_browser_context.h"
20 #include "content/public/test/test_browser_thread_bundle.h" 21 #include "content/public/test/test_browser_thread_bundle.h"
21 #include "content/test/mock_platform_notification_service.h" 22 #include "content/test/mock_platform_notification_service.h"
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 // Callback to provide when unregistering a Service Worker. Will write the 79 // Callback to provide when unregistering a Service Worker. Will write the
79 // resulting status code to |store_status|. 80 // resulting status code to |store_status|.
80 void DidUnregisterServiceWorker(ServiceWorkerStatusCode* store_status, 81 void DidUnregisterServiceWorker(ServiceWorkerStatusCode* store_status,
81 ServiceWorkerStatusCode status) { 82 ServiceWorkerStatusCode status) {
82 DCHECK(store_status); 83 DCHECK(store_status);
83 *store_status = status; 84 *store_status = status;
84 } 85 }
85 86
86 // Callback to provide when reading multiple notifications from the database. 87 // Callback to provide when reading multiple notifications from the database.
87 // Will store the success value in the class member, and write the read 88 // Will store the success value in the class member, and write the read
88 // notification datas to |store_notification_datas|. 89 // notification datas to |*out_notification_datas|.
89 void DidReadAllNotificationDatas( 90 void DidReadAllNotificationDatas(
90 std::vector<NotificationDatabaseData>* store_notification_datas, 91 std::vector<NotificationDatabaseData>* out_notification_datas,
91 bool success, 92 bool success,
92 const std::vector<NotificationDatabaseData>& notification_datas) { 93 const std::vector<NotificationDatabaseData>& notification_datas) {
93 DCHECK(store_notification_datas); 94 DCHECK(out_notification_datas);
94 95
95 success_ = success; 96 success_ = success;
96 *store_notification_datas = notification_datas; 97 *out_notification_datas = notification_datas;
98 }
99
100 // Callback to provide when calling GetDisplayedNotifications on the
101 // PlatformNotificationService.
102 void DidGetDisplayedNotifications(
103 std::unique_ptr<std::set<std::string>>* out_notifications,
104 bool* out_supports_synchronization,
105 std::unique_ptr<std::set<std::string>> notifications,
106 bool supports_synchronization) {
107 DCHECK(out_notifications);
108 DCHECK(out_supports_synchronization);
109
110 *out_notifications = std::move(notifications);
111 *out_supports_synchronization = supports_synchronization;
97 } 112 }
98 113
99 protected: 114 protected:
100 // Creates a new PlatformNotificationContextImpl instance. When using this 115 // Creates a new PlatformNotificationContextImpl instance. When using this
101 // method, the underlying database will always be created in memory. 116 // method, the underlying database will always be created in memory.
102 PlatformNotificationContextImpl* CreatePlatformNotificationContext() { 117 PlatformNotificationContextImpl* CreatePlatformNotificationContext() {
103 PlatformNotificationContextImpl* context = 118 PlatformNotificationContextImpl* context =
104 new PlatformNotificationContextImpl(base::FilePath(), &browser_context_, 119 new PlatformNotificationContextImpl(base::FilePath(), &browser_context_,
105 nullptr); 120 nullptr);
106 context->Initialize(); 121 context->Initialize();
107 122
108 OverrideTaskRunnerForTesting(context); 123 OverrideTaskRunnerForTesting(context);
109 return context; 124 return context;
110 } 125 }
111 126
127 // Creates and returns a fake clock for the |context| that can be used to
128 // advance the time. Used by tests that care about the deletion grace period.
129 base::SimpleTestClock* CreateFakeClock(
130 PlatformNotificationContextImpl* context) {
131 base::SimpleTestClock* clock = new base::SimpleTestClock();
132 context->clock_ = base::WrapUnique(clock);
133 return clock;
134 }
135
112 // Overrides the task runner in |context| with the current message loop 136 // Overrides the task runner in |context| with the current message loop
113 // proxy, to reduce the number of threads involved in the tests. 137 // proxy, to reduce the number of threads involved in the tests.
114 void OverrideTaskRunnerForTesting(PlatformNotificationContextImpl* context) { 138 void OverrideTaskRunnerForTesting(PlatformNotificationContextImpl* context) {
115 context->SetTaskRunnerForTesting(base::ThreadTaskRunnerHandle::Get()); 139 context->SetTaskRunnerForTesting(base::ThreadTaskRunnerHandle::Get());
116 } 140 }
117 141
118 // Returns the testing browsing context that can be used for this test. 142 // Returns the testing browsing context that can be used for this test.
119 BrowserContext* browser_context() { return &browser_context_; } 143 BrowserContext* browser_context() { return &browser_context_; }
120 144
121 // Returns whether the last invoked callback finished successfully. 145 // Returns whether the last invoked callback finished successfully.
(...skipping 378 matching lines...) Expand 10 before | Expand all | Expand 10 after
500 } 524 }
501 } 525 }
502 526
503 TEST_F(PlatformNotificationContextTest, SynchronizeNotifications) { 527 TEST_F(PlatformNotificationContextTest, SynchronizeNotifications) {
504 NotificationBrowserClient notification_browser_client; 528 NotificationBrowserClient notification_browser_client;
505 SetBrowserClientForTesting(&notification_browser_client); 529 SetBrowserClientForTesting(&notification_browser_client);
506 530
507 scoped_refptr<PlatformNotificationContextImpl> context = 531 scoped_refptr<PlatformNotificationContextImpl> context =
508 CreatePlatformNotificationContext(); 532 CreatePlatformNotificationContext();
509 533
534 base::SimpleTestClock* clock = CreateFakeClock(context.get());
535
510 GURL origin("https://example.com"); 536 GURL origin("https://example.com");
511 NotificationDatabaseData notification_database_data; 537 NotificationDatabaseData notification_database_data;
512 notification_database_data.service_worker_registration_id = 538 notification_database_data.service_worker_registration_id =
513 kFakeServiceWorkerRegistrationId; 539 kFakeServiceWorkerRegistrationId;
514 PlatformNotificationData notification_data; 540 PlatformNotificationData notification_data;
515 content::NotificationResources notification_resources; 541 content::NotificationResources notification_resources;
516 542
517 context->WriteNotificationData( 543 context->WriteNotificationData(
518 origin, notification_database_data, 544 origin, notification_database_data,
519 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData, 545 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData,
520 base::Unretained(this))); 546 base::Unretained(this)));
521 547
522 base::RunLoop().RunUntilIdle(); 548 base::RunLoop().RunUntilIdle();
523 ASSERT_TRUE(success()); 549 ASSERT_TRUE(success());
524 EXPECT_FALSE(notification_id().empty()); 550 EXPECT_FALSE(notification_id().empty());
525 551
552 // Forward the time by a minute to ensure that we're past the grace period for
553 // newly shown notifications, making sure that the list of displayed
554 // notifications retrieved from the PlatformNotificationService will be used.
555 clock->Advance(base::TimeDelta::FromMinutes(1));
556
526 PlatformNotificationService* service = 557 PlatformNotificationService* service =
527 notification_browser_client.GetPlatformNotificationService(); 558 notification_browser_client.GetPlatformNotificationService();
528 559
529 service->DisplayPersistentNotification(browser_context(), notification_id(), 560 service->DisplayPersistentNotification(browser_context(), notification_id(),
530 origin, origin, notification_data, 561 origin, origin, notification_data,
531 notification_resources); 562 notification_resources);
532 563
533 std::vector<NotificationDatabaseData> notification_database_datas; 564 std::vector<NotificationDatabaseData> notification_database_datas;
534 context->ReadAllNotificationDataForServiceWorkerRegistration( 565 context->ReadAllNotificationDataForServiceWorkerRegistration(
535 origin, kFakeServiceWorkerRegistrationId, 566 origin, kFakeServiceWorkerRegistrationId,
(...skipping 22 matching lines...) Expand all
558 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData, 589 base::Bind(&PlatformNotificationContextTest::DidReadNotificationData,
559 base::Unretained(this))); 590 base::Unretained(this)));
560 591
561 base::RunLoop().RunUntilIdle(); 592 base::RunLoop().RunUntilIdle();
562 593
563 // The notification was removed, so we shouldn't be able to read it from 594 // The notification was removed, so we shouldn't be able to read it from
564 // the database anymore. 595 // the database anymore.
565 EXPECT_FALSE(success()); 596 EXPECT_FALSE(success());
566 } 597 }
567 598
599 TEST_F(PlatformNotificationContextTest, SynchronizeNotificationsDisplayRace) {
600 // This test verifies that a possible race condition where a developer shows
601 // a notification, and then gets all shown notifications before waiting for
602 // acknowledgement (which, in itself, is too optimistic), is made less severe
603 // by remembering the most recently shown notification for a brief period of
604 // time.
605
606 NotificationBrowserClient notification_browser_client;
607 SetBrowserClientForTesting(&notification_browser_client);
608
609 scoped_refptr<PlatformNotificationContextImpl> context =
610 CreatePlatformNotificationContext();
611
612 base::SimpleTestClock* clock = CreateFakeClock(context.get());
613
614 GURL origin("https://example.com");
615 NotificationDatabaseData notification_database_data;
616 notification_database_data.service_worker_registration_id =
617 kFakeServiceWorkerRegistrationId;
618 PlatformNotificationData notification_data;
619 content::NotificationResources notification_resources;
620
621 context->WriteNotificationData(
622 origin, notification_database_data,
623 base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData,
624 base::Unretained(this)));
625
626 base::RunLoop().RunUntilIdle();
627 ASSERT_TRUE(success());
628 EXPECT_FALSE(notification_id().empty());
629
630 // The |notification_data| deliberately has not been dispatched to the
631 // PlatformNotificationService yet. This will cause the mock to return that
632 // no notifications have been displayed, simulating the race condition.
633
634 std::unique_ptr<std::set<std::string>> notifications;
635 bool supports_synchronization = false;
636
637 PlatformNotificationService* service =
638 notification_browser_client.GetPlatformNotificationService();
639 service->GetDisplayedNotifications(
640 browser_context(),
641 base::Bind(&PlatformNotificationContextTest::DidGetDisplayedNotifications,
642 base::Unretained(this), &notifications,
643 &supports_synchronization));
644
645 base::RunLoop().RunUntilIdle();
646 ASSERT_TRUE(supports_synchronization);
647 ASSERT_EQ(notifications->size(), 0u);
648
649 // Reading all notifications should still return the one notification that was
650 // displayed, since it was the most recent one.
651
652 std::vector<NotificationDatabaseData> notification_database_datas;
653 context->ReadAllNotificationDataForServiceWorkerRegistration(
654 origin, kFakeServiceWorkerRegistrationId,
655 base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas,
656 base::Unretained(this), &notification_database_datas));
657
658 base::RunLoop().RunUntilIdle();
659
660 ASSERT_TRUE(success());
661 ASSERT_EQ(1u, notification_database_datas.size());
662
663 // Now forward the time by a minute, which means that the grace period should
664 // have exceeded. Getting the notifications again should ignore it now.
665 clock->Advance(base::TimeDelta::FromMinutes(1));
666
667 context->ReadAllNotificationDataForServiceWorkerRegistration(
668 origin, kFakeServiceWorkerRegistrationId,
669 base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas,
670 base::Unretained(this), &notification_database_datas));
671
672 base::RunLoop().RunUntilIdle();
673
674 ASSERT_TRUE(success());
675 ASSERT_EQ(0u, notification_database_datas.size());
676 }
677
568 } // namespace content 678 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698