| 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..75abe8570addf937403479f464624acd741fa395
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/input/input_queue_unittest.cc
|
| @@ -0,0 +1,341 @@
|
| +// 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/input_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:
|
| + 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_;
|
| + }
|
| +
|
| + virtual std::vector<InputEvent> OnInputEventAck(
|
| + const InputEvent& acked_event) OVERRIDE {
|
| + std::vector<InputEvent> followup_events;
|
| + switch (acked_event.type) {
|
| + case INPUT_EVENT_NEEDS_ACK:
|
| + acked_events_.push_back(acked_event);
|
| + break;
|
| + case INPUT_EVENT_NEEDS_ACK_AND_HAS_FOLLOWUP:
|
| + acked_events_with_followup_.push_back(acked_event);
|
| + if (event_to_inject_)
|
| + followup_events.push_back(*event_to_inject_);
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| + return followup_events;
|
| + }
|
| +
|
| + 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), NULL);
|
| + }
|
| +
|
| + 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;
|
| + return InputQueue::ACK_OK == queue_->OnEventPacketAck(*current_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, NullAckObserverOK) {
|
| + 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(1u, 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.
|
| + InputQueue::AckResult result = queue_->OnEventPacketAck(EventPacket());
|
| + EXPECT_EQ(InputQueue::ACK_UNEXPECTED, result);
|
| +
|
| + 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_)));
|
| + bool 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());
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace content
|
| +
|
|
|