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
=== modified file 'autopilot/input/_uinput.py'
--- autopilot/input/_uinput.py 2014-01-21 04:44:16 +0000
+++ autopilot/input/_uinput.py 2014-01-21 09:50:21 +0000
@@ -20,10 +20,10 @@
2020
21"""UInput device drivers."""21"""UInput device drivers."""
2222
23from autopilot import utilities
23from autopilot.input import Keyboard as KeyboardBase24from autopilot.input import Keyboard as KeyboardBase
24from autopilot.input import Touch as TouchBase25from autopilot.input import Touch as TouchBase
25from autopilot.input._common import get_center_point26from autopilot.input._common import get_center_point
26from autopilot.utilities import sleep
27import autopilot.platform27import autopilot.platform
2828
29import logging29import logging
@@ -135,7 +135,7 @@
135 for key in self._sanitise_keys(keys):135 for key in self._sanitise_keys(keys):
136 for key_button in self._get_key_buttons(key):136 for key_button in self._get_key_buttons(key):
137 self._device.press(key_button)137 self._device.press(key_button)
138 sleep(delay)138 utilities.sleep(delay)
139139
140 def release(self, keys, delay=0.1):140 def release(self, keys, delay=0.1):
141 """Send key release events only.141 """Send key release events only.
@@ -156,7 +156,7 @@
156 for key in reversed(self._sanitise_keys(keys)):156 for key in reversed(self._sanitise_keys(keys)):
157 for key_button in reversed(self._get_key_buttons(key)):157 for key_button in reversed(self._get_key_buttons(key)):
158 self._device.release(key_button)158 self._device.release(key_button)
159 sleep(delay)159 utilities.sleep(delay)
160160
161 def press_and_release(self, keys, delay=0.1):161 def press_and_release(self, keys, delay=0.1):
162 """Press and release all items in 'keys'.162 """Press and release all items in 'keys'.
@@ -214,67 +214,16 @@
214 return key_buttons214 return key_buttons
215215
216216
217last_tracking_id = 0217@utilities.deprecated('Touch')
218
219
220def get_next_tracking_id():
221 global last_tracking_id
222 last_tracking_id += 1
223 return last_tracking_id
224
225
226def create_touch_device(res_x=None, res_y=None):218def create_touch_device(res_x=None, res_y=None):
227 """Create and return a UInput touch device.219 """Create and return a UInput touch device.
228220
229 If res_x and res_y are not specified, they will be queried from the system.221 If res_x and res_y are not specified, they will be queried from the system.
230222
231 """223 """
232224 return UInput(events=_get_touch_events(), name='autopilot-finger',
233 if res_x is None or res_y is None:225 version=0x2, devnode=_get_devnode_path())
234 from autopilot.display import Display226
235 display = Display.create()
236 # TODO: This calculation needs to become part of the display module:
237 l = r = t = b = 0
238 for screen in range(display.get_num_screens()):
239 geometry = display.get_screen_geometry(screen)
240 if geometry[0] < l:
241 l = geometry[0]
242 if geometry[1] < t:
243 t = geometry[1]
244 if geometry[0] + geometry[2] > r:
245 r = geometry[0] + geometry[2]
246 if geometry[1] + geometry[3] > b:
247 b = geometry[1] + geometry[3]
248 res_x = r - l
249 res_y = b - t
250
251 # android uses BTN_TOOL_FINGER, whereas desktop uses BTN_TOUCH. I have no
252 # idea why...
253 touch_tool = e.BTN_TOOL_FINGER
254 if autopilot.platform.model() == 'Desktop':
255 touch_tool = e.BTN_TOUCH
256
257 cap_mt = {
258 e.EV_ABS: [
259 (e.ABS_X, (0, res_x, 0, 0)),
260 (e.ABS_Y, (0, res_y, 0, 0)),
261 (e.ABS_PRESSURE, (0, 65535, 0, 0)),
262 (e.ABS_MT_POSITION_X, (0, res_x, 0, 0)),
263 (e.ABS_MT_POSITION_Y, (0, res_y, 0, 0)),
264 (e.ABS_MT_TOUCH_MAJOR, (0, 30, 0, 0)),
265 (e.ABS_MT_TRACKING_ID, (0, 65535, 0, 0)),
266 (e.ABS_MT_PRESSURE, (0, 255, 0, 0)),
267 (e.ABS_MT_SLOT, (0, 9, 0, 0)),
268 ],
269 e.EV_KEY: [
270 touch_tool,
271 ]
272 }
273
274 return UInput(cap_mt, name='autopilot-finger', version=0x2,
275 devnode=_get_devnode_path())
276
277_touch_device = create_touch_device()
278227
279# Multiouch notes:228# Multiouch notes:
280# ----------------229# ----------------
@@ -319,58 +268,177 @@
319# about this is that the SLOT refers to a finger number, and the TRACKING_ID268# about this is that the SLOT refers to a finger number, and the TRACKING_ID
320# identifies a unique touch for the duration of it's existance.269# identifies a unique touch for the duration of it's existance.
321270
322_touch_fingers_in_use = []271
323272def _get_touch_events(res_x, res_y):
324273 if res_x is None or res_y is None:
325def _get_touch_finger():274 res_x, res_y = _get_system_resolution()
326 """Claim a touch finger id for use.275
327276 touch_tool = _get_touch_tool()
328 :raises: RuntimeError if no more fingers are available.277
329278 events = {
330 """279 e.EV_ABS: [
331 global _touch_fingers_in_use280 (e.ABS_X, (0, res_x, 0, 0)),
332281 (e.ABS_Y, (0, res_y, 0, 0)),
333 for i in range(9):282 (e.ABS_PRESSURE, (0, 65535, 0, 0)),
334 if i not in _touch_fingers_in_use:283 (e.ABS_MT_POSITION_X, (0, res_x, 0, 0)),
335 _touch_fingers_in_use.append(i)284 (e.ABS_MT_POSITION_Y, (0, res_y, 0, 0)),
336 return i285 (e.ABS_MT_TOUCH_MAJOR, (0, 30, 0, 0)),
337 raise RuntimeError("All available fingers have been used already.")286 (e.ABS_MT_TRACKING_ID, (0, 65535, 0, 0)),
338287 (e.ABS_MT_PRESSURE, (0, 255, 0, 0)),
339288 (e.ABS_MT_SLOT, (0, 9, 0, 0)),
340def _release_touch_finger(finger_num):289 ],
341 """Relase a previously-claimed finger id.290 e.EV_KEY: [
342291 touch_tool,
343 :raises: RuntimeError if the finger given was never claimed, or was already292 ]
344 released.293 }
345294 return events
346 """295
347 global _touch_fingers_in_use296
348297def _get_system_resolution():
349 if finger_num not in _touch_fingers_in_use:298 from autopilot.display import Display
350 raise RuntimeError(299 display = Display.create()
351 "Finger %d was never claimed, or has already been released." %300 # TODO: This calculation needs to become part of the display module:
352 (finger_num))301 l = r = t = b = 0
353 _touch_fingers_in_use.remove(finger_num)302 for screen in range(display.get_num_screens()):
354 assert(finger_num not in _touch_fingers_in_use)303 geometry = display.get_screen_geometry(screen)
304 if geometry[0] < l:
305 l = geometry[0]
306 if geometry[1] < t:
307 t = geometry[1]
308 if geometry[0] + geometry[2] > r:
309 r = geometry[0] + geometry[2]
310 if geometry[1] + geometry[3] > b:
311 b = geometry[1] + geometry[3]
312 res_x = r - l
313 res_y = b - t
314 return res_x, res_y
315
316
317def _get_touch_tool():
318 # android uses BTN_TOOL_FINGER, whereas desktop uses BTN_TOUCH. I have
319 # no idea why...
320 if autopilot.platform.model() == 'Desktop':
321 touch_tool = e.BTN_TOUCH
322 else:
323 touch_tool = e.BTN_TOOL_FINGER
324 return touch_tool
325
326
327class _UInputTouchDevice(object):
328 """Wrapper for the UInput Touch to execute its primitives.
329
330 If res_x and res_y are not specified, they will be queried from the system.
331
332 """
333
334 _touch_fingers_in_use = []
335 _max_number_of_fingers = 9
336 _last_tracking_id = 0
337
338 def __init__(self, res_x=None, res_y=None, device_class=UInput):
339 super(_UInputTouchDevice, self).__init__()
340 self._device = device_class(
341 events=_get_touch_events(res_x, res_y), name='autopilot-finger',
342 version=0x2, devnode=_get_devnode_path())
343 self._touch_finger_slot = None
344
345 @property
346 def pressed(self):
347 return self._touch_finger_slot is not None
348
349 def finger_down(self, x, y):
350 """Internal: moves finger "finger" down on the touchscreen.
351
352 :param x: The finger will be moved to this x coordinate.
353 :param y: The finger will be moved to this y coordinate.
354
355 """
356 if self.pressed:
357 raise RuntimeError("Cannot press finger: it's already pressed.")
358 self._touch_finger_slot = self._get_free_touch_finger_slot()
359
360 self._device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
361 self._device.write(
362 e.EV_ABS, e.ABS_MT_TRACKING_ID, self._get_next_tracking_id())
363 press_value = 1
364 self._device.write(e.EV_KEY, e.BTN_TOOL_FINGER, press_value)
365 self._device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
366 self._device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
367 self._device.write(e.EV_ABS, e.ABS_MT_PRESSURE, 400)
368 self._device.syn()
369
370 def _get_free_touch_finger_slot(self):
371 """Return the id of a free touch finger.
372
373 :raises: RuntimeError if no more fingers are available.
374
375 """
376 for i in range(_UInputTouchDevice._max_number_of_fingers):
377 if i not in _UInputTouchDevice._touch_fingers_in_use:
378 _UInputTouchDevice._touch_fingers_in_use.append(i)
379 return i
380 raise RuntimeError('All available fingers have been used already.')
381
382 def _get_next_tracking_id(self):
383 _UInputTouchDevice._last_tracking_id += 1
384 return _UInputTouchDevice._last_tracking_id
385
386 def finger_move(self, x, y):
387 """Internal: moves finger "finger" on the touchscreen to pos (x,y)
388
389 NOTE: The finger has to be down for this to have any effect.
390
391 """
392 if not self.pressed:
393 raise RuntimeError('Attempting to move without finger being down.')
394 else:
395 self._device.write(
396 e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
397 self._device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
398 self._device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
399 self._device.syn()
400
401 def finger_up(self):
402 """Internal: moves finger "finger" up from the touchscreen"""
403 if not self.pressed:
404 raise RuntimeError("Cannot release finger: it's not pressed.")
405 self._device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
406 lift_tracking_id = -1
407 self._device.write(e.EV_ABS, e.ABS_MT_TRACKING_ID, lift_tracking_id)
408 release_value = 0
409 self._device.write(e.EV_KEY, e.BTN_TOOL_FINGER, release_value)
410 self._device.syn()
411 self._release_touch_finger()
412
413 def _release_touch_finger(self):
414 """Release the touch finger."""
415 if (self._touch_finger_slot not in
416 _UInputTouchDevice._touch_fingers_in_use):
417 raise RuntimeError(
418 "Finger %d was never claimed, or has already been released." %
419 self._touch_finger_slot)
420 _UInputTouchDevice._touch_fingers_in_use.remove(
421 self._touch_finger_slot)
422 self._touch_finger_slot = None
355423
356424
357class Touch(TouchBase):425class Touch(TouchBase):
358 """Low level interface to generate single finger touch events."""426 """Low level interface to generate single finger touch events."""
359427
360 def __init__(self):428 def __init__(self, device_class=_UInputTouchDevice):
361 super(Touch, self).__init__()429 super(Touch, self).__init__()
362 self._touch_finger = None430 Touch._device = device_class()
363431
364 @property432 @property
365 def pressed(self):433 def pressed(self):
366 return self._touch_finger is not None434 return self._device.pressed
367435
368 def tap(self, x, y):436 def tap(self, x, y):
369 """Click (or 'tap') at given x and y coordinates."""437 """Click (or 'tap') at given x and y coordinates."""
370 logger.debug("Tapping at: %d,%d", x, y)438 logger.debug("Tapping at: %d,%d", x, y)
371 self._finger_down(x, y)439 self._device.finger_down(x, y)
372 sleep(0.1)440 utilities.sleep(0.1)
373 self._finger_up()441 self._device.finger_up()
374442
375 def tap_object(self, object):443 def tap_object(self, object):
376 """Click (or 'tap') a given object"""444 """Click (or 'tap') a given object"""
@@ -382,12 +450,12 @@
382 """Press and hold a given object or at the given coordinates450 """Press and hold a given object or at the given coordinates
383 Call release() when the object has been pressed long enough"""451 Call release() when the object has been pressed long enough"""
384 logger.debug("Pressing at: %d,%d", x, y)452 logger.debug("Pressing at: %d,%d", x, y)
385 self._finger_down(x, y)453 self._device.finger_down(x, y)
386454
387 def release(self):455 def release(self):
388 """Release a previously pressed finger"""456 """Release a previously pressed finger"""
389 logger.debug("Releasing")457 logger.debug("Releasing")
390 self._finger_up()458 self._device.finger_up()
391459
392 def move(self, x, y):460 def move(self, x, y):
393 """Moves the pointing "finger" to pos(x,y).461 """Moves the pointing "finger" to pos(x,y).
@@ -395,14 +463,12 @@
395 NOTE: The finger has to be down for this to have any effect.463 NOTE: The finger has to be down for this to have any effect.
396464
397 """465 """
398 if self._touch_finger is None:466 self._device.finger_move(x, y)
399 raise RuntimeError("Attempting to move without finger being down.")
400 self._finger_move(x, y)
401467
402 def drag(self, x1, y1, x2, y2):468 def drag(self, x1, y1, x2, y2):
403 """Perform a drag gesture from (x1,y1) to (x2,y2)"""469 """Perform a drag gesture from (x1,y1) to (x2,y2)"""
404 logger.debug("Dragging from %d,%d to %d,%d", x1, y1, x2, y2)470 logger.debug("Dragging from %d,%d to %d,%d", x1, y1, x2, y2)
405 self._finger_down(x1, y1)471 self._device.finger_down(x1, y1)
406472
407 # Let's drag in 100 steps for now...473 # Let's drag in 100 steps for now...
408 dx = 1.0 * (x2 - x1) / 100474 dx = 1.0 * (x2 - x1) / 100
@@ -410,52 +476,13 @@
410 cur_x = x1 + dx476 cur_x = x1 + dx
411 cur_y = y1 + dy477 cur_y = y1 + dy
412 for i in range(0, 100):478 for i in range(0, 100):
413 self._finger_move(int(cur_x), int(cur_y))479 self._device.finger_move(int(cur_x), int(cur_y))
414 sleep(0.002)480 utilities.sleep(0.002)
415 cur_x += dx481 cur_x += dx
416 cur_y += dy482 cur_y += dy
417 # Make sure we actually end up at target483 # Make sure we actually end up at target
418 self._finger_move(x2, y2)484 self._device.finger_move(x2, y2)
419 self._finger_up()485 self._device.finger_up()
420
421 def _finger_down(self, x, y):
422 """Internal: moves finger "finger" down on the touchscreen.
423
424 :param x: The finger will be moved to this x coordinate.
425 :param y: The finger will be moved to this y coordinate.
426
427 """
428 if self._touch_finger is not None:
429 raise RuntimeError("Cannot press finger: it's already pressed.")
430 self._touch_finger = _get_touch_finger()
431
432 _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
433 _touch_device.write(
434 e.EV_ABS, e.ABS_MT_TRACKING_ID, get_next_tracking_id())
435 _touch_device.write(e.EV_KEY, e.BTN_TOOL_FINGER, 1)
436 _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
437 _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
438 _touch_device.write(e.EV_ABS, e.ABS_MT_PRESSURE, 400)
439 _touch_device.syn()
440
441 def _finger_move(self, x, y):
442 """Internal: moves finger "finger" on the touchscreen to pos (x,y)
443 NOTE: The finger has to be down for this to have any effect."""
444 if self._touch_finger is not None:
445 _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
446 _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
447 _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
448 _touch_device.syn()
449
450 def _finger_up(self):
451 """Internal: moves finger "finger" up from the touchscreen"""
452 if self._touch_finger is None:
453 raise RuntimeError("Cannot release finger: it's not pressed.")
454 _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
455 _touch_device.write(e.EV_ABS, e.ABS_MT_TRACKING_ID, -1)
456 _touch_device.write(e.EV_KEY, e.BTN_TOOL_FINGER, 0)
457 _touch_device.syn()
458 self._touch_finger = _release_touch_finger(self._touch_finger)
459486
460487
461# veebers: there should be a better way to handle this.488# veebers: there should be a better way to handle this.
462489
=== modified file 'autopilot/tests/unit/test_input.py'
--- autopilot/tests/unit/test_input.py 2014-01-21 04:52:16 +0000
+++ autopilot/tests/unit/test_input.py 2014-01-21 09:50:21 +0000
@@ -317,3 +317,264 @@
317 self.assertEqual(317 self.assertEqual(
318 expected_press_calls + expected_release_calls,318 expected_press_calls + expected_release_calls,
319 self.keyboard._device.mock_calls)319 self.keyboard._device.mock_calls)
320
321
322class UInputTouchDeviceTestCase(TestCase):
323 """Test the integration with evdev.UInput for the touch device."""
324
325 def setUp(self):
326 super(UInputTouchDeviceTestCase, self).setUp()
327 self._number_of_slots = 9
328
329 # Return to the original fingers after the test.
330 self.addCleanup(
331 self._set_fingers_in_use,
332 _uinput._UInputTouchDevice._touch_fingers_in_use,
333 _uinput._UInputTouchDevice._last_tracking_id)
334
335 # Always start the tests without fingers in use.
336 _uinput._UInputTouchDevice._touch_fingers_in_use = []
337 _uinput._UInputTouchDevice._last_tracking_id = 0
338
339 def _set_fingers_in_use(self, touch_fingers_in_use, last_tracking_id):
340 _uinput._UInputTouchDevice._touch_fingers_in_use = touch_fingers_in_use
341 _uinput._UInputTouchDevice._last_tracking_id = last_tracking_id
342
343 def test_finger_down_should_use_free_slot(self):
344 for slot in range(self._number_of_slots):
345 touch = self._get_touch_device()
346
347 touch.finger_down(0, 0)
348
349 self._assert_finger_down_emitted_write_and_syn(
350 touch, slot=slot, tracking_id=mock.ANY, x=0, y=0)
351
352 def _get_touch_device(self):
353 touch = _uinput._UInputTouchDevice(device_class=mock.Mock)
354 touch._device.mock_add_spec(uinput.UInput, spec_set=True)
355 return touch
356
357 def _assert_finger_down_emitted_write_and_syn(
358 self, touch, slot, tracking_id, x, y):
359 press_value = 1
360 expected_calls = [
361 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
362 mock.call.write(
363 ecodes.EV_ABS, ecodes.ABS_MT_TRACKING_ID, tracking_id),
364 mock.call.write(
365 ecodes.EV_KEY, ecodes.BTN_TOOL_FINGER, press_value),
366 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_X, x),
367 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_Y, y),
368 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_PRESSURE, 400),
369 mock.call.syn()
370 ]
371 self.assertEqual(expected_calls, touch._device.mock_calls)
372
373 def test_finger_down_without_free_slots_should_raise_error(self):
374 # Claim all the available slots.
375 for slot in range(self._number_of_slots):
376 touch = self._get_touch_device()
377 touch.finger_down(0, 0)
378
379 touch = self._get_touch_device()
380
381 # Try to use one more.
382 error = self.assertRaises(RuntimeError, touch.finger_down, 11, 11)
383 self.assertEqual(
384 'All available fingers have been used already.', str(error))
385
386 def test_finger_down_should_use_unique_tracking_id(self):
387 for number in range(self._number_of_slots):
388 touch = self._get_touch_device()
389 touch.finger_down(0, 0)
390
391 self._assert_finger_down_emitted_write_and_syn(
392 touch, slot=mock.ANY, tracking_id=number + 1, x=0, y=0)
393
394 def test_finger_down_should_not_reuse_tracking_ids(self):
395 # Claim and release all the available slots once.
396 for number in range(self._number_of_slots):
397 touch = self._get_touch_device()
398 touch.finger_down(0, 0)
399 touch.finger_up()
400
401 touch = self._get_touch_device()
402
403 touch.finger_down(12, 12)
404 self._assert_finger_down_emitted_write_and_syn(
405 touch, slot=mock.ANY, tracking_id=number + 2, x=12, y=12)
406
407 def test_finger_down_with_finger_pressed_should_raise_error(self):
408 touch = self._get_touch_device()
409 touch.finger_down(0, 0)
410
411 error = self.assertRaises(RuntimeError, touch.finger_down, 0, 0)
412 self.assertEqual(
413 "Cannot press finger: it's already pressed.", str(error))
414
415 def test_finger_move_without_finger_pressed_should_raise_error(self):
416 touch = self._get_touch_device()
417
418 error = self.assertRaises(RuntimeError, touch.finger_move, 10, 10)
419 self.assertEqual(
420 'Attempting to move without finger being down.', str(error))
421
422 def test_finger_move_should_use_assigned_slot(self):
423 for slot in range(self._number_of_slots):
424 touch = self._get_touch_device()
425 touch.finger_down(0, 0)
426 touch._device.reset_mock()
427
428 touch.finger_move(10, 10)
429
430 self._assert_finger_move_emitted_write_and_syn(
431 touch, slot=slot, x=10, y=10)
432
433 def _assert_finger_move_emitted_write_and_syn(self, touch, slot, x, y):
434 expected_calls = [
435 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
436 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_X, x),
437 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_Y, y),
438 mock.call.syn()
439 ]
440 self.assertEqual(expected_calls, touch._device.mock_calls)
441
442 def test_finger_move_should_reuse_assigned_slot(self):
443 first_slot = 0
444 touch = self._get_touch_device()
445 touch.finger_down(1, 1)
446 touch._device.reset_mock()
447
448 touch.finger_move(13, 13)
449 self._assert_finger_move_emitted_write_and_syn(
450 touch, slot=first_slot, x=13, y=13)
451 touch._device.reset_mock()
452
453 touch.finger_move(14, 14)
454 self._assert_finger_move_emitted_write_and_syn(
455 touch, slot=first_slot, x=14, y=14)
456
457 def test_finger_up_without_finger_pressed_should_raise_error(self):
458 touch = self._get_touch_device()
459
460 error = self.assertRaises(RuntimeError, touch.finger_up)
461 self.assertEqual(
462 "Cannot release finger: it's not pressed.", str(error))
463
464 def test_finger_up_should_use_assigned_slot(self):
465 fingers = []
466 for slot in range(self._number_of_slots):
467 touch = self._get_touch_device()
468 touch.finger_down(0, 0)
469 touch._device.reset_mock()
470 fingers.append(touch)
471
472 for slot, touch in enumerate(fingers):
473 touch.finger_up()
474
475 self._assert_finger_up_emitted_write_and_syn(touch, slot=slot)
476
477 def _assert_finger_up_emitted_write_and_syn(self, touch, slot):
478 lift_tracking_id = -1
479 release_value = 0
480 expected_calls = [
481 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
482 mock.call.write(
483 ecodes.EV_ABS, ecodes.ABS_MT_TRACKING_ID, lift_tracking_id),
484 mock.call.write(
485 ecodes.EV_KEY, ecodes.BTN_TOOL_FINGER, release_value),
486 mock.call.syn()
487 ]
488 self.assertEqual(expected_calls, touch._device.mock_calls)
489
490 def test_finger_up_should_release_slot(self):
491 fingers = []
492 # Claim all the available slots.
493 for slot in range(self._number_of_slots):
494 touch = self._get_touch_device()
495 touch.finger_down(0, 0)
496 fingers.append(touch)
497
498 slot_to_reuse = 3
499 fingers[slot_to_reuse].finger_up()
500
501 touch = self._get_touch_device()
502
503 # Try to use one more.
504 touch.finger_down(15, 15)
505 self._assert_finger_down_emitted_write_and_syn(
506 touch, slot=slot_to_reuse, tracking_id=mock.ANY, x=15, y=15)
507
508 def test_pressed_with_finger_down(self):
509 touch = self._get_touch_device()
510 touch.finger_down(0, 0)
511
512 self.assertTrue(touch.pressed)
513
514 def test_pressed_without_finger_down(self):
515 touch = self._get_touch_device()
516 self.assertFalse(touch.pressed)
517
518 def test_pressed_after_finger_up(self):
519 touch = self._get_touch_device()
520 touch.finger_down(0, 0)
521 touch.finger_up()
522
523 self.assertFalse(touch.pressed)
524
525 def test_pressed_with_other_finger_down(self):
526 other_touch = self._get_touch_device()
527 other_touch.finger_down(0, 0)
528
529 touch = self._get_touch_device()
530 self.assertFalse(touch.pressed)
531
532
533class UInputTouchTestCase(TestCase):
534 """Test UInput Touch helper for autopilot tests."""
535
536 def setUp(self):
537 super(UInputTouchTestCase, self).setUp()
538 self.touch = _uinput.Touch(device_class=mock.Mock)
539 self.touch._device.mock_add_spec(
540 _uinput._UInputTouchDevice, spec_set=True)
541 # Mock the sleeps so we don't have to spend time actually sleeping.
542 self.addCleanup(utilities.sleep.disable_mock)
543 utilities.sleep.enable_mock()
544
545 def test_tap(self):
546 expected_calls = [
547 mock.call.finger_down(0, 0),
548 mock.call.finger_up()
549 ]
550
551 self.touch.tap(0, 0)
552 self.assertEqual(expected_calls, self.touch._device.mock_calls)
553
554 def test_tap_object(self):
555 object_ = type('Dummy', (object,), {'globalRect': (0, 0, 10, 10)})
556 expected_calls = [
557 mock.call.finger_down(5, 5),
558 mock.call.finger_up()
559 ]
560
561 self.touch.tap_object(object_)
562 self.assertEqual(expected_calls, self.touch._device.mock_calls)
563
564 def test_press(self):
565 expected_calls = [mock.call.finger_down(0, 0)]
566
567 self.touch.press(0, 0)
568 self.assertEqual(expected_calls, self.touch._device.mock_calls)
569
570 def test_release(self):
571 expected_calls = [mock.call.finger_up()]
572
573 self.touch.release()
574 self.assertEqual(expected_calls, self.touch._device.mock_calls)
575
576 def test_move(self):
577 expected_calls = [mock.call.finger_move(10, 10)]
578
579 self.touch.move(10, 10)
580 self.assertEqual(expected_calls, self.touch._device.mock_calls)

Subscribers

People subscribed via source and target branches

to all changes: