| // 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.GetTilt(i), b.GetTilt(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.ToInternalValue(), 0); |
| // 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::TOOL_TYPE_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 |