Home | History | Annotate | Download | only in gesture_detection
      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/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"
     14 
     15 using base::TimeDelta;
     16 using base::TimeTicks;
     17 using ui::test::MockMotionEvent;
     18 
     19 namespace ui {
     20 namespace {
     21 
     22 const TimeDelta kTenMillis = TimeDelta::FromMilliseconds(10);
     23 const TimeDelta kOneSecond = TimeDelta::FromSeconds(1);
     24 const float kEpsilson = .01f;
     25 
     26 const char* GetStrategyName(VelocityTracker::Strategy strategy) {
     27   switch (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";
     36   };
     37   NOTREACHED() << "Invalid strategy";
     38   return "";
     39 }
     40 
     41 }  // namespace
     42 
     43 class VelocityTrackerTest : public testing::Test {
     44  public:
     45   VelocityTrackerTest() {}
     46   virtual ~VelocityTrackerTest() {}
     47 
     48  protected:
     49   static MockMotionEvent Sample(MotionEvent::Action action,
     50                                 gfx::PointF p0,
     51                                 TimeTicks t0,
     52                                 gfx::Vector2dF v,
     53                                 TimeDelta dt) {
     54     const gfx::PointF p = p0 + ScaleVector2d(v, dt.InSecondsF());
     55     return MockMotionEvent(action, t0 + dt, p.x(), p.y());
     56   }
     57 
     58   static void ApplyMovementSequence(VelocityTrackerState* state,
     59                                     gfx::PointF p0,
     60                                     gfx::Vector2dF v,
     61                                     TimeTicks t0,
     62                                     TimeDelta t,
     63                                     size_t samples) {
     64     EXPECT_TRUE(!!samples);
     65     if (!samples)
     66       return;
     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));
     71   }
     72 
     73   static void ApplyMovement(VelocityTrackerState* state,
     74                             gfx::PointF p0,
     75                             gfx::Vector2dF v,
     76                             TimeTicks t0,
     77                             TimeDelta t,
     78                             size_t samples) {
     79     EXPECT_TRUE(!!samples);
     80     if (!samples)
     81       return;
     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));
     85   }
     86 };
     87 
     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;
     92 
     93   for (int i = 0; i <= VelocityTracker::STRATEGY_MAX; ++i) {
     94     VelocityTracker::Strategy strategy =
     95         static_cast<VelocityTracker::Strategy>(i);
     96 
     97     SCOPED_TRACE(GetStrategyName(strategy));
     98     VelocityTrackerState state(strategy);
     99 
    100     // Default state should report zero velocity.
    101     EXPECT_EQ(0, state.GetXVelocity(0));
    102     EXPECT_EQ(0, state.GetYVelocity(0));
    103 
    104     // Sample a constant velocity sequence.
    105     ApplyMovementSequence(&state, p0, v, TimeTicks::Now(), kOneSecond, samples);
    106 
    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());
    111 
    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());
    115 
    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));
    121   }
    122 }
    123 
    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;
    129 
    130   VelocityTrackerState state;
    131   ApplyMovementSequence(&state, p0, v, TimeTicks::Now(), dt, samples);
    132 
    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);
    137 
    138   state.ComputeCurrentVelocity(1000, 1000);
    139   EXPECT_NEAR(-1000, state.GetXVelocity(0), kEpsilson);
    140   EXPECT_NEAR(1000, state.GetYVelocity(0), kEpsilson);
    141 }
    142 
    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;
    148 
    149   for (int i = 0; i <= VelocityTracker::STRATEGY_MAX; ++i) {
    150     VelocityTracker::Strategy strategy =
    151         static_cast<VelocityTracker::Strategy>(i);
    152 
    153     SCOPED_TRACE(GetStrategyName(strategy));
    154     VelocityTrackerState state(strategy);
    155 
    156     base::TimeTicks t0 = base::TimeTicks::Now();
    157     base::TimeDelta dt = kTenMillis * 10;
    158     state.AddMovement(
    159         Sample(MotionEvent::ACTION_DOWN, p0, t0, vFast, base::TimeDelta()));
    160 
    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);
    167 
    168     // Apply some slow movement.
    169     pCurr += ScaleVector2d(vFast, dt.InSecondsF());
    170     tCurr += dt;
    171     ApplyMovement(&state, pCurr, vSlow, tCurr, dt, samples);
    172 
    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);
    178     vOldY = vCurrentY;
    179 
    180     // Apply some additional fast movement.
    181     pCurr += ScaleVector2d(vSlow, dt.InSecondsF());
    182     tCurr += dt;
    183     ApplyMovement(&state, pCurr, vFast, tCurr, dt, samples);
    184 
    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);
    190   }
    191 }
    192 
    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;
    199 
    200   VelocityTrackerState state;
    201   state.AddMovement(
    202       Sample(MotionEvent::ACTION_DOWN, p0, t0, v, base::TimeDelta()));
    203 
    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);
    209 
    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()));
    215 
    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));
    220 }
    221 
    222 }  // namespace ui
    223