| // Copyright 2021 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/browser/renderer_host/visible_time_request_trigger.h" | 
 |  | 
 | #include <tuple> | 
 | #include <utility> | 
 |  | 
 | #include "base/time/time.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "third_party/blink/public/mojom/widget/record_content_to_visible_time_request.mojom.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | class VisibleTimeRequestTriggerTest : public testing::Test { | 
 |  protected: | 
 |   using RecordContentToVisibleTimeRequest = | 
 |       blink::mojom::RecordContentToVisibleTimeRequest; | 
 |   using RecordContentToVisibleTimeRequestPtr = | 
 |       blink::mojom::RecordContentToVisibleTimeRequestPtr; | 
 |  | 
 |   // Converts a base::TimeDelta into a base::TimeTicks value suitable for | 
 |   // storing in the `event_start_time` field of a | 
 |   // RecordContentToVisibleTimeRequest. | 
 |   static base::TimeTicks StartTimeFromDelta(base::TimeDelta delta) { | 
 |     return base::TimeTicks() + delta; | 
 |   } | 
 |  | 
 |   // Returns a request with the given `start_time`, which is given as a delta | 
 |   // from 0 since callers don't have an easy way to create base::TimeTicks | 
 |   // directly. | 
 |   static RecordContentToVisibleTimeRequestPtr CreateRequestPtr( | 
 |       base::TimeDelta start_time, | 
 |       bool destination_is_loaded = false, | 
 |       bool show_reason_tab_switching = false, | 
 |       bool show_reason_bfcache_restore = false, | 
 |       bool show_reason_unfolding = false) { | 
 |     return RecordContentToVisibleTimeRequest::New( | 
 |         StartTimeFromDelta(start_time), destination_is_loaded, | 
 |         show_reason_tab_switching, show_reason_bfcache_restore, | 
 |         show_reason_unfolding); | 
 |   } | 
 |  | 
 |   // Expects that all fields of `request` and `expected` match. | 
 |   static void ExpectEqualRequests( | 
 |       RecordContentToVisibleTimeRequestPtr request, | 
 |       const RecordContentToVisibleTimeRequest& expected) { | 
 |     ASSERT_TRUE(request); | 
 |     EXPECT_EQ(request->event_start_time, expected.event_start_time); | 
 |     EXPECT_EQ(request->destination_is_loaded, expected.destination_is_loaded); | 
 |     EXPECT_EQ(request->show_reason_tab_switching, | 
 |               expected.show_reason_tab_switching); | 
 |     EXPECT_EQ(request->show_reason_bfcache_restore, | 
 |               expected.show_reason_bfcache_restore); | 
 |   } | 
 | }; | 
 |  | 
 | TEST_F(VisibleTimeRequestTriggerTest, MergeEmpty) { | 
 |   EXPECT_TRUE( | 
 |       VisibleTimeRequestTrigger::ConsumeAndMergeRequests(nullptr, nullptr) | 
 |           .is_null()); | 
 |   ExpectEqualRequests(VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |                           nullptr, RecordContentToVisibleTimeRequest::New()), | 
 |                       {}); | 
 |   ExpectEqualRequests(VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |                           RecordContentToVisibleTimeRequest::New(), nullptr), | 
 |                       {}); | 
 |   ExpectEqualRequests(VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |                           RecordContentToVisibleTimeRequest::New(), | 
 |                           RecordContentToVisibleTimeRequest::New()), | 
 |                       {}); | 
 | } | 
 |  | 
 | TEST_F(VisibleTimeRequestTriggerTest, MergeStartTimes) { | 
 |   // Overflowing can make a clock wrap around to negatives. | 
 |   constexpr auto kNegative = base::TimeDelta::Min(); | 
 |   constexpr auto kZero = base::TimeDelta(); | 
 |  | 
 |   auto expect_merged_start_time = [](base::TimeDelta left_time, | 
 |                                      base::TimeDelta right_time, | 
 |                                      base::TimeDelta expected_time) { | 
 |     SCOPED_TRACE(::testing::Message() << "Merging requests with start times " | 
 |                                       << left_time << " and " << right_time); | 
 |     RecordContentToVisibleTimeRequestPtr merged_request = | 
 |         VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |             CreateRequestPtr(left_time), CreateRequestPtr(right_time)); | 
 |     ASSERT_TRUE(merged_request); | 
 |     EXPECT_EQ(merged_request->event_start_time, | 
 |               StartTimeFromDelta(expected_time)); | 
 |   }; | 
 |  | 
 |   // Comparisons with negative times. | 
 |   expect_merged_start_time(kNegative, kNegative, kNegative); | 
 |   expect_merged_start_time(kNegative, kZero, kNegative); | 
 |   expect_merged_start_time(kNegative, base::Seconds(10), kNegative); | 
 |   expect_merged_start_time(kNegative, base::TimeDelta::Max(), kNegative); | 
 |  | 
 |   // Comparisons with time 0. | 
 |   expect_merged_start_time(kZero, kNegative, kNegative); | 
 |   expect_merged_start_time(kZero, kZero, kZero); | 
 |   expect_merged_start_time(kZero, base::Seconds(10), kZero); | 
 |   expect_merged_start_time(kZero, base::TimeDelta::Max(), kZero); | 
 |  | 
 |   // Comparisons between a mid-range time and boundary conditions. | 
 |   expect_merged_start_time(base::Seconds(10), kNegative, kNegative); | 
 |   expect_merged_start_time(base::Seconds(10), kZero, kZero); | 
 |   expect_merged_start_time(base::Seconds(10), base::TimeDelta::Max(), | 
 |                            base::Seconds(10)); | 
 |  | 
 |   // Comparisons between two mid-range times. | 
 |   expect_merged_start_time(base::Seconds(10), base::Seconds(10), | 
 |                            base::Seconds(10)); | 
 |   expect_merged_start_time(base::Seconds(10), base::Seconds(100), | 
 |                            base::Seconds(10)); | 
 |   expect_merged_start_time(base::Seconds(100), base::Seconds(10), | 
 |                            base::Seconds(10)); | 
 |  | 
 |   // Comparisons with the max time. | 
 |   expect_merged_start_time(base::TimeDelta::Max(), kNegative, kNegative); | 
 |   expect_merged_start_time(base::TimeDelta::Max(), kZero, kZero); | 
 |   expect_merged_start_time(base::TimeDelta::Max(), base::Seconds(10), | 
 |                            base::Seconds(10)); | 
 |   expect_merged_start_time(base::TimeDelta::Max(), base::TimeDelta::Max(), | 
 |                            base::TimeDelta::Max()); | 
 | } | 
 |  | 
 | TEST_F(VisibleTimeRequestTriggerTest, MergeRequests) { | 
 |   // Iterate over all possible combinations of request parameters. Tuple | 
 |   // contains `destination_is_loaded`, `show_reason_tab_switching`, | 
 |   // `show_reason_bfcache_restore`. | 
 |   using ParamTuple = std::tuple<bool, bool, bool>; | 
 |   constexpr ParamTuple kRequestParams[] = { | 
 |       // show_reason_tab_switching = true | 
 |       ParamTuple(false, true, false), | 
 |       ParamTuple(true, true, false), | 
 |       ParamTuple(false, true, true), | 
 |       ParamTuple(true, true, true), | 
 |       // show_reason_tab_switching = false | 
 |       ParamTuple(false, false, false), | 
 |       ParamTuple(false, false, true), | 
 |   }; | 
 |  | 
 |   auto request_with_params = [](const ParamTuple& params) { | 
 |     return CreateRequestPtr(base::TimeDelta(), std::get<0>(params), | 
 |                             std::get<1>(params), std::get<2>(params)); | 
 |   }; | 
 |  | 
 |   auto request_with_union_of_params = [](const ParamTuple& params1, | 
 |                                          const ParamTuple& params2) { | 
 |     const bool destination_is_loaded = | 
 |         std::get<0>(params1) || std::get<0>(params2); | 
 |     const bool show_reason_tab_switching = | 
 |         std::get<1>(params1) || std::get<1>(params2); | 
 |     const bool show_reason_bfcache_restore = | 
 |         std::get<2>(params1) || std::get<2>(params2); | 
 |     return CreateRequestPtr(base::TimeDelta(), destination_is_loaded, | 
 |                             show_reason_tab_switching, | 
 |                             show_reason_bfcache_restore); | 
 |   }; | 
 |  | 
 |   for (const ParamTuple& params : kRequestParams) { | 
 |     SCOPED_TRACE(::testing::Message() | 
 |                  << "With params " << std::get<0>(params) << "," | 
 |                  << std::get<1>(params) << "," << std::get<2>(params)); | 
 |  | 
 |     { | 
 |       // Check that these fields are set in the result if they're set in either | 
 |       // or both of the requests. | 
 |       const auto expected = request_with_params(params); | 
 |  | 
 |       ExpectEqualRequests(VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |                               RecordContentToVisibleTimeRequest::New(), | 
 |                               request_with_params(params)), | 
 |                           *expected); | 
 |       ExpectEqualRequests(VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |                               request_with_params(params), | 
 |                               RecordContentToVisibleTimeRequest::New()), | 
 |                           *expected); | 
 |       ExpectEqualRequests( | 
 |           VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |               request_with_params(params), request_with_params(params)), | 
 |           *expected); | 
 |     } | 
 |  | 
 |     // Check that when these fields are combined with another set of fields, | 
 |     // all fields are set in the result. | 
 |     for (const ParamTuple& params2 : kRequestParams) { | 
 |       SCOPED_TRACE(::testing::Message() | 
 |                    << "Combining with params " << std::get<0>(params2) << "," | 
 |                    << std::get<1>(params2) << "," << std::get<2>(params2)); | 
 |       const auto expected = request_with_union_of_params(params, params2); | 
 |       ExpectEqualRequests( | 
 |           VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |               request_with_params(params), request_with_params(params2)), | 
 |           *expected); | 
 |       ExpectEqualRequests( | 
 |           VisibleTimeRequestTrigger::ConsumeAndMergeRequests( | 
 |               request_with_params(params2), request_with_params(params)), | 
 |           *expected); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(VisibleTimeRequestTriggerTest, UpdateAndTakeRequest) { | 
 |   VisibleTimeRequestTrigger trigger; | 
 |   EXPECT_TRUE(trigger.TakeRequest().is_null()); | 
 |  | 
 |   // Calling Update then Take should clear the stored request. | 
 |   { | 
 |     const auto expected = | 
 |         CreateRequestPtr(base::Seconds(1), /*destination_is_loaded=*/false, | 
 |                          /*show_reason_tab_switching=*/true); | 
 |     trigger.UpdateRequest(StartTimeFromDelta(base::Seconds(1)), | 
 |                           /*destination_is_loaded=*/false, | 
 |                           /*show_reason_tab_switching=*/true, | 
 |                           /*show_reason_bfcache_restore=*/false); | 
 |     ExpectEqualRequests(trigger.TakeRequest(), *expected); | 
 |     EXPECT_TRUE(trigger.TakeRequest().is_null()); | 
 |   } | 
 |  | 
 |   // Calling Update twice should merge the requests. | 
 |   { | 
 |     const auto expected = | 
 |         CreateRequestPtr(base::Seconds(2), /*destination_is_loaded=*/true, | 
 |                          /*show_reason_tab_switching=*/true, | 
 |                          /*show_reason_bfcache_restore=*/true); | 
 |     trigger.UpdateRequest(StartTimeFromDelta(base::Seconds(2)), | 
 |                           /*destination_is_loaded=*/true, | 
 |                           /*show_reason_tab_switching=*/true, | 
 |                           /*show_reason_bfcache_restore=*/false); | 
 |     trigger.UpdateRequest(StartTimeFromDelta(base::Seconds(3)), | 
 |                           /*destination_is_loaded=*/false, | 
 |                           /*show_reason_tab_switching=*/false, | 
 |                           /*show_reason_bfcache_restore=*/true); | 
 |     ExpectEqualRequests(trigger.TakeRequest(), *expected); | 
 |     EXPECT_TRUE(trigger.TakeRequest().is_null()); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | }  // namespace content |