blob: 2cbc72ececfbd0a5ec259d89f887ef56a84602c2 [file] [log] [blame]
// 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