1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/base/prediction/prediction_metrics_handler.h"
6 
7 #include "base/test/metrics/histogram_tester.h"
8 #include "testing/gmock/include/gmock/gmock.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "ui/base/prediction/input_predictor_unittest_helpers.h"
11 #include "ui/base/prediction/prediction_unittest_helpers.h"
12 
13 using base::Bucket;
14 using testing::ElementsAre;
15 
16 namespace ui {
17 namespace test {
18 namespace {
19 
MillisecondsToTestTimeTicks(int64_t ms)20 base::TimeTicks MillisecondsToTestTimeTicks(int64_t ms) {
21   return PredictionUnittestHelpers::GetStaticTimeStampForTests() +
22          base::TimeDelta::FromMilliseconds(ms);
23 }
24 
25 }  // namespace
26 
27 class PredictionMetricsHandlerTest : public testing::Test {
28  public:
PredictionMetricsHandlerTest()29   explicit PredictionMetricsHandlerTest() {}
30 
SetUp()31   void SetUp() override {
32     metrics_handler_ = std::make_unique<PredictionMetricsHandler>(
33         "Event.InputEventPrediction.Scroll");
34     histogram_tester_ = std::make_unique<base::HistogramTester>();
35   }
36 
37   DISALLOW_COPY_AND_ASSIGN(PredictionMetricsHandlerTest);
38 
GetInterpolatedEventForPredictedEvent(const base::TimeTicks & timestamp,gfx::PointF * interpolated)39   int GetInterpolatedEventForPredictedEvent(const base::TimeTicks& timestamp,
40                                             gfx::PointF* interpolated) {
41     return metrics_handler_->GetInterpolatedEventForPredictedEvent(
42         timestamp, interpolated);
43   }
44 
HistogramSizeEq(const char * histogram_name,int size)45   ::testing::AssertionResult HistogramSizeEq(const char* histogram_name,
46                                              int size) {
47     uint64_t histogram_size =
48         histogram_tester_->GetAllSamples(histogram_name).size();
49     if (static_cast<uint64_t>(size) == histogram_size) {
50       return ::testing::AssertionSuccess();
51     } else {
52       return ::testing::AssertionFailure()
53              << histogram_name << " expected " << size << " entries, but had "
54              << histogram_size;
55     }
56   }
57 
HasPredictionHistograms()58   bool HasPredictionHistograms() {
59     uint64_t histogram_size =
60         histogram_tester_
61             ->GetAllSamples("Event.InputEventPrediction.Scroll.WrongDirection")
62             .size();
63     return histogram_size > 0u;
64   }
65 
Reset()66   void Reset() {
67     histogram_tester_.reset(new base::HistogramTester());
68     metrics_handler_->Reset();
69   }
70 
histogram_tester()71   const base::HistogramTester& histogram_tester() { return *histogram_tester_; }
72 
73  protected:
74   std::unique_ptr<PredictionMetricsHandler> metrics_handler_;
75   std::unique_ptr<base::HistogramTester> histogram_tester_;
76 };
77 
TEST_F(PredictionMetricsHandlerTest,CanComputeMetricsTest)78 TEST_F(PredictionMetricsHandlerTest, CanComputeMetricsTest) {
79   base::TimeTicks start_time =
80       PredictionUnittestHelpers::GetStaticTimeStampForTests();
81   base::TimeDelta dt = base::TimeDelta::FromMilliseconds(8);
82 
83   // Need at least 2 real events to start comput metrics.
84   {
85     metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time + 3 * dt,
86                                    start_time);
87     metrics_handler_->AddPredictedEvent(gfx::PointF(0, 0), start_time + 3 * dt,
88                                         start_time);
89     metrics_handler_->EvaluatePrediction();
90     EXPECT_FALSE(HasPredictionHistograms());
91   }
92 
93   // Need at least a real event strictly after the second predicted event.
94   Reset();
95   {
96     metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time, start_time);
97     metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time + dt,
98                                    start_time);
99     metrics_handler_->AddPredictedEvent(gfx::PointF(0, 0), start_time + 2 * dt,
100                                         start_time);
101     metrics_handler_->AddPredictedEvent(gfx::PointF(0, 0), start_time + 3 * dt,
102                                         start_time);
103     metrics_handler_->EvaluatePrediction();
104     EXPECT_FALSE(HasPredictionHistograms());
105 
106     metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time + 3 * dt,
107                                    start_time);
108     metrics_handler_->EvaluatePrediction();
109     EXPECT_FALSE(HasPredictionHistograms());
110 
111     metrics_handler_->AddRealEvent(gfx::PointF(0, 0), start_time + 3.1 * dt,
112                                    start_time);
113     metrics_handler_->EvaluatePrediction();
114     EXPECT_TRUE(HasPredictionHistograms());
115   }
116 }
117 
TEST_F(PredictionMetricsHandlerTest,InterpolationTest)118 TEST_F(PredictionMetricsHandlerTest, InterpolationTest) {
119   base::TimeTicks start_time =
120       PredictionUnittestHelpers::GetStaticTimeStampForTests();
121   base::TimeDelta dt = base::TimeDelta::FromMilliseconds(8);
122   gfx::PointF interpolated;
123 
124   metrics_handler_->AddRealEvent(gfx::PointF(2, 2), start_time + 1 * dt,
125                                  start_time);
126   metrics_handler_->AddRealEvent(gfx::PointF(3, 3), start_time + 2 * dt,
127                                  start_time);
128   metrics_handler_->AddRealEvent(gfx::PointF(5, 5), start_time + 3 * dt,
129                                  start_time);
130   metrics_handler_->AddRealEvent(gfx::PointF(8, 8), start_time + 4 * dt,
131                                  start_time);
132 
133   EXPECT_EQ(0, GetInterpolatedEventForPredictedEvent(start_time + 1.5 * dt,
134                                                      &interpolated));
135   EXPECT_EQ(interpolated, gfx::PointF(2.5, 2.5));
136 
137   EXPECT_EQ(2, GetInterpolatedEventForPredictedEvent(start_time + 3.5 * dt,
138                                                      &interpolated));
139   EXPECT_EQ(interpolated, gfx::PointF(6.5, 6.5));
140 }
141 // For test purpose and simplify, we are predicted in the middle of 2 real
142 // events, which is also the frame time (i.e. a prediction of 4 ms)
AddEvents(PredictionMetricsHandler * metrics_handler)143 void AddEvents(PredictionMetricsHandler* metrics_handler) {
144   metrics_handler->AddRealEvent(gfx::PointF(1, 1),
145                                 MillisecondsToTestTimeTicks(8),
146                                 MillisecondsToTestTimeTicks(12));  // R0
147   metrics_handler->AddRealEvent(gfx::PointF(2, 2),
148                                 MillisecondsToTestTimeTicks(16),
149                                 MillisecondsToTestTimeTicks(20));  // R1
150   metrics_handler->AddRealEvent(gfx::PointF(4, 4),
151                                 MillisecondsToTestTimeTicks(24),
152                                 MillisecondsToTestTimeTicks(28));  // R2
153   metrics_handler->AddRealEvent(gfx::PointF(7, 7),
154                                 MillisecondsToTestTimeTicks(32),
155                                 MillisecondsToTestTimeTicks(36));  // R3
156   metrics_handler->AddRealEvent(gfx::PointF(5, 5),
157                                 MillisecondsToTestTimeTicks(40),
158                                 MillisecondsToTestTimeTicks(44));  // R4
159   metrics_handler->AddRealEvent(gfx::PointF(3, 3),
160                                 MillisecondsToTestTimeTicks(48),
161                                 MillisecondsToTestTimeTicks(54));  // R5
162 
163   // P0 | Interpolation from R0-R1 is (1.5,1.5)
164   // UnderPrediction
165   metrics_handler->AddPredictedEvent(gfx::PointF(1, 1),
166                                      MillisecondsToTestTimeTicks(12),
167                                      MillisecondsToTestTimeTicks(12));
168   // P1 | Interpolation from R1-R2 is (3,3)
169   // OverPrediction | RightDirection
170   metrics_handler->AddPredictedEvent(gfx::PointF(3.5, 3.5),
171                                      MillisecondsToTestTimeTicks(20),
172                                      MillisecondsToTestTimeTicks(20));
173   // P2 | Interpolation from R2-R3 is (5.5,5.5)
174   // UnderPrediction | RightDirection
175   metrics_handler->AddPredictedEvent(gfx::PointF(5, 5),
176                                      MillisecondsToTestTimeTicks(28),
177                                      MillisecondsToTestTimeTicks(28));
178   // P3 | Interpolation from R3-R4 is (6,6)
179   // UnderPrediction | WrongDirection
180   metrics_handler->AddPredictedEvent(gfx::PointF(7, 7),
181                                      MillisecondsToTestTimeTicks(36),
182                                      MillisecondsToTestTimeTicks(36));
183   // P4 | Interpolation from R4-R5 is (4,4)
184   // OverPrediction | RightDirection
185   metrics_handler->AddPredictedEvent(gfx::PointF(3, 3),
186                                      MillisecondsToTestTimeTicks(44),
187                                      MillisecondsToTestTimeTicks(44));
188 }
189 
TEST_F(PredictionMetricsHandlerTest,PredictionMetricTest)190 TEST_F(PredictionMetricsHandlerTest, PredictionMetricTest) {
191   AddEvents(metrics_handler_.get());
192   metrics_handler_->EvaluatePrediction();
193 
194   EXPECT_THAT(histogram_tester().GetAllSamples(
195                   "Event.InputEventPrediction.Scroll.OverPrediction"),
196               ElementsAre(Bucket(0, 1), Bucket(1, 1)));
197 
198   EXPECT_THAT(histogram_tester().GetAllSamples(
199                   "Event.InputEventPrediction.Scroll.UnderPrediction"),
200               ElementsAre(Bucket(0, 2), Bucket(1, 1)));
201 
202   EXPECT_THAT(histogram_tester().GetAllSamples(
203                   "Event.InputEventPrediction.Scroll.WrongDirection"),
204               ElementsAre(Bucket(0, 3), Bucket(1, 1)));
205 
206   EXPECT_THAT(histogram_tester().GetAllSamples(
207                   "Event.InputEventPrediction.Scroll.PredictionJitter"),
208               ElementsAre(Bucket(1, 2), Bucket(2, 2)));
209 
210   EXPECT_THAT(histogram_tester().GetAllSamples(
211                   "Event.InputEventPrediction.Scroll.VisualJitter"),
212               ElementsAre(Bucket(1, 2), Bucket(2, 2)));
213 }
214 
215 // Test that it doesn't crash when predicted event is prior to first real event.
TEST_F(PredictionMetricsHandlerTest,PredictedTimePriorToReal)216 TEST_F(PredictionMetricsHandlerTest, PredictedTimePriorToReal) {
217   metrics_handler_->AddRealEvent(gfx::PointF(1, 1),
218                                  MillisecondsToTestTimeTicks(8),
219                                  MillisecondsToTestTimeTicks(12));
220   metrics_handler_->AddRealEvent(gfx::PointF(2, 2),
221                                  MillisecondsToTestTimeTicks(10),
222                                  MillisecondsToTestTimeTicks(12));
223 
224   metrics_handler_->AddPredictedEvent(gfx::PointF(0, 0),
225                                       MillisecondsToTestTimeTicks(7),
226                                       MillisecondsToTestTimeTicks(12));
227   metrics_handler_->EvaluatePrediction();
228   // No prediction metrics result.
229   EXPECT_TRUE(
230       HistogramSizeEq("Event.InputEventPrediction.Scroll.PredictionJitter", 0));
231 }
232 
233 }  // namespace test
234 }  // namespace ui
235