| Index: content/browser/renderer_host/input/input_queue_unittest.cc
|
| diff --git a/content/browser/renderer_host/input/input_queue_unittest.cc b/content/browser/renderer_host/input/input_queue_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c44b727f34d23da71b02d1b4f707d619fc14be40
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/input/input_queue_unittest.cc
|
| @@ -0,0 +1,379 @@
|
| +// Copyright (c) 2013 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 <vector>
|
| +
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "content/browser/renderer_host/input/event_ack_handler.h"
|
| +#include "content/browser/renderer_host/input/input_queue.h"
|
| +#include "content/browser/renderer_host/input/input_queue_client.h"
|
| +#include "content/common/input/event_packet.h"
|
| +#include "content/common/input/input_event.h"
|
| +#include "content/common/input_messages.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "ui/base/latency_info.h"
|
| +
|
| +namespace content {
|
| +namespace {
|
| +
|
| +using WebKit::WebGestureEvent;
|
| +using WebKit::WebInputEvent;
|
| +using WebKit::WebKeyboardEvent;
|
| +using WebKit::WebMouseEvent;
|
| +using WebKit::WebMouseWheelEvent;
|
| +using WebKit::WebTouchEvent;
|
| +
|
| +class InputQueueTest : public testing::Test,
|
| + public InputQueueClient,
|
| + public EventAckHandler {
|
| + public:
|
| + InputQueueTest()
|
| + : queue_(new InputQueue(this)),
|
| + routing_id_(0),
|
| + num_flush_completions_(0),
|
| + num_flush_requests_(0),
|
| + last_input_id_(0) {
|
| + }
|
| +
|
| + // InputQueueClient
|
| + virtual void Deliver(const EventPacket& packet) OVERRIDE {
|
| + EXPECT_LT(0u, packet.events.size());
|
| + current_packet_.reset(new EventPacket(packet));
|
| + }
|
| +
|
| + virtual void DidFlush() OVERRIDE {
|
| + ++num_flush_completions_;
|
| + }
|
| +
|
| + virtual void SetNeedsFlush() OVERRIDE {
|
| + ++num_flush_requests_;
|
| + }
|
| +
|
| + // EventAckHandler
|
| + virtual void OnInputEventAck(const InputEvent& acked_event) OVERRIDE {
|
| + acked_events_.push_back(acked_event);
|
| + }
|
| +
|
| + virtual void OnInputEventAck(const InputEvent& acked_event,
|
| + EventInjector* injector) OVERRIDE {
|
| + ASSERT_TRUE(injector != NULL);
|
| + acked_events_with_followup_.push_back(acked_event);
|
| + if (event_to_inject_)
|
| + injector->InjectEvent(*event_to_inject_, this);
|
| + }
|
| +
|
| + int num_flush_requests() const { return num_flush_requests_; }
|
| + int num_flush_completions() const { return num_flush_completions_; }
|
| +
|
| + protected:
|
| +
|
| + void QueueEvent(WebInputEvent::Type web_type, bool has_followup = false) {
|
| + scoped_ptr<WebInputEvent> web_event;
|
| + if (WebInputEvent::isMouseEventType(web_type)) {
|
| + web_event.reset(new WebMouseEvent());
|
| + } else if (WebInputEvent::isKeyboardEventType(web_type)) {
|
| + web_event.reset(new WebKeyboardEvent());
|
| + } else if (WebInputEvent::isTouchEventType(web_type)) {
|
| + web_event.reset(new WebTouchEvent());
|
| + } else if (WebInputEvent::isUserGestureEventType(web_type)) {
|
| + web_event.reset(new WebGestureEvent());
|
| + } else {
|
| + web_event.reset(new WebMouseWheelEvent());
|
| + }
|
| + web_event->type = web_type;
|
| +
|
| + InputEventType type = has_followup ? INPUT_EVENT_NEEDS_ACK_AND_HAS_FOLLOWUP
|
| + : INPUT_EVENT_NEEDS_ACK;
|
| +
|
| + QueueEvent(InputMsg_HandleInputEvent(routing_id_,
|
| + web_event.get(),
|
| + ui::LatencyInfo(),
|
| + false), type);
|
| + }
|
| +
|
| + void QueueEvent(const IPC::Message& message, InputEventType type) {
|
| + queue_->QueueEvent(InputEvent(NewInputID(), type, message), this);
|
| + }
|
| +
|
| + bool Flush(InputEventState ack_state) {
|
| + StartFlush();
|
| + return FinishFlush(ack_state);
|
| + }
|
| +
|
| + void StartFlush() {
|
| + acked_events_.clear();
|
| + acked_events_with_followup_.clear();
|
| + current_packet_.reset();
|
| + queue_->FlushEventsInCurrentFrame();
|
| + }
|
| +
|
| + bool FinishFlush(InputEventState ack_state) {
|
| + if (!current_packet_)
|
| + return false;
|
| + for (size_t i = 0; i < current_packet_->events.size(); ++i)
|
| + current_packet_->events[i].state = ack_state;
|
| + bool valid_packet = queue_->OnEventPacketAck(*current_packet_);
|
| + return valid_packet;
|
| + }
|
| +
|
| + int64 NewInputID() {
|
| + return ++last_input_id_;
|
| + }
|
| +
|
| + scoped_ptr<InputQueue> queue_;
|
| +
|
| + int routing_id_;
|
| + scoped_ptr<EventPacket> current_packet_;
|
| + std::vector<InputEvent> acked_events_;
|
| + std::vector<InputEvent> acked_events_with_followup_;
|
| + scoped_ptr<InputEvent> event_to_inject_;
|
| +
|
| + int num_flush_completions_;
|
| + int num_flush_requests_;
|
| + int last_input_id_;
|
| +};
|
| +
|
| +TEST_F(InputQueueTest, SetNeedsFlushOnQueueEvent) {
|
| + EXPECT_EQ(0, num_flush_requests());
|
| +
|
| + QueueEvent(WebInputEvent::MouseDown);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +
|
| + // Additional queued events should not trigger additional flush requests.
|
| + QueueEvent(WebInputEvent::MouseUp);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| + QueueEvent(WebInputEvent::TouchStart);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, NoSetNeedsFlushOnQueueIfFlushing) {
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +
|
| + queue_->FlushEventsInCurrentFrame();
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +
|
| + // Events queued after a flush will not trigger an additional flush request.
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| + QueueEvent(WebInputEvent::GestureScrollEnd);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, SetNeedsFlushAfterDidFlushIfEventsQueued) {
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +
|
| + StartFlush();
|
| +
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +
|
| + // An additional flush request is sent for the event queued after the flush.
|
| + EXPECT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_ABSORBED));
|
| + EXPECT_EQ(1, num_flush_completions());
|
| + EXPECT_EQ(2, num_flush_requests());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, EventPacketSentAfterFlush) {
|
| + EXPECT_EQ(NULL, current_packet_.get());
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + EXPECT_EQ(NULL, current_packet_.get());
|
| + StartFlush();
|
| + EXPECT_TRUE(NULL != current_packet_.get());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, AcksHandledInProperOrder) {
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + QueueEvent(WebInputEvent::GestureScrollEnd);
|
| + QueueEvent(WebInputEvent::GestureFlingStart);
|
| +
|
| + queue_->FlushEventsInCurrentFrame();
|
| + current_packet_->events[0].state = INPUT_EVENT_IMPL_THREAD_ABSORBED;
|
| + current_packet_->events[1].state = INPUT_EVENT_MAIN_THREAD_ABSORBED;
|
| + current_packet_->events[2].state = INPUT_EVENT_MAIN_THREAD_NO_HANDLER_EXISTS;
|
| + queue_->OnEventPacketAck(*current_packet_);
|
| + EXPECT_EQ(1, num_flush_completions());
|
| +
|
| + EXPECT_EQ(acked_events_[0].state, INPUT_EVENT_IMPL_THREAD_ABSORBED);
|
| + EXPECT_EQ(acked_events_[1].state, INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(acked_events_[2].state, INPUT_EVENT_MAIN_THREAD_NO_HANDLER_EXISTS);
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, NullAckHandlerOK) {
|
| + queue_->QueueEvent(
|
| + InputEvent(NewInputID(),
|
| + INPUT_EVENT_ONE_WAY,
|
| + InputMsg_Copy(routing_id_)),
|
| + NULL);
|
| + queue_->QueueEvent(
|
| + InputEvent(NewInputID(),
|
| + INPUT_EVENT_NEEDS_ACK,
|
| + InputMsg_Copy(routing_id_)),
|
| + NULL);
|
| +
|
| + Flush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(0u, acked_events_.size());
|
| + EXPECT_EQ(1, num_flush_completions());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, AckInjectorOnlyIfHasFollowup) {
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + QueueEvent(WebInputEvent::GestureScrollEnd, true);
|
| + QueueEvent(WebInputEvent::GestureFlingStart, true);
|
| +
|
| + Flush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(1u, acked_events_.size());
|
| + EXPECT_EQ(2u, acked_events_with_followup_.size());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, FlushOnEmptyQueueIgnored) {
|
| + Flush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(0, num_flush_requests());
|
| + EXPECT_EQ(0, num_flush_completions());
|
| +
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + Flush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| + EXPECT_EQ(1, num_flush_completions());
|
| +
|
| + Flush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| + EXPECT_EQ(1, num_flush_completions());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, FlushContinuesUntilAllEventsProcessed) {
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + QueueEvent(WebInputEvent::GestureScrollEnd);
|
| + QueueEvent(WebInputEvent::GestureFlingStart);
|
| +
|
| + EXPECT_EQ(1, num_flush_requests());
|
| + Flush(INPUT_EVENT_IMPL_THREAD_COULD_NOT_DELIVER);
|
| + EXPECT_EQ(0, num_flush_completions());
|
| +
|
| + FinishFlush(INPUT_EVENT_MAIN_THREAD_COULD_NOT_DELIVER);
|
| + EXPECT_EQ(0, num_flush_completions());
|
| +
|
| + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED));
|
| + EXPECT_EQ(1, num_flush_completions());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, InvalidPacketAckIgnored) {
|
| + // Packet never flushed, any ack should be ignored.
|
| + bool valid_packet_ack = queue_->OnEventPacketAck(EventPacket());
|
| + EXPECT_FALSE(valid_packet_ack);
|
| +
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + StartFlush();
|
| + // Tamper with the sent packet by adding an extra event.
|
| + current_packet_->events.push_back(
|
| + InputEvent(NewInputID(),
|
| + INPUT_EVENT_NEEDS_ACK,
|
| + InputMsg_Copy(routing_id_)));
|
| + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(0, num_flush_completions());
|
| + EXPECT_FALSE(valid_packet_ack);
|
| +
|
| + // Fix the packet.
|
| + current_packet_->events.pop_back();
|
| + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(1, num_flush_completions());
|
| + EXPECT_TRUE(valid_packet_ack);
|
| +
|
| + // Tamper with the packet by changing an event type.
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + StartFlush();
|
| + current_packet_->events[0].type = INPUT_EVENT_ONE_WAY;
|
| + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(1, num_flush_completions());
|
| + EXPECT_FALSE(valid_packet_ack);
|
| +
|
| + // Fix the packet.
|
| + current_packet_->events[0].type = INPUT_EVENT_NEEDS_ACK;
|
| + valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(2, num_flush_completions());
|
| + EXPECT_TRUE(valid_packet_ack);
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, InjectedEventsAckedBeforeDidFlush) {
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + QueueEvent(WebInputEvent::GestureScrollEnd, true);
|
| +
|
| + event_to_inject_.reset(
|
| + new InputEvent(NewInputID(),
|
| + INPUT_EVENT_ONE_WAY,
|
| + InputMsg_Copy(routing_id_)));
|
| + Flush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(0, num_flush_completions());
|
| +
|
| + // The injected event should now be in the event packet.
|
| + EXPECT_EQ(1u, current_packet_->events.size());
|
| + EXPECT_EQ(1u, acked_events_with_followup_.size());
|
| + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED));
|
| + EXPECT_EQ(1, num_flush_completions());
|
| +
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + QueueEvent(WebInputEvent::GestureScrollEnd, true);
|
| + event_to_inject_.reset(
|
| + new InputEvent(NewInputID(),
|
| + INPUT_EVENT_NEEDS_ACK_AND_HAS_FOLLOWUP,
|
| + InputMsg_Copy(routing_id_)));
|
| + Flush(INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + // |event_to_inject_| should now be in the event packet.
|
| + EXPECT_EQ(1u, acked_events_with_followup_.size());
|
| + EXPECT_EQ(1u, current_packet_->events.size());
|
| + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED));
|
| +
|
| + // |event_to_inject_| should still be in the event packet.
|
| + EXPECT_EQ(2u, acked_events_with_followup_.size());
|
| + EXPECT_EQ(1u, current_packet_->events.size());
|
| + EXPECT_EQ(1, num_flush_completions());
|
| + event_to_inject_.reset();
|
| +
|
| + ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED));
|
| + EXPECT_EQ(2, num_flush_completions());
|
| +}
|
| +
|
| +TEST_F(InputQueueTest, DelayedPacketsBasic) {
|
| + // Ignore drop requests when there is no outstanding packet.
|
| + queue_->OnEventPacketAckDelayed();
|
| + EXPECT_EQ(0, num_flush_requests());
|
| + EXPECT_EQ(0, num_flush_completions());
|
| +
|
| + QueueEvent(WebInputEvent::GestureScrollBegin);
|
| + QueueEvent(WebInputEvent::GestureScrollEnd);
|
| + QueueEvent(WebInputEvent::GestureFlingStart);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +
|
| + // A delayed packet signal should trigger a DidFlush(), but the queue is still
|
| + // waiting for the original ack.
|
| + StartFlush();
|
| + queue_->OnEventPacketAckDelayed();
|
| + EXPECT_EQ(1, num_flush_completions());
|
| + EXPECT_EQ(0U, acked_events_.size());
|
| +
|
| + // An Event queued after the delay will not trigger a flush request; that will
|
| + // happen when the delayed packet is finally ack'ed.
|
| + EXPECT_EQ(1, num_flush_requests());
|
| + QueueEvent(WebInputEvent::GestureFlingStart);
|
| + EXPECT_EQ(1, num_flush_requests());
|
| +
|
| + // Ack'ing the delayed packet should behave normally.
|
| + EXPECT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_ABSORBED));
|
| + EXPECT_EQ(2, num_flush_requests());
|
| + EXPECT_EQ(1, num_flush_completions());
|
| + EXPECT_EQ(acked_events_[0].state, INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(acked_events_[1].state, INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| + EXPECT_EQ(acked_events_[2].state, INPUT_EVENT_MAIN_THREAD_ABSORBED);
|
| +
|
| + // Honor the second flush request.
|
| + EXPECT_TRUE(Flush(INPUT_EVENT_MAIN_THREAD_ABSORBED));
|
| + EXPECT_EQ(2, num_flush_requests());
|
| + EXPECT_EQ(2, num_flush_completions());
|
| + EXPECT_EQ(1U, acked_events_.size());
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace content
|
| +
|
|
|