velocity_tracker_unittest.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright 2014 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 "base/basictypes.h"
6#include "base/logging.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/time/time.h"
9#include "testing/gtest/include/gtest/gtest.h"
10#include "ui/events/gesture_detection/mock_motion_event.h"
11#include "ui/events/gesture_detection/velocity_tracker_state.h"
12#include "ui/gfx/geometry/point_f.h"
13#include "ui/gfx/geometry/vector2d_f.h"
14
15using base::TimeDelta;
16using base::TimeTicks;
17
18namespace ui {
19namespace {
20
21const TimeDelta kTenMillis = TimeDelta::FromMilliseconds(10);
22const TimeDelta kOneSecond = TimeDelta::FromSeconds(1);
23const float kEpsilson = .01f;
24
25const char* GetStrategyName(VelocityTracker::Strategy strategy) {
26  switch (strategy) {
27    case VelocityTracker::LSQ1: return "LSQ1";
28    case VelocityTracker::LSQ2: return "LSQ2";
29    case VelocityTracker::LSQ3: return "LSQ3";
30    case VelocityTracker::WLSQ2_DELTA: return "WLSQ2_DELTA";
31    case VelocityTracker::WLSQ2_CENTRAL: return "WLSQ2_CENTRAL";
32    case VelocityTracker::WLSQ2_RECENT: return "WLSQ2_RECENT";
33    case VelocityTracker::INT1: return "INT1";
34    case VelocityTracker::INT2: return "INT2";
35  };
36  NOTREACHED() << "Invalid strategy";
37  return "";
38}
39
40}  // namespace
41
42class VelocityTrackerTest : public testing::Test {
43 public:
44  VelocityTrackerTest() {}
45  virtual ~VelocityTrackerTest() {}
46
47 protected:
48  static MockMotionEvent Sample(MotionEvent::Action action,
49                                gfx::PointF p0,
50                                TimeTicks t0,
51                                gfx::Vector2dF v,
52                                TimeDelta dt) {
53    const gfx::PointF p = p0 + ScaleVector2d(v, dt.InSecondsF());
54    return MockMotionEvent(action, t0 + dt, p.x(), p.y());
55  }
56
57  static void ApplyMovementSequence(VelocityTrackerState* state,
58                                    gfx::PointF p0,
59                                    gfx::Vector2dF v,
60                                    TimeTicks t0,
61                                    TimeDelta t,
62                                    size_t samples) {
63    EXPECT_TRUE(!!samples);
64    if (!samples)
65      return;
66    const base::TimeDelta dt = t / samples;
67    state->AddMovement(Sample(MotionEvent::ACTION_DOWN, p0, t0, v, dt * 0));
68    ApplyMovement(state, p0, v, t0, t, samples);
69    state->AddMovement(Sample(MotionEvent::ACTION_UP, p0, t0, v, t));
70  }
71
72  static void ApplyMovement(VelocityTrackerState* state,
73                            gfx::PointF p0,
74                            gfx::Vector2dF v,
75                            TimeTicks t0,
76                            TimeDelta t,
77                            size_t samples) {
78    EXPECT_TRUE(!!samples);
79    if (!samples)
80      return;
81    const base::TimeDelta dt = t / samples;
82    for (size_t i = 0; i < samples; ++i)
83      state->AddMovement(Sample(MotionEvent::ACTION_MOVE, p0, t0, v, dt * i));
84  }
85};
86
87TEST_F(VelocityTrackerTest, Basic) {
88  const gfx::PointF p0(0, 0);
89  const gfx::Vector2dF v(0, 500);
90  const size_t samples = 60;
91
92  for (int i = 0; i <= VelocityTracker::STRATEGY_MAX; ++i) {
93    VelocityTracker::Strategy strategy =
94        static_cast<VelocityTracker::Strategy>(i);
95
96    SCOPED_TRACE(GetStrategyName(strategy));
97    VelocityTrackerState state(strategy);
98
99    // Default state should report zero velocity.
100    EXPECT_EQ(0, state.GetXVelocity(0));
101    EXPECT_EQ(0, state.GetYVelocity(0));
102
103    // Sample a constant velocity sequence.
104    ApplyMovementSequence(&state, p0, v, TimeTicks::Now(), kOneSecond, samples);
105
106    // The computed velocity should match that of the input.
107    state.ComputeCurrentVelocity(1000, 20000);
108    EXPECT_NEAR(v.x(), state.GetXVelocity(0), kEpsilson * v.x());
109    EXPECT_NEAR(v.y(), state.GetYVelocity(0), kEpsilson * v.y());
110
111    // A pointer ID of -1 should report the velocity of the active pointer.
112    EXPECT_NEAR(v.x(), state.GetXVelocity(-1), kEpsilson * v.x());
113    EXPECT_NEAR(v.y(), state.GetYVelocity(-1), kEpsilson * v.y());
114
115    // Invalid pointer ID's should report zero velocity.
116    EXPECT_EQ(0, state.GetXVelocity(1));
117    EXPECT_EQ(0, state.GetYVelocity(1));
118    EXPECT_EQ(0, state.GetXVelocity(7));
119    EXPECT_EQ(0, state.GetYVelocity(7));
120  }
121}
122
123TEST_F(VelocityTrackerTest, MaxVelocity) {
124  const gfx::PointF p0(0, 0);
125  const gfx::Vector2dF v(-50000, 50000);
126  const size_t samples = 3;
127  const base::TimeDelta dt = kTenMillis * 2;
128
129  VelocityTrackerState state;
130  ApplyMovementSequence(&state, p0, v, TimeTicks::Now(), dt, samples);
131
132  // The computed velocity should be restricted to the provided maximum.
133  state.ComputeCurrentVelocity(1000, 100);
134  EXPECT_NEAR(-100, state.GetXVelocity(0), kEpsilson);
135  EXPECT_NEAR(100, state.GetYVelocity(0), kEpsilson);
136
137  state.ComputeCurrentVelocity(1000, 1000);
138  EXPECT_NEAR(-1000, state.GetXVelocity(0), kEpsilson);
139  EXPECT_NEAR(1000, state.GetYVelocity(0), kEpsilson);
140}
141
142TEST_F(VelocityTrackerTest, VaryingVelocity) {
143  const gfx::PointF p0(0, 0);
144  const gfx::Vector2dF vFast(0, 500);
145  const gfx::Vector2dF vSlow = ScaleVector2d(vFast, 0.5f);
146  const size_t samples = 12;
147
148  for (int i = 0; i <= VelocityTracker::STRATEGY_MAX; ++i) {
149    VelocityTracker::Strategy strategy =
150        static_cast<VelocityTracker::Strategy>(i);
151
152    SCOPED_TRACE(GetStrategyName(strategy));
153    VelocityTrackerState state(strategy);
154
155    base::TimeTicks t0 = base::TimeTicks::Now();
156    base::TimeDelta dt = kTenMillis * 10;
157    state.AddMovement(
158        Sample(MotionEvent::ACTION_DOWN, p0, t0, vFast, base::TimeDelta()));
159
160    // Apply some fast movement and compute the velocity.
161    gfx::PointF pCurr = p0;
162    base::TimeTicks tCurr = t0;
163    ApplyMovement(&state, pCurr, vFast, tCurr, dt, samples);
164    state.ComputeCurrentVelocity(1000, 20000);
165    float vOldY = state.GetYVelocity(0);
166
167    // Apply some slow movement.
168    pCurr += ScaleVector2d(vFast, dt.InSecondsF());
169    tCurr += dt;
170    ApplyMovement(&state, pCurr, vSlow, tCurr, dt, samples);
171
172    // The computed velocity should have decreased.
173    state.ComputeCurrentVelocity(1000, 20000);
174    float vCurrentY = state.GetYVelocity(0);
175    EXPECT_GT(vFast.y(), vCurrentY);
176    EXPECT_GT(vOldY, vCurrentY);
177    vOldY = vCurrentY;
178
179    // Apply some additional fast movement.
180    pCurr += ScaleVector2d(vSlow, dt.InSecondsF());
181    tCurr += dt;
182    ApplyMovement(&state, pCurr, vFast, tCurr, dt, samples);
183
184    // The computed velocity should have increased.
185    state.ComputeCurrentVelocity(1000, 20000);
186    vCurrentY = state.GetYVelocity(0);
187    EXPECT_LT(vSlow.y(), vCurrentY);
188    EXPECT_LT(vOldY, vCurrentY);
189  }
190}
191
192
193}  // namespace ui
194