Chromium Code Reviews| Index: ui/events/event_rewriter_unittest.cc |
| diff --git a/ui/events/event_rewriter_unittest.cc b/ui/events/event_rewriter_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9754552650587dde0aeb86267c85f7f440bd1bc5 |
| --- /dev/null |
| +++ b/ui/events/event_rewriter_unittest.cc |
| @@ -0,0 +1,251 @@ |
| +// Copyright (c) 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 <list> |
| +#include <map> |
| +#include <set> |
| +#include <utility> |
| + |
| +#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 change and test event type. |
| + |
| +// 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() { CheckAllReceived(); } |
|
sadrul
2014/03/24 21:15:38
virtual
|
| + |
| + // EventProcessor: |
| + virtual EventDispatchDetails OnEventFromSource(Event* event) OVERRIDE { |
|
sadrul
2014/03/24 21:15:38
Overrides typically go at the end of the block. So
|
| + EXPECT_FALSE(expected_events_.empty()); |
| + EXPECT_EQ(expected_events_.front(), event->type()); |
| + expected_events_.pop_front(); |
| + return EventDispatchDetails(); |
| + } |
| + |
| + void AddExpectedEvent(EventType type) { expected_events_.push_back(type); } |
| + // Test that all expected events have been received. |
| + void CheckAllReceived() { EXPECT_TRUE(expected_events_.empty()); } |
| + |
| + private: |
| + std::list<EventType> expected_events_; |
|
sadrul
2014/03/24 21:15:38
DISALLOW_COPY_AND_ASSIGN
|
| +}; |
| + |
| +// Allocates events, testing that none are leaked. |
| +class TestEventFactory { |
| + public: |
| + TestEventFactory() {} |
| + ~TestEventFactory() { EXPECT_TRUE(events_.empty()); } |
| + |
| + Event* New(EventType type) { |
|
sadrul
2014/03/24 21:15:38
This should return a scoped_ptr<>
|
| + Event* event = new TestEvent(*this, type); |
| + EXPECT_TRUE(event); |
| + events_.insert(event); |
| + return event; |
| + } |
| + void Check(Event* event) { EXPECT_NE(events_.end(), events_.find(event)); } |
| + void Delete(Event* event) { |
| + EventList::iterator find = events_.find(event); |
| + EXPECT_NE(events_.end(), find); |
| + events_.erase(find); |
| + } |
| + |
| + private: |
| + class TestEvent : public Event { |
| + public: |
| + TestEvent(TestEventFactory& factory, EventType type) |
| + : Event(type, base::TimeDelta(), 0), factory_(factory) {} |
| + ~TestEvent() { factory_.Delete(this); } |
| + |
| + private: |
| + TestEventFactory& factory_; |
| + }; |
| + typedef std::set<Event*> EventList; |
| + EventList events_; |
| + DISALLOW_COPY_AND_ASSIGN(TestEventFactory); |
| +}; |
| + |
| +// Trivial EventSource that does nothing but send events. |
| +class TestEventRewriteSource : public EventSource { |
| + public: |
| + explicit TestEventRewriteSource(EventProcessor* processor) |
| + : processor_(processor) {} |
| + virtual EventProcessor* GetEventProcessor() OVERRIDE { return processor_; } |
| + void Send(EventType type) { |
| + scoped_ptr<Event> event(event_factory_.New(type)); |
| + (void)SendEventToProcessor(event.get()); |
|
sadrul
2014/03/24 21:15:38
You don't need to Delete the event in the event-fa
|
| + } |
| + |
| + private: |
| + TestEventFactory event_factory_; |
| + EventProcessor* processor_; |
| +}; |
| + |
| +// This EventRewriter always returns the same status; it is used to test |
| +// simple rewriting, and rewriter addition, removal, and sequencing. |
| +// EVENT_REWRITE_DISPATCH_ANOTHER is not supported here. |
| +class TestConstantEventRewriter : public EventRewriter { |
| + public: |
| + TestConstantEventRewriter(EventRewriteStatus status, EventType type) |
| + : status_(status), type_(type) { |
| + EXPECT_NE(EVENT_REWRITE_DISPATCH_ANOTHER, status); |
|
sadrul
2014/03/24 21:15:38
CHECK instead
|
| + } |
| + |
| + virtual EventRewriteStatus RewriteEvent(const Event& event, |
| + Event** rewritten_event) OVERRIDE { |
| + EXPECT_TRUE(rewritten_event); |
| + if (status_ == EVENT_REWRITE_REWRITTEN) { |
| + *rewritten_event = event_factory_.New(type_); |
| + } |
| + return status_; |
| + } |
| + virtual EventRewriteStatus NextDispatchEvent(const Event& last_event, |
| + Event** new_event) OVERRIDE { |
| + EXPECT_TRUE(false); |
|
sadrul
2014/03/24 21:15:38
Replace this with a NOTREACHED instead
|
| + return status_; |
| + } |
| + |
| + private: |
| + EventRewriteStatus status_; |
| + EventType type_; |
| + TestEventFactory event_factory_; |
| +}; |
| + |
| +// This EventRewriter runs a simple state machine; it is used to test |
| +// EVENT_REWRITE_DISPATCH_ANOTHER. |
| +class TestStateMachineEventRewriter : public EventRewriter { |
| + public: |
| + TestStateMachineEventRewriter() : state_(0), last_rewritten_event_(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, RewriteResult>( |
| + RewriteCase(from_state, from_type), r)); |
| + } |
| + virtual EventRewriteStatus RewriteEvent(const Event& event, |
| + Event** rewritten_event) OVERRIDE { |
| + EXPECT_TRUE(rewritten_event); |
| + 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_ = event_factory_.New(find->second.type); |
| + *rewritten_event = last_rewritten_event_; |
| + } |
| + state_ = find->second.state; |
| + return find->second.status; |
| + } |
| + virtual EventRewriteStatus NextDispatchEvent(const Event& last_event, |
| + Event** new_event) OVERRIDE { |
| + EXPECT_TRUE(last_rewritten_event_); |
| + EXPECT_EQ(last_rewritten_event_->type(), last_event.type()); |
| + event_factory_.Check(last_rewritten_event_); |
|
sadrul
2014/03/24 21:15:38
We should add a unique-identifer for each TestEven
kpschoedel
2014/03/25 18:12:01
Is it really necessary to pass back last_event? If
sadrul
2014/03/25 18:24:35
I am thinking the rewriter will need to maintain l
|
| + last_rewritten_event_ = 0; |
| + return RewriteEvent(last_event, new_event); |
| + } |
| + |
| + private: |
| + typedef std::pair<int, EventType> RewriteCase; |
| + struct RewriteResult { |
| + int state; |
| + EventType type; |
| + EventRewriteStatus status; |
| + }; |
| + typedef std::map<RewriteCase, RewriteResult> RewriteRules; |
| + |
| + int state_; |
| + RewriteRules rules_; |
| + TestEventFactory event_factory_; |
| + Event* last_rewritten_event_; |
| +}; |
| + |
| +} // 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 |