#include "cc/metrics/events_metrics_manager.h"
#include <array>
#include <memory>
#include <optional>
#include <tuple>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "cc/metrics/event_metrics.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/types/event_type.h"
#include "ui/events/types/scroll_input_type.h"
namespace cc {
namespace {
MATCHER(UniquePtrMatches, negation ? "do not match" : "match") {
return std::get<0>(arg).get() == std::get<1>(arg);
}
EventsMetricsManager::ScopedMonitor::DoneCallback CreateSimpleDoneCallback(
std::unique_ptr<EventMetrics> metrics) {
return base::BindOnce(
[](std::unique_ptr<EventMetrics> metrics, bool handled) {
std::unique_ptr<EventMetrics> result =
handled ? std::move(metrics) : nullptr;
return result;
},
std::move(metrics));
}
}
using ::testing::Each;
using ::testing::IsEmpty;
using ::testing::Message;
using ::testing::Pointee;
using ::testing::Pointwise;
using ::testing::Property;
class EventsMetricsManagerTest : public testing::Test {
public:
EventsMetricsManagerTest() = default;
~EventsMetricsManagerTest() override = default;
protected:
std::unique_ptr<EventMetrics> CreateEventMetrics(ui::EventType type) {
auto [event_time, arrived_in_browser_main_timestamp] =
NextEventTimestamps();
return EventMetrics::CreateForTesting(type, event_time,
arrived_in_browser_main_timestamp,
&test_tick_clock_, std::nullopt);
}
std::unique_ptr<ScrollEventMetrics> CreateScrollEventMetrics(
ui::EventType type,
bool is_inertial) {
auto [event_time, arrived_in_browser_main_timestamp] =
NextEventTimestamps();
return ScrollEventMetrics::CreateForTesting(
type, ui::ScrollInputType::kTouchscreen, is_inertial, event_time,
arrived_in_browser_main_timestamp, &test_tick_clock_);
}
std::unique_ptr<ScrollEventMetrics> CreateScrollUpdateEventMetrics(
ui::EventType type,
bool is_inertial,
ScrollUpdateEventMetrics::ScrollUpdateType scroll_update_type) {
auto [event_time, arrived_in_browser_main_timestamp] =
NextEventTimestamps();
return ScrollUpdateEventMetrics::CreateForTesting(
type, ui::ScrollInputType::kTouchscreen, is_inertial,
scroll_update_type,
4.2f, event_time, arrived_in_browser_main_timestamp,
&test_tick_clock_,
std::nullopt);
}
std::tuple<base::TimeTicks, base::TimeTicks> NextEventTimestamps() {
test_tick_clock_.Advance(base::Microseconds(10));
base::TimeTicks event_time = test_tick_clock_.NowTicks();
test_tick_clock_.Advance(base::Microseconds(5));
base::TimeTicks arrived_in_browser_main_timestamp =
test_tick_clock_.NowTicks();
test_tick_clock_.Advance(base::Microseconds(10));
return {event_time, arrived_in_browser_main_timestamp};
}
EventsMetricsManager manager_;
base::SimpleTestTickClock test_tick_clock_;
};
TEST_F(EventsMetricsManagerTest, EventsMetricsSaved) {
enum class Behavior {
kDoNotSave,
kSaveInsideScope,
kSaveOutsideScope,
};
auto events = std::to_array<
std::pair<std::unique_ptr<EventMetrics>, Behavior>>({
{CreateEventMetrics(ui::EventType::kMousePressed), Behavior::kDoNotSave},
{CreateEventMetrics(ui::EventType::kMousePressed),
Behavior::kSaveInsideScope},
{CreateEventMetrics(ui::EventType::kMousePressed),
Behavior::kSaveOutsideScope},
{CreateEventMetrics(ui::EventType::kMouseEntered),
Behavior::kSaveInsideScope},
});
EXPECT_NE(events[0].first, nullptr);
EXPECT_NE(events[1].first, nullptr);
EXPECT_NE(events[2].first, nullptr);
EXPECT_EQ(events[3].first, nullptr);
const EventMetrics* expected_saved_events[] = {
events[1].first.get(),
};
for (auto& event : events) {
{
auto monitor = manager_.GetScopedMonitor(
CreateSimpleDoneCallback(std::move(event.first)));
if (event.second == Behavior::kSaveInsideScope)
manager_.SaveActiveEventMetrics();
}
if (event.second == Behavior::kSaveOutsideScope)
manager_.SaveActiveEventMetrics();
}
EXPECT_THAT(manager_.TakeSavedEventsMetrics(),
Pointwise(UniquePtrMatches(), expected_saved_events));
EXPECT_THAT(manager_.TakeSavedEventsMetrics(), IsEmpty());
}
TEST_F(EventsMetricsManagerTest, ScrollUpdateAndEndSavedEvenWithoutCall) {
auto events = std::to_array<std::unique_ptr<EventMetrics>>(
{CreateScrollUpdateEventMetrics(
ui::EventType::kGestureScrollUpdate,
false,
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued),
CreateScrollEventMetrics(ui::EventType::kGestureScrollEnd,
false)});
EXPECT_EQ(events[0]->type(), EventMetrics::EventType::kGestureScrollUpdate);
EXPECT_EQ(events[1]->type(), EventMetrics::EventType::kGestureScrollEnd);
const EventMetrics* expected_saved_events[] = {events[0].get(),
events[1].get()};
for (auto& metrics : events) {
metrics->set_caused_frame_update(true);
{
auto monitor = manager_.GetScopedMonitor(
base::BindOnce([](std::unique_ptr<EventMetrics> metrics,
bool handled) { return metrics; },
std::move(metrics)));
}
}
auto saved_events_metrics = manager_.TakeSavedEventsMetrics();
EXPECT_THAT(saved_events_metrics,
Pointwise(UniquePtrMatches(), expected_saved_events));
EXPECT_THAT(
saved_events_metrics,
Each(Pointee(Property(&EventMetrics::caused_frame_update, false))));
}
TEST_F(EventsMetricsManagerTest, NestedEventsMetrics) {
struct Configs {
ui::EventType outer_event_type;
bool save_outer_metrics_before_inner;
ui::EventType inner_event_type;
bool save_inner_metrics;
bool save_outer_metrics_after_inner;
};
auto configs = std::to_array<Configs>({
{
ui::EventType::kMousePressed,
true,
ui::EventType::kMouseReleased,
true,
false,
},
{
ui::EventType::kMousePressed,
false,
ui::EventType::kMouseReleased,
true,
true,
},
{
ui::EventType::kMousePressed,
true,
ui::EventType::kMouseReleased,
true,
true,
},
{
ui::EventType::kMousePressed,
false,
ui::EventType::kUnknown,
false,
true,
},
{
ui::EventType::kUnknown,
false,
ui::EventType::kMousePressed,
true,
false,
},
});
for (size_t i = 0; i < std::size(configs); i++) {
auto& config = configs[i];
std::vector<const EventMetrics*> expected_saved_metrics;
{
std::unique_ptr<EventMetrics> outer_metrics;
std::optional<const EventMetrics*> expected_saved_outer_metrics;
if (config.outer_event_type != ui::EventType::kUnknown) {
outer_metrics = CreateEventMetrics(config.outer_event_type);
DCHECK_NE(outer_metrics, nullptr);
expected_saved_outer_metrics = outer_metrics.get();
}
auto outer_monitor = manager_.GetScopedMonitor(
CreateSimpleDoneCallback(std::move(outer_metrics)));
if (config.save_outer_metrics_before_inner)
manager_.SaveActiveEventMetrics();
{
std::unique_ptr<EventMetrics> inner_metrics;
if (config.inner_event_type != ui::EventType::kUnknown) {
inner_metrics = CreateEventMetrics(config.inner_event_type);
DCHECK_NE(inner_metrics, nullptr);
expected_saved_metrics.push_back(inner_metrics.get());
}
auto inner_monitor = manager_.GetScopedMonitor(
CreateSimpleDoneCallback(std::move(inner_metrics)));
if (config.save_inner_metrics)
manager_.SaveActiveEventMetrics();
}
if (expected_saved_outer_metrics) {
expected_saved_metrics.push_back(*expected_saved_outer_metrics);
}
if (config.save_outer_metrics_after_inner)
manager_.SaveActiveEventMetrics();
}
SCOPED_TRACE(Message() << "Config #" << i);
EXPECT_THAT(manager_.TakeSavedEventsMetrics(),
Pointwise(UniquePtrMatches(), expected_saved_metrics));
}
}
TEST_F(EventsMetricsManagerTest,
DropSavedEventMetricsForNoFrameUpdatePreservesScrollUpdateAndEnd) {
auto events = std::to_array<std::unique_ptr<EventMetrics>>(
{CreateEventMetrics(ui::EventType::kTouchMoved),
CreateScrollUpdateEventMetrics(
ui::EventType::kGestureScrollUpdate,
false,
ScrollUpdateEventMetrics::ScrollUpdateType::kContinued),
CreateEventMetrics(ui::EventType::kTouchReleased),
CreateScrollEventMetrics(ui::EventType::kGestureScrollEnd,
false)});
EXPECT_EQ(events[0]->type(), EventMetrics::EventType::kTouchMoved);
EXPECT_EQ(events[1]->type(), EventMetrics::EventType::kGestureScrollUpdate);
EXPECT_EQ(events[2]->type(), EventMetrics::EventType::kTouchReleased);
EXPECT_EQ(events[3]->type(), EventMetrics::EventType::kGestureScrollEnd);
const EventMetrics* expected_saved_events[] = {events[1].get(),
events[3].get()};
for (auto& event : events) {
event->set_caused_frame_update(true);
auto monitor =
manager_.GetScopedMonitor(CreateSimpleDoneCallback(std::move(event)));
manager_.SaveActiveEventMetrics();
}
manager_.DropSavedEventMetricsForNoFrameUpdate();
auto preserved_metrics = manager_.TakeSavedEventsMetrics();
EXPECT_THAT(preserved_metrics,
Pointwise(UniquePtrMatches(), expected_saved_events));
EXPECT_THAT(
preserved_metrics,
Each(Pointee(Property(&EventMetrics::caused_frame_update, false))));
}
}