Index: content/browser/notifications/platform_notification_context_unittest.cc |
diff --git a/content/browser/notifications/platform_notification_context_unittest.cc b/content/browser/notifications/platform_notification_context_unittest.cc |
index 945fd16a4f7328d45a99f251fd107899dc7b1665..42f6bef89870226c5a6baf3a7d3b57eaad2222ff 100644 |
--- a/content/browser/notifications/platform_notification_context_unittest.cc |
+++ b/content/browser/notifications/platform_notification_context_unittest.cc |
@@ -9,6 +9,7 @@ |
#include "base/files/scoped_temp_dir.h" |
#include "base/run_loop.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "base/test/simple_test_clock.h" |
#include "base/threading/thread_task_runner_handle.h" |
#include "content/browser/notifications/platform_notification_context_impl.h" |
#include "content/browser/service_worker/embedded_worker_test_helper.h" |
@@ -85,15 +86,29 @@ class PlatformNotificationContextTest : public ::testing::Test { |
// Callback to provide when reading multiple notifications from the database. |
// Will store the success value in the class member, and write the read |
- // notification datas to |store_notification_datas|. |
+ // notification datas to |*out_notification_datas|. |
void DidReadAllNotificationDatas( |
- std::vector<NotificationDatabaseData>* store_notification_datas, |
+ std::vector<NotificationDatabaseData>* out_notification_datas, |
bool success, |
const std::vector<NotificationDatabaseData>& notification_datas) { |
- DCHECK(store_notification_datas); |
+ DCHECK(out_notification_datas); |
success_ = success; |
- *store_notification_datas = notification_datas; |
+ *out_notification_datas = notification_datas; |
+ } |
+ |
+ // Callback to provide when calling GetDisplayedNotifications on the |
+ // PlatformNotificationService. |
+ void DidGetDisplayedNotifications( |
+ std::unique_ptr<std::set<std::string>>* out_notifications, |
+ bool* out_supports_synchronization, |
+ std::unique_ptr<std::set<std::string>> notifications, |
+ bool supports_synchronization) { |
+ DCHECK(out_notifications); |
+ DCHECK(out_supports_synchronization); |
+ |
+ *out_notifications = std::move(notifications); |
+ *out_supports_synchronization = supports_synchronization; |
} |
protected: |
@@ -109,6 +124,15 @@ class PlatformNotificationContextTest : public ::testing::Test { |
return context; |
} |
+ // Creates and returns a fake clock for the |context| that can be used to |
+ // advance the time. Used by tests that care about the deletion grace period. |
+ base::SimpleTestClock* CreateFakeClock( |
+ PlatformNotificationContextImpl* context) { |
+ base::SimpleTestClock* clock = new base::SimpleTestClock(); |
+ context->clock_ = base::WrapUnique(clock); |
+ return clock; |
+ } |
+ |
// Overrides the task runner in |context| with the current message loop |
// proxy, to reduce the number of threads involved in the tests. |
void OverrideTaskRunnerForTesting(PlatformNotificationContextImpl* context) { |
@@ -507,6 +531,8 @@ TEST_F(PlatformNotificationContextTest, SynchronizeNotifications) { |
scoped_refptr<PlatformNotificationContextImpl> context = |
CreatePlatformNotificationContext(); |
+ base::SimpleTestClock* clock = CreateFakeClock(context.get()); |
+ |
GURL origin("https://example.com"); |
NotificationDatabaseData notification_database_data; |
notification_database_data.service_worker_registration_id = |
@@ -523,6 +549,11 @@ TEST_F(PlatformNotificationContextTest, SynchronizeNotifications) { |
ASSERT_TRUE(success()); |
EXPECT_FALSE(notification_id().empty()); |
+ // Forward the time by a minute to ensure that we're past the grace period for |
+ // newly shown notifications, making sure that the list of displayed |
+ // notifications retrieved from the PlatformNotificationService will be used. |
+ clock->Advance(base::TimeDelta::FromMinutes(1)); |
+ |
PlatformNotificationService* service = |
notification_browser_client.GetPlatformNotificationService(); |
@@ -565,4 +596,83 @@ TEST_F(PlatformNotificationContextTest, SynchronizeNotifications) { |
EXPECT_FALSE(success()); |
} |
+TEST_F(PlatformNotificationContextTest, SynchronizeNotificationsDisplayRace) { |
+ // This test verifies that a possible race condition where a developer shows |
+ // a notification, and then gets all shown notifications before waiting for |
+ // acknowledgement (which, in itself, is too optimistic), is made less severe |
+ // by remembering the most recently shown notification for a brief period of |
+ // time. |
+ |
+ NotificationBrowserClient notification_browser_client; |
+ SetBrowserClientForTesting(¬ification_browser_client); |
+ |
+ scoped_refptr<PlatformNotificationContextImpl> context = |
+ CreatePlatformNotificationContext(); |
+ |
+ base::SimpleTestClock* clock = CreateFakeClock(context.get()); |
+ |
+ GURL origin("https://example.com"); |
+ NotificationDatabaseData notification_database_data; |
+ notification_database_data.service_worker_registration_id = |
+ kFakeServiceWorkerRegistrationId; |
+ PlatformNotificationData notification_data; |
+ content::NotificationResources notification_resources; |
+ |
+ context->WriteNotificationData( |
+ origin, notification_database_data, |
+ base::Bind(&PlatformNotificationContextTest::DidWriteNotificationData, |
+ base::Unretained(this))); |
+ |
+ base::RunLoop().RunUntilIdle(); |
+ ASSERT_TRUE(success()); |
+ EXPECT_FALSE(notification_id().empty()); |
+ |
+ // The |notification_data| deliberately has not been dispatched to the |
+ // PlatformNotificationService yet. This will cause the mock to return that |
+ // no notifications have been displayed, simulating the race condition. |
+ |
+ std::unique_ptr<std::set<std::string>> notifications; |
+ bool supports_synchronization = false; |
+ |
+ PlatformNotificationService* service = |
+ notification_browser_client.GetPlatformNotificationService(); |
+ service->GetDisplayedNotifications( |
+ browser_context(), |
+ base::Bind(&PlatformNotificationContextTest::DidGetDisplayedNotifications, |
+ base::Unretained(this), ¬ifications, |
+ &supports_synchronization)); |
+ |
+ base::RunLoop().RunUntilIdle(); |
+ ASSERT_TRUE(supports_synchronization); |
+ ASSERT_EQ(notifications->size(), 0u); |
+ |
+ // Reading all notifications should still return the one notification that was |
+ // displayed, since it was the most recent one. |
+ |
+ std::vector<NotificationDatabaseData> notification_database_datas; |
+ context->ReadAllNotificationDataForServiceWorkerRegistration( |
+ origin, kFakeServiceWorkerRegistrationId, |
+ base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas, |
+ base::Unretained(this), ¬ification_database_datas)); |
+ |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ ASSERT_TRUE(success()); |
+ ASSERT_EQ(1u, notification_database_datas.size()); |
+ |
+ // Now forward the time by a minute, which means that the grace period should |
+ // have exceeded. Getting the notifications again should ignore it now. |
+ clock->Advance(base::TimeDelta::FromMinutes(1)); |
+ |
+ context->ReadAllNotificationDataForServiceWorkerRegistration( |
+ origin, kFakeServiceWorkerRegistrationId, |
+ base::Bind(&PlatformNotificationContextTest::DidReadAllNotificationDatas, |
+ base::Unretained(this), ¬ification_database_datas)); |
+ |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ ASSERT_TRUE(success()); |
+ ASSERT_EQ(0u, notification_database_datas.size()); |
+} |
+ |
} // namespace content |