Merge lp:~elopio/autopilot/no_uinput_side-effects2 into lp:~elopio/autopilot/no_uinput_side-effects

Proposed by Leo Arias
Status: Superseded
Proposed branch: lp:~elopio/autopilot/no_uinput_side-effects2
Merge into: lp:~elopio/autopilot/no_uinput_side-effects
Diff against target: 680 lines (+434/-146)
2 files modified
autopilot/input/_uinput.py (+173/-146)
autopilot/tests/unit/test_input.py (+261/-0)
To merge this branch: bzr merge lp:~elopio/autopilot/no_uinput_side-effects2
Reviewer Review Type Date Requested Status
Autopilot Hackers Pending
Review via email: mp+202414@code.launchpad.net

This proposal supersedes a proposal from 2014-01-21.

This proposal has been superseded by a proposal from 2014-01-21.

Commit message

Not yet ready, just teasing jenkins.

To post a comment you must log in.
425. By Leo Arias

Added coverate for uinput.Touch.

426. By Leo Arias

Create the Touch device with a dummy resolution.

427. By Leo Arias

Fixed pep8.

428. By Leo Arias

Make sure that the keyboard mock will be used.

429. By Leo Arias

updated the pressed keys list.

430. By Leo Arias

Typo.

431. By Leo Arias

    updated the pressed keys list.

432. By Leo Arias

Fixed the release all.

433. By Leo Arias

Added a unit test for the previous bug.

434. By Leo Arias

Added the failing test case.

435. By Leo Arias

Fixed the test with multiple fingers.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'autopilot/input/_uinput.py'
2--- autopilot/input/_uinput.py 2014-01-21 04:44:16 +0000
3+++ autopilot/input/_uinput.py 2014-01-21 09:50:21 +0000
4@@ -20,10 +20,10 @@
5
6 """UInput device drivers."""
7
8+from autopilot import utilities
9 from autopilot.input import Keyboard as KeyboardBase
10 from autopilot.input import Touch as TouchBase
11 from autopilot.input._common import get_center_point
12-from autopilot.utilities import sleep
13 import autopilot.platform
14
15 import logging
16@@ -135,7 +135,7 @@
17 for key in self._sanitise_keys(keys):
18 for key_button in self._get_key_buttons(key):
19 self._device.press(key_button)
20- sleep(delay)
21+ utilities.sleep(delay)
22
23 def release(self, keys, delay=0.1):
24 """Send key release events only.
25@@ -156,7 +156,7 @@
26 for key in reversed(self._sanitise_keys(keys)):
27 for key_button in reversed(self._get_key_buttons(key)):
28 self._device.release(key_button)
29- sleep(delay)
30+ utilities.sleep(delay)
31
32 def press_and_release(self, keys, delay=0.1):
33 """Press and release all items in 'keys'.
34@@ -214,67 +214,16 @@
35 return key_buttons
36
37
38-last_tracking_id = 0
39-
40-
41-def get_next_tracking_id():
42- global last_tracking_id
43- last_tracking_id += 1
44- return last_tracking_id
45-
46-
47+@utilities.deprecated('Touch')
48 def create_touch_device(res_x=None, res_y=None):
49 """Create and return a UInput touch device.
50
51 If res_x and res_y are not specified, they will be queried from the system.
52
53 """
54-
55- if res_x is None or res_y is None:
56- from autopilot.display import Display
57- display = Display.create()
58- # TODO: This calculation needs to become part of the display module:
59- l = r = t = b = 0
60- for screen in range(display.get_num_screens()):
61- geometry = display.get_screen_geometry(screen)
62- if geometry[0] < l:
63- l = geometry[0]
64- if geometry[1] < t:
65- t = geometry[1]
66- if geometry[0] + geometry[2] > r:
67- r = geometry[0] + geometry[2]
68- if geometry[1] + geometry[3] > b:
69- b = geometry[1] + geometry[3]
70- res_x = r - l
71- res_y = b - t
72-
73- # android uses BTN_TOOL_FINGER, whereas desktop uses BTN_TOUCH. I have no
74- # idea why...
75- touch_tool = e.BTN_TOOL_FINGER
76- if autopilot.platform.model() == 'Desktop':
77- touch_tool = e.BTN_TOUCH
78-
79- cap_mt = {
80- e.EV_ABS: [
81- (e.ABS_X, (0, res_x, 0, 0)),
82- (e.ABS_Y, (0, res_y, 0, 0)),
83- (e.ABS_PRESSURE, (0, 65535, 0, 0)),
84- (e.ABS_MT_POSITION_X, (0, res_x, 0, 0)),
85- (e.ABS_MT_POSITION_Y, (0, res_y, 0, 0)),
86- (e.ABS_MT_TOUCH_MAJOR, (0, 30, 0, 0)),
87- (e.ABS_MT_TRACKING_ID, (0, 65535, 0, 0)),
88- (e.ABS_MT_PRESSURE, (0, 255, 0, 0)),
89- (e.ABS_MT_SLOT, (0, 9, 0, 0)),
90- ],
91- e.EV_KEY: [
92- touch_tool,
93- ]
94- }
95-
96- return UInput(cap_mt, name='autopilot-finger', version=0x2,
97- devnode=_get_devnode_path())
98-
99-_touch_device = create_touch_device()
100+ return UInput(events=_get_touch_events(), name='autopilot-finger',
101+ version=0x2, devnode=_get_devnode_path())
102+
103
104 # Multiouch notes:
105 # ----------------
106@@ -319,58 +268,177 @@
107 # about this is that the SLOT refers to a finger number, and the TRACKING_ID
108 # identifies a unique touch for the duration of it's existance.
109
110-_touch_fingers_in_use = []
111-
112-
113-def _get_touch_finger():
114- """Claim a touch finger id for use.
115-
116- :raises: RuntimeError if no more fingers are available.
117-
118- """
119- global _touch_fingers_in_use
120-
121- for i in range(9):
122- if i not in _touch_fingers_in_use:
123- _touch_fingers_in_use.append(i)
124- return i
125- raise RuntimeError("All available fingers have been used already.")
126-
127-
128-def _release_touch_finger(finger_num):
129- """Relase a previously-claimed finger id.
130-
131- :raises: RuntimeError if the finger given was never claimed, or was already
132- released.
133-
134- """
135- global _touch_fingers_in_use
136-
137- if finger_num not in _touch_fingers_in_use:
138- raise RuntimeError(
139- "Finger %d was never claimed, or has already been released." %
140- (finger_num))
141- _touch_fingers_in_use.remove(finger_num)
142- assert(finger_num not in _touch_fingers_in_use)
143+
144+def _get_touch_events(res_x, res_y):
145+ if res_x is None or res_y is None:
146+ res_x, res_y = _get_system_resolution()
147+
148+ touch_tool = _get_touch_tool()
149+
150+ events = {
151+ e.EV_ABS: [
152+ (e.ABS_X, (0, res_x, 0, 0)),
153+ (e.ABS_Y, (0, res_y, 0, 0)),
154+ (e.ABS_PRESSURE, (0, 65535, 0, 0)),
155+ (e.ABS_MT_POSITION_X, (0, res_x, 0, 0)),
156+ (e.ABS_MT_POSITION_Y, (0, res_y, 0, 0)),
157+ (e.ABS_MT_TOUCH_MAJOR, (0, 30, 0, 0)),
158+ (e.ABS_MT_TRACKING_ID, (0, 65535, 0, 0)),
159+ (e.ABS_MT_PRESSURE, (0, 255, 0, 0)),
160+ (e.ABS_MT_SLOT, (0, 9, 0, 0)),
161+ ],
162+ e.EV_KEY: [
163+ touch_tool,
164+ ]
165+ }
166+ return events
167+
168+
169+def _get_system_resolution():
170+ from autopilot.display import Display
171+ display = Display.create()
172+ # TODO: This calculation needs to become part of the display module:
173+ l = r = t = b = 0
174+ for screen in range(display.get_num_screens()):
175+ geometry = display.get_screen_geometry(screen)
176+ if geometry[0] < l:
177+ l = geometry[0]
178+ if geometry[1] < t:
179+ t = geometry[1]
180+ if geometry[0] + geometry[2] > r:
181+ r = geometry[0] + geometry[2]
182+ if geometry[1] + geometry[3] > b:
183+ b = geometry[1] + geometry[3]
184+ res_x = r - l
185+ res_y = b - t
186+ return res_x, res_y
187+
188+
189+def _get_touch_tool():
190+ # android uses BTN_TOOL_FINGER, whereas desktop uses BTN_TOUCH. I have
191+ # no idea why...
192+ if autopilot.platform.model() == 'Desktop':
193+ touch_tool = e.BTN_TOUCH
194+ else:
195+ touch_tool = e.BTN_TOOL_FINGER
196+ return touch_tool
197+
198+
199+class _UInputTouchDevice(object):
200+ """Wrapper for the UInput Touch to execute its primitives.
201+
202+ If res_x and res_y are not specified, they will be queried from the system.
203+
204+ """
205+
206+ _touch_fingers_in_use = []
207+ _max_number_of_fingers = 9
208+ _last_tracking_id = 0
209+
210+ def __init__(self, res_x=None, res_y=None, device_class=UInput):
211+ super(_UInputTouchDevice, self).__init__()
212+ self._device = device_class(
213+ events=_get_touch_events(res_x, res_y), name='autopilot-finger',
214+ version=0x2, devnode=_get_devnode_path())
215+ self._touch_finger_slot = None
216+
217+ @property
218+ def pressed(self):
219+ return self._touch_finger_slot is not None
220+
221+ def finger_down(self, x, y):
222+ """Internal: moves finger "finger" down on the touchscreen.
223+
224+ :param x: The finger will be moved to this x coordinate.
225+ :param y: The finger will be moved to this y coordinate.
226+
227+ """
228+ if self.pressed:
229+ raise RuntimeError("Cannot press finger: it's already pressed.")
230+ self._touch_finger_slot = self._get_free_touch_finger_slot()
231+
232+ self._device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
233+ self._device.write(
234+ e.EV_ABS, e.ABS_MT_TRACKING_ID, self._get_next_tracking_id())
235+ press_value = 1
236+ self._device.write(e.EV_KEY, e.BTN_TOOL_FINGER, press_value)
237+ self._device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
238+ self._device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
239+ self._device.write(e.EV_ABS, e.ABS_MT_PRESSURE, 400)
240+ self._device.syn()
241+
242+ def _get_free_touch_finger_slot(self):
243+ """Return the id of a free touch finger.
244+
245+ :raises: RuntimeError if no more fingers are available.
246+
247+ """
248+ for i in range(_UInputTouchDevice._max_number_of_fingers):
249+ if i not in _UInputTouchDevice._touch_fingers_in_use:
250+ _UInputTouchDevice._touch_fingers_in_use.append(i)
251+ return i
252+ raise RuntimeError('All available fingers have been used already.')
253+
254+ def _get_next_tracking_id(self):
255+ _UInputTouchDevice._last_tracking_id += 1
256+ return _UInputTouchDevice._last_tracking_id
257+
258+ def finger_move(self, x, y):
259+ """Internal: moves finger "finger" on the touchscreen to pos (x,y)
260+
261+ NOTE: The finger has to be down for this to have any effect.
262+
263+ """
264+ if not self.pressed:
265+ raise RuntimeError('Attempting to move without finger being down.')
266+ else:
267+ self._device.write(
268+ e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
269+ self._device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
270+ self._device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
271+ self._device.syn()
272+
273+ def finger_up(self):
274+ """Internal: moves finger "finger" up from the touchscreen"""
275+ if not self.pressed:
276+ raise RuntimeError("Cannot release finger: it's not pressed.")
277+ self._device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
278+ lift_tracking_id = -1
279+ self._device.write(e.EV_ABS, e.ABS_MT_TRACKING_ID, lift_tracking_id)
280+ release_value = 0
281+ self._device.write(e.EV_KEY, e.BTN_TOOL_FINGER, release_value)
282+ self._device.syn()
283+ self._release_touch_finger()
284+
285+ def _release_touch_finger(self):
286+ """Release the touch finger."""
287+ if (self._touch_finger_slot not in
288+ _UInputTouchDevice._touch_fingers_in_use):
289+ raise RuntimeError(
290+ "Finger %d was never claimed, or has already been released." %
291+ self._touch_finger_slot)
292+ _UInputTouchDevice._touch_fingers_in_use.remove(
293+ self._touch_finger_slot)
294+ self._touch_finger_slot = None
295
296
297 class Touch(TouchBase):
298 """Low level interface to generate single finger touch events."""
299
300- def __init__(self):
301+ def __init__(self, device_class=_UInputTouchDevice):
302 super(Touch, self).__init__()
303- self._touch_finger = None
304+ Touch._device = device_class()
305
306 @property
307 def pressed(self):
308- return self._touch_finger is not None
309+ return self._device.pressed
310
311 def tap(self, x, y):
312 """Click (or 'tap') at given x and y coordinates."""
313 logger.debug("Tapping at: %d,%d", x, y)
314- self._finger_down(x, y)
315- sleep(0.1)
316- self._finger_up()
317+ self._device.finger_down(x, y)
318+ utilities.sleep(0.1)
319+ self._device.finger_up()
320
321 def tap_object(self, object):
322 """Click (or 'tap') a given object"""
323@@ -382,12 +450,12 @@
324 """Press and hold a given object or at the given coordinates
325 Call release() when the object has been pressed long enough"""
326 logger.debug("Pressing at: %d,%d", x, y)
327- self._finger_down(x, y)
328+ self._device.finger_down(x, y)
329
330 def release(self):
331 """Release a previously pressed finger"""
332 logger.debug("Releasing")
333- self._finger_up()
334+ self._device.finger_up()
335
336 def move(self, x, y):
337 """Moves the pointing "finger" to pos(x,y).
338@@ -395,14 +463,12 @@
339 NOTE: The finger has to be down for this to have any effect.
340
341 """
342- if self._touch_finger is None:
343- raise RuntimeError("Attempting to move without finger being down.")
344- self._finger_move(x, y)
345+ self._device.finger_move(x, y)
346
347 def drag(self, x1, y1, x2, y2):
348 """Perform a drag gesture from (x1,y1) to (x2,y2)"""
349 logger.debug("Dragging from %d,%d to %d,%d", x1, y1, x2, y2)
350- self._finger_down(x1, y1)
351+ self._device.finger_down(x1, y1)
352
353 # Let's drag in 100 steps for now...
354 dx = 1.0 * (x2 - x1) / 100
355@@ -410,52 +476,13 @@
356 cur_x = x1 + dx
357 cur_y = y1 + dy
358 for i in range(0, 100):
359- self._finger_move(int(cur_x), int(cur_y))
360- sleep(0.002)
361+ self._device.finger_move(int(cur_x), int(cur_y))
362+ utilities.sleep(0.002)
363 cur_x += dx
364 cur_y += dy
365 # Make sure we actually end up at target
366- self._finger_move(x2, y2)
367- self._finger_up()
368-
369- def _finger_down(self, x, y):
370- """Internal: moves finger "finger" down on the touchscreen.
371-
372- :param x: The finger will be moved to this x coordinate.
373- :param y: The finger will be moved to this y coordinate.
374-
375- """
376- if self._touch_finger is not None:
377- raise RuntimeError("Cannot press finger: it's already pressed.")
378- self._touch_finger = _get_touch_finger()
379-
380- _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
381- _touch_device.write(
382- e.EV_ABS, e.ABS_MT_TRACKING_ID, get_next_tracking_id())
383- _touch_device.write(e.EV_KEY, e.BTN_TOOL_FINGER, 1)
384- _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
385- _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
386- _touch_device.write(e.EV_ABS, e.ABS_MT_PRESSURE, 400)
387- _touch_device.syn()
388-
389- def _finger_move(self, x, y):
390- """Internal: moves finger "finger" on the touchscreen to pos (x,y)
391- NOTE: The finger has to be down for this to have any effect."""
392- if self._touch_finger is not None:
393- _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
394- _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
395- _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
396- _touch_device.syn()
397-
398- def _finger_up(self):
399- """Internal: moves finger "finger" up from the touchscreen"""
400- if self._touch_finger is None:
401- raise RuntimeError("Cannot release finger: it's not pressed.")
402- _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
403- _touch_device.write(e.EV_ABS, e.ABS_MT_TRACKING_ID, -1)
404- _touch_device.write(e.EV_KEY, e.BTN_TOOL_FINGER, 0)
405- _touch_device.syn()
406- self._touch_finger = _release_touch_finger(self._touch_finger)
407+ self._device.finger_move(x2, y2)
408+ self._device.finger_up()
409
410
411 # veebers: there should be a better way to handle this.
412
413=== modified file 'autopilot/tests/unit/test_input.py'
414--- autopilot/tests/unit/test_input.py 2014-01-21 04:52:16 +0000
415+++ autopilot/tests/unit/test_input.py 2014-01-21 09:50:21 +0000
416@@ -317,3 +317,264 @@
417 self.assertEqual(
418 expected_press_calls + expected_release_calls,
419 self.keyboard._device.mock_calls)
420+
421+
422+class UInputTouchDeviceTestCase(TestCase):
423+ """Test the integration with evdev.UInput for the touch device."""
424+
425+ def setUp(self):
426+ super(UInputTouchDeviceTestCase, self).setUp()
427+ self._number_of_slots = 9
428+
429+ # Return to the original fingers after the test.
430+ self.addCleanup(
431+ self._set_fingers_in_use,
432+ _uinput._UInputTouchDevice._touch_fingers_in_use,
433+ _uinput._UInputTouchDevice._last_tracking_id)
434+
435+ # Always start the tests without fingers in use.
436+ _uinput._UInputTouchDevice._touch_fingers_in_use = []
437+ _uinput._UInputTouchDevice._last_tracking_id = 0
438+
439+ def _set_fingers_in_use(self, touch_fingers_in_use, last_tracking_id):
440+ _uinput._UInputTouchDevice._touch_fingers_in_use = touch_fingers_in_use
441+ _uinput._UInputTouchDevice._last_tracking_id = last_tracking_id
442+
443+ def test_finger_down_should_use_free_slot(self):
444+ for slot in range(self._number_of_slots):
445+ touch = self._get_touch_device()
446+
447+ touch.finger_down(0, 0)
448+
449+ self._assert_finger_down_emitted_write_and_syn(
450+ touch, slot=slot, tracking_id=mock.ANY, x=0, y=0)
451+
452+ def _get_touch_device(self):
453+ touch = _uinput._UInputTouchDevice(device_class=mock.Mock)
454+ touch._device.mock_add_spec(uinput.UInput, spec_set=True)
455+ return touch
456+
457+ def _assert_finger_down_emitted_write_and_syn(
458+ self, touch, slot, tracking_id, x, y):
459+ press_value = 1
460+ expected_calls = [
461+ mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
462+ mock.call.write(
463+ ecodes.EV_ABS, ecodes.ABS_MT_TRACKING_ID, tracking_id),
464+ mock.call.write(
465+ ecodes.EV_KEY, ecodes.BTN_TOOL_FINGER, press_value),
466+ mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_X, x),
467+ mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_Y, y),
468+ mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_PRESSURE, 400),
469+ mock.call.syn()
470+ ]
471+ self.assertEqual(expected_calls, touch._device.mock_calls)
472+
473+ def test_finger_down_without_free_slots_should_raise_error(self):
474+ # Claim all the available slots.
475+ for slot in range(self._number_of_slots):
476+ touch = self._get_touch_device()
477+ touch.finger_down(0, 0)
478+
479+ touch = self._get_touch_device()
480+
481+ # Try to use one more.
482+ error = self.assertRaises(RuntimeError, touch.finger_down, 11, 11)
483+ self.assertEqual(
484+ 'All available fingers have been used already.', str(error))
485+
486+ def test_finger_down_should_use_unique_tracking_id(self):
487+ for number in range(self._number_of_slots):
488+ touch = self._get_touch_device()
489+ touch.finger_down(0, 0)
490+
491+ self._assert_finger_down_emitted_write_and_syn(
492+ touch, slot=mock.ANY, tracking_id=number + 1, x=0, y=0)
493+
494+ def test_finger_down_should_not_reuse_tracking_ids(self):
495+ # Claim and release all the available slots once.
496+ for number in range(self._number_of_slots):
497+ touch = self._get_touch_device()
498+ touch.finger_down(0, 0)
499+ touch.finger_up()
500+
501+ touch = self._get_touch_device()
502+
503+ touch.finger_down(12, 12)
504+ self._assert_finger_down_emitted_write_and_syn(
505+ touch, slot=mock.ANY, tracking_id=number + 2, x=12, y=12)
506+
507+ def test_finger_down_with_finger_pressed_should_raise_error(self):
508+ touch = self._get_touch_device()
509+ touch.finger_down(0, 0)
510+
511+ error = self.assertRaises(RuntimeError, touch.finger_down, 0, 0)
512+ self.assertEqual(
513+ "Cannot press finger: it's already pressed.", str(error))
514+
515+ def test_finger_move_without_finger_pressed_should_raise_error(self):
516+ touch = self._get_touch_device()
517+
518+ error = self.assertRaises(RuntimeError, touch.finger_move, 10, 10)
519+ self.assertEqual(
520+ 'Attempting to move without finger being down.', str(error))
521+
522+ def test_finger_move_should_use_assigned_slot(self):
523+ for slot in range(self._number_of_slots):
524+ touch = self._get_touch_device()
525+ touch.finger_down(0, 0)
526+ touch._device.reset_mock()
527+
528+ touch.finger_move(10, 10)
529+
530+ self._assert_finger_move_emitted_write_and_syn(
531+ touch, slot=slot, x=10, y=10)
532+
533+ def _assert_finger_move_emitted_write_and_syn(self, touch, slot, x, y):
534+ expected_calls = [
535+ mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
536+ mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_X, x),
537+ mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_Y, y),
538+ mock.call.syn()
539+ ]
540+ self.assertEqual(expected_calls, touch._device.mock_calls)
541+
542+ def test_finger_move_should_reuse_assigned_slot(self):
543+ first_slot = 0
544+ touch = self._get_touch_device()
545+ touch.finger_down(1, 1)
546+ touch._device.reset_mock()
547+
548+ touch.finger_move(13, 13)
549+ self._assert_finger_move_emitted_write_and_syn(
550+ touch, slot=first_slot, x=13, y=13)
551+ touch._device.reset_mock()
552+
553+ touch.finger_move(14, 14)
554+ self._assert_finger_move_emitted_write_and_syn(
555+ touch, slot=first_slot, x=14, y=14)
556+
557+ def test_finger_up_without_finger_pressed_should_raise_error(self):
558+ touch = self._get_touch_device()
559+
560+ error = self.assertRaises(RuntimeError, touch.finger_up)
561+ self.assertEqual(
562+ "Cannot release finger: it's not pressed.", str(error))
563+
564+ def test_finger_up_should_use_assigned_slot(self):
565+ fingers = []
566+ for slot in range(self._number_of_slots):
567+ touch = self._get_touch_device()
568+ touch.finger_down(0, 0)
569+ touch._device.reset_mock()
570+ fingers.append(touch)
571+
572+ for slot, touch in enumerate(fingers):
573+ touch.finger_up()
574+
575+ self._assert_finger_up_emitted_write_and_syn(touch, slot=slot)
576+
577+ def _assert_finger_up_emitted_write_and_syn(self, touch, slot):
578+ lift_tracking_id = -1
579+ release_value = 0
580+ expected_calls = [
581+ mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
582+ mock.call.write(
583+ ecodes.EV_ABS, ecodes.ABS_MT_TRACKING_ID, lift_tracking_id),
584+ mock.call.write(
585+ ecodes.EV_KEY, ecodes.BTN_TOOL_FINGER, release_value),
586+ mock.call.syn()
587+ ]
588+ self.assertEqual(expected_calls, touch._device.mock_calls)
589+
590+ def test_finger_up_should_release_slot(self):
591+ fingers = []
592+ # Claim all the available slots.
593+ for slot in range(self._number_of_slots):
594+ touch = self._get_touch_device()
595+ touch.finger_down(0, 0)
596+ fingers.append(touch)
597+
598+ slot_to_reuse = 3
599+ fingers[slot_to_reuse].finger_up()
600+
601+ touch = self._get_touch_device()
602+
603+ # Try to use one more.
604+ touch.finger_down(15, 15)
605+ self._assert_finger_down_emitted_write_and_syn(
606+ touch, slot=slot_to_reuse, tracking_id=mock.ANY, x=15, y=15)
607+
608+ def test_pressed_with_finger_down(self):
609+ touch = self._get_touch_device()
610+ touch.finger_down(0, 0)
611+
612+ self.assertTrue(touch.pressed)
613+
614+ def test_pressed_without_finger_down(self):
615+ touch = self._get_touch_device()
616+ self.assertFalse(touch.pressed)
617+
618+ def test_pressed_after_finger_up(self):
619+ touch = self._get_touch_device()
620+ touch.finger_down(0, 0)
621+ touch.finger_up()
622+
623+ self.assertFalse(touch.pressed)
624+
625+ def test_pressed_with_other_finger_down(self):
626+ other_touch = self._get_touch_device()
627+ other_touch.finger_down(0, 0)
628+
629+ touch = self._get_touch_device()
630+ self.assertFalse(touch.pressed)
631+
632+
633+class UInputTouchTestCase(TestCase):
634+ """Test UInput Touch helper for autopilot tests."""
635+
636+ def setUp(self):
637+ super(UInputTouchTestCase, self).setUp()
638+ self.touch = _uinput.Touch(device_class=mock.Mock)
639+ self.touch._device.mock_add_spec(
640+ _uinput._UInputTouchDevice, spec_set=True)
641+ # Mock the sleeps so we don't have to spend time actually sleeping.
642+ self.addCleanup(utilities.sleep.disable_mock)
643+ utilities.sleep.enable_mock()
644+
645+ def test_tap(self):
646+ expected_calls = [
647+ mock.call.finger_down(0, 0),
648+ mock.call.finger_up()
649+ ]
650+
651+ self.touch.tap(0, 0)
652+ self.assertEqual(expected_calls, self.touch._device.mock_calls)
653+
654+ def test_tap_object(self):
655+ object_ = type('Dummy', (object,), {'globalRect': (0, 0, 10, 10)})
656+ expected_calls = [
657+ mock.call.finger_down(5, 5),
658+ mock.call.finger_up()
659+ ]
660+
661+ self.touch.tap_object(object_)
662+ self.assertEqual(expected_calls, self.touch._device.mock_calls)
663+
664+ def test_press(self):
665+ expected_calls = [mock.call.finger_down(0, 0)]
666+
667+ self.touch.press(0, 0)
668+ self.assertEqual(expected_calls, self.touch._device.mock_calls)
669+
670+ def test_release(self):
671+ expected_calls = [mock.call.finger_up()]
672+
673+ self.touch.release()
674+ self.assertEqual(expected_calls, self.touch._device.mock_calls)
675+
676+ def test_move(self):
677+ expected_calls = [mock.call.finger_move(10, 10)]
678+
679+ self.touch.move(10, 10)
680+ self.assertEqual(expected_calls, self.touch._device.mock_calls)

Subscribers

People subscribed via source and target branches

to all changes: