blob: e8dfadb2cfa428f24ecfe2f661ab727c52736485 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <strstream>
#include <utility>
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "content/common/content_to_visible_time_reporter.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/presentation_feedback.h"
namespace content {
constexpr char kDurationWithSavedFramesHistogram[] =
"Browser.Tabs.TotalSwitchDuration.WithSavedFrames";
constexpr char kDurationNoSavedFramesHistogram[] =
"Browser.Tabs.TotalSwitchDuration.NoSavedFrames_Loaded";
constexpr char kDurationNoSavedFramesNotFrozenHistogram[] =
"Browser.Tabs.TotalSwitchDuration.NoSavedFrames_Loaded_NotFrozen";
constexpr char kDurationNoSavedFramesFrozenHistogram[] =
"Browser.Tabs.TotalSwitchDuration.NoSavedFrames_Loaded_Frozen";
constexpr char kDurationNoSavedFramesUnloadedHistogram[] =
"Browser.Tabs.TotalSwitchDuration.NoSavedFrames_NotLoaded";
constexpr char kIncompleteDurationWithSavedFramesHistogram[] =
"Browser.Tabs.TotalIncompleteSwitchDuration.WithSavedFrames";
constexpr char kIncompleteDurationNoSavedFramesHistogram[] =
"Browser.Tabs.TotalIncompleteSwitchDuration.NoSavedFrames_Loaded";
constexpr char kIncompleteDurationNoSavedFramesNotFrozenHistogram[] =
"Browser.Tabs.TotalIncompleteSwitchDuration.NoSavedFrames_Loaded_NotFrozen";
constexpr char kIncompleteDurationNoSavedFramesFrozenHistogram[] =
"Browser.Tabs.TotalIncompleteSwitchDuration.NoSavedFrames_Loaded_Frozen";
constexpr char kIncompleteDurationNoSavedFramesUnloadedHistogram[] =
"Browser.Tabs.TotalIncompleteSwitchDuration.NoSavedFrames_NotLoaded";
constexpr char kResultWithSavedFramesHistogram[] =
"Browser.Tabs.TabSwitchResult.WithSavedFrames";
constexpr char kResultNoSavedFramesHistogram[] =
"Browser.Tabs.TabSwitchResult.NoSavedFrames_Loaded";
constexpr char kResultNoSavedFramesNotFrozenHistogram[] =
"Browser.Tabs.TabSwitchResult.NoSavedFrames_Loaded_NotFrozen";
constexpr char kResultNoSavedFramesFrozenHistogram[] =
"Browser.Tabs.TabSwitchResult.NoSavedFrames_Loaded_Frozen";
constexpr char kResultNoSavedFramesUnloadedHistogram[] =
"Browser.Tabs.TabSwitchResult.NoSavedFrames_NotLoaded";
constexpr char kWebContentsUnOccludedHistogram[] =
"Aura.WebContentsWindowUnOccludedTime";
constexpr char kBfcacheRestoreHistogram[] =
"BackForwardCache.Restore.NavigationToFirstPaint";
constexpr base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(42);
constexpr base::TimeDelta kOtherDuration =
base::TimeDelta::FromMilliseconds(4242);
class ContentToVisibleTimeReporterTest : public testing::Test {
protected:
void SetUp() override {
// Expect all histograms to be empty.
ExpectHistogramsEmptyExcept({});
}
void ExpectHistogramsEmptyExcept(
std::vector<const char*> histograms_with_values) {
constexpr const char* kAllHistograms[] = {
kDurationWithSavedFramesHistogram,
kDurationNoSavedFramesHistogram,
kDurationNoSavedFramesNotFrozenHistogram,
kDurationNoSavedFramesFrozenHistogram,
kDurationNoSavedFramesUnloadedHistogram,
kIncompleteDurationWithSavedFramesHistogram,
kIncompleteDurationNoSavedFramesHistogram,
kIncompleteDurationNoSavedFramesNotFrozenHistogram,
kIncompleteDurationNoSavedFramesFrozenHistogram,
kIncompleteDurationNoSavedFramesUnloadedHistogram,
kResultWithSavedFramesHistogram,
kResultNoSavedFramesHistogram,
kResultNoSavedFramesNotFrozenHistogram,
kResultNoSavedFramesFrozenHistogram,
kResultNoSavedFramesUnloadedHistogram,
kWebContentsUnOccludedHistogram};
for (const char* histogram : kAllHistograms) {
if (!base::Contains(histograms_with_values, histogram))
ExpectTotalSamples(histogram, 0);
}
}
void ExpectTotalSamples(const char* histogram_name, int expected_count) {
SCOPED_TRACE(base::StringPrintf("Expect %d samples in %s.", expected_count,
histogram_name));
EXPECT_EQ(static_cast<int>(
histogram_tester_.GetAllSamples(histogram_name).size()),
expected_count);
}
void ExpectTimeBucketCount(const char* histogram_name,
base::TimeDelta value,
int count) {
histogram_tester_.ExpectTimeBucketCount(histogram_name, value, count);
}
void ExpectResultBucketCount(
const char* histogram_name,
ContentToVisibleTimeReporter::TabSwitchResult value,
int count) {
histogram_tester_.ExpectBucketCount(histogram_name, value, count);
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
ContentToVisibleTimeReporter tab_switch_time_recorder_;
base::HistogramTester histogram_tester_;
};
// Time is properly recorded to histogram when we have saved frames and if we
// have a proper matching TabWasShown and callback execution.
TEST_F(ContentToVisibleTimeReporterTest, TimeIsRecordedWithSavedFrames) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
true /* has_saved_frames */,
{start, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start);
const auto end = start + kDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end, end - start, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback).Run(presentation_feedback);
ExpectHistogramsEmptyExcept(
{kDurationWithSavedFramesHistogram, kResultWithSavedFramesHistogram});
// Duration.
ExpectTotalSamples(kDurationWithSavedFramesHistogram, 1);
ExpectTimeBucketCount(kDurationWithSavedFramesHistogram, kDuration, 1);
// Result.
ExpectTotalSamples(kResultWithSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultWithSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
}
// Time is properly recorded to histogram when we have no saved frame and if we
// have a proper matching TabWasShown and callback execution.
TEST_F(ContentToVisibleTimeReporterTest, TimeIsRecordedNoSavedFrameNotFrozen) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */,
{start, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start);
const auto end = start + kDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end, end - start, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback).Run(presentation_feedback);
ExpectHistogramsEmptyExcept({kDurationNoSavedFramesHistogram,
kDurationNoSavedFramesNotFrozenHistogram,
kResultNoSavedFramesHistogram,
kResultNoSavedFramesNotFrozenHistogram});
// Duration.
ExpectTotalSamples(kDurationNoSavedFramesHistogram, 1);
ExpectTimeBucketCount(kDurationNoSavedFramesHistogram, kDuration, 1);
ExpectTotalSamples(kDurationNoSavedFramesNotFrozenHistogram, 1);
ExpectTimeBucketCount(kDurationNoSavedFramesNotFrozenHistogram, kDuration, 1);
// Result.
ExpectTotalSamples(kResultNoSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
ExpectTotalSamples(kResultNoSavedFramesNotFrozenHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesNotFrozenHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
}
// Same as TimeIsRecordedNoSavedFrame but with the destination frame frozen.
TEST_F(ContentToVisibleTimeReporterTest, TimeIsRecordedNoSavedFrameFrozen) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */,
{start, /* destination_is_loaded */ true,
/* destination_is_frozen */ true, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start);
const auto end = start + kDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end, end - start, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback).Run(presentation_feedback);
ExpectHistogramsEmptyExcept(
{kDurationNoSavedFramesHistogram, kDurationNoSavedFramesFrozenHistogram,
kResultNoSavedFramesHistogram, kResultNoSavedFramesFrozenHistogram});
// Duration.
ExpectTotalSamples(kDurationNoSavedFramesHistogram, 1);
ExpectTimeBucketCount(kDurationNoSavedFramesHistogram, kDuration, 1);
ExpectTotalSamples(kDurationNoSavedFramesFrozenHistogram, 1);
ExpectTimeBucketCount(kDurationNoSavedFramesFrozenHistogram, kDuration, 1);
// Result.
ExpectTotalSamples(kResultNoSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
ExpectTotalSamples(kResultNoSavedFramesFrozenHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesFrozenHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
}
// Same as TimeIsRecordedNoSavedFrame but with the destination frame unloaded.
TEST_F(ContentToVisibleTimeReporterTest, TimeIsRecordedNoSavedFrameUnloaded) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */,
{start, /* destination_is_loaded */ false,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start);
const auto end = start + kDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end, end - start, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback).Run(presentation_feedback);
ExpectHistogramsEmptyExcept({kDurationNoSavedFramesUnloadedHistogram,
kResultNoSavedFramesUnloadedHistogram});
// Duration.
ExpectTotalSamples(kDurationNoSavedFramesUnloadedHistogram, 1);
ExpectTimeBucketCount(kDurationNoSavedFramesUnloadedHistogram, kDuration, 1);
// Result.
ExpectTotalSamples(kResultNoSavedFramesUnloadedHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesUnloadedHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
}
// A failure should be reported if gfx::PresentationFeedback contains the
// kFailure flag.
TEST_F(ContentToVisibleTimeReporterTest, PresentationFailureWithSavedFrames) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
true /* has_saved_frames */,
{start, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start);
std::move(callback).Run(gfx::PresentationFeedback::Failure());
ExpectHistogramsEmptyExcept({kResultWithSavedFramesHistogram});
// Result (no duration is recorded on presentation failure).
ExpectTotalSamples(kResultWithSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultWithSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kPresentationFailure, 1);
}
// A failure should be reported if gfx::PresentationFeedback contains the
// kFailure flag.
TEST_F(ContentToVisibleTimeReporterTest, PresentationFailureNoSavedFrames) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */,
{start, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start);
std::move(callback).Run(gfx::PresentationFeedback::Failure());
ExpectHistogramsEmptyExcept(
{kResultNoSavedFramesHistogram, kResultNoSavedFramesNotFrozenHistogram});
// Result (no duration is recorded on presentation failure).
ExpectTotalSamples(kResultNoSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kPresentationFailure, 1);
ExpectTotalSamples(kResultNoSavedFramesNotFrozenHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesNotFrozenHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kPresentationFailure, 1);
}
// An incomplete tab switch is reported when no frame is shown before a tab is
// hidden.
TEST_F(ContentToVisibleTimeReporterTest,
HideBeforePresentFrameWithSavedFrames) {
const auto start1 = base::TimeTicks::Now();
auto callback1 = tab_switch_time_recorder_.TabWasShown(
true /* has_saved_frames */,
{start1, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start1);
task_environment_.FastForwardBy(kDuration);
tab_switch_time_recorder_.TabWasHidden();
ExpectHistogramsEmptyExcept({kResultWithSavedFramesHistogram,
kIncompleteDurationWithSavedFramesHistogram});
// Duration.
ExpectTotalSamples(kIncompleteDurationWithSavedFramesHistogram, 1);
ExpectTimeBucketCount(kIncompleteDurationWithSavedFramesHistogram, kDuration,
1);
// Result.
ExpectTotalSamples(kResultWithSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultWithSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kIncomplete, 1);
const auto start2 = base::TimeTicks::Now();
auto callback2 = tab_switch_time_recorder_.TabWasShown(
true /* has_saved_frames */,
{start2, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start2);
const auto end2 = start2 + kOtherDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end2, end2 - start2, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback2).Run(presentation_feedback);
ExpectHistogramsEmptyExcept({kDurationWithSavedFramesHistogram,
kResultWithSavedFramesHistogram,
kIncompleteDurationWithSavedFramesHistogram});
// Duration.
ExpectTotalSamples(kIncompleteDurationWithSavedFramesHistogram, 1);
ExpectTotalSamples(kDurationWithSavedFramesHistogram, 1);
ExpectTimeBucketCount(kDurationWithSavedFramesHistogram, kOtherDuration, 1);
// Result.
ExpectTotalSamples(kResultWithSavedFramesHistogram, 2);
ExpectResultBucketCount(
kResultWithSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kIncomplete, 1);
ExpectResultBucketCount(
kResultWithSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
}
// An incomplete tab switch is reported when no frame is shown before a tab is
// hidden.
TEST_F(ContentToVisibleTimeReporterTest, HideBeforePresentFrameNoSavedFrames) {
const auto start1 = base::TimeTicks::Now();
auto callback1 = tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */,
{start1, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start1);
task_environment_.FastForwardBy(kDuration);
tab_switch_time_recorder_.TabWasHidden();
ExpectHistogramsEmptyExcept(
{kIncompleteDurationNoSavedFramesHistogram,
kIncompleteDurationNoSavedFramesNotFrozenHistogram,
kResultNoSavedFramesHistogram, kResultNoSavedFramesNotFrozenHistogram});
// Duration.
ExpectTotalSamples(kIncompleteDurationNoSavedFramesHistogram, 1);
ExpectTimeBucketCount(kIncompleteDurationNoSavedFramesHistogram, kDuration,
1);
ExpectTotalSamples(kIncompleteDurationNoSavedFramesNotFrozenHistogram, 1);
ExpectTimeBucketCount(kIncompleteDurationNoSavedFramesNotFrozenHistogram,
kDuration, 1);
// Result.
ExpectTotalSamples(kResultNoSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kIncomplete, 1);
ExpectTotalSamples(kResultNoSavedFramesNotFrozenHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesNotFrozenHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kIncomplete, 1);
const auto start2 = base::TimeTicks::Now();
auto callback2 = tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */,
{start2, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ false},
start2);
const auto end2 = start2 + kOtherDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end2, end2 - start2, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback2).Run(presentation_feedback);
ExpectHistogramsEmptyExcept(
{kIncompleteDurationNoSavedFramesHistogram,
kIncompleteDurationNoSavedFramesNotFrozenHistogram,
kDurationNoSavedFramesHistogram,
kDurationNoSavedFramesNotFrozenHistogram, kResultNoSavedFramesHistogram,
kResultNoSavedFramesNotFrozenHistogram});
// Duration.
ExpectTotalSamples(kIncompleteDurationNoSavedFramesHistogram, 1);
ExpectTimeBucketCount(kIncompleteDurationNoSavedFramesHistogram, kDuration,
1);
ExpectTotalSamples(kIncompleteDurationNoSavedFramesNotFrozenHistogram, 1);
ExpectTimeBucketCount(kIncompleteDurationNoSavedFramesNotFrozenHistogram,
kDuration, 1);
ExpectTotalSamples(kDurationNoSavedFramesHistogram, 1);
ExpectTimeBucketCount(kDurationNoSavedFramesHistogram, kOtherDuration, 1);
ExpectTotalSamples(kDurationNoSavedFramesNotFrozenHistogram, 1);
ExpectTimeBucketCount(kDurationNoSavedFramesNotFrozenHistogram,
kOtherDuration, 1);
// Result.
ExpectTotalSamples(kResultNoSavedFramesHistogram, 2);
ExpectResultBucketCount(
kResultNoSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kIncomplete, 1);
ExpectResultBucketCount(
kResultNoSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
ExpectTotalSamples(kResultNoSavedFramesNotFrozenHistogram, 2);
ExpectResultBucketCount(
kResultNoSavedFramesNotFrozenHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kIncomplete, 1);
ExpectResultBucketCount(
kResultNoSavedFramesNotFrozenHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
}
// Time is properly recorded to histogram when we have unoccluded event.
TEST_F(ContentToVisibleTimeReporterTest, UnoccludedTimeIsRecorded) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
true /* has_saved_frames */,
{start, base::Optional<bool>() /* destination_is_loaded */,
base::Optional<bool>() /* destination_is_frozen */,
/* show_reason_tab_switching */ false,
/* show_reason_unoccluded */ true,
/* show_reason_bfcache_restore */ false},
start);
const auto end = start + kDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end, end - start, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback).Run(presentation_feedback);
ExpectHistogramsEmptyExcept({kWebContentsUnOccludedHistogram});
// UnOccluded.
ExpectTotalSamples(kWebContentsUnOccludedHistogram, 1);
ExpectTimeBucketCount(kWebContentsUnOccludedHistogram, kDuration, 1);
}
// Time is properly recorded to histogram when we have unoccluded event
// and some other events too.
TEST_F(ContentToVisibleTimeReporterTest,
TimeIsRecordedWithSavedFramesPlusUnoccludedTimeIsRecorded) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
true /* has_saved_frames */,
{start, /* destination_is_loaded */ true,
/* destination_is_frozen */ false, /* show_reason_tab_switching */ true,
/* show_reason_unoccluded */ true,
/* show_reason_bfcache_restore */ false},
start);
const auto end = start + kDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end, end - start, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback).Run(presentation_feedback);
ExpectHistogramsEmptyExcept({kDurationWithSavedFramesHistogram,
kResultWithSavedFramesHistogram,
kWebContentsUnOccludedHistogram});
// Duration.
ExpectTotalSamples(kDurationWithSavedFramesHistogram, 1);
ExpectTimeBucketCount(kDurationWithSavedFramesHistogram, kDuration, 1);
// Result.
ExpectTotalSamples(kResultWithSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultWithSavedFramesHistogram,
ContentToVisibleTimeReporter::TabSwitchResult::kSuccess, 1);
// UnOccluded.
ExpectTotalSamples(kWebContentsUnOccludedHistogram, 1);
ExpectTimeBucketCount(kWebContentsUnOccludedHistogram, kDuration, 1);
}
// Time is properly recorded to histogram when we have bfcache restore event.
TEST_F(ContentToVisibleTimeReporterTest, BfcacheRestoreTimeIsRecorded) {
const auto start = base::TimeTicks::Now();
auto callback = tab_switch_time_recorder_.TabWasShown(
false /* has_saved_frames */,
{start, base::Optional<bool>() /* destination_is_loaded */,
base::Optional<bool>() /* destination_is_frozen */,
/* show_reason_tab_switching */ false,
/* show_reason_unoccluded */ false,
/* show_reason_bfcache_restore */ true},
start);
const auto end = start + kDuration;
auto presentation_feedback = gfx::PresentationFeedback(
end, end - start, gfx::PresentationFeedback::Flags::kHWCompletion);
std::move(callback).Run(presentation_feedback);
ExpectHistogramsEmptyExcept({kBfcacheRestoreHistogram});
// Bfcache restore.
ExpectTotalSamples(kBfcacheRestoreHistogram, 1);
ExpectTimeBucketCount(kBfcacheRestoreHistogram, kDuration, 1);
}
class RecordContentToVisibleTimeRequestTest : public testing::Test {
protected:
// event_start_times are random, so caller is expected to provide failure
// message useful for debugging timestamps inequality.
void ExpectEqual(const RecordContentToVisibleTimeRequest& left,
const RecordContentToVisibleTimeRequest& right,
const std::string& msg) const {
EXPECT_EQ(left.event_start_time, right.event_start_time) << msg;
EXPECT_EQ(left.destination_is_loaded, right.destination_is_loaded);
EXPECT_EQ(left.destination_is_frozen, right.destination_is_frozen);
EXPECT_EQ(left.show_reason_tab_switching, right.show_reason_tab_switching);
EXPECT_EQ(left.show_reason_unoccluded, right.show_reason_unoccluded);
EXPECT_EQ(left.show_reason_bfcache_restore,
right.show_reason_bfcache_restore);
}
};
TEST_F(RecordContentToVisibleTimeRequestTest, MergeEmpty) {
// Merge two empty requests
RecordContentToVisibleTimeRequest request;
request.UpdateRequest(RecordContentToVisibleTimeRequest());
ExpectEqual(request, RecordContentToVisibleTimeRequest(), std::string());
}
// Merge two requests. Tuple represents the parameters of the two requests.
class RecordContentToVisibleTimeRequest_MergeRequestTest
: public RecordContentToVisibleTimeRequestTest,
public testing::WithParamInterface<std::tuple<bool,
bool,
bool,
bool,
bool,
bool,
bool,
bool,
bool,
bool>> {
protected:
RecordContentToVisibleTimeRequest GetRequest1() const {
const base::TimeTicks timestamp = RandomRequestTimeTicks();
return RecordContentToVisibleTimeRequest(
timestamp,
std::get<0>(GetParam())
? base::Optional<bool>(true)
: base::Optional<bool>() /* destination_is_loaded */,
std::get<1>(GetParam())
? base::Optional<bool>(true)
: base::Optional<bool>() /* destination_is_frozen */,
std::get<2>(GetParam()) /* show_reason_tab_switching */,
std::get<3>(GetParam()) /* show_reason_unoccluded */,
std::get<4>(GetParam()) /* show_reason_bfcache_restore */);
}
RecordContentToVisibleTimeRequest GetRequest2() const {
const base::TimeTicks timestamp = RandomRequestTimeTicks();
return RecordContentToVisibleTimeRequest(
timestamp,
std::get<5>(GetParam())
? base::Optional<bool>(true)
: base::Optional<bool>() /* destination_is_loaded */,
std::get<6>(GetParam())
? base::Optional<bool>(true)
: base::Optional<bool>() /* destination_is_frozen */,
std::get<7>(GetParam()) /* show_reason_tab_switching */,
std::get<8>(GetParam()) /* show_reason_unoccluded */,
std::get<9>(GetParam()) /* show_reason_bfcache_restore */);
}
bool isOptionalBoolTrue(const base::Optional<bool>& data) {
return data.has_value() && data.value();
}
private:
// Returns random moment between (Now) and (Now - random time in the past)
base::TimeTicks RandomRequestTimeTicks() const {
uint16_t number;
base::RandBytes(&number, sizeof(number));
return base::TimeTicks::Now() - base::TimeDelta::FromMilliseconds(number);
}
};
TEST_P(RecordContentToVisibleTimeRequest_MergeRequestTest, DoMerge) {
RecordContentToVisibleTimeRequest request1 = GetRequest1();
const RecordContentToVisibleTimeRequest request2 = GetRequest2();
// Timestamps are random, log them in case of failure.
std::ostringstream buf;
buf << "Original request1.event_start_time = " << request1.event_start_time
<< ", merged request2.event_start_time = " << request2.event_start_time
<< ".";
request1.UpdateRequest(request2);
// We expect to get minimal timestamp and all the boolean flags set in any
// request.
const RecordContentToVisibleTimeRequest expected(
std::min(request1.event_start_time, request2.event_start_time),
(request1.destination_is_loaded.has_value() ||
request2.destination_is_loaded.has_value())
? isOptionalBoolTrue(request1.destination_is_loaded) ||
isOptionalBoolTrue(request2.destination_is_loaded)
: base::Optional<bool>(),
(request1.destination_is_frozen.has_value() ||
request2.destination_is_frozen.has_value())
? isOptionalBoolTrue(request1.destination_is_frozen) ||
isOptionalBoolTrue(request2.destination_is_frozen)
: base::Optional<bool>(),
request1.show_reason_tab_switching || request2.show_reason_tab_switching,
request1.show_reason_unoccluded || request2.show_reason_unoccluded,
request1.show_reason_bfcache_restore ||
request2.show_reason_bfcache_restore);
ExpectEqual(request1, expected, buf.str());
}
INSTANTIATE_TEST_SUITE_P(All,
RecordContentToVisibleTimeRequest_MergeRequestTest,
testing::Combine(testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Bool()));
} // namespace content