blob: a5471f73cfff8ceb3b52875727d3fb9674e3b534 [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 "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/gesture_detection/mock_motion_event.h"
#include "ui/events/gesture_detection/touch_disposition_gesture_filter.h"
namespace ui {
class TouchDispositionGestureFilterTest
: public testing::Test,
public TouchDispositionGestureFilterClient {
public:
TouchDispositionGestureFilterTest()
: cancel_after_next_gesture_(false), sent_gesture_count_(0) {}
virtual ~TouchDispositionGestureFilterTest() {}
// testing::Test
virtual void SetUp() OVERRIDE {
queue_.reset(new TouchDispositionGestureFilter(this));
}
virtual void TearDown() OVERRIDE {
queue_.reset();
}
// TouchDispositionGestureFilterClient
virtual void ForwardGestureEvent(const GestureEventData& event) OVERRIDE {
++sent_gesture_count_;
last_sent_gesture_time_ = event.time;
sent_gestures_.push_back(event.type());
last_sent_gesture_location_ = gfx::PointF(event.x, event.y);
last_sent_gesture_raw_location_ = gfx::PointF(event.raw_x, event.raw_y);
if (cancel_after_next_gesture_) {
CancelTouchPoint();
SendTouchNotConsumedAck();
cancel_after_next_gesture_ = false;
}
}
protected:
typedef std::vector<EventType> GestureList;
::testing::AssertionResult GesturesMatch(const GestureList& expected,
const GestureList& actual) {
if (expected.size() != actual.size()) {
return ::testing::AssertionFailure()
<< "actual.size(" << actual.size()
<< ") != expected.size(" << expected.size() << ")";
}
for (size_t i = 0; i < expected.size(); ++i) {
if (expected[i] != actual[i]) {
return ::testing::AssertionFailure()
<< "actual[" << i << "] ("
<< actual[i]
<< ") != expected[" << i << "] ("
<< expected[i] << ")";
}
}
return ::testing::AssertionSuccess();
}
GestureList Gestures(EventType type) {
return GestureList(1, type);
}
GestureList Gestures(EventType type0, EventType type1) {
GestureList gestures(2);
gestures[0] = type0;
gestures[1] = type1;
return gestures;
}
GestureList Gestures(EventType type0,
EventType type1,
EventType type2) {
GestureList gestures(3);
gestures[0] = type0;
gestures[1] = type1;
gestures[2] = type2;
return gestures;
}
GestureList Gestures(EventType type0,
EventType type1,
EventType type2,
EventType type3) {
GestureList gestures(4);
gestures[0] = type0;
gestures[1] = type1;
gestures[2] = type2;
gestures[3] = type3;
return gestures;
}
void SendTouchGestures() {
touch_event_.SetTime(base::TimeTicks::Now());
EXPECT_EQ(TouchDispositionGestureFilter::SUCCESS,
SendTouchGestures(touch_event_, pending_gesture_packet_));
GestureEventDataPacket gesture_packet;
std::swap(gesture_packet, pending_gesture_packet_);
}
TouchDispositionGestureFilter::PacketResult
SendTouchGestures(const MotionEvent& touch,
const GestureEventDataPacket& packet) {
GestureEventDataPacket touch_packet =
GestureEventDataPacket::FromTouch(touch);
for (size_t i = 0; i < packet.gesture_count(); ++i)
touch_packet.Push(packet.gesture(i));
return queue_->OnGesturePacket(touch_packet);
}
TouchDispositionGestureFilter::PacketResult
SendTimeoutGesture(EventType type) {
return queue_->OnGesturePacket(
GestureEventDataPacket::FromTouchTimeout(CreateGesture(type)));
}
TouchDispositionGestureFilter::PacketResult
SendGesturePacket(const GestureEventDataPacket& packet) {
return queue_->OnGesturePacket(packet);
}
void SendTouchEventAck(bool event_consumed) {
queue_->OnTouchEventAck(event_consumed);
}
void SendTouchConsumedAck() { SendTouchEventAck(true); }
void SendTouchNotConsumedAck() { SendTouchEventAck(false); }
void PushGesture(EventType type) {
pending_gesture_packet_.Push(CreateGesture(type));
}
void PressTouchPoint(int x, int y) {
touch_event_.PressPoint(x, y);
SendTouchGestures();
}
void MoveTouchPoint(size_t index, int x, int y) {
touch_event_.MovePoint(index, x, y);
SendTouchGestures();
}
void ReleaseTouchPoint() {
touch_event_.ReleasePoint();
SendTouchGestures();
}
void CancelTouchPoint() {
touch_event_.CancelPoint();
SendTouchGestures();
}
void SetRawTouchOffset(const gfx::Vector2dF& raw_offset) {
touch_event_.SetRawOffset(raw_offset.x(), raw_offset.y());
}
void ResetTouchPoints() { touch_event_ = MockMotionEvent(); }
bool GesturesSent() const { return !sent_gestures_.empty(); }
base::TimeTicks LastSentGestureTime() const {
return last_sent_gesture_time_;
}
base::TimeTicks CurrentTouchTime() const {
return touch_event_.GetEventTime();
}
bool IsEmpty() const { return queue_->IsEmpty(); }
GestureList GetAndResetSentGestures() {
GestureList sent_gestures;
sent_gestures.swap(sent_gestures_);
return sent_gestures;
}
const gfx::PointF& LastSentGestureLocation() const {
return last_sent_gesture_location_;
}
const gfx::PointF& LastSentGestureRawLocation() const {
return last_sent_gesture_raw_location_;
}
void SetCancelAfterNextGesture(bool cancel_after_next_gesture) {
cancel_after_next_gesture_ = cancel_after_next_gesture;
}
GestureEventData CreateGesture(EventType type) {
return GestureEventData(GestureEventDetails(type, 0, 0),
0,
base::TimeTicks(),
touch_event_.GetX(0),
touch_event_.GetY(0),
touch_event_.GetRawX(0),
touch_event_.GetRawY(0),
1,
gfx::RectF(0, 0, 0, 0));
}
private:
scoped_ptr<TouchDispositionGestureFilter> queue_;
bool cancel_after_next_gesture_;
MockMotionEvent touch_event_;
GestureEventDataPacket pending_gesture_packet_;
size_t sent_gesture_count_;
base::TimeTicks last_sent_gesture_time_;
GestureList sent_gestures_;
gfx::PointF last_sent_gesture_location_;
gfx::PointF last_sent_gesture_raw_location_;
};
TEST_F(TouchDispositionGestureFilterTest, BasicNoGestures) {
PressTouchPoint(1, 1);
EXPECT_FALSE(GesturesSent());
MoveTouchPoint(0, 2, 2);
EXPECT_FALSE(GesturesSent());
// No gestures should be dispatched by the ack, as the queued packets
// contained no gestures.
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
// Release the touch gesture.
ReleaseTouchPoint();
SendTouchConsumedAck();
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, BasicGestures) {
// An unconsumed touch's gesture should be sent.
PushGesture(ET_GESTURE_BEGIN);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
EXPECT_FALSE(GesturesSent());
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
// Multiple gestures can be queued for a single event.
PushGesture(ET_SCROLL_FLING_START);
PushGesture(ET_SCROLL_FLING_CANCEL);
PushGesture(ET_GESTURE_END);
ReleaseTouchPoint();
EXPECT_FALSE(GesturesSent());
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_SCROLL_FLING_START,
ET_SCROLL_FLING_CANCEL,
ET_GESTURE_END),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, BasicGesturesConsumed) {
// A consumed touch's gesture should not be sent.
PushGesture(ET_GESTURE_BEGIN);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 2, 2);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_SCROLL_FLING_START);
PushGesture(ET_SCROLL_FLING_CANCEL);
PushGesture(ET_GESTURE_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, ConsumedThenNotConsumed) {
// A consumed touch's gesture should not be sent.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
// Even if the subsequent touch is not consumed, continue dropping gestures.
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 2, 2);
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
// Even if the subsequent touch had no consumer, continue dropping gestures.
PushGesture(ET_SCROLL_FLING_START);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, NotConsumedThenConsumed) {
// A not consumed touch's gesture should be sent.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
// A newly consumed gesture should not be sent.
PushGesture(ET_GESTURE_PINCH_BEGIN);
PressTouchPoint(10, 10);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
// And subsequent non-consumed pinch updates should not be sent.
PushGesture(ET_GESTURE_SCROLL_UPDATE);
PushGesture(ET_GESTURE_PINCH_UPDATE);
MoveTouchPoint(0, 2, 2);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_UPDATE),
GetAndResetSentGestures()));
// End events dispatched only when their start events were.
PushGesture(ET_GESTURE_PINCH_END);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_SCROLL_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, ScrollAlternatelyConsumed) {
// A consumed touch's gesture should not be sent.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
for (size_t i = 0; i < 3; ++i) {
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 2, 2);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 3, 3);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_UPDATE),
GetAndResetSentGestures()));
}
PushGesture(ET_GESTURE_SCROLL_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, NotConsumedThenNoConsumer) {
// An unconsumed touch's gesture should be sent.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
// If the subsequent touch has no consumer (e.g., a secondary pointer is
// pressed but not on a touch handling rect), send the gesture.
PushGesture(ET_GESTURE_PINCH_BEGIN);
PressTouchPoint(2, 2);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
GetAndResetSentGestures()));
// End events should be dispatched when their start events were, independent
// of the ack state.
PushGesture(ET_GESTURE_PINCH_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_END),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_SCROLL_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, EndingEventsSent) {
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_PINCH_BEGIN);
PressTouchPoint(2, 2);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
GetAndResetSentGestures()));
// Consuming the touchend event can't suppress the match end gesture.
PushGesture(ET_GESTURE_PINCH_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_END),
GetAndResetSentGestures()));
// But other events in the same packet are still suppressed.
PushGesture(ET_GESTURE_SCROLL_UPDATE);
PushGesture(ET_GESTURE_SCROLL_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
// ET_GESTURE_SCROLL_END and ET_SCROLL_FLING_START behave the same in this
// regard.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
PushGesture(ET_SCROLL_FLING_START);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_SCROLL_FLING_START),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, EndingEventsNotSent) {
// Consuming a begin event ensures no end events are sent.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_PINCH_BEGIN);
PressTouchPoint(2, 2);
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_PINCH_END);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_SCROLL_END);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, UpdateEventsSuppressedPerEvent) {
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
// Consuming a single scroll or pinch update should suppress only that event.
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 2, 2);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_PINCH_BEGIN);
PressTouchPoint(2, 2);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_PINCH_UPDATE);
MoveTouchPoint(1, 2, 3);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
// Subsequent updates should not be affected.
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 4, 4);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_UPDATE),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_PINCH_UPDATE);
MoveTouchPoint(0, 4, 5);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_UPDATE),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_PINCH_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_END),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_SCROLL_END);
ReleaseTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, UpdateEventsDependOnBeginEvents) {
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
// Scroll and pinch gestures depend on the scroll begin gesture being
// dispatched.
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 2, 2);
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_PINCH_BEGIN);
PressTouchPoint(2, 2);
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_PINCH_UPDATE);
MoveTouchPoint(1, 2, 3);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_PINCH_END);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_SCROLL_END);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, MultipleTouchSequences) {
// Queue two touch-to-gestures sequences.
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
PushGesture(ET_GESTURE_TAP);
ReleaseTouchPoint();
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
PushGesture(ET_GESTURE_SCROLL_END);
ReleaseTouchPoint();
// The first gesture sequence should not be allowed.
SendTouchConsumedAck();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
// The subsequent sequence should "reset" allowance.
SendTouchNotConsumedAck();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN,
ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, FlingCancelledOnNewTouchSequence) {
const gfx::Vector2dF raw_offset(1.3f, 3.7f);
SetRawTouchOffset(raw_offset);
// Simulate a fling.
PushGesture(ET_GESTURE_TAP_DOWN);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(
Gestures(
ET_GESTURE_TAP_DOWN, ET_GESTURE_TAP_CANCEL, ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
PushGesture(ET_SCROLL_FLING_START);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_SCROLL_FLING_START),
GetAndResetSentGestures()));
// A new touch sequence should cancel the outstanding fling.
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_SCROLL_FLING_CANCEL),
GetAndResetSentGestures()));
EXPECT_EQ(CurrentTouchTime(), LastSentGestureTime());
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(1, 1));
EXPECT_EQ(LastSentGestureRawLocation(), gfx::PointF(1, 1) + raw_offset);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, ScrollEndedOnTouchReleaseIfNoFling) {
// Simulate a scroll.
PushGesture(ET_GESTURE_TAP_DOWN);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(
Gestures(
ET_GESTURE_TAP_DOWN, ET_GESTURE_TAP_CANCEL, ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
EXPECT_EQ(CurrentTouchTime(), LastSentGestureTime());
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(1, 1));
}
TEST_F(TouchDispositionGestureFilterTest, ScrollEndedOnNewTouchSequence) {
// Simulate a scroll.
PushGesture(ET_GESTURE_TAP_DOWN);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(
Gestures(
ET_GESTURE_TAP_DOWN, ET_GESTURE_TAP_CANCEL, ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
// A new touch sequence should end the outstanding scroll.
ResetTouchPoints();
PressTouchPoint(2, 3);
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
EXPECT_EQ(CurrentTouchTime(), LastSentGestureTime());
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(2, 3));
}
TEST_F(TouchDispositionGestureFilterTest, FlingCancelledOnScrollBegin) {
// Simulate a fling sequence.
PushGesture(ET_GESTURE_TAP_DOWN);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PushGesture(ET_SCROLL_FLING_START);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN,
ET_GESTURE_TAP_CANCEL,
ET_GESTURE_SCROLL_BEGIN,
ET_SCROLL_FLING_START),
GetAndResetSentGestures()));
// The new fling should cancel the preceding one.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PushGesture(ET_SCROLL_FLING_START);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_SCROLL_FLING_CANCEL,
ET_GESTURE_SCROLL_BEGIN,
ET_SCROLL_FLING_START),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, FlingNotCancelledIfGFCEventReceived) {
// Simulate a fling that is started then cancelled.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
PushGesture(ET_SCROLL_FLING_START);
MoveTouchPoint(0, 2, 3);
SendTouchNotConsumedAck();
PushGesture(ET_SCROLL_FLING_CANCEL);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN,
ET_SCROLL_FLING_START,
ET_SCROLL_FLING_CANCEL),
GetAndResetSentGestures()));
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(2, 3));
// A new touch sequence will not inject a ET_SCROLL_FLING_CANCEL, as the fling
// has already been cancelled.
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, TapCancelledWhenScrollBegins) {
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
// If the subsequent touch turns into a scroll, the tap should be cancelled.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
MoveTouchPoint(0, 2, 2);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL,
ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, TapCancelledWhenTouchConsumed) {
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
// If the subsequent touch is consumed, the tap should be cancelled.
PushGesture(ET_GESTURE_SCROLL_BEGIN);
MoveTouchPoint(0, 2, 2);
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL),
GetAndResetSentGestures()));
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(2, 2));
}
TEST_F(TouchDispositionGestureFilterTest,
TapNotCancelledIfTapEndingEventReceived) {
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(
GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN), GetAndResetSentGestures()));
PushGesture(ET_GESTURE_TAP);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SHOW_PRESS, ET_GESTURE_TAP),
GetAndResetSentGestures()));
// The tap should not be cancelled as it was terminated by a |ET_GESTURE_TAP|.
PressTouchPoint(2, 2);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, TimeoutGestures) {
// If the sequence is allowed, and there are no preceding gestures, the
// timeout gestures should be forwarded immediately.
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
SendTimeoutGesture(ET_GESTURE_SHOW_PRESS);
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SHOW_PRESS),
GetAndResetSentGestures()));
SendTimeoutGesture(ET_GESTURE_LONG_PRESS);
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_LONG_PRESS),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_LONG_TAP);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL,
ET_GESTURE_LONG_TAP),
GetAndResetSentGestures()));
// If the sequence is disallowed, and there are no preceding gestures, the
// timeout gestures should be dropped immediately.
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
SendTimeoutGesture(ET_GESTURE_SHOW_PRESS);
EXPECT_FALSE(GesturesSent());
ReleaseTouchPoint();
SendTouchNotConsumedAck();
// If the sequence has a pending ack, the timeout gestures should
// remain queued until the ack is received.
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
EXPECT_FALSE(GesturesSent());
SendTimeoutGesture(ET_GESTURE_LONG_PRESS);
EXPECT_FALSE(GesturesSent());
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN,
ET_GESTURE_LONG_PRESS),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, SpuriousAcksIgnored) {
// Acks received when the queue is empty will be safely ignored.
ASSERT_TRUE(IsEmpty());
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 3,3);
SendTouchNotConsumedAck();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN,
ET_GESTURE_SCROLL_UPDATE),
GetAndResetSentGestures()));
// Even if all packets have been dispatched, the filter may not be empty as
// there could be follow-up timeout events. Spurious acks in such cases
// should also be safely ignored.
ASSERT_FALSE(IsEmpty());
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, PacketWithInvalidTypeIgnored) {
GestureEventDataPacket packet;
EXPECT_EQ(TouchDispositionGestureFilter::INVALID_PACKET_TYPE,
SendGesturePacket(packet));
EXPECT_TRUE(IsEmpty());
}
TEST_F(TouchDispositionGestureFilterTest, PacketsWithInvalidOrderIgnored) {
EXPECT_EQ(TouchDispositionGestureFilter::INVALID_PACKET_ORDER,
SendTimeoutGesture(ET_GESTURE_SHOW_PRESS));
EXPECT_TRUE(IsEmpty());
}
TEST_F(TouchDispositionGestureFilterTest, ConsumedTouchCancel) {
// An unconsumed touch's gesture should be sent.
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
EXPECT_FALSE(GesturesSent());
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_TAP_CANCEL);
PushGesture(ET_GESTURE_SCROLL_END);
CancelTouchPoint();
EXPECT_FALSE(GesturesSent());
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL,
ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, TimeoutEventAfterRelease) {
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_TAP_DOWN);
PushGesture(ET_GESTURE_TAP_UNCONFIRMED);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(
GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN, ET_GESTURE_TAP_UNCONFIRMED),
GetAndResetSentGestures()));
SendTimeoutGesture(ET_GESTURE_TAP);
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SHOW_PRESS, ET_GESTURE_TAP),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, ShowPressInsertedBeforeTap) {
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
SendTimeoutGesture(ET_GESTURE_TAP_UNCONFIRMED);
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_UNCONFIRMED),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_TAP);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SHOW_PRESS,
ET_GESTURE_TAP),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, ShowPressNotInsertedIfAlreadySent) {
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
SendTimeoutGesture(ET_GESTURE_SHOW_PRESS);
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SHOW_PRESS),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_TAP);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, TapAndScrollCancelledOnTouchCancel) {
const gfx::Vector2dF raw_offset(1.3f, 3.7f);
SetRawTouchOffset(raw_offset);
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
// A cancellation motion event should cancel the tap.
CancelTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL),
GetAndResetSentGestures()));
EXPECT_EQ(CurrentTouchTime(), LastSentGestureTime());
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(1, 1));
EXPECT_EQ(LastSentGestureRawLocation(), gfx::PointF(1, 1) + raw_offset);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
// A cancellation motion event should end the scroll, even if the touch was
// consumed.
CancelTouchPoint();
SendTouchConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
EXPECT_EQ(CurrentTouchTime(), LastSentGestureTime());
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(1, 1));
EXPECT_EQ(LastSentGestureRawLocation(), gfx::PointF(1, 1) + raw_offset);
}
TEST_F(TouchDispositionGestureFilterTest,
ConsumedScrollUpdateMakesFlingScrollEnd) {
// A consumed touch's gesture should not be sent.
PushGesture(ET_GESTURE_BEGIN);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(
GesturesMatch(Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_SCROLL_UPDATE);
MoveTouchPoint(0, 2, 2);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_SCROLL_FLING_START);
PushGesture(ET_SCROLL_FLING_CANCEL);
PushGesture(ET_GESTURE_END);
ReleaseTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_END, ET_GESTURE_SCROLL_END),
GetAndResetSentGestures()));
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(2, 2));
PushGesture(ET_GESTURE_BEGIN);
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
}
TEST_F(TouchDispositionGestureFilterTest, TapCancelledOnTouchCancel) {
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
// A cancellation motion event should cancel the tap.
CancelTouchPoint();
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL),
GetAndResetSentGestures()));
EXPECT_EQ(CurrentTouchTime(), LastSentGestureTime());
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(1, 1));
}
// Test that a GestureEvent whose dispatch causes a cancel event to be fired
// won't cause a crash.
TEST_F(TouchDispositionGestureFilterTest, TestCancelMidGesture) {
SetCancelAfterNextGesture(true);
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN,
ET_GESTURE_TAP_CANCEL),
GetAndResetSentGestures()));
EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(1, 1));
}
// Test that a MultiFingerSwipe event is dispatched when appropriate.
TEST_F(TouchDispositionGestureFilterTest, TestAllowedMultiFingerSwipe) {
PushGesture(ET_GESTURE_SCROLL_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_PINCH_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
GetAndResetSentGestures()));
PushGesture(ET_GESTURE_SWIPE);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SWIPE),
GetAndResetSentGestures()));
}
// Test that a MultiFingerSwipe event is dispatched when appropriate.
TEST_F(TouchDispositionGestureFilterTest, TestDisallowedMultiFingerSwipe) {
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
PushGesture(ET_GESTURE_SCROLL_BEGIN);
MoveTouchPoint(0, 0, 0);
SendTouchConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_PINCH_BEGIN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
PushGesture(ET_GESTURE_SWIPE);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_FALSE(GesturesSent());
}
TEST_F(TouchDispositionGestureFilterTest, TapCancelOnSecondFingerDown) {
PushGesture(ET_GESTURE_TAP_DOWN);
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
GetAndResetSentGestures()));
PressTouchPoint(1, 1);
SendTouchNotConsumedAck();
EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL),
GetAndResetSentGestures()));
}
} // namespace ui