// 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/event_rewriter.h" #include #include #include #include #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/test/test_event_processor.h" namespace ui { namespace { // To test the handling of |EventRewriter|s through |EventSource|, // we rewrite and test event types. class TestEvent : public Event { public: explicit TestEvent(EventType type) : Event(type, base::TimeDelta(), 0), unique_id_(next_unique_id_++) {} ~TestEvent() override {} int unique_id() const { return unique_id_; } private: static int next_unique_id_; int unique_id_; }; int TestEvent::next_unique_id_ = 0; // TestEventRewriteProcessor is set up with a sequence of event types, // and fails if the events received via OnEventFromSource() do not match // this sequence. These expected event types are consumed on receipt. class TestEventRewriteProcessor : public test::TestEventProcessor { public: TestEventRewriteProcessor() {} ~TestEventRewriteProcessor() override { CheckAllReceived(); } void AddExpectedEvent(EventType type) { expected_events_.push_back(type); } // Test that all expected events have been received. void CheckAllReceived() { EXPECT_TRUE(expected_events_.empty()); } // EventProcessor: EventDispatchDetails OnEventFromSource(Event* event) override { EXPECT_FALSE(expected_events_.empty()); EXPECT_EQ(expected_events_.front(), event->type()); expected_events_.pop_front(); return EventDispatchDetails(); } private: std::list expected_events_; DISALLOW_COPY_AND_ASSIGN(TestEventRewriteProcessor); }; // Trivial EventSource that does nothing but send events. class TestEventRewriteSource : public EventSource { public: explicit TestEventRewriteSource(EventProcessor* processor) : processor_(processor) {} EventProcessor* GetEventProcessor() override { return processor_; } void Send(EventType type) { scoped_ptr event(new TestEvent(type)); (void)SendEventToProcessor(event.get()); } private: EventProcessor* processor_; }; // This EventRewriter always returns the same status, and if rewriting, the // same event type; it is used to test simple rewriting, and rewriter addition, // removal, and sequencing. Consequently EVENT_REWRITE_DISPATCH_ANOTHER is not // supported here (calls to NextDispatchEvent() would continue indefinitely). class TestConstantEventRewriter : public EventRewriter { public: TestConstantEventRewriter(EventRewriteStatus status, EventType type) : status_(status), type_(type) { CHECK_NE(EVENT_REWRITE_DISPATCH_ANOTHER, status); } EventRewriteStatus RewriteEvent(const Event& event, scoped_ptr* rewritten_event) override { if (status_ == EVENT_REWRITE_REWRITTEN) rewritten_event->reset(new TestEvent(type_)); return status_; } EventRewriteStatus NextDispatchEvent(const Event& last_event, scoped_ptr* new_event) override { NOTREACHED(); return status_; } private: EventRewriteStatus status_; EventType type_; }; // This EventRewriter runs a simple state machine; it is used to test // EVENT_REWRITE_DISPATCH_ANOTHER. class TestStateMachineEventRewriter : public EventRewriter { public: TestStateMachineEventRewriter() : last_rewritten_event_(0), state_(0) {} void AddRule(int from_state, EventType from_type, int to_state, EventType to_type, EventRewriteStatus to_status) { RewriteResult r = {to_state, to_type, to_status}; rules_.insert(std::pair( RewriteCase(from_state, from_type), r)); } EventRewriteStatus RewriteEvent(const Event& event, scoped_ptr* rewritten_event) override { RewriteRules::iterator find = rules_.find(RewriteCase(state_, event.type())); if (find == rules_.end()) return EVENT_REWRITE_CONTINUE; if ((find->second.status == EVENT_REWRITE_REWRITTEN) || (find->second.status == EVENT_REWRITE_DISPATCH_ANOTHER)) { last_rewritten_event_ = new TestEvent(find->second.type); rewritten_event->reset(last_rewritten_event_); } else { last_rewritten_event_ = 0; } state_ = find->second.state; return find->second.status; } EventRewriteStatus NextDispatchEvent(const Event& last_event, scoped_ptr* new_event) override { EXPECT_TRUE(last_rewritten_event_); const TestEvent* arg_last = static_cast(&last_event); EXPECT_EQ(last_rewritten_event_->unique_id(), arg_last->unique_id()); const TestEvent* arg_new = static_cast(new_event->get()); EXPECT_FALSE(arg_new && arg_last->unique_id() == arg_new->unique_id()); return RewriteEvent(last_event, new_event); } private: typedef std::pair RewriteCase; struct RewriteResult { int state; EventType type; EventRewriteStatus status; }; typedef std::map RewriteRules; RewriteRules rules_; TestEvent* last_rewritten_event_; int state_; }; } // namespace TEST(EventRewriterTest, EventRewriting) { // TestEventRewriter r0 always rewrites events to ET_CANCEL_MODE; // it is placed at the beginning of the chain and later removed, // to verify that rewriter removal works. TestConstantEventRewriter r0(EVENT_REWRITE_REWRITTEN, ET_CANCEL_MODE); // TestEventRewriter r1 always returns EVENT_REWRITE_CONTINUE; // it is placed at the beginning of the chain to verify that a // later rewriter sees the events. TestConstantEventRewriter r1(EVENT_REWRITE_CONTINUE, ET_UNKNOWN); // TestEventRewriter r2 has a state machine, primarily to test // |EVENT_REWRITE_DISPATCH_ANOTHER|. TestStateMachineEventRewriter r2; // TestEventRewriter r3 always rewrites events to ET_CANCEL_MODE; // it is placed at the end of the chain to verify that previously // rewritten events are not passed further down the chain. TestConstantEventRewriter r3(EVENT_REWRITE_REWRITTEN, ET_CANCEL_MODE); TestEventRewriteProcessor p; TestEventRewriteSource s(&p); s.AddEventRewriter(&r0); s.AddEventRewriter(&r1); s.AddEventRewriter(&r2); // These events should be rewritten by r0 to ET_CANCEL_MODE. p.AddExpectedEvent(ET_CANCEL_MODE); s.Send(ET_MOUSE_DRAGGED); p.AddExpectedEvent(ET_CANCEL_MODE); s.Send(ET_MOUSE_PRESSED); p.CheckAllReceived(); // Remove r0, and verify that it's gone and that events make it through. s.AddEventRewriter(&r3); s.RemoveEventRewriter(&r0); r2.AddRule(0, ET_SCROLL_FLING_START, 0, ET_SCROLL_FLING_CANCEL, EVENT_REWRITE_REWRITTEN); p.AddExpectedEvent(ET_SCROLL_FLING_CANCEL); s.Send(ET_SCROLL_FLING_START); p.CheckAllReceived(); s.RemoveEventRewriter(&r3); // Verify EVENT_REWRITE_DISPATCH_ANOTHER using a state machine // (that happens to be analogous to sticky keys). r2.AddRule(0, ET_KEY_PRESSED, 1, ET_KEY_PRESSED, EVENT_REWRITE_CONTINUE); r2.AddRule(1, ET_MOUSE_PRESSED, 0, ET_MOUSE_PRESSED, EVENT_REWRITE_CONTINUE); r2.AddRule(1, ET_KEY_RELEASED, 2, ET_KEY_RELEASED, EVENT_REWRITE_DISCARD); r2.AddRule(2, ET_MOUSE_RELEASED, 3, ET_MOUSE_RELEASED, EVENT_REWRITE_DISPATCH_ANOTHER); r2.AddRule(3, ET_MOUSE_RELEASED, 0, ET_KEY_RELEASED, EVENT_REWRITE_REWRITTEN); p.AddExpectedEvent(ET_KEY_PRESSED); s.Send(ET_KEY_PRESSED); s.Send(ET_KEY_RELEASED); p.AddExpectedEvent(ET_MOUSE_PRESSED); s.Send(ET_MOUSE_PRESSED); // Removing rewriters r1 and r3 shouldn't affect r2. s.RemoveEventRewriter(&r1); s.RemoveEventRewriter(&r3); // Continue with the state-based rewriting. p.AddExpectedEvent(ET_MOUSE_RELEASED); p.AddExpectedEvent(ET_KEY_RELEASED); s.Send(ET_MOUSE_RELEASED); p.CheckAllReceived(); } } // namespace ui