// 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 "ui/events/gesture_detection/touch_disposition_gesture_filter.h"

#include <stddef.h>
#include <stdint.h>

#include <memory>

#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/test/motion_event_test_utils.h"

using ui::test::MockMotionEvent;

namespace ui {
namespace {

const int kDefaultEventFlags = EF_ALT_DOWN | EF_SHIFT_DOWN;

}  // namespace

class TouchDispositionGestureFilterTest
    : public testing::Test,
      public TouchDispositionGestureFilterClient {
 public:
  TouchDispositionGestureFilterTest()
      : cancel_after_next_gesture_(false), sent_gesture_count_(0) {}
  ~TouchDispositionGestureFilterTest() override {}

  // testing::Test
  void SetUp() override {
    queue_.reset(new TouchDispositionGestureFilter(this));
    touch_event_.set_flags(kDefaultEventFlags);
  }

  void TearDown() override { queue_.reset(); }

  // TouchDispositionGestureFilterClient
  void ForwardGestureEvent(const GestureEventData& event) override {
    EXPECT_EQ(GestureDeviceType::DEVICE_TOUCHSCREEN,
              event.details.device_type());
    ++sent_gesture_count_;
    last_sent_gesture_.reset(new GestureEventData(event));
    sent_gestures_.push_back(event.type());
    if (event.type() == ET_GESTURE_SHOW_PRESS)
      show_press_bounding_box_ = event.details.bounding_box();
    if (cancel_after_next_gesture_) {
      cancel_after_next_gesture_ = false;
      SendPacket(CancelTouchPoint(), NoGestures());
      SendTouchNotConsumedAckForLastTouch();
    }
  }

 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 NoGestures() { return GestureList(0); }

  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;
  }

  TouchDispositionGestureFilter::PacketResult SendTouchGesturesWithResult(
      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);
  }

  uint32_t SendTouchGestures(const MotionEvent& touch,
                             const GestureEventDataPacket& packet) {
    SendTouchGesturesWithResult(touch, packet);
    last_sent_touch_event_id_ = touch.GetUniqueEventId();
    return touch.GetUniqueEventId();
  }

  TouchDispositionGestureFilter::PacketResult
  SendTimeoutGesture(EventType type) {
    return queue_->OnGesturePacket(
        GestureEventDataPacket::FromTouchTimeout(CreateGesture(type)));
  }

  TouchDispositionGestureFilter::PacketResult
  SendGesturePacket(const GestureEventDataPacket& packet) {
    return queue_->OnGesturePacket(packet);
  }

  void SendTouchEventAck(uint32_t touch_event_id,
                         bool event_consumed,
                         bool is_source_touch_event_set_non_blocking) {
    queue_->OnTouchEventAck(touch_event_id, event_consumed,
                            is_source_touch_event_set_non_blocking);
  }

  void SendTouchConsumedAck(uint32_t touch_event_id) {
    SendTouchEventAck(touch_event_id, true /* event_consumed */,
                      false /* is_source_touch_event_set_non_blocking */);
  }

  void SendTouchNotConsumedAck(uint32_t touch_event_id) {
    SendTouchEventAck(touch_event_id, false /* event_consumed */,
                      false /* is_source_touch_event_set_non_blocking */);
  }

  void SendTouchConsumedAckForLastTouch() {
    SendTouchEventAck(last_sent_touch_event_id_, true /* event_consumed */,
                      false /* is_source_touch_event_set_non_blocking */);
  }

  void SendTouchNotConsumedAckForLastTouch() {
    SendTouchEventAck(last_sent_touch_event_id_, false /* event_consumed */,
                      false /* is_source_touch_event_set_non_blocking */);
  }

  void PushGesture(EventType type) {
    pending_gesture_packet_.Push(CreateGesture(type));
  }

  void PushGesture(EventType type, float x, float y, float diameter) {
    pending_gesture_packet_.Push(CreateGesture(type, x, y, diameter));
  }

  const MockMotionEvent& PressTouchPoint(float x, float y) {
    touch_event_.PressPoint(x, y);
    touch_event_.SetRawOffset(raw_offset_.x(), raw_offset_.y());
    return touch_event_;
  }

  const MockMotionEvent& PressTouchPoint() { return PressTouchPoint(0, 0); }

  const MockMotionEvent& MoveTouchPoint() {
    touch_event_.MovePoint(0, 0, 0);
    touch_event_.set_event_time(base::TimeTicks::Now());
    return touch_event_;
  }

  const MockMotionEvent& ReleaseTouchPoint() {
    touch_event_.ReleasePoint();
    touch_event_.set_event_time(base::TimeTicks::Now());
    return touch_event_;
  }

  const MockMotionEvent& CancelTouchPoint() {
    touch_event_.CancelPoint();
    touch_event_.set_event_time(base::TimeTicks::Now());
    return touch_event_;
  }

  uint32_t SendPacket(const MockMotionEvent& touch_event,
                      GestureList gesture_list) {
    GestureEventDataPacket gesture_packet;
    for (EventType type : gesture_list)
      gesture_packet.Push(CreateGesture(type));
    last_sent_touch_event_id_ = touch_event.GetUniqueEventId();
    EXPECT_EQ(TouchDispositionGestureFilter::SUCCESS,
              SendTouchGesturesWithResult(touch_event, gesture_packet));
    return touch_event.GetUniqueEventId();
  }

  void SetRawTouchOffset(const gfx::Vector2dF& raw_offset) {
    raw_offset_ = raw_offset;
  }

  const MockMotionEvent& ResetTouchPoints() {
    touch_event_ = MockMotionEvent();
    return touch_event_;
  }

  bool GesturesSent() const { return !sent_gestures_.empty(); }

  const GestureEventData& last_sent_gesture() const {
    CHECK(last_sent_gesture_);
    return *last_sent_gesture_;
  }

  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;
  }

  gfx::PointF LastSentGestureLocation() const {
    return gfx::PointF(last_sent_gesture().x, last_sent_gesture().y);
  }

  gfx::PointF LastSentGestureRawLocation() const {
    return gfx::PointF(last_sent_gesture().raw_x, last_sent_gesture().raw_y);
  }

  int LastSentGestureFlags() const {
    return last_sent_gesture().flags;
  }

  const gfx::Rect& ShowPressBoundingBox() const {
    return show_press_bounding_box_;
  }

  void SetCancelAfterNextGesture(bool cancel_after_next_gesture) {
    cancel_after_next_gesture_ = cancel_after_next_gesture;
  }

  GestureEventData CreateGesture(EventType type) {
    return CreateGesture(type, 0, 0, 0);
  }

  GestureEventData CreateGesture(EventType type,
                                 float x,
                                 float y,
                                 float diameter) {
    GestureEventDetails details(type);
    details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
    return GestureEventData(
        details, 0, MotionEvent::TOOL_TYPE_FINGER, base::TimeTicks(), x, y, 0,
        0, 1,
        gfx::RectF(x - diameter / 2, y - diameter / 2, diameter, diameter),
        kDefaultEventFlags, 0U);
  }

 private:
  std::unique_ptr<TouchDispositionGestureFilter> queue_;
  bool cancel_after_next_gesture_;
  MockMotionEvent touch_event_;
  GestureEventDataPacket pending_gesture_packet_;
  size_t sent_gesture_count_;
  GestureList sent_gestures_;
  gfx::Vector2dF raw_offset_;
  std::unique_ptr<GestureEventData> last_sent_gesture_;
  gfx::Rect show_press_bounding_box_;
  uint32_t last_sent_touch_event_id_;
};

TEST_F(TouchDispositionGestureFilterTest, BasicNoGestures) {
  uint32_t touch_press_event_id = PressTouchPoint().GetUniqueEventId();
  EXPECT_FALSE(GesturesSent());

  uint32_t touch_move_event_id = MoveTouchPoint().GetUniqueEventId();
  EXPECT_FALSE(GesturesSent());

  // No gestures should be dispatched by the ack, as the queued packets
  // contained no gestures.
  SendTouchConsumedAck(touch_press_event_id);
  EXPECT_FALSE(GesturesSent());

  // Release the touch gesture.
  uint32_t touch_release_event_id = ReleaseTouchPoint().GetUniqueEventId();
  SendTouchConsumedAck(touch_move_event_id);
  SendTouchConsumedAck(touch_release_event_id);
  EXPECT_FALSE(GesturesSent());
}

TEST_F(TouchDispositionGestureFilterTest, BasicGestures) {
  GestureList press_gestures =
      Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN);
  // An unconsumed touch's gesture should be sent.
  SendPacket(PressTouchPoint(), press_gestures);
  EXPECT_FALSE(GesturesSent());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(press_gestures, GetAndResetSentGestures()));

  // Multiple gestures can be queued for a single event.
  GestureList release_gestures =
      Gestures(ET_SCROLL_FLING_START, ET_SCROLL_FLING_CANCEL, ET_GESTURE_END);
  SendPacket(ReleaseTouchPoint(), release_gestures);
  EXPECT_FALSE(GesturesSent());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(release_gestures, GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, BasicGesturesConsumed) {
  // A consumed touch's gesture should not be sent.
  SendPacket(PressTouchPoint(),
             Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(
      ReleaseTouchPoint(),
      Gestures(ET_SCROLL_FLING_START, ET_SCROLL_FLING_CANCEL, ET_GESTURE_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());
}

TEST_F(TouchDispositionGestureFilterTest, ConsumedThenNotConsumed) {
  // A consumed touch's gesture should not be sent.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  // Even if the subsequent touch is not consumed, continue dropping gestures.
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  // Even if the subsequent touch had no consumer, continue dropping gestures.
  SendPacket(ReleaseTouchPoint(), Gestures(ET_SCROLL_FLING_START));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());
}

TEST_F(TouchDispositionGestureFilterTest, NotConsumedThenConsumed) {
  // A not consumed touch's gesture should be sent.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));

  // A newly consumed gesture should not be sent.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_PINCH_BEGIN));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  // And subsequent non-consumed pinch updates should not be sent.
  SendPacket(MoveTouchPoint(),
             Gestures(ET_GESTURE_SCROLL_UPDATE, ET_GESTURE_PINCH_UPDATE));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_UPDATE),
                            GetAndResetSentGestures()));

  // End events dispatched only when their start events were.
  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_PINCH_END));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_SCROLL_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, ScrollAlternatelyConsumed) {
  // A consumed touch's gesture should not be sent.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));

  for (size_t i = 0; i < 3; ++i) {
    SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
    SendTouchConsumedAckForLastTouch();
    EXPECT_FALSE(GesturesSent());

    SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
    SendTouchNotConsumedAckForLastTouch();
    EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_UPDATE),
                              GetAndResetSentGestures()));
  }

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_SCROLL_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, NotConsumedThenNoConsumer) {
  // An unconsumed touch's gesture should be sent.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  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.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_PINCH_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
                            GetAndResetSentGestures()));

  // End events should be dispatched when their start events were, independent
  // of the ack state.
  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_PINCH_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_END),
                            GetAndResetSentGestures()));

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_SCROLL_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, EndingEventsSent) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));

  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_PINCH_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
                            GetAndResetSentGestures()));

  // Consuming the touchend event can't suppress the match end gesture.
  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_PINCH_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_END),
                            GetAndResetSentGestures()));

  // But other events in the same packet are still suppressed.
  SendPacket(ReleaseTouchPoint(),
             Gestures(ET_GESTURE_SCROLL_UPDATE, ET_GESTURE_SCROLL_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
                            GetAndResetSentGestures()));

  // ET_GESTURE_SCROLL_END and ET_SCROLL_FLING_START behave the same in this
  // regard.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));

  SendPacket(ReleaseTouchPoint(), Gestures(ET_SCROLL_FLING_START));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_SCROLL_FLING_START),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, EndingEventsNotSent) {
  // Consuming a begin event ensures no end events are sent.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_PINCH_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_PINCH_END));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_SCROLL_END));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());
}

TEST_F(TouchDispositionGestureFilterTest, UpdateEventsSuppressedPerEvent) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));

  // Consuming a single scroll or pinch update should suppress only that event.
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_PINCH_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
                            GetAndResetSentGestures()));

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_PINCH_UPDATE));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  // Subsequent updates should not be affected.
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_UPDATE),
                            GetAndResetSentGestures()));

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_PINCH_UPDATE));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_UPDATE),
                            GetAndResetSentGestures()));

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_PINCH_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_END),
                            GetAndResetSentGestures()));

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_SCROLL_END));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, UpdateEventsDependOnBeginEvents) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  // Scroll and pinch gestures depend on the scroll begin gesture being
  // dispatched.
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_PINCH_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_PINCH_UPDATE));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_PINCH_END));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_SCROLL_END));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());
}

TEST_F(TouchDispositionGestureFilterTest, MultipleTouchSequences) {
  // Queue two touch-to-gestures sequences.
  uint32_t touch_press_event_id1 =
      SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  uint32_t touch_release_event_id1 =
      SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_TAP));
  uint32_t touch_press_event_id2 =
      SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  uint32_t touch_release_event_id2 =
      SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_SCROLL_END));

  // The first gesture sequence should not be allowed.
  SendTouchConsumedAck(touch_press_event_id1);
  SendTouchNotConsumedAck(touch_release_event_id1);
  EXPECT_FALSE(GesturesSent());

  // The subsequent sequence should "reset" allowance.
  SendTouchNotConsumedAck(touch_press_event_id2);
  SendTouchNotConsumedAck(touch_release_event_id2);
  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.
  SendPacket(PressTouchPoint(),
             Gestures(ET_GESTURE_TAP_DOWN, ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(
      Gestures(
          ET_GESTURE_TAP_DOWN, ET_GESTURE_TAP_CANCEL, ET_GESTURE_SCROLL_BEGIN),
      GetAndResetSentGestures()));
  SendPacket(ReleaseTouchPoint(), Gestures(ET_SCROLL_FLING_START));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_SCROLL_FLING_START),
                            GetAndResetSentGestures()));

  // A new touch sequence should cancel the outstanding fling.
  SendPacket(PressTouchPoint(1, 1), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  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);
  SendPacket(ReleaseTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());
}

TEST_F(TouchDispositionGestureFilterTest, ScrollEndedOnTouchReleaseIfNoFling) {
  // Simulate a scroll.
  // Touch position will be used for synthesized scroll end gesture.
  SendPacket(PressTouchPoint(2, 3),
             Gestures(ET_GESTURE_TAP_DOWN, ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(
      Gestures(
          ET_GESTURE_TAP_DOWN, ET_GESTURE_TAP_CANCEL, ET_GESTURE_SCROLL_BEGIN),
      GetAndResetSentGestures()));
  SendPacket(ReleaseTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END),
                            GetAndResetSentGestures()));
  EXPECT_EQ(CurrentTouchTime(), LastSentGestureTime());
  EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(2, 3));
}

TEST_F(TouchDispositionGestureFilterTest, ScrollEndedOnNewTouchSequence) {
  // Simulate a scroll.
  SendPacket(PressTouchPoint(),
             Gestures(ET_GESTURE_TAP_DOWN, ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  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();

  // Touch position will be used for synthesized scroll end gesture.
  SendPacket(PressTouchPoint(2, 3), NoGestures());
  SendTouchConsumedAckForLastTouch();
  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.
  SendPacket(PressTouchPoint(),
             Gestures(ET_GESTURE_TAP_DOWN, ET_GESTURE_SCROLL_BEGIN,
                      ET_SCROLL_FLING_START));
  SendTouchNotConsumedAckForLastTouch();
  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.
  SendPacket(ReleaseTouchPoint(),
             Gestures(ET_GESTURE_SCROLL_BEGIN, ET_SCROLL_FLING_START));
  SendTouchNotConsumedAckForLastTouch();
  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.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  SendPacket(MoveTouchPoint(), Gestures(ET_SCROLL_FLING_START));
  SendTouchNotConsumedAckForLastTouch();
  GestureEventDataPacket packet;
  packet.Push(CreateGesture(ET_SCROLL_FLING_CANCEL, 2, 3, 0));
  SendTouchGestures(ReleaseTouchPoint(), packet);
  SendTouchNotConsumedAckForLastTouch();
  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.
  SendPacket(PressTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  SendPacket(ReleaseTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());
}

TEST_F(TouchDispositionGestureFilterTest, TapCancelledWhenScrollBegins) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
                            GetAndResetSentGestures()));

  // If the subsequent touch turns into a scroll, the tap should be cancelled.
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL,
                                     ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, TapCancelledWhenTouchConsumed) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
                            GetAndResetSentGestures()));

  // If the subsequent touch is consumed, the tap should be cancelled.
  GestureEventDataPacket packet;
  packet.Push(CreateGesture(ET_GESTURE_SCROLL_BEGIN, 2, 3, 0));
  SendTouchGestures(MoveTouchPoint(), packet);

  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(
      GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL, ET_GESTURE_SCROLL_BEGIN),
                    GetAndResetSentGestures()));
  EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(2, 3));
}

TEST_F(TouchDispositionGestureFilterTest,
       TapNotCancelledIfTapEndingEventReceived) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(
      GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN), GetAndResetSentGestures()));

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_TAP));
  SendTouchNotConsumedAckForLastTouch();
  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|.
  SendPacket(PressTouchPoint(), NoGestures());
  SendTouchConsumedAckForLastTouch();
  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.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  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()));

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_LONG_TAP));
  SendTouchNotConsumedAckForLastTouch();
  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.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendTimeoutGesture(ET_GESTURE_SHOW_PRESS);
  EXPECT_FALSE(GesturesSent());
  SendPacket(ReleaseTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();

  // If the sequence has a pending ack, the timeout gestures should
  // remain queued until the ack is received.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  EXPECT_FALSE(GesturesSent());

  SendTimeoutGesture(ET_GESTURE_LONG_PRESS);
  EXPECT_FALSE(GesturesSent());

  SendTouchNotConsumedAckForLastTouch();
  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(0);
  EXPECT_FALSE(GesturesSent());

  uint32_t touch_press_event_id =
      SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  uint32_t touch_move_event_id =
      SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAck(touch_press_event_id);
  SendTouchNotConsumedAck(touch_move_event_id);
  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(0);
  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.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  EXPECT_FALSE(GesturesSent());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
                            GetAndResetSentGestures()));

  SendPacket(CancelTouchPoint(),
             Gestures(ET_GESTURE_TAP_CANCEL, ET_GESTURE_SCROLL_END));
  EXPECT_FALSE(GesturesSent());
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL,
                                     ET_GESTURE_SCROLL_END),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, TimeoutEventAfterRelease) {
  SendPacket(PressTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());
  SendPacket(ReleaseTouchPoint(),
             Gestures(ET_GESTURE_TAP_DOWN, ET_GESTURE_TAP_UNCONFIRMED));
  SendTouchNotConsumedAckForLastTouch();
  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) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
                            GetAndResetSentGestures()));

  SendTimeoutGesture(ET_GESTURE_TAP_UNCONFIRMED);
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_UNCONFIRMED),
                            GetAndResetSentGestures()));

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_TAP));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SHOW_PRESS,
                                     ET_GESTURE_TAP),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, ShowPressNotInsertedIfAlreadySent) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
                            GetAndResetSentGestures()));

  SendTimeoutGesture(ET_GESTURE_SHOW_PRESS);
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SHOW_PRESS),
                            GetAndResetSentGestures()));

  SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_TAP));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, TapAndScrollCancelledOnTouchCancel) {
  const gfx::Vector2dF raw_offset(1.3f, 3.7f);
  SetRawTouchOffset(raw_offset);

  SendPacket(PressTouchPoint(1, 1), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
                            GetAndResetSentGestures()));

  // A cancellation motion event should cancel the tap.
  SendPacket(CancelTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  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);

  SendPacket(PressTouchPoint(1, 1), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));

  // A cancellation motion event should end the scroll, even if the touch was
  // consumed.
  SendPacket(CancelTouchPoint(), NoGestures());
  SendTouchConsumedAckForLastTouch();
  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.
  SendPacket(PressTouchPoint(),
             Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();

  EXPECT_TRUE(
      GesturesMatch(Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN),
                    GetAndResetSentGestures()));

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  GestureEventDataPacket packet;
  packet.Push(CreateGesture(ET_SCROLL_FLING_START));
  packet.Push(CreateGesture(ET_SCROLL_FLING_CANCEL));
  packet.Push(CreateGesture(ET_GESTURE_END, 2, 3, 0));
  SendTouchGestures(ReleaseTouchPoint(), packet);

  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_END, ET_GESTURE_END),
                            GetAndResetSentGestures()));
  EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(2, 3));

  SendPacket(PressTouchPoint(),
             Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, TapCancelledOnTouchCancel) {
  // Touch position is used for synthesized tap cancel.
  SendPacket(PressTouchPoint(2, 3), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
                            GetAndResetSentGestures()));

  // A cancellation motion event should cancel the tap.
  SendPacket(CancelTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL),
                            GetAndResetSentGestures()));
  EXPECT_EQ(CurrentTouchTime(), LastSentGestureTime());
  EXPECT_EQ(LastSentGestureLocation(), gfx::PointF(2, 3));
}

// Test that a GestureEvent whose dispatch causes a cancel event to be fired
// won't cause a crash.
TEST_F(TouchDispositionGestureFilterTest, TestCancelMidGesture) {
  SetCancelAfterNextGesture(true);
  // Synthesized tap cancel uses touch position.
  SendPacket(PressTouchPoint(1, 1), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  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) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));

  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_PINCH_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
                            GetAndResetSentGestures()));

  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SWIPE));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SWIPE),
                            GetAndResetSentGestures()));
}

  // Test that a MultiFingerSwipe event is dispatched when appropriate.
TEST_F(TouchDispositionGestureFilterTest, TestDisallowedMultiFingerSwipe) {
  SendPacket(PressTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SCROLL_BEGIN),
                            GetAndResetSentGestures()));

  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_PINCH_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_PINCH_BEGIN),
                            GetAndResetSentGestures()));

  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_SWIPE));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());
}

TEST_F(TouchDispositionGestureFilterTest, TapCancelOnSecondFingerDown) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN),
                            GetAndResetSentGestures()));

  SendPacket(PressTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, ShowPressBoundingBox) {
  GestureEventDataPacket press_packet;
  press_packet.Push(CreateGesture(ET_GESTURE_TAP_DOWN, 9, 9, 8));
  SendTouchGestures(PressTouchPoint(), press_packet);

  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(
      GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN), GetAndResetSentGestures()));

  GestureEventDataPacket release_packet;
  release_packet.Push(CreateGesture(ET_GESTURE_TAP, 10, 10, 10));
  SendTouchGestures(ReleaseTouchPoint(), release_packet);

  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_SHOW_PRESS, ET_GESTURE_TAP),
                            GetAndResetSentGestures()));
  EXPECT_EQ(gfx::Rect(5, 5, 10, 10), ShowPressBoundingBox());
}

TEST_F(TouchDispositionGestureFilterTest, TapCancelledBeforeGestureEnd) {
  SendPacket(PressTouchPoint(),
             Gestures(ET_GESTURE_BEGIN, ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_BEGIN, 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()));
  SendPacket(CancelTouchPoint(), Gestures(ET_GESTURE_END));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL, ET_GESTURE_END),
                            GetAndResetSentGestures()));
}

TEST_F(TouchDispositionGestureFilterTest, EventFlagPropagation) {
  // Real gestures should propagate flags from their causal touches.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_TAP_DOWN));
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(
      GesturesMatch(Gestures(ET_GESTURE_TAP_DOWN), GetAndResetSentGestures()));
  EXPECT_EQ(kDefaultEventFlags, LastSentGestureFlags());

  // Synthetic gestures lack flags.
  SendPacket(PressTouchPoint(), NoGestures());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_TAP_CANCEL),
                            GetAndResetSentGestures()));
  EXPECT_EQ(0, LastSentGestureFlags());
}


TEST_F(TouchDispositionGestureFilterTest, PreviousScrollPrevented) {
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_BEGIN));
  EXPECT_FALSE(GesturesSent());
  SendTouchNotConsumedAckForLastTouch();
  EXPECT_TRUE(
      GesturesMatch(Gestures(ET_GESTURE_BEGIN), GetAndResetSentGestures()));

  // The sent scroll update should always reflect whether any preceding scroll
  // update has been dropped.
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();
  ASSERT_TRUE(GesturesSent());
  EXPECT_FALSE(last_sent_gesture()
                   .details.previous_scroll_update_in_sequence_prevented());
  GetAndResetSentGestures();

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchConsumedAckForLastTouch();
  EXPECT_FALSE(GesturesSent());

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();
  ASSERT_TRUE(GesturesSent());
  EXPECT_TRUE(last_sent_gesture()
                  .details.previous_scroll_update_in_sequence_prevented());
  GetAndResetSentGestures();

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();
  ASSERT_TRUE(GesturesSent());
  EXPECT_TRUE(last_sent_gesture()
                  .details.previous_scroll_update_in_sequence_prevented());
}

TEST_F(TouchDispositionGestureFilterTest, AckQueueBack) {
  SendPacket(PressTouchPoint(1, 1), Gestures(ET_GESTURE_BEGIN));
  SendTouchNotConsumedAckForLastTouch();

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_BEGIN));
  SendTouchNotConsumedAckForLastTouch();
  GetAndResetSentGestures();

  // Pending touch move.
  GestureEventDataPacket packet1;
  packet1.Push(CreateGesture(ET_GESTURE_SCROLL_UPDATE, 2, 3, 0));
  uint32_t touch_event_id = SendTouchGestures(MoveTouchPoint(), packet1);
  EXPECT_FALSE(GesturesSent());

  // Additional pending touch move.
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));

  // Ack back of the queue consumed.
  SendTouchConsumedAckForLastTouch();

  // Ack the pending touch.
  GetAndResetSentGestures();
  SendTouchNotConsumedAck(touch_event_id);

  // The consumed touch doesn't produce a gesture.
  EXPECT_TRUE(GesturesMatch(
      Gestures(ET_GESTURE_SCROLL_UPDATE),
      GetAndResetSentGestures()));
  EXPECT_EQ(gfx::PointF(2, 3), LastSentGestureLocation());

  // Pending touch move.
  touch_event_id =
      SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  EXPECT_FALSE(GesturesSent());

  // Ack back of the queue unconsumed (twice).
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();

  GestureEventDataPacket packet2;
  packet2.Push(CreateGesture(ET_GESTURE_SCROLL_UPDATE, 7, 8, 0));
  SendTouchGestures(MoveTouchPoint(), packet2);
  EXPECT_FALSE(GesturesSent());

  SendTouchNotConsumedAckForLastTouch();

  // Ack the pending touch.
  GetAndResetSentGestures();
  SendTouchNotConsumedAck(touch_event_id);

  // Both touches have now been acked.
  EXPECT_TRUE(
      GesturesMatch(Gestures(ET_GESTURE_SCROLL_UPDATE, ET_GESTURE_SCROLL_UPDATE,
                             ET_GESTURE_SCROLL_UPDATE),
                    GetAndResetSentGestures()));
  EXPECT_EQ(gfx::PointF(7, 8), LastSentGestureLocation());
}

TEST_F(TouchDispositionGestureFilterTest, AckQueueGestureAtBack) {
  // Send gesture sequence
  uint32_t touch_press_event_id1 =
      SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_BEGIN));
  uint32_t touch_release_event_id1 =
      SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_END));

  // Send second gesture sequence, and synchronously ack it.
  SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_BEGIN));
  SendTouchNotConsumedAckForLastTouch();

  GestureEventDataPacket packet;
  packet.Push(CreateGesture(ET_GESTURE_END, 2, 3, 0));
  SendTouchGestures(ReleaseTouchPoint(), packet);
  SendTouchNotConsumedAckForLastTouch();

  // The second gesture sequence is blocked on the first.
  EXPECT_FALSE(GesturesSent());

  SendTouchNotConsumedAck(touch_press_event_id1);
  SendTouchNotConsumedAck(touch_release_event_id1);

  // Both gestures have now been acked.
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_BEGIN, ET_GESTURE_END,
                                     ET_GESTURE_BEGIN, ET_GESTURE_END),
                            GetAndResetSentGestures()));
  EXPECT_EQ(gfx::PointF(2, 3), LastSentGestureLocation());
}

TEST_F(TouchDispositionGestureFilterTest,
       SyncAcksOnlyTriggerAppropriateGestures) {
  // Queue a touch press.
  uint32_t touch_press_event_id =
      SendPacket(PressTouchPoint(), Gestures(ET_GESTURE_BEGIN));

  // Send and synchronously ack two touch moves.
  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();

  SendPacket(MoveTouchPoint(), Gestures(ET_GESTURE_SCROLL_UPDATE));
  SendTouchNotConsumedAckForLastTouch();

  // Queue a touch release.
  uint32_t touch_release_event_id =
      SendPacket(ReleaseTouchPoint(), Gestures(ET_GESTURE_END));

  EXPECT_FALSE(GesturesSent());

  // Ack the touch press. All events but the release should be acked.
  SendTouchNotConsumedAck(touch_press_event_id);
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_BEGIN, ET_GESTURE_SCROLL_UPDATE,
                                     ET_GESTURE_SCROLL_UPDATE),
                            GetAndResetSentGestures()));

  // The touch release still requires an ack.
  SendTouchNotConsumedAck(touch_release_event_id);
  EXPECT_TRUE(GesturesMatch(Gestures(ET_GESTURE_END),
                            GetAndResetSentGestures()));
}

}  // namespace ui
