Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(283)

Unified Diff: content/browser/renderer_host/input/input_queue_unittest.cc

Issue 20356003: Provided batched input delivery with a BufferedInputRouter (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: BufferedInputRouter unit tests Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
+

Powered by Google App Engine
This is Rietveld 408576698