blob: 6b118318031ce6b91001b3ea59997720aaa2f011 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/picker/picker_session_metrics.h"
#include "ash/test/ash_test_base.h"
#include "base/scoped_observation.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
using ::testing::AllOf;
using ::testing::Ge;
using ::testing::IsEmpty;
using ::testing::Le;
base::TimeTicks WaitUntilNextFramePresented(ui::Compositor* compositor) {
base::TimeTicks presentation_timestamp;
base::RunLoop run_loop;
compositor->RequestSuccessfulPresentationTimeForNextFrame(
base::BindLambdaForTesting([&](base::TimeTicks timestamp) {
run_loop.Quit();
presentation_timestamp = timestamp;
}));
run_loop.Run();
return presentation_timestamp;
}
class PickerSessionMetricsTest : public AshTestBase {
public:
PickerSessionMetricsTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
};
TEST_F(PickerSessionMetricsTest,
DoesNotRecordMetricsWithoutCallingStartRecording) {
base::HistogramTester histogram;
PickerSessionMetrics metrics(base::TimeTicks::Now());
metrics.MarkInputFocus();
metrics.MarkContentsChanged();
EXPECT_THAT(histogram.GetTotalCountsForPrefix("Ash.Picker.Session"),
IsEmpty());
}
TEST_F(PickerSessionMetricsTest, RecordsFirstFocusLatency) {
base::HistogramTester histogram;
std::unique_ptr<views::Widget> widget = CreateFramelessTestWidget();
const auto trigger_event_timestamp = base::TimeTicks::Now();
task_environment()->FastForwardBy(base::Seconds(1));
PickerSessionMetrics metrics(trigger_event_timestamp);
metrics.StartRecording(*widget);
task_environment()->FastForwardBy(base::Seconds(1));
metrics.MarkInputFocus();
histogram.ExpectUniqueTimeSample("Ash.Picker.Session.InputReadyLatency",
base::Seconds(2), 1);
}
TEST_F(PickerSessionMetricsTest, RecordsOnlyFirstFocusLatency) {
base::HistogramTester histogram;
std::unique_ptr<views::Widget> widget = CreateFramelessTestWidget();
const auto trigger_event_timestamp = base::TimeTicks::Now();
task_environment()->FastForwardBy(base::Seconds(1));
PickerSessionMetrics metrics(trigger_event_timestamp);
metrics.StartRecording(*widget);
task_environment()->FastForwardBy(base::Seconds(1));
metrics.MarkInputFocus();
// Mark a second focus. Only the first focus should be recorded.
task_environment()->FastForwardBy(base::Seconds(1));
metrics.MarkInputFocus();
histogram.ExpectUniqueTimeSample("Ash.Picker.Session.InputReadyLatency",
base::Seconds(2), 1);
}
TEST_F(PickerSessionMetricsTest, RecordsPresentationLatencyForSearchField) {
base::HistogramTester histogram;
std::unique_ptr<views::Widget> widget = CreateFramelessTestWidget();
PickerSessionMetrics metrics(base::TimeTicks::Now());
metrics.StartRecording(*widget);
const base::TimeTicks contents_changed_timestamp = base::TimeTicks::Now();
const base::TimeTicks presentation_timestamp_before =
WaitUntilNextFramePresented(widget->GetCompositor());
metrics.MarkContentsChanged();
widget->SchedulePaintInRect(gfx::Rect(0, 0, 1, 1));
const base::TimeTicks presentation_timestamp_after =
WaitUntilNextFramePresented(widget->GetCompositor());
histogram.ExpectTotalCount(
"Ash.Picker.Session.PresentationLatency.SearchField", 1);
// There may be intermediate frames between `presentation_timestamp_before`
// and `presentation_timestamp_after`. Thus, these two timestamps can only
// be used to bound the metric value.
const base::TimeDelta latency_lower_bound =
presentation_timestamp_before - contents_changed_timestamp;
const base::TimeDelta latency_upper_bound =
presentation_timestamp_after - contents_changed_timestamp;
EXPECT_THAT(histogram.GetTotalSum(
"Ash.Picker.Session.PresentationLatency.SearchField"),
AllOf(Ge(latency_lower_bound.InMilliseconds()),
Le(latency_upper_bound.InMilliseconds())));
}
} // namespace
} // namespace ash