1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/chromeos/touch_exploration_controller.h"
7 #include "base/time/time.h"
8 #include "ui/aura/client/cursor_client.h"
9 #include "ui/aura/test/aura_test_base.h"
10 #include "ui/aura/test/event_generator.h"
11 #include "ui/aura/test/test_cursor_client.h"
12 #include "ui/aura/window.h"
13 #include "ui/events/event.h"
14 #include "ui/events/event_utils.h"
15 #include "ui/gfx/geometry/point.h"
16 #include "ui/gl/gl_implementation.h"
17 #include "ui/gl/gl_surface.h"
22 // Records all mouse and touch events.
23 class EventCapturer
: public ui::EventHandler
{
26 virtual ~EventCapturer() {}
32 virtual void OnEvent(ui::Event
* event
) OVERRIDE
{
33 if (event
->IsMouseEvent()) {
35 new ui::MouseEvent(static_cast<ui::MouseEvent
&>(*event
)));
36 } else if (event
->IsTouchEvent()) {
38 new ui::TouchEvent(static_cast<ui::TouchEvent
&>(*event
)));
42 // Stop event propagation so we don't click on random stuff that
43 // might break test assumptions.
44 event
->StopPropagation();
45 // If there is a possibility that we're in an infinite loop, we should
46 // exit early with a sensible error rather than letting the test time out.
47 ASSERT_LT(events_
.size(), 100u);
49 const ScopedVector
<ui::LocatedEvent
>& captured_events() const {
54 ScopedVector
<ui::LocatedEvent
> events_
;
56 DISALLOW_COPY_AND_ASSIGN(EventCapturer
);
61 class TouchExplorationTest
: public aura::test::AuraTestBase
{
63 TouchExplorationTest() {}
64 virtual ~TouchExplorationTest() {}
66 virtual void SetUp() OVERRIDE
{
67 if (gfx::GetGLImplementation() == gfx::kGLImplementationNone
)
68 gfx::GLSurface::InitializeOneOffForTests();
69 aura::test::AuraTestBase::SetUp();
70 cursor_client_
.reset(new aura::test::TestCursorClient(root_window()));
71 root_window()->AddPreTargetHandler(&event_capturer_
);
74 virtual void TearDown() OVERRIDE
{
75 root_window()->RemovePreTargetHandler(&event_capturer_
);
76 SwitchTouchExplorationMode(false);
77 cursor_client_
.reset();
78 aura::test::AuraTestBase::TearDown();
81 const ScopedVector
<ui::LocatedEvent
>& GetCapturedEvents() {
82 return event_capturer_
.captured_events();
85 void ClearCapturedEvents() {
86 event_capturer_
.Reset();
90 aura::client::CursorClient
* cursor_client() { return cursor_client_
.get(); }
92 void SwitchTouchExplorationMode(bool on
) {
93 if (!on
&& touch_exploration_controller_
.get())
94 touch_exploration_controller_
.reset();
95 else if (on
&& !touch_exploration_controller_
.get())
96 touch_exploration_controller_
.reset(
97 new ui::TouchExplorationController(root_window()));
100 bool IsInTouchToMouseMode() {
101 aura::client::CursorClient
* cursor_client
=
102 aura::client::GetCursorClient(root_window());
103 return cursor_client
&&
104 cursor_client
->IsMouseEventsEnabled() &&
105 !cursor_client
->IsCursorVisible();
109 EventCapturer event_capturer_
;
110 scoped_ptr
<ui::TouchExplorationController
> touch_exploration_controller_
;
111 scoped_ptr
<aura::test::TestCursorClient
> cursor_client_
;
113 DISALLOW_COPY_AND_ASSIGN(TouchExplorationTest
);
116 // Executes a number of assertions to confirm that |e1| and |e2| are touch
117 // events and are equal to each other.
118 void ConfirmEventsAreTouchAndEqual(ui::Event
* e1
, ui::Event
* e2
) {
119 ASSERT_TRUE(e1
->IsTouchEvent());
120 ASSERT_TRUE(e2
->IsTouchEvent());
121 ui::TouchEvent
* touch_event1
= static_cast<ui::TouchEvent
*>(e1
);
122 ui::TouchEvent
* touch_event2
= static_cast<ui::TouchEvent
*>(e2
);
123 EXPECT_EQ(touch_event1
->type(), touch_event2
->type());
124 EXPECT_EQ(touch_event1
->location(), touch_event2
->location());
125 EXPECT_EQ(touch_event1
->touch_id(), touch_event2
->touch_id());
126 EXPECT_EQ(touch_event1
->flags(), touch_event2
->flags());
127 EXPECT_EQ(touch_event1
->time_stamp(), touch_event2
->time_stamp());
130 // Executes a number of assertions to confirm that |e1| and |e2| are mouse
131 // events and are equal to each other.
132 void ConfirmEventsAreMouseAndEqual(ui::Event
* e1
, ui::Event
* e2
) {
133 ASSERT_TRUE(e1
->IsMouseEvent());
134 ASSERT_TRUE(e2
->IsMouseEvent());
135 ui::MouseEvent
* mouse_event1
= static_cast<ui::MouseEvent
*>(e1
);
136 ui::MouseEvent
* mouse_event2
= static_cast<ui::MouseEvent
*>(e2
);
137 EXPECT_EQ(mouse_event1
->type(), mouse_event2
->type());
138 EXPECT_EQ(mouse_event1
->location(), mouse_event2
->location());
139 EXPECT_EQ(mouse_event1
->root_location(), mouse_event2
->root_location());
140 EXPECT_EQ(mouse_event1
->flags(), mouse_event2
->flags());
143 #define CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(e1, e2) \
144 ASSERT_NO_FATAL_FAILURE(ConfirmEventsAreTouchAndEqual(e1, e2))
146 #define CONFIRM_EVENTS_ARE_MOUSE_AND_EQUAL(e1, e2) \
147 ASSERT_NO_FATAL_FAILURE(ConfirmEventsAreMouseAndEqual(e1, e2))
149 // TODO(mfomitchev): Need to investigate why we don't get mouse enter/exit
150 // events when running these tests as part of ui_unittests. We do get them when
151 // the tests are run as part of ash unit tests.
153 // Simple test to confirm one-finger touches are transformed into mouse moves.
154 TEST_F(TouchExplorationTest
, OneFingerTouch
) {
155 SwitchTouchExplorationMode(true);
156 cursor_client()->ShowCursor();
157 cursor_client()->DisableMouseEvents();
158 aura::test::EventGenerator
generator(root_window());
159 gfx::Point location_start
= generator
.current_location();
160 gfx::Point
location_end(11, 12);
161 generator
.PressTouch();
162 EXPECT_TRUE(IsInTouchToMouseMode());
163 generator
.MoveTouch(location_end
);
164 // Confirm the actual mouse moves are unaffected.
165 ui::MouseEvent
mouse_move(ui::ET_MOUSE_MOVED
,
170 generator
.Dispatch(&mouse_move
);
171 generator
.ReleaseTouch();
173 const ScopedVector
<ui::LocatedEvent
>& captured_events
= GetCapturedEvents();
174 ScopedVector
<ui::LocatedEvent
>::const_iterator it
;
175 // TODO(mfomitchev): mouse enter/exit events
176 int num_mouse_moves
= 0;
177 for (it
= captured_events
.begin(); it
!= captured_events
.end(); ++it
) {
178 int type
= (*it
)->type();
179 // Ignore enter and exit mouse events synthesized when the mouse cursor is
181 if (type
== ui::ET_MOUSE_ENTERED
|| type
== ui::ET_MOUSE_EXITED
)
183 EXPECT_EQ(ui::ET_MOUSE_MOVED
, (*it
)->type());
184 if (num_mouse_moves
== 0)
185 EXPECT_EQ(location_start
, (*it
)->location());
186 if (num_mouse_moves
== 1 || num_mouse_moves
== 3)
187 EXPECT_EQ(location_end
, (*it
)->location());
188 if (num_mouse_moves
== 2)
189 CONFIRM_EVENTS_ARE_MOUSE_AND_EQUAL(*it
, &mouse_move
);
190 if (num_mouse_moves
!= 2) {
191 EXPECT_TRUE((*it
)->flags() & ui::EF_IS_SYNTHESIZED
);
192 EXPECT_TRUE((*it
)->flags() & ui::EF_TOUCH_ACCESSIBILITY
);
196 EXPECT_EQ(4, num_mouse_moves
);
199 // Turn the touch exploration mode on in the middle of the touch gesture.
200 // Confirm that events from the finger which was touching when the mode was
201 // turned on don't get rewritten.
202 TEST_F(TouchExplorationTest
, TurnOnMidTouch
) {
203 SwitchTouchExplorationMode(false);
204 cursor_client()->ShowCursor();
205 cursor_client()->DisableMouseEvents();
206 aura::test::EventGenerator
generator(root_window());
207 generator
.PressTouchId(1);
208 EXPECT_TRUE(cursor_client()->IsCursorVisible());
209 EXPECT_FALSE(cursor_client()->IsMouseEventsEnabled());
210 ClearCapturedEvents();
212 // Enable touch exploration mode while the first finger is touching the
213 // screen. Ensure that subsequent events from that first finger are not
214 // affected by the touch exploration mode, while the touch events from another
215 // finger get rewritten.
216 SwitchTouchExplorationMode(true);
217 ui::TouchEvent
touch_move(ui::ET_TOUCH_MOVED
,
220 ui::EventTimeForNow());
221 generator
.Dispatch(&touch_move
);
222 EXPECT_TRUE(cursor_client()->IsCursorVisible());
223 EXPECT_FALSE(cursor_client()->IsMouseEventsEnabled());
224 const ScopedVector
<ui::LocatedEvent
>& captured_events
= GetCapturedEvents();
225 ASSERT_EQ(1u, captured_events
.size());
226 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[0], &touch_move
);
227 ClearCapturedEvents();
229 // The press from the second finger should get rewritten.
230 generator
.PressTouchId(2);
231 EXPECT_TRUE(IsInTouchToMouseMode());
232 // TODO(mfomitchev): mouse enter/exit events
233 ScopedVector
<ui::LocatedEvent
>::const_iterator it
;
234 for (it
= captured_events
.begin(); it
!= captured_events
.end(); ++it
) {
235 if ((*it
)->type() == ui::ET_MOUSE_MOVED
)
238 EXPECT_NE(captured_events
.end(), it
);
239 ClearCapturedEvents();
241 // The release of the first finger shouldn't be affected.
242 ui::TouchEvent
touch_release(ui::ET_TOUCH_RELEASED
,
245 ui::EventTimeForNow());
246 generator
.Dispatch(&touch_release
);
247 ASSERT_EQ(1u, captured_events
.size());
248 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[0], &touch_release
);
249 ClearCapturedEvents();
251 // The move and release from the second finger should get rewritten.
252 generator
.MoveTouchId(gfx::Point(13, 14), 2);
253 generator
.ReleaseTouchId(2);
254 ASSERT_EQ(2u, captured_events
.size());
255 EXPECT_EQ(ui::ET_MOUSE_MOVED
, captured_events
[0]->type());
256 EXPECT_EQ(ui::ET_MOUSE_MOVED
, captured_events
[1]->type());
259 TEST_F(TouchExplorationTest
, TwoFingerTouch
) {
260 SwitchTouchExplorationMode(true);
261 aura::test::EventGenerator
generator(root_window());
262 generator
.PressTouchId(1);
263 ClearCapturedEvents();
265 // Confirm events from the second finger go through as is.
266 cursor_client()->ShowCursor();
267 cursor_client()->DisableMouseEvents();
268 ui::TouchEvent
touch_press(ui::ET_TOUCH_PRESSED
,
271 ui::EventTimeForNow());
272 generator
.Dispatch(&touch_press
);
273 EXPECT_TRUE(cursor_client()->IsCursorVisible());
274 EXPECT_FALSE(cursor_client()->IsMouseEventsEnabled());
275 const ScopedVector
<ui::LocatedEvent
>& captured_events
= GetCapturedEvents();
276 // TODO(mfomitchev): mouse enter/exit events
277 // There will be a ET_MOUSE_EXITED event synthesized when the mouse cursor is
278 // hidden - ignore it.
279 ScopedVector
<ui::LocatedEvent
>::const_iterator it
;
280 for (it
= captured_events
.begin(); it
!= captured_events
.end(); ++it
) {
281 if ((*it
)->type() == ui::ET_TOUCH_PRESSED
) {
282 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(*it
, &touch_press
);
286 EXPECT_NE(captured_events
.end(), it
);
287 ClearCapturedEvents();
288 ui::TouchEvent
touch_move(ui::ET_TOUCH_MOVED
,
291 ui::EventTimeForNow());
292 generator
.Dispatch(&touch_move
);
293 ASSERT_EQ(1u, captured_events
.size());
294 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[0], &touch_move
);
295 ClearCapturedEvents();
297 // Confirm mouse moves go through unaffected.
298 ui::MouseEvent
mouse_move(ui::ET_MOUSE_MOVED
,
303 generator
.Dispatch(&mouse_move
);
304 // TODO(mfomitchev): mouse enter/exit events
305 // Ignore synthesized ET_MOUSE_ENTERED/ET_MOUSE_EXITED
306 for (it
= captured_events
.begin(); it
!= captured_events
.end(); ++it
) {
307 if ((*it
)->type() == ui::ET_MOUSE_MOVED
) {
308 CONFIRM_EVENTS_ARE_MOUSE_AND_EQUAL(*it
, &mouse_move
);
312 EXPECT_NE(captured_events
.end(), it
);
313 ClearCapturedEvents();
315 // Have some other fingers touch/move/release
316 generator
.PressTouchId(3);
317 generator
.PressTouchId(4);
318 generator
.MoveTouchId(gfx::Point(30, 31), 3);
319 generator
.ReleaseTouchId(3);
320 generator
.ReleaseTouchId(4);
321 ClearCapturedEvents();
323 // Events from the first finger should not go through while the second finger
325 gfx::Point touch1_location
= gfx::Point(15, 16);
326 generator
.MoveTouchId(touch1_location
, 1);
327 EXPECT_EQ(0u, GetCapturedEvents().size());
329 EXPECT_TRUE(cursor_client()->IsCursorVisible());
330 EXPECT_FALSE(cursor_client()->IsMouseEventsEnabled());
332 // A release of the second finger should go through, plus there should be a
333 // mouse move at |touch1_location| generated.
334 ui::TouchEvent
touch_release(ui::ET_TOUCH_RELEASED
,
337 ui::EventTimeForNow());
338 generator
.Dispatch(&touch_release
);
339 EXPECT_TRUE(IsInTouchToMouseMode());
340 ASSERT_GE(captured_events
.size(), 2u);
341 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[0], &touch_release
);
342 // TODO(mfomitchev): mouse enter/exit events
343 // Ignore synthesized ET_MOUSE_ENTERED/ET_MOUSE_EXITED
344 for (it
= captured_events
.begin(); it
!= captured_events
.end(); ++it
) {
345 if ((*it
)->type() == ui::ET_MOUSE_MOVED
) {
346 EXPECT_EQ(touch1_location
, (*it
)->location());
350 EXPECT_NE(captured_events
.end(), it
);
353 TEST_F(TouchExplorationTest
, MultiFingerTouch
) {
354 SwitchTouchExplorationMode(true);
355 aura::test::EventGenerator
generator(root_window());
356 generator
.PressTouchId(1);
357 generator
.PressTouchId(2);
358 ClearCapturedEvents();
360 // Confirm events from other fingers go through as is.
361 ui::TouchEvent
touch3_press(ui::ET_TOUCH_PRESSED
,
364 ui::EventTimeForNow());
365 ui::TouchEvent
touch3_move1(ui::ET_TOUCH_MOVED
,
368 ui::EventTimeForNow());
369 ui::TouchEvent
touch4_press(ui::ET_TOUCH_PRESSED
,
372 ui::EventTimeForNow());
373 ui::TouchEvent
touch3_move2(ui::ET_TOUCH_MOVED
,
376 ui::EventTimeForNow());
377 ui::TouchEvent
touch4_move(ui::ET_TOUCH_MOVED
,
380 ui::EventTimeForNow());
381 ui::TouchEvent
touch3_release(ui::ET_TOUCH_RELEASED
,
384 ui::EventTimeForNow());
385 ui::TouchEvent
touch4_release(ui::ET_TOUCH_RELEASED
,
388 ui::EventTimeForNow());
389 generator
.Dispatch(&touch3_press
);
390 generator
.Dispatch(&touch3_move1
);
391 generator
.Dispatch(&touch4_press
);
392 generator
.Dispatch(&touch3_move2
);
393 generator
.Dispatch(&touch4_move
);
394 generator
.Dispatch(&touch3_release
);
395 generator
.Dispatch(&touch4_release
);
397 const ScopedVector
<ui::LocatedEvent
>& captured_events
= GetCapturedEvents();
398 ASSERT_EQ(7u, captured_events
.size());
399 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[0], &touch3_press
);
400 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[1], &touch3_move1
);
401 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[2], &touch4_press
);
402 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[3], &touch3_move2
);
403 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[4], &touch4_move
);
404 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[5], &touch3_release
);
405 CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events
[6], &touch4_release
);
408 // Test the case when there are multiple fingers on the screen and the first
409 // finger is released. This should be rewritten as a release of the second
410 // finger. Additionally, if the second finger is the only finger left touching,
411 // we should enter a mouse move mode, and a mouse move event should be
413 TEST_F(TouchExplorationTest
, FirstFingerLifted
) {
414 SwitchTouchExplorationMode(true);
415 aura::test::EventGenerator
generator(root_window());
416 generator
.PressTouchId(1);
417 generator
.PressTouchId(2);
418 gfx::Point
touch2_location(10, 11);
419 generator
.MoveTouchId(touch2_location
, 2);
420 generator
.PressTouchId(3);
421 gfx::Point
touch3_location(20, 21);
422 generator
.MoveTouchId(touch3_location
, 3);
423 ClearCapturedEvents();
425 // Release of finger 1 should be rewritten as a release of finger 2.
426 generator
.ReleaseTouchId(1);
427 const ScopedVector
<ui::LocatedEvent
>& captured_events
= GetCapturedEvents();
428 ASSERT_EQ(1u, captured_events
.size());
429 EXPECT_EQ(ui::ET_TOUCH_RELEASED
, captured_events
[0]->type());
430 ui::TouchEvent
* touch_event
=
431 static_cast<ui::TouchEvent
*>(captured_events
[0]);
432 EXPECT_EQ(2, touch_event
->touch_id());
433 EXPECT_EQ(touch2_location
, touch_event
->location());
434 ClearCapturedEvents();
436 // Release of finger 2 should be rewritten as a release of finger 3, plus
437 // we should enter the mouse move mode and a mouse move event should be
439 cursor_client()->ShowCursor();
440 cursor_client()->DisableMouseEvents();
441 generator
.ReleaseTouchId(2);
442 EXPECT_TRUE(IsInTouchToMouseMode());
443 ASSERT_GE(2u, captured_events
.size());
444 EXPECT_EQ(ui::ET_TOUCH_RELEASED
, captured_events
[0]->type());
445 touch_event
= static_cast<ui::TouchEvent
*>(captured_events
[0]);
446 EXPECT_EQ(3, touch_event
->touch_id());
447 EXPECT_EQ(touch3_location
, touch_event
->location());
448 // TODO(mfomitchev): mouse enter/exit events
449 ScopedVector
<ui::LocatedEvent
>::const_iterator it
;
450 for (it
= captured_events
.begin(); it
!= captured_events
.end(); ++it
) {
451 if ((*it
)->type() == ui::ET_MOUSE_MOVED
) {
452 EXPECT_EQ(touch3_location
, (*it
)->location());
456 EXPECT_NE(captured_events
.end(), it
);