blob: 9b7087742e7ba602f7e08fc8331d23183d463370 [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 <utility>
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_task_environment.h"
#include "content/common/tab_switch_time_recorder.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 base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(42);
constexpr base::TimeDelta kOtherDuration =
base::TimeDelta::FromMilliseconds(4242);
class TabSwitchTimeRecorderTest : 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};
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,
TabSwitchTimeRecorder::TabSwitchResult value,
int count) {
histogram_tester_.ExpectBucketCount(histogram_name, value, count);
}
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
base::test::ScopedTaskEnvironment::NowSource::MAIN_THREAD_MOCK_TIME};
TabSwitchTimeRecorder 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(TabSwitchTimeRecorderTest, 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},
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,
TabSwitchTimeRecorder::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(TabSwitchTimeRecorderTest, 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},
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,
TabSwitchTimeRecorder::TabSwitchResult::kSuccess, 1);
ExpectTotalSamples(kResultNoSavedFramesNotFrozenHistogram, 1);
ExpectResultBucketCount(kResultNoSavedFramesNotFrozenHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kSuccess, 1);
}
// Same as TimeIsRecordedNoSavedFrame but with the destination frame frozen.
TEST_F(TabSwitchTimeRecorderTest, 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},
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,
TabSwitchTimeRecorder::TabSwitchResult::kSuccess, 1);
ExpectTotalSamples(kResultNoSavedFramesFrozenHistogram, 1);
ExpectResultBucketCount(kResultNoSavedFramesFrozenHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kSuccess, 1);
}
// Same as TimeIsRecordedNoSavedFrame but with the destination frame unloaded.
TEST_F(TabSwitchTimeRecorderTest, 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},
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,
TabSwitchTimeRecorder::TabSwitchResult::kSuccess, 1);
}
// A failure should be reported if gfx::PresentationFeedback contains the
// kFailure flag.
TEST_F(TabSwitchTimeRecorderTest, 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},
start);
std::move(callback).Run(gfx::PresentationFeedback::Failure());
ExpectHistogramsEmptyExcept({kResultWithSavedFramesHistogram});
// Result (no duration is recorded on presentation failure).
ExpectTotalSamples(kResultWithSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultWithSavedFramesHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kPresentationFailure, 1);
}
// A failure should be reported if gfx::PresentationFeedback contains the
// kFailure flag.
TEST_F(TabSwitchTimeRecorderTest, 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},
start);
std::move(callback).Run(gfx::PresentationFeedback::Failure());
ExpectHistogramsEmptyExcept(
{kResultNoSavedFramesHistogram, kResultNoSavedFramesNotFrozenHistogram});
// Result (no duration is recorded on presentation failure).
ExpectTotalSamples(kResultNoSavedFramesHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kPresentationFailure, 1);
ExpectTotalSamples(kResultNoSavedFramesNotFrozenHistogram, 1);
ExpectResultBucketCount(
kResultNoSavedFramesNotFrozenHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kPresentationFailure, 1);
}
// An incomplete tab switch is reported when no frame is shown before a tab is
// hidden.
TEST_F(TabSwitchTimeRecorderTest, 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},
start1);
scoped_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,
TabSwitchTimeRecorder::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},
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,
TabSwitchTimeRecorder::TabSwitchResult::kIncomplete,
1);
ExpectResultBucketCount(kResultWithSavedFramesHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kSuccess, 1);
}
// An incomplete tab switch is reported when no frame is shown before a tab is
// hidden.
TEST_F(TabSwitchTimeRecorderTest, 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},
start1);
scoped_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,
TabSwitchTimeRecorder::TabSwitchResult::kIncomplete,
1);
ExpectTotalSamples(kResultNoSavedFramesNotFrozenHistogram, 1);
ExpectResultBucketCount(kResultNoSavedFramesNotFrozenHistogram,
TabSwitchTimeRecorder::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},
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,
TabSwitchTimeRecorder::TabSwitchResult::kIncomplete,
1);
ExpectResultBucketCount(kResultNoSavedFramesHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kSuccess, 1);
ExpectTotalSamples(kResultNoSavedFramesNotFrozenHistogram, 2);
ExpectResultBucketCount(kResultNoSavedFramesNotFrozenHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kIncomplete,
1);
ExpectResultBucketCount(kResultNoSavedFramesNotFrozenHistogram,
TabSwitchTimeRecorder::TabSwitchResult::kSuccess, 1);
}
} // namespace content