blob: a6f7843630938b0cdf71ccfedb56eb910fd1dcf4 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include "base/logging.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/gesture_detection/motion_event_buffer.h"
#include "ui/events/test/motion_event_test_utils.h"
using base::TimeDelta;
using base::TimeTicks;
using ui::test::MockMotionEvent;
namespace ui {
const int kSmallDeltaMs = 1;
const int kLargeDeltaMs = 50;
const int kResampleDeltaMs = 5;
const float kVelocityEpsilon = 0.01f;
const float kDeltaEpsilon = 0.1f;
#define EXPECT_EVENT_EQ(A, B) \
{ \
SCOPED_TRACE(testing::Message()); \
ExpectEquals((A), (B)); \
}
#define EXPECT_EVENT_IGNORING_HISTORY_EQ(A, B) \
{ \
SCOPED_TRACE(testing::Message()); \
ExpectEqualsIgnoringHistory((A), (B)); \
}
#define EXPECT_EVENT_HISTORY_EQ(A, I, B) \
{ \
SCOPED_TRACE(testing::Message()); \
ExpectEqualsHistoryIndex((A), (I), (B)); \
}
class MotionEventBufferTest : public testing::Test,
public MotionEventBufferClient {
public:
MotionEventBufferTest() : needs_flush_(false) {}
~MotionEventBufferTest() override {}
// MotionEventBufferClient implementation.
void ForwardMotionEvent(const MotionEvent& event) override {
forwarded_events_.push_back(event.Clone());
}
void SetNeedsFlush() override { needs_flush_ = true; }
bool GetAndResetNeedsFlush() {
bool needs_flush = needs_flush_;
needs_flush_ = false;
return needs_flush;
}
std::vector<std::unique_ptr<MotionEvent>> GetAndResetForwardedEvents() {
std::vector<std::unique_ptr<MotionEvent>> forwarded_events;
forwarded_events.swap(forwarded_events_);
return forwarded_events;
}
const MotionEvent* GetLastEvent() const {
return forwarded_events_.empty() ? nullptr : forwarded_events_.back().get();
}
static base::TimeDelta LargeDelta() {
return base::TimeDelta::FromMilliseconds(kLargeDeltaMs);
}
static base::TimeDelta SmallDelta() {
return base::TimeDelta::FromMilliseconds(kSmallDeltaMs);
}
static base::TimeDelta ResampleDelta() {
return base::TimeDelta::FromMilliseconds(kResampleDeltaMs);
}
static void ExpectEqualsImpl(const MotionEvent& a,
const MotionEvent& b,
bool ignore_history) {
EXPECT_EQ(a.GetAction(), b.GetAction());
if (a.GetAction() == MotionEvent::Action::POINTER_DOWN ||
a.GetAction() == MotionEvent::Action::POINTER_UP) {
EXPECT_EQ(a.GetActionIndex(), b.GetActionIndex());
}
EXPECT_EQ(a.GetButtonState(), b.GetButtonState());
EXPECT_EQ(a.GetEventTime(), b.GetEventTime());
ASSERT_EQ(a.GetPointerCount(), b.GetPointerCount());
for (size_t i = 0; i < a.GetPointerCount(); ++i) {
int bi = b.FindPointerIndexOfId(a.GetPointerId(i));
ASSERT_NE(bi, -1);
EXPECT_EQ(a.GetX(i), b.GetX(bi));
EXPECT_EQ(a.GetY(i), b.GetY(bi));
EXPECT_EQ(a.GetRawX(i), b.GetRawX(bi));
EXPECT_EQ(a.GetRawY(i), b.GetRawY(bi));
EXPECT_EQ(a.GetTouchMajor(i), b.GetTouchMajor(bi));
EXPECT_EQ(a.GetTouchMinor(i), b.GetTouchMinor(bi));
EXPECT_EQ(a.GetOrientation(i), b.GetOrientation(bi));
EXPECT_EQ(a.GetPressure(i), b.GetPressure(bi));
EXPECT_EQ(a.GetTiltX(i), b.GetTiltX(bi));
EXPECT_EQ(a.GetTiltY(i), b.GetTiltY(bi));
EXPECT_EQ(a.GetToolType(i), b.GetToolType(bi));
}
if (ignore_history)
return;
ASSERT_EQ(a.GetHistorySize(), b.GetHistorySize());
for (size_t h = 0; h < a.GetHistorySize(); ++h)
ExpectEqualsHistoryIndex(a, h, b);
}
// Verify that all public data of |a|, excluding history, equals that of |b|.
static void ExpectEqualsIgnoringHistory(const MotionEvent& a,
const MotionEvent& b) {
const bool ignore_history = true;
ExpectEqualsImpl(a, b, ignore_history);
}
// Verify that all public data of |a| equals that of |b|.
static void ExpectEquals(const MotionEvent& a, const MotionEvent& b) {
const bool ignore_history = false;
ExpectEqualsImpl(a, b, ignore_history);
}
// Verify that the historical data of |a| given by |historical_index|
// corresponds to the *raw* data of |b|.
static void ExpectEqualsHistoryIndex(const MotionEvent& a,
size_t history_index,
const MotionEvent& b) {
ASSERT_LT(history_index, a.GetHistorySize());
EXPECT_EQ(a.GetPointerCount(), b.GetPointerCount());
EXPECT_TRUE(a.GetHistoricalEventTime(history_index) == b.GetEventTime());
for (size_t i = 0; i < a.GetPointerCount(); ++i) {
int bi = b.FindPointerIndexOfId(a.GetPointerId(i));
ASSERT_NE(bi, -1);
EXPECT_EQ(a.GetHistoricalX(i, history_index), b.GetX(bi));
EXPECT_EQ(a.GetHistoricalY(i, history_index), b.GetY(bi));
EXPECT_EQ(a.GetHistoricalTouchMajor(i, history_index),
b.GetTouchMajor(bi));
}
}
protected:
void RunResample(base::TimeDelta flush_time_delta,
base::TimeDelta event_time_delta) {
for (base::TimeDelta offset; offset < event_time_delta;
offset += event_time_delta / 3) {
SCOPED_TRACE(testing::Message()
<< "Resample(offset="
<< static_cast<int>(offset.InMilliseconds()) << "ms)");
RunResample(flush_time_delta, event_time_delta, offset);
}
}
// Given an event and flush sampling frequency, inject a stream of events,
// flushing at appropriate points in the stream. Verify that the continuous
// velocity sampled by the *input* stream matches the discrete velocity
// as computed from the resampled *output* stream.
void RunResample(base::TimeDelta flush_time_delta,
base::TimeDelta event_time_delta,
base::TimeDelta event_time_offset) {
base::TimeTicks event_time = base::TimeTicks::Now();
base::TimeTicks flush_time =
event_time + flush_time_delta - event_time_offset;
base::TimeTicks max_event_time =
event_time + base::TimeDelta::FromSecondsD(0.5f);
const size_t min_expected_events =
static_cast<size_t>((max_event_time - flush_time) /
std::max(event_time_delta, flush_time_delta));
MotionEventBuffer buffer(this, true);
gfx::Vector2dF velocity(33.f, -11.f);
gfx::PointF position(17.f, 42.f);
std::unique_ptr<MotionEvent> last_flushed_event;
size_t events = 0;
float last_dx = 0, last_dy = 0;
base::TimeDelta last_dt;
while (event_time < max_event_time) {
position += gfx::ScaleVector2d(velocity, event_time_delta.InSecondsF());
MockMotionEvent move(MotionEvent::Action::MOVE, event_time, position.x(),
position.y());
buffer.OnMotionEvent(move);
event_time += event_time_delta;
while (flush_time < event_time) {
buffer.Flush(flush_time);
flush_time += flush_time_delta;
const MotionEvent* current_flushed_event = GetLastEvent();
if (current_flushed_event) {
if (!last_flushed_event) {
last_flushed_event = current_flushed_event->Clone();
continue;
}
base::TimeDelta dt = current_flushed_event->GetEventTime() -
last_flushed_event->GetEventTime();
EXPECT_GE(dt, base::TimeDelta());
// A time delta of 0 is possible if the flush rate is greater than the
// event rate, in which case we can simply skip forward.
if (dt.is_zero())
continue;
const float dx =
current_flushed_event->GetX() - last_flushed_event->GetX();
const float dy =
current_flushed_event->GetY() - last_flushed_event->GetY();
const float dt_s = (current_flushed_event->GetEventTime() -
last_flushed_event->GetEventTime()).InSecondsF();
// The discrete velocity should mirror the constant velocity.
EXPECT_NEAR(velocity.x(), dx / dt_s, kVelocityEpsilon);
EXPECT_NEAR(velocity.y(), dy / dt_s, kVelocityEpsilon);
// The impulse delta for each frame should remain constant.
if (last_dy)
EXPECT_NEAR(dx, last_dx, kDeltaEpsilon);
if (last_dy)
EXPECT_NEAR(dy, last_dy, kDeltaEpsilon);
// The timestamp delta should remain constant.
if (!last_dt.is_zero())
EXPECT_TRUE((dt - last_dt).InMillisecondsF() < kDeltaEpsilon);
last_dx = dx;
last_dy = dy;
last_dt = dt;
last_flushed_event = current_flushed_event->Clone();
events += GetAndResetForwardedEvents().size();
}
}
}
events += GetAndResetForwardedEvents().size();
EXPECT_GE(events, min_expected_events);
}
private:
std::vector<std::unique_ptr<MotionEvent>> forwarded_events_;
bool needs_flush_;
};
TEST_F(MotionEventBufferTest, BufferEmpty) {
MotionEventBuffer buffer(this, true);
buffer.Flush(base::TimeTicks::Now());
EXPECT_FALSE(GetAndResetNeedsFlush());
EXPECT_FALSE(GetLastEvent());
}
TEST_F(MotionEventBufferTest, BufferWithOneMoveNotResampled) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent move(MotionEvent::Action::MOVE, event_time, 4.f, 4.f);
buffer.OnMotionEvent(move);
EXPECT_TRUE(GetAndResetNeedsFlush());
EXPECT_FALSE(GetLastEvent());
buffer.Flush(event_time + ResampleDelta());
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(move, *GetLastEvent());
EXPECT_EQ(1U, GetAndResetForwardedEvents().size());
}
TEST_F(MotionEventBufferTest, BufferFlushedOnNonActionMove) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
buffer.OnMotionEvent(move0);
EXPECT_TRUE(GetAndResetNeedsFlush());
EXPECT_FALSE(GetLastEvent());
event_time += base::TimeDelta::FromMilliseconds(5);
// The second move should remain buffered.
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f);
buffer.OnMotionEvent(move1);
EXPECT_FALSE(GetAndResetNeedsFlush());
EXPECT_FALSE(GetLastEvent());
// The third move should remain buffered.
MockMotionEvent move2(MotionEvent::Action::MOVE, event_time, 3.f, 3.f);
buffer.OnMotionEvent(move2);
EXPECT_FALSE(GetAndResetNeedsFlush());
EXPECT_FALSE(GetLastEvent());
// The up should flush the buffer.
MockMotionEvent up(MotionEvent::Action::UP, event_time, 4.f, 4.f);
buffer.OnMotionEvent(up);
EXPECT_FALSE(GetAndResetNeedsFlush());
// The flushed events should include the up and the moves, with the latter
// combined into a single event with history.
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(2U, events.size());
EXPECT_EVENT_EQ(up, *events.back());
EXPECT_EQ(2U, events.front()->GetHistorySize());
EXPECT_EVENT_IGNORING_HISTORY_EQ(*events.front(), move2);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 0, move0);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 1, move1);
}
TEST_F(MotionEventBufferTest, BufferFlushedOnIncompatibleActionMove) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
buffer.OnMotionEvent(move0);
EXPECT_TRUE(GetAndResetNeedsFlush());
EXPECT_FALSE(GetLastEvent());
event_time += base::TimeDelta::FromMilliseconds(5);
// The second move has a different pointer count, flushing the first.
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f, 3.f,
3.f);
buffer.OnMotionEvent(move1);
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(move0, *GetLastEvent());
event_time += base::TimeDelta::FromMilliseconds(5);
// The third move has differing tool types, flushing the second.
MockMotionEvent move2(move1);
move2.SetToolType(0, MotionEvent::ToolType::STYLUS);
buffer.OnMotionEvent(move2);
EXPECT_FALSE(GetAndResetNeedsFlush());
EXPECT_EVENT_EQ(move1, *GetLastEvent());
event_time += base::TimeDelta::FromMilliseconds(5);
// The flushed event should only include the latest move event.
buffer.Flush(event_time);
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(3U, events.size());
EXPECT_EVENT_EQ(move2, *events.back());
EXPECT_FALSE(GetAndResetNeedsFlush());
event_time += base::TimeDelta::FromMilliseconds(5);
// Events with different pointer ids should not combine.
PointerProperties pointer0(5.f, 5.f, 1.f);
pointer0.id = 1;
PointerProperties pointer1(10.f, 10.f, 2.f);
pointer1.id = 2;
MotionEventGeneric move3(MotionEvent::Action::MOVE, event_time, pointer0);
move3.PushPointer(pointer1);
buffer.OnMotionEvent(move3);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
MotionEventGeneric move4(MotionEvent::Action::MOVE, event_time, pointer0);
pointer1.id = 7;
move4.PushPointer(pointer1);
buffer.OnMotionEvent(move2);
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(move3, *GetLastEvent());
}
TEST_F(MotionEventBufferTest, OnlyActionMoveBuffered) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent down(MotionEvent::Action::DOWN, event_time, 1.f, 1.f);
buffer.OnMotionEvent(down);
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(down, *GetLastEvent());
GetAndResetForwardedEvents();
MockMotionEvent up(MotionEvent::Action::UP, event_time, 2.f, 2.f);
buffer.OnMotionEvent(up);
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(up, *GetLastEvent());
GetAndResetForwardedEvents();
MockMotionEvent cancel(MotionEvent::Action::CANCEL, event_time, 3.f, 3.f);
buffer.OnMotionEvent(cancel);
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(cancel, *GetLastEvent());
GetAndResetForwardedEvents();
MockMotionEvent move(MotionEvent::Action::MOVE, event_time, 4.f, 4.f);
buffer.OnMotionEvent(move);
EXPECT_TRUE(GetAndResetNeedsFlush());
EXPECT_FALSE(GetLastEvent());
base::TimeTicks flush_time = move.GetEventTime() + ResampleDelta();
buffer.Flush(flush_time);
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(move, *GetLastEvent());
}
TEST_F(MotionEventBufferTest, OutOfOrderPointersBuffered) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
PointerProperties p0(1.f, 2.f, 3.f);
p0.id = 1;
PointerProperties p1(2.f, 1.f, 0.5f);
p1.id = 2;
MotionEventGeneric move0(MotionEvent::Action::MOVE, event_time, p0);
move0.PushPointer(p1);
buffer.OnMotionEvent(move0);
EXPECT_TRUE(GetAndResetNeedsFlush());
ASSERT_FALSE(GetLastEvent());
event_time += base::TimeDelta::FromMilliseconds(5);
// The second move should remain buffered even if the logical pointers are
// in a different order.
MotionEventGeneric move1(MotionEvent::Action::MOVE, event_time, p1);
move1.PushPointer(p0);
buffer.OnMotionEvent(move1);
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_FALSE(GetLastEvent());
// As the two events are logically the same but for ordering and time, the
// synthesized event should yield a logically identical event.
base::TimeTicks flush_time = move1.GetEventTime() + ResampleDelta();
buffer.Flush(flush_time);
EXPECT_FALSE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EVENT_IGNORING_HISTORY_EQ(move1, *events.front());
EXPECT_EVENT_HISTORY_EQ(*events.front(), 0, move0);
}
TEST_F(MotionEventBufferTest, FlushedEventsNeverLaterThanFlushTime) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The second move should remain buffered.
event_time += LargeDelta();
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// A flush occurring too early should not forward any events.
base::TimeTicks flush_time = move0.GetEventTime() - ResampleDelta();
buffer.Flush(flush_time);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// With resampling enabled, a flush occurring before the resample
// offset should not forward any events.
flush_time = move0.GetEventTime();
buffer.Flush(flush_time);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// Only the first event should get flushed, as the flush timestamp precedes
// the second's timestamp by a sufficient amount (preventing interpolation).
flush_time = move0.GetEventTime() + ResampleDelta();
buffer.Flush(flush_time);
// There should only be one flushed event.
EXPECT_TRUE(GetAndResetNeedsFlush());
ASSERT_TRUE(GetLastEvent());
EXPECT_TRUE(GetLastEvent()->GetEventTime() <= flush_time);
GetAndResetForwardedEvents();
// Flushing again with a similar timestamp should have no effect other than
// triggering another flush request.
flush_time += base::TimeDelta::FromMilliseconds(1);
buffer.Flush(flush_time);
EXPECT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// Flushing after the second move's time should trigger forwarding.
flush_time = move1.GetEventTime() + ResampleDelta();
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(move1, *GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
}
TEST_F(MotionEventBufferTest, NoResamplingWhenDisabled) {
base::TimeTicks event_time = base::TimeTicks::Now();
const bool resampling_enabled = false;
MotionEventBuffer buffer(this, resampling_enabled);
// Queue two events.
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
event_time += base::TimeDelta::FromMilliseconds(5);
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 15.f, 30.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// Flush at a time between the first and second events.
base::TimeTicks interpolated_time =
move0.GetEventTime() + (move1.GetEventTime() - move0.GetEventTime()) / 2;
base::TimeTicks flush_time = interpolated_time;
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// There should only be one flushed event, with the second remaining buffered
// and no resampling having occurred.
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EVENT_EQ(move0, *events.front());
// The second move should be flushed without resampling.
flush_time = move1.GetEventTime();
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(move1, *GetLastEvent());
GetAndResetForwardedEvents();
// Now queue two more events.
move0 = MockMotionEvent(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The second move should remain buffered.
event_time += base::TimeDelta::FromMilliseconds(5);
move1 = MockMotionEvent(MotionEvent::Action::MOVE, event_time, 10.f, 20.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// Sample at a time beyond the first and second events.
flush_time =
move1.GetEventTime() + (move1.GetEventTime() - move0.GetEventTime());
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// There should only be one flushed event, with the first event in the history
// and the second event as the actual event data (no resampling).
events = GetAndResetForwardedEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EQ(1U, events.front()->GetHistorySize());
EXPECT_EVENT_IGNORING_HISTORY_EQ(*events.front(), move1);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 0, move0);
}
TEST_F(MotionEventBufferTest, NoResamplingWithOutOfOrderActionMove) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The second move should remain buffered.
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 10.f, 20.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// Sample at a time beyond the first and second events.
base::TimeTicks extrapolated_time =
move1.GetEventTime() + (move1.GetEventTime() - move0.GetEventTime());
base::TimeTicks flush_time = extrapolated_time + ResampleDelta();
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// There should only be one flushed event, with the event extrapolated from
// the two events.
base::TimeTicks expected_time =
move1.GetEventTime() + (move1.GetEventTime() - move0.GetEventTime()) / 2;
std::vector<std::unique_ptr<MotionEvent>> events0 =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events0.size());
EXPECT_EQ(2U, events0.front()->GetHistorySize());
EXPECT_EQ(expected_time, events0.front()->GetEventTime());
EXPECT_FALSE(GetAndResetNeedsFlush());
// Try enqueuing an event *after* the second event but *before* the
// extrapolated event. It should be dropped.
event_time = move1.GetEventTime() + base::TimeDelta::FromMilliseconds(1);
MockMotionEvent move2(MotionEvent::Action::MOVE, event_time, 15.f, 25.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// Finally queue an event *after* the extrapolated event.
event_time = expected_time + base::TimeDelta::FromMilliseconds(1);
MockMotionEvent move3(MotionEvent::Action::MOVE, event_time, 15.f, 25.f);
buffer.OnMotionEvent(move3);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The flushed event should simply be the latest event.
flush_time = event_time + ResampleDelta();
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
std::vector<std::unique_ptr<MotionEvent>> events1 =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events1.size());
EXPECT_EVENT_EQ(move3, *events1.front());
EXPECT_FALSE(GetAndResetNeedsFlush());
}
TEST_F(MotionEventBufferTest, NoResamplingWithSmallTimeDeltaBetweenMoves) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
// The first move should be buffered.
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The second move should remain buffered.
event_time += SmallDelta();
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
base::TimeTicks flush_time = event_time + ResampleDelta();
buffer.Flush(flush_time);
EXPECT_FALSE(GetAndResetNeedsFlush());
// There should only be one flushed event, and no resampling should have
// occured between the first and the second as they were temporally too close.
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EQ(1U, events.front()->GetHistorySize());
EXPECT_EVENT_IGNORING_HISTORY_EQ(*events.front(), move1);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 0, move0);
}
TEST_F(MotionEventBufferTest, NoResamplingWithMismatchBetweenMoves) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
// The first move should be buffered.
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 1.f, 1.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The second move should remain buffered.
event_time += SmallDelta();
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 2.f, 2.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
base::TimeTicks flush_time = event_time + ResampleDelta();
buffer.Flush(flush_time);
EXPECT_FALSE(GetAndResetNeedsFlush());
// There should only be one flushed event, and no resampling should have
// occured between the first and the second as they were temporally too close.
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EQ(1U, events.front()->GetHistorySize());
EXPECT_EVENT_IGNORING_HISTORY_EQ(*events.front(), move1);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 0, move0);
}
TEST_F(MotionEventBufferTest, Interpolation) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The second move should remain buffered.
event_time += base::TimeDelta::FromMilliseconds(5);
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 15.f, 30.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// Sample at a time between the first and second events.
base::TimeTicks interpolated_time =
move0.GetEventTime() + (move1.GetEventTime() - move0.GetEventTime()) / 3;
base::TimeTicks flush_time = interpolated_time + ResampleDelta();
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// There should only be one flushed event, with the event interpolated between
// the two events. The second event should remain buffered.
float alpha = (interpolated_time - move0.GetEventTime()).InMillisecondsF() /
(move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
MockMotionEvent interpolated_event(
MotionEvent::Action::MOVE, interpolated_time,
move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * alpha,
move0.GetY(0) + (move1.GetY(0) - move0.GetY(0)) * alpha);
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EQ(1U, events.front()->GetHistorySize());
EXPECT_EVENT_IGNORING_HISTORY_EQ(*events.front(), interpolated_event);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 0, move0);
// The second move should be flushed without resampling.
flush_time = move1.GetEventTime() + ResampleDelta();
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_EVENT_EQ(move1, *GetLastEvent());
}
TEST_F(MotionEventBufferTest, Extrapolation) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The second move should remain buffered.
event_time += base::TimeDelta::FromMilliseconds(5);
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 10.f, 20.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// Sample at a time beyond the first and second events.
base::TimeTicks extrapolated_time =
move1.GetEventTime() + (move1.GetEventTime() - move0.GetEventTime());
base::TimeTicks flush_time = extrapolated_time + ResampleDelta();
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// There should only be one flushed event, with the event extrapolated from
// the two events. The first and second events should be in the history.
// Note that the maximum extrapolation is limited by *half* of the time delta
// between the two events, hence we divide the relative delta by 2 in
// determining the extrapolated event.
base::TimeTicks expected_time =
move1.GetEventTime() + (move1.GetEventTime() - move0.GetEventTime()) / 2;
float expected_alpha =
(expected_time - move0.GetEventTime()).InMillisecondsF() /
(move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
MockMotionEvent extrapolated_event(
MotionEvent::Action::MOVE, expected_time,
move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * expected_alpha,
move0.GetY(0) + (move1.GetY(0) - move0.GetY(0)) * expected_alpha);
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EQ(2U, events.front()->GetHistorySize());
EXPECT_EVENT_IGNORING_HISTORY_EQ(*events.front(), extrapolated_event);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 0, move0);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 1, move1);
}
TEST_F(MotionEventBufferTest, ExtrapolationHorizonLimited) {
base::TimeTicks event_time = base::TimeTicks::Now();
MotionEventBuffer buffer(this, true);
MockMotionEvent move0(MotionEvent::Action::MOVE, event_time, 5.f, 10.f);
buffer.OnMotionEvent(move0);
ASSERT_FALSE(GetLastEvent());
EXPECT_TRUE(GetAndResetNeedsFlush());
// The second move should remain buffered.
event_time += base::TimeDelta::FromMilliseconds(24);
MockMotionEvent move1(MotionEvent::Action::MOVE, event_time, 10.f, 20.f);
buffer.OnMotionEvent(move1);
ASSERT_FALSE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// Sample at a time beyond the first and second events.
base::TimeTicks extrapolated_time =
event_time + base::TimeDelta::FromMilliseconds(24);
base::TimeTicks flush_time = extrapolated_time + ResampleDelta();
buffer.Flush(flush_time);
ASSERT_TRUE(GetLastEvent());
EXPECT_FALSE(GetAndResetNeedsFlush());
// There should only be one flushed event, with the event extrapolated from
// the two events. The first and second events should be in the history.
// Note that the maximum extrapolation is limited by 8 ms.
base::TimeTicks expected_time =
move1.GetEventTime() + base::TimeDelta::FromMilliseconds(8);
float expected_alpha =
(expected_time - move0.GetEventTime()).InMillisecondsF() /
(move1.GetEventTime() - move0.GetEventTime()).InMillisecondsF();
MockMotionEvent extrapolated_event(
MotionEvent::Action::MOVE, expected_time,
move0.GetX(0) + (move1.GetX(0) - move0.GetX(0)) * expected_alpha,
move0.GetY(0) + (move1.GetY(0) - move0.GetY(0)) * expected_alpha);
std::vector<std::unique_ptr<MotionEvent>> events =
GetAndResetForwardedEvents();
ASSERT_EQ(1U, events.size());
EXPECT_EQ(2U, events.front()->GetHistorySize());
EXPECT_EVENT_IGNORING_HISTORY_EQ(*events.front(), extrapolated_event);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 0, move0);
EXPECT_EVENT_HISTORY_EQ(*events.front(), 1, move1);
}
TEST_F(MotionEventBufferTest, Resampling30to60) {
base::TimeDelta flush_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 60.);
base::TimeDelta event_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 30.);
RunResample(flush_time_delta, event_time_delta);
}
TEST_F(MotionEventBufferTest, Resampling60to60) {
base::TimeDelta flush_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 60.);
base::TimeDelta event_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 60.);
RunResample(flush_time_delta, event_time_delta);
}
TEST_F(MotionEventBufferTest, Resampling100to60) {
base::TimeDelta flush_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 60.);
base::TimeDelta event_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 100.);
RunResample(flush_time_delta, event_time_delta);
}
TEST_F(MotionEventBufferTest, Resampling120to60) {
base::TimeDelta flush_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 60.);
base::TimeDelta event_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 120.);
RunResample(flush_time_delta, event_time_delta);
}
TEST_F(MotionEventBufferTest, Resampling150to60) {
base::TimeDelta flush_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 60.);
base::TimeDelta event_time_delta =
base::TimeDelta::FromMillisecondsD(1000. / 150.);
RunResample(flush_time_delta, event_time_delta);
}
} // namespace ui