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.
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/velocity_tracker_state.h"
11 #include "ui/events/test/mock_motion_event.h"
12 #include "ui/gfx/geometry/point_f.h"
13 #include "ui/gfx/geometry/vector2d_f.h"
15 using base::TimeDelta
;
16 using base::TimeTicks
;
17 using ui::test::MockMotionEvent
;
22 const TimeDelta kTenMillis
= TimeDelta::FromMilliseconds(10);
23 const TimeDelta kOneSecond
= TimeDelta::FromSeconds(1);
24 const float kEpsilson
= .01f
;
26 const char* GetStrategyName(VelocityTracker::Strategy strategy
) {
28 case VelocityTracker::LSQ1
: return "LSQ1";
29 case VelocityTracker::LSQ2
: return "LSQ2";
30 case VelocityTracker::LSQ3
: return "LSQ3";
31 case VelocityTracker::WLSQ2_DELTA
: return "WLSQ2_DELTA";
32 case VelocityTracker::WLSQ2_CENTRAL
: return "WLSQ2_CENTRAL";
33 case VelocityTracker::WLSQ2_RECENT
: return "WLSQ2_RECENT";
34 case VelocityTracker::INT1
: return "INT1";
35 case VelocityTracker::INT2
: return "INT2";
37 NOTREACHED() << "Invalid strategy";
43 class VelocityTrackerTest
: public testing::Test
{
45 VelocityTrackerTest() {}
46 virtual ~VelocityTrackerTest() {}
49 static MockMotionEvent
Sample(MotionEvent::Action action
,
54 const gfx::PointF p
= p0
+ ScaleVector2d(v
, dt
.InSecondsF());
55 return MockMotionEvent(action
, t0
+ dt
, p
.x(), p
.y());
58 static void ApplyMovementSequence(VelocityTrackerState
* state
,
64 EXPECT_TRUE(!!samples
);
67 const base::TimeDelta dt
= t
/ samples
;
68 state
->AddMovement(Sample(MotionEvent::ACTION_DOWN
, p0
, t0
, v
, dt
* 0));
69 ApplyMovement(state
, p0
, v
, t0
, t
, samples
);
70 state
->AddMovement(Sample(MotionEvent::ACTION_UP
, p0
, t0
, v
, t
));
73 static void ApplyMovement(VelocityTrackerState
* state
,
79 EXPECT_TRUE(!!samples
);
82 const base::TimeDelta dt
= t
/ samples
;
83 for (size_t i
= 0; i
< samples
; ++i
)
84 state
->AddMovement(Sample(MotionEvent::ACTION_MOVE
, p0
, t0
, v
, dt
* i
));
88 TEST_F(VelocityTrackerTest
, Basic
) {
89 const gfx::PointF
p0(0, 0);
90 const gfx::Vector2dF
v(0, 500);
91 const size_t samples
= 60;
93 for (int i
= 0; i
<= VelocityTracker::STRATEGY_MAX
; ++i
) {
94 VelocityTracker::Strategy strategy
=
95 static_cast<VelocityTracker::Strategy
>(i
);
97 SCOPED_TRACE(GetStrategyName(strategy
));
98 VelocityTrackerState
state(strategy
);
100 // Default state should report zero velocity.
101 EXPECT_EQ(0, state
.GetXVelocity(0));
102 EXPECT_EQ(0, state
.GetYVelocity(0));
104 // Sample a constant velocity sequence.
105 ApplyMovementSequence(&state
, p0
, v
, TimeTicks::Now(), kOneSecond
, samples
);
107 // The computed velocity should match that of the input.
108 state
.ComputeCurrentVelocity(1000, 20000);
109 EXPECT_NEAR(v
.x(), state
.GetXVelocity(0), kEpsilson
* v
.x());
110 EXPECT_NEAR(v
.y(), state
.GetYVelocity(0), kEpsilson
* v
.y());
112 // A pointer ID of -1 should report the velocity of the active pointer.
113 EXPECT_NEAR(v
.x(), state
.GetXVelocity(-1), kEpsilson
* v
.x());
114 EXPECT_NEAR(v
.y(), state
.GetYVelocity(-1), kEpsilson
* v
.y());
116 // Invalid pointer ID's should report zero velocity.
117 EXPECT_EQ(0, state
.GetXVelocity(1));
118 EXPECT_EQ(0, state
.GetYVelocity(1));
119 EXPECT_EQ(0, state
.GetXVelocity(7));
120 EXPECT_EQ(0, state
.GetYVelocity(7));
124 TEST_F(VelocityTrackerTest
, MaxVelocity
) {
125 const gfx::PointF
p0(0, 0);
126 const gfx::Vector2dF
v(-50000, 50000);
127 const size_t samples
= 3;
128 const base::TimeDelta dt
= kTenMillis
* 2;
130 VelocityTrackerState state
;
131 ApplyMovementSequence(&state
, p0
, v
, TimeTicks::Now(), dt
, samples
);
133 // The computed velocity should be restricted to the provided maximum.
134 state
.ComputeCurrentVelocity(1000, 100);
135 EXPECT_NEAR(-100, state
.GetXVelocity(0), kEpsilson
);
136 EXPECT_NEAR(100, state
.GetYVelocity(0), kEpsilson
);
138 state
.ComputeCurrentVelocity(1000, 1000);
139 EXPECT_NEAR(-1000, state
.GetXVelocity(0), kEpsilson
);
140 EXPECT_NEAR(1000, state
.GetYVelocity(0), kEpsilson
);
143 TEST_F(VelocityTrackerTest
, VaryingVelocity
) {
144 const gfx::PointF
p0(0, 0);
145 const gfx::Vector2dF
vFast(0, 500);
146 const gfx::Vector2dF vSlow
= ScaleVector2d(vFast
, 0.5f
);
147 const size_t samples
= 12;
149 for (int i
= 0; i
<= VelocityTracker::STRATEGY_MAX
; ++i
) {
150 VelocityTracker::Strategy strategy
=
151 static_cast<VelocityTracker::Strategy
>(i
);
153 SCOPED_TRACE(GetStrategyName(strategy
));
154 VelocityTrackerState
state(strategy
);
156 base::TimeTicks t0
= base::TimeTicks::Now();
157 base::TimeDelta dt
= kTenMillis
* 10;
159 Sample(MotionEvent::ACTION_DOWN
, p0
, t0
, vFast
, base::TimeDelta()));
161 // Apply some fast movement and compute the velocity.
162 gfx::PointF pCurr
= p0
;
163 base::TimeTicks tCurr
= t0
;
164 ApplyMovement(&state
, pCurr
, vFast
, tCurr
, dt
, samples
);
165 state
.ComputeCurrentVelocity(1000, 20000);
166 float vOldY
= state
.GetYVelocity(0);
168 // Apply some slow movement.
169 pCurr
+= ScaleVector2d(vFast
, dt
.InSecondsF());
171 ApplyMovement(&state
, pCurr
, vSlow
, tCurr
, dt
, samples
);
173 // The computed velocity should have decreased.
174 state
.ComputeCurrentVelocity(1000, 20000);
175 float vCurrentY
= state
.GetYVelocity(0);
176 EXPECT_GT(vFast
.y(), vCurrentY
);
177 EXPECT_GT(vOldY
, vCurrentY
);
180 // Apply some additional fast movement.
181 pCurr
+= ScaleVector2d(vSlow
, dt
.InSecondsF());
183 ApplyMovement(&state
, pCurr
, vFast
, tCurr
, dt
, samples
);
185 // The computed velocity should have increased.
186 state
.ComputeCurrentVelocity(1000, 20000);
187 vCurrentY
= state
.GetYVelocity(0);
188 EXPECT_LT(vSlow
.y(), vCurrentY
);
189 EXPECT_LT(vOldY
, vCurrentY
);
193 TEST_F(VelocityTrackerTest
, DelayedActionUp
) {
194 const gfx::PointF
p0(0, 0);
195 const gfx::Vector2dF
v(-50000, 50000);
196 const size_t samples
= 10;
197 const base::TimeTicks t0
= base::TimeTicks::Now();
198 const base::TimeDelta dt
= kTenMillis
* 2;
200 VelocityTrackerState state
;
202 Sample(MotionEvent::ACTION_DOWN
, p0
, t0
, v
, base::TimeDelta()));
204 // Apply the movement and verify a (non-zero) velocity.
205 ApplyMovement(&state
, p0
, v
, t0
, dt
, samples
);
206 state
.ComputeCurrentVelocity(1000, 1000);
207 EXPECT_NEAR(-1000, state
.GetXVelocity(0), kEpsilson
);
208 EXPECT_NEAR(1000, state
.GetYVelocity(0), kEpsilson
);
210 // Apply the delayed ACTION_UP.
211 const gfx::PointF p1
= p0
+ ScaleVector2d(v
, dt
.InSecondsF());
212 const base::TimeTicks t1
= t0
+ dt
+ kTenMillis
* 10;
213 state
.AddMovement(Sample(
214 MotionEvent::ACTION_UP
, p1
, t1
, v
, base::TimeDelta()));
216 // The tracked velocity should have been reset.
217 state
.ComputeCurrentVelocity(1000, 1000);
218 EXPECT_EQ(0.f
, state
.GetXVelocity(0));
219 EXPECT_EQ(0.f
, state
.GetYVelocity(0));