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

Proposed by Leo Arias
Status: Superseded
Proposed branch: lp:~elopio/autopilot/no_uinput_side-effects2
Merge into: lp:autopilot
Diff against target: 1143 lines (+731/-202)
4 files modified
autopilot/input/_uinput.py (+258/-192)
autopilot/tests/functional/test_input_stack.py (+6/-5)
autopilot/tests/unit/test_input.py (+465/-5)
debian/control (+2/-0)
To merge this branch: bzr merge lp:~elopio/autopilot/no_uinput_side-effects2
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Autopilot Hackers Pending
Review via email: mp+202467@code.launchpad.net

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

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

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:425
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~elopio/autopilot/no_uinput_side-effects2/+merge/202467/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/autopilot-ci/419/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-trusty-amd64-ci/145/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-trusty-armhf-ci/145/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/2495/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2497/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/autopilot-ci/419/rebuild

review: Needs Fixing (continuous-integration)
426. By Leo Arias

Create the Touch device with a dummy resolution.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:426
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~elopio/autopilot/no_uinput_side-effects2/+merge/202467/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/autopilot-ci/420/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-trusty-amd64-ci/146/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/autopilot-trusty-armhf-ci/146/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-trusty/2497/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/generic-mediumtests-builder-trusty-amd64/2499/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/autopilot-ci/420/rebuild

review: Needs Fixing (continuous-integration)
427. By Leo Arias

Fixed pep8.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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 2013-11-07 05:53:36 +0000
+++ autopilot/input/_uinput.py 2014-01-21 22:20:18 +0000
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#2#
3# Autopilot Functional Test Tool3# Autopilot Functional Test Tool
4# Copyright (C) 2012-2013 Canonical4# Copyright (C) 2012, 2013, 2014 Canonical
5#5#
6# This program is free software: you can redistribute it and/or modify6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by7# it under the terms of the GNU General Public License as published by
@@ -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
@@ -33,11 +33,6 @@
3333
34logger = logging.getLogger(__name__)34logger = logging.getLogger(__name__)
3535
36PRESS = 1
37RELEASE = 0
38
39_PRESSED_KEYS = []
40
4136
42def _get_devnode_path():37def _get_devnode_path():
43 """Provide a fallback uinput node for devices which don't support udev"""38 """Provide a fallback uinput node for devices which don't support udev"""
@@ -47,13 +42,76 @@
47 return devnode42 return devnode
4843
4944
45class _UInputKeyboardDevice(object):
46 """Wrapper for the UInput Keyboard to execute its primitives."""
47
48 def __init__(self, device_class=UInput):
49 super(_UInputKeyboardDevice, self).__init__()
50 self._device = device_class(devnode=_get_devnode_path())
51 self._pressed_keys_ecodes = []
52
53 def press(self, key):
54 """Press one key button.
55
56 It ignores case, so, for example, 'a' and 'A' are mapped to the same
57 key.
58
59 """
60 ecode = self._get_ecode_for_key(key)
61 logger.debug('Pressing %s (%r).', key, ecode)
62 self._emit_press_event(ecode)
63 self._pressed_keys_ecodes.append(ecode)
64
65 def _get_ecode_for_key(self, key):
66 key_name = key if key.startswith('KEY_') else 'KEY_' + key
67 key_name = key_name.upper()
68 ecode = e.ecodes.get(key_name, None)
69 if ecode is None:
70 raise ValueError('Unknown key name: %s.' % key)
71 return ecode
72
73 def _emit_press_event(self, ecode):
74 press_value = 1
75 self._emit(ecode, press_value)
76
77 def _emit(self, ecode, value):
78 self._device.write(e.EV_KEY, ecode, value)
79 self._device.syn()
80
81 def release(self, key):
82 """Release one key button.
83
84 It ignores case, so, for example, 'a' and 'A' are mapped to the same
85 key.
86
87 """
88 ecode = self._get_ecode_for_key(key)
89 if ecode in self._pressed_keys_ecodes:
90 logger.debug('Releasing %s (%r).', key, ecode)
91 self._emit_release_event(ecode)
92 self._pressed_keys_ecodes.remove(ecode)
93 else:
94 raise ValueError('Key %r not pressed.' % key)
95
96 def _emit_release_event(self, ecode):
97 release_value = 0
98 self._emit(ecode, release_value)
99
100 def release_pressed_keys(self):
101 """Release all the keys that are currently pressed."""
102 for ecode in self._pressed_keys_ecodes:
103 self._emit_release_event(ecode)
104 self._pressed_keys_ecodes = []
105
106
50class Keyboard(KeyboardBase):107class Keyboard(KeyboardBase):
51108
52 _device = UInput(devnode=_get_devnode_path())109 _device = None
53110
54 def _emit(self, event, value):111 def __init__(self, device_class=_UInputKeyboardDevice):
55 Keyboard._device.write(e.EV_KEY, event, value)112 super(Keyboard, self).__init__()
56 Keyboard._device.syn()113 if Keyboard._device is None:
114 Keyboard._device = device_class()
57115
58 def _sanitise_keys(self, keys):116 def _sanitise_keys(self, keys):
59 if keys == '+':117 if keys == '+':
@@ -76,11 +134,9 @@
76 raise TypeError("'keys' argument must be a string.")134 raise TypeError("'keys' argument must be a string.")
77135
78 for key in self._sanitise_keys(keys):136 for key in self._sanitise_keys(keys):
79 for event in Keyboard._get_events_for_key(key):137 for key_button in self._get_key_buttons(key):
80 logger.debug("Pressing %s (%r)", key, event)138 self._device.press(key_button)
81 _PRESSED_KEYS.append(event)139 utilities.sleep(delay)
82 self._emit(event, PRESS)
83 sleep(delay)
84140
85 def release(self, keys, delay=0.1):141 def release(self, keys, delay=0.1):
86 """Send key release events only.142 """Send key release events only.
@@ -99,12 +155,9 @@
99 raise TypeError("'keys' argument must be a string.")155 raise TypeError("'keys' argument must be a string.")
100156
101 for key in reversed(self._sanitise_keys(keys)):157 for key in reversed(self._sanitise_keys(keys)):
102 for event in Keyboard._get_events_for_key(key):158 for key_button in reversed(self._get_key_buttons(key)):
103 logger.debug("Releasing %s (%r)", key, event)159 self._device.release(key_button)
104 if event in _PRESSED_KEYS:160 utilities.sleep(delay)
105 _PRESSED_KEYS.remove(event)
106 self._emit(event, RELEASE)
107 sleep(delay)
108161
109 def press_and_release(self, keys, delay=0.1):162 def press_and_release(self, keys, delay=0.1):
110 """Press and release all items in 'keys'.163 """Press and release all items in 'keys'.
@@ -145,98 +198,33 @@
145 any keys that were pressed and not released.198 any keys that were pressed and not released.
146199
147 """200 """
148 global _PRESSED_KEYS201 cls._device.release_pressed_keys()
149 if len(_PRESSED_KEYS) == 0:202
150 return203 def _get_key_buttons(self, key):
151204 """Return a list of the key buttons required to press.
152 def _release(event):205
153 Keyboard._device.write(e.EV_KEY, event, RELEASE)206 Multiple buttons will be returned when the key specified requires more
154 Keyboard._device.syn()
155 for event in _PRESSED_KEYS:
156 logger.warning("Releasing key %r as part of cleanup call.", event)
157 _release(event)
158 _PRESSED_KEYS = []
159
160 @staticmethod
161 def _get_events_for_key(key):
162 """Return a list of events required to generate 'key' as an input.
163
164 Multiple keys will be returned when the key specified requires more
165 than one keypress to generate (for example, upper-case letters).207 than one keypress to generate (for example, upper-case letters).
166208
167 """209 """
168 events = []210 key_buttons = []
169 if key.isupper() or key in _SHIFTED_KEYS:211 if key.isupper() or key in _SHIFTED_KEYS:
170 events.append(e.KEY_LEFTSHIFT)212 key_buttons.append('KEY_LEFTSHIFT')
171 keyname = _UINPUT_CODE_TRANSLATIONS.get(key.upper(), key)213 key_name = _UINPUT_CODE_TRANSLATIONS.get(key.upper(), key)
172 evt = getattr(e, 'KEY_' + keyname.upper(), None)214 key_buttons.append(key_name)
173 if evt is None:215 return key_buttons
174 raise ValueError("Unknown key name: '%s'" % key)216
175 events.append(evt)217
176 return events218@utilities.deprecated('Touch')
177
178
179last_tracking_id = 0
180
181
182def get_next_tracking_id():
183 global last_tracking_id
184 last_tracking_id += 1
185 return last_tracking_id
186
187
188def create_touch_device(res_x=None, res_y=None):219def create_touch_device(res_x=None, res_y=None):
189 """Create and return a UInput touch device.220 """Create and return a UInput touch device.
190221
191 If res_x and res_y are not specified, they will be queried from the system.222 If res_x and res_y are not specified, they will be queried from the system.
192223
193 """224 """
194225 return UInput(events=_get_touch_events(), name='autopilot-finger',
195 if res_x is None or res_y is None:226 version=0x2, devnode=_get_devnode_path())
196 from autopilot.display import Display227
197 display = Display.create()
198 # TODO: This calculation needs to become part of the display module:
199 l = r = t = b = 0
200 for screen in range(display.get_num_screens()):
201 geometry = display.get_screen_geometry(screen)
202 if geometry[0] < l:
203 l = geometry[0]
204 if geometry[1] < t:
205 t = geometry[1]
206 if geometry[0] + geometry[2] > r:
207 r = geometry[0] + geometry[2]
208 if geometry[1] + geometry[3] > b:
209 b = geometry[1] + geometry[3]
210 res_x = r - l
211 res_y = b - t
212
213 # android uses BTN_TOOL_FINGER, whereas desktop uses BTN_TOUCH. I have no
214 # idea why...
215 touch_tool = e.BTN_TOOL_FINGER
216 if autopilot.platform.model() == 'Desktop':
217 touch_tool = e.BTN_TOUCH
218
219 cap_mt = {
220 e.EV_ABS: [
221 (e.ABS_X, (0, res_x, 0, 0)),
222 (e.ABS_Y, (0, res_y, 0, 0)),
223 (e.ABS_PRESSURE, (0, 65535, 0, 0)),
224 (e.ABS_MT_POSITION_X, (0, res_x, 0, 0)),
225 (e.ABS_MT_POSITION_Y, (0, res_y, 0, 0)),
226 (e.ABS_MT_TOUCH_MAJOR, (0, 30, 0, 0)),
227 (e.ABS_MT_TRACKING_ID, (0, 65535, 0, 0)),
228 (e.ABS_MT_PRESSURE, (0, 255, 0, 0)),
229 (e.ABS_MT_SLOT, (0, 9, 0, 0)),
230 ],
231 e.EV_KEY: [
232 touch_tool,
233 ]
234 }
235
236 return UInput(cap_mt, name='autopilot-finger', version=0x2,
237 devnode=_get_devnode_path())
238
239_touch_device = create_touch_device()
240228
241# Multiouch notes:229# Multiouch notes:
242# ----------------230# ----------------
@@ -281,58 +269,177 @@
281# about this is that the SLOT refers to a finger number, and the TRACKING_ID269# about this is that the SLOT refers to a finger number, and the TRACKING_ID
282# identifies a unique touch for the duration of it's existance.270# identifies a unique touch for the duration of it's existance.
283271
284_touch_fingers_in_use = []272
285273def _get_touch_events(res_x, res_y):
286274 if res_x is None or res_y is None:
287def _get_touch_finger():275 res_x, res_y = _get_system_resolution()
288 """Claim a touch finger id for use.276
289277 touch_tool = _get_touch_tool()
290 :raises: RuntimeError if no more fingers are available.278
291279 events = {
292 """280 e.EV_ABS: [
293 global _touch_fingers_in_use281 (e.ABS_X, (0, res_x, 0, 0)),
294282 (e.ABS_Y, (0, res_y, 0, 0)),
295 for i in range(9):283 (e.ABS_PRESSURE, (0, 65535, 0, 0)),
296 if i not in _touch_fingers_in_use:284 (e.ABS_MT_POSITION_X, (0, res_x, 0, 0)),
297 _touch_fingers_in_use.append(i)285 (e.ABS_MT_POSITION_Y, (0, res_y, 0, 0)),
298 return i286 (e.ABS_MT_TOUCH_MAJOR, (0, 30, 0, 0)),
299 raise RuntimeError("All available fingers have been used already.")287 (e.ABS_MT_TRACKING_ID, (0, 65535, 0, 0)),
300288 (e.ABS_MT_PRESSURE, (0, 255, 0, 0)),
301289 (e.ABS_MT_SLOT, (0, 9, 0, 0)),
302def _release_touch_finger(finger_num):290 ],
303 """Relase a previously-claimed finger id.291 e.EV_KEY: [
304292 touch_tool,
305 :raises: RuntimeError if the finger given was never claimed, or was already293 ]
306 released.294 }
307295 return events
308 """296
309 global _touch_fingers_in_use297
310298def _get_system_resolution():
311 if finger_num not in _touch_fingers_in_use:299 from autopilot.display import Display
312 raise RuntimeError(300 display = Display.create()
313 "Finger %d was never claimed, or has already been released." %301 # TODO: This calculation needs to become part of the display module:
314 (finger_num))302 l = r = t = b = 0
315 _touch_fingers_in_use.remove(finger_num)303 for screen in range(display.get_num_screens()):
316 assert(finger_num not in _touch_fingers_in_use)304 geometry = display.get_screen_geometry(screen)
305 if geometry[0] < l:
306 l = geometry[0]
307 if geometry[1] < t:
308 t = geometry[1]
309 if geometry[0] + geometry[2] > r:
310 r = geometry[0] + geometry[2]
311 if geometry[1] + geometry[3] > b:
312 b = geometry[1] + geometry[3]
313 res_x = r - l
314 res_y = b - t
315 return res_x, res_y
316
317
318def _get_touch_tool():
319 # android uses BTN_TOOL_FINGER, whereas desktop uses BTN_TOUCH. I have
320 # no idea why...
321 if autopilot.platform.model() == 'Desktop':
322 touch_tool = e.BTN_TOUCH
323 else:
324 touch_tool = e.BTN_TOOL_FINGER
325 return touch_tool
326
327
328class _UInputTouchDevice(object):
329 """Wrapper for the UInput Touch to execute its primitives.
330
331 If res_x and res_y are not specified, they will be queried from the system.
332
333 """
334
335 _touch_fingers_in_use = []
336 _max_number_of_fingers = 9
337 _last_tracking_id = 0
338
339 def __init__(self, res_x=None, res_y=None, device_class=UInput):
340 super(_UInputTouchDevice, self).__init__()
341 self._device = device_class(
342 events=_get_touch_events(res_x, res_y), name='autopilot-finger',
343 version=0x2, devnode=_get_devnode_path())
344 self._touch_finger_slot = None
345
346 @property
347 def pressed(self):
348 return self._touch_finger_slot is not None
349
350 def finger_down(self, x, y):
351 """Internal: moves finger "finger" down on the touchscreen.
352
353 :param x: The finger will be moved to this x coordinate.
354 :param y: The finger will be moved to this y coordinate.
355
356 """
357 if self.pressed:
358 raise RuntimeError("Cannot press finger: it's already pressed.")
359 self._touch_finger_slot = self._get_free_touch_finger_slot()
360
361 self._device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
362 self._device.write(
363 e.EV_ABS, e.ABS_MT_TRACKING_ID, self._get_next_tracking_id())
364 press_value = 1
365 self._device.write(e.EV_KEY, e.BTN_TOOL_FINGER, press_value)
366 self._device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
367 self._device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
368 self._device.write(e.EV_ABS, e.ABS_MT_PRESSURE, 400)
369 self._device.syn()
370
371 def _get_free_touch_finger_slot(self):
372 """Return the id of a free touch finger.
373
374 :raises: RuntimeError if no more fingers are available.
375
376 """
377 for i in range(_UInputTouchDevice._max_number_of_fingers):
378 if i not in _UInputTouchDevice._touch_fingers_in_use:
379 _UInputTouchDevice._touch_fingers_in_use.append(i)
380 return i
381 raise RuntimeError('All available fingers have been used already.')
382
383 def _get_next_tracking_id(self):
384 _UInputTouchDevice._last_tracking_id += 1
385 return _UInputTouchDevice._last_tracking_id
386
387 def finger_move(self, x, y):
388 """Internal: moves finger "finger" on the touchscreen to pos (x,y)
389
390 NOTE: The finger has to be down for this to have any effect.
391
392 """
393 if not self.pressed:
394 raise RuntimeError('Attempting to move without finger being down.')
395 else:
396 self._device.write(
397 e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
398 self._device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
399 self._device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
400 self._device.syn()
401
402 def finger_up(self):
403 """Internal: moves finger "finger" up from the touchscreen"""
404 if not self.pressed:
405 raise RuntimeError("Cannot release finger: it's not pressed.")
406 self._device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger_slot)
407 lift_tracking_id = -1
408 self._device.write(e.EV_ABS, e.ABS_MT_TRACKING_ID, lift_tracking_id)
409 release_value = 0
410 self._device.write(e.EV_KEY, e.BTN_TOOL_FINGER, release_value)
411 self._device.syn()
412 self._release_touch_finger()
413
414 def _release_touch_finger(self):
415 """Release the touch finger."""
416 if (self._touch_finger_slot not in
417 _UInputTouchDevice._touch_fingers_in_use):
418 raise RuntimeError(
419 "Finger %d was never claimed, or has already been released." %
420 self._touch_finger_slot)
421 _UInputTouchDevice._touch_fingers_in_use.remove(
422 self._touch_finger_slot)
423 self._touch_finger_slot = None
317424
318425
319class Touch(TouchBase):426class Touch(TouchBase):
320 """Low level interface to generate single finger touch events."""427 """Low level interface to generate single finger touch events."""
321428
322 def __init__(self):429 def __init__(self, device_class=_UInputTouchDevice):
323 super(Touch, self).__init__()430 super(Touch, self).__init__()
324 self._touch_finger = None431 Touch._device = device_class()
325432
326 @property433 @property
327 def pressed(self):434 def pressed(self):
328 return self._touch_finger is not None435 return self._device.pressed
329436
330 def tap(self, x, y):437 def tap(self, x, y):
331 """Click (or 'tap') at given x and y coordinates."""438 """Click (or 'tap') at given x and y coordinates."""
332 logger.debug("Tapping at: %d,%d", x, y)439 logger.debug("Tapping at: %d,%d", x, y)
333 self._finger_down(x, y)440 self._device.finger_down(x, y)
334 sleep(0.1)441 utilities.sleep(0.1)
335 self._finger_up()442 self._device.finger_up()
336443
337 def tap_object(self, object):444 def tap_object(self, object):
338 """Click (or 'tap') a given object"""445 """Click (or 'tap') a given object"""
@@ -344,12 +451,12 @@
344 """Press and hold a given object or at the given coordinates451 """Press and hold a given object or at the given coordinates
345 Call release() when the object has been pressed long enough"""452 Call release() when the object has been pressed long enough"""
346 logger.debug("Pressing at: %d,%d", x, y)453 logger.debug("Pressing at: %d,%d", x, y)
347 self._finger_down(x, y)454 self._device.finger_down(x, y)
348455
349 def release(self):456 def release(self):
350 """Release a previously pressed finger"""457 """Release a previously pressed finger"""
351 logger.debug("Releasing")458 logger.debug("Releasing")
352 self._finger_up()459 self._device.finger_up()
353460
354 def move(self, x, y):461 def move(self, x, y):
355 """Moves the pointing "finger" to pos(x,y).462 """Moves the pointing "finger" to pos(x,y).
@@ -357,14 +464,12 @@
357 NOTE: The finger has to be down for this to have any effect.464 NOTE: The finger has to be down for this to have any effect.
358465
359 """466 """
360 if self._touch_finger is None:467 self._device.finger_move(x, y)
361 raise RuntimeError("Attempting to move without finger being down.")
362 self._finger_move(x, y)
363468
364 def drag(self, x1, y1, x2, y2):469 def drag(self, x1, y1, x2, y2):
365 """Perform a drag gesture from (x1,y1) to (x2,y2)"""470 """Perform a drag gesture from (x1,y1) to (x2,y2)"""
366 logger.debug("Dragging from %d,%d to %d,%d", x1, y1, x2, y2)471 logger.debug("Dragging from %d,%d to %d,%d", x1, y1, x2, y2)
367 self._finger_down(x1, y1)472 self._device.finger_down(x1, y1)
368473
369 # Let's drag in 100 steps for now...474 # Let's drag in 100 steps for now...
370 dx = 1.0 * (x2 - x1) / 100475 dx = 1.0 * (x2 - x1) / 100
@@ -372,52 +477,13 @@
372 cur_x = x1 + dx477 cur_x = x1 + dx
373 cur_y = y1 + dy478 cur_y = y1 + dy
374 for i in range(0, 100):479 for i in range(0, 100):
375 self._finger_move(int(cur_x), int(cur_y))480 self._device.finger_move(int(cur_x), int(cur_y))
376 sleep(0.002)481 utilities.sleep(0.002)
377 cur_x += dx482 cur_x += dx
378 cur_y += dy483 cur_y += dy
379 # Make sure we actually end up at target484 # Make sure we actually end up at target
380 self._finger_move(x2, y2)485 self._device.finger_move(x2, y2)
381 self._finger_up()486 self._device.finger_up()
382
383 def _finger_down(self, x, y):
384 """Internal: moves finger "finger" down on the touchscreen.
385
386 :param x: The finger will be moved to this x coordinate.
387 :param y: The finger will be moved to this y coordinate.
388
389 """
390 if self._touch_finger is not None:
391 raise RuntimeError("Cannot press finger: it's already pressed.")
392 self._touch_finger = _get_touch_finger()
393
394 _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
395 _touch_device.write(
396 e.EV_ABS, e.ABS_MT_TRACKING_ID, get_next_tracking_id())
397 _touch_device.write(e.EV_KEY, e.BTN_TOOL_FINGER, 1)
398 _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
399 _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
400 _touch_device.write(e.EV_ABS, e.ABS_MT_PRESSURE, 400)
401 _touch_device.syn()
402
403 def _finger_move(self, x, y):
404 """Internal: moves finger "finger" on the touchscreen to pos (x,y)
405 NOTE: The finger has to be down for this to have any effect."""
406 if self._touch_finger is not None:
407 _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
408 _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_X, int(x))
409 _touch_device.write(e.EV_ABS, e.ABS_MT_POSITION_Y, int(y))
410 _touch_device.syn()
411
412 def _finger_up(self):
413 """Internal: moves finger "finger" up from the touchscreen"""
414 if self._touch_finger is None:
415 raise RuntimeError("Cannot release finger: it's not pressed.")
416 _touch_device.write(e.EV_ABS, e.ABS_MT_SLOT, self._touch_finger)
417 _touch_device.write(e.EV_ABS, e.ABS_MT_TRACKING_ID, -1)
418 _touch_device.write(e.EV_KEY, e.BTN_TOOL_FINGER, 0)
419 _touch_device.syn()
420 self._touch_finger = _release_touch_finger(self._touch_finger)
421487
422488
423# veebers: there should be a better way to handle this.489# veebers: there should be a better way to handle this.
424490
=== modified file 'autopilot/tests/functional/test_input_stack.py'
--- autopilot/tests/functional/test_input_stack.py 2013-12-16 00:20:40 +0000
+++ autopilot/tests/functional/test_input_stack.py 2014-01-21 22:20:18 +0000
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#2#
3# Autopilot Functional Test Tool3# Autopilot Functional Test Tool
4# Copyright (C) 2012-2013 Canonical4# Copyright (C) 2012, 2013, 2014 Canonical
5#5#
6# This program is free software: you can redistribute it and/or modify6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by7# it under the terms of the GNU General Public License as published by
@@ -150,8 +150,8 @@
150 from autopilot.input._X11 import _PRESSED_KEYS150 from autopilot.input._X11 import _PRESSED_KEYS
151 return _PRESSED_KEYS151 return _PRESSED_KEYS
152 elif self.backend == 'UInput':152 elif self.backend == 'UInput':
153 from autopilot.input._uinput import _PRESSED_KEYS153 from autopilot.input import _uinput
154 return _PRESSED_KEYS154 return _uinput.Keyboard._device._pressed_keys_ecodes
155 else:155 else:
156 self.fail("Don't know how to get pressed keys list for backend "156 self.fail("Don't know how to get pressed keys list for backend "
157 + self.backend157 + self.backend
@@ -551,8 +551,9 @@
551 test_result = FakeTestCase("test_press_key").run()551 test_result = FakeTestCase("test_press_key").run()
552552
553 self.assertThat(test_result.wasSuccessful(), Equals(True))553 self.assertThat(test_result.wasSuccessful(), Equals(True))
554 from autopilot.input._uinput import _PRESSED_KEYS554 from autopilot.input import _uinput
555 self.assertThat(_PRESSED_KEYS, Equals([]))555 self.assertThat(
556 _uinput.Keyboard._device._pressed_keys_ecodes, Equals([]))
556557
557 @patch('autopilot.input._X11.fake_input', new=lambda *args: None, )558 @patch('autopilot.input._X11.fake_input', new=lambda *args: None, )
558 def test_mouse_button_released(self):559 def test_mouse_button_released(self):
559560
=== modified file 'autopilot/tests/unit/test_input.py'
--- autopilot/tests/unit/test_input.py 2013-12-10 03:10:11 +0000
+++ autopilot/tests/unit/test_input.py 2014-01-21 22:20:18 +0000
@@ -1,7 +1,7 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2#2#
3# Autopilot Functional Test Tool3# Autopilot Functional Test Tool
4# Copyright (C) 2013 Canonical4# Copyright (C) 2013, 2014 Canonical
5#5#
6# This program is free software: you can redistribute it and/or modify6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by7# it under the terms of the GNU General Public License as published by
@@ -17,11 +17,15 @@
17# along with this program. If not, see <http://www.gnu.org/licenses/>.17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#18#
1919
20from mock import patch20import mock
21import testscenarios
22from evdev import ecodes, uinput
21from testtools import TestCase23from testtools import TestCase
22from testtools.matchers import raises24from testtools.matchers import raises
2325
26from autopilot.input import _uinput
24from autopilot.input._common import get_center_point27from autopilot.input._common import get_center_point
28from autopilot import utilities
2529
2630
27class Empty(object):31class Empty(object):
@@ -84,7 +88,7 @@
84 raises(expected_exception)88 raises(expected_exception)
85 )89 )
8690
87 @patch('autopilot.input._common.logger')91 @mock.patch('autopilot.input._common.logger')
88 def test_get_center_point_logs_with_globalRect(self, mock_logger):92 def test_get_center_point_logs_with_globalRect(self, mock_logger):
89 obj = make_fake_object(globalRect=True)93 obj = make_fake_object(globalRect=True)
90 x, y = get_center_point(obj)94 x, y = get_center_point(obj)
@@ -100,7 +104,7 @@
100 self.assertEqual(123, x)104 self.assertEqual(123, x)
101 self.assertEqual(345, y)105 self.assertEqual(345, y)
102106
103 @patch('autopilot.input._common.logger')107 @mock.patch('autopilot.input._common.logger')
104 def test_get_center_point_logs_with_center_points(self, mock_logger):108 def test_get_center_point_logs_with_center_points(self, mock_logger):
105 obj = make_fake_object(center=True)109 obj = make_fake_object(center=True)
106 x, y = get_center_point(obj)110 x, y = get_center_point(obj)
@@ -116,7 +120,7 @@
116 self.assertEqual(110, x)120 self.assertEqual(110, x)
117 self.assertEqual(120, y)121 self.assertEqual(120, y)
118122
119 @patch('autopilot.input._common.logger')123 @mock.patch('autopilot.input._common.logger')
120 def test_get_center_point_logs_with_xywh(self, mock_logger):124 def test_get_center_point_logs_with_xywh(self, mock_logger):
121 obj = make_fake_object(xywh=True)125 obj = make_fake_object(xywh=True)
122 x, y = get_center_point(obj)126 x, y = get_center_point(obj)
@@ -151,3 +155,459 @@
151155
152 self.assertEqual(123, x)156 self.assertEqual(123, x)
153 self.assertEqual(345, y)157 self.assertEqual(345, y)
158
159
160class UInputKeyboardDeviceTestCase(TestCase):
161 """Test the integration with evdev.UInput for the keyboard."""
162
163 _PRESS_VALUE = 1
164 _RELEASE_VALUE = 0
165
166 def setUp(self):
167 super(UInputKeyboardDeviceTestCase, self).setUp()
168 self.keyboard = _uinput._UInputKeyboardDevice(device_class=mock.Mock)
169 self.keyboard._device.mock_add_spec(uinput.UInput, spec_set=True)
170
171 def test_press_key_should_emit_write_and_syn(self):
172 self.keyboard.press('KEY_A')
173 self._assert_key_press_emitted_write_and_syn('KEY_A')
174
175 def _assert_key_press_emitted_write_and_syn(self, key):
176 self._assert_emitted_write_and_syn(key, self._PRESS_VALUE)
177
178 def _assert_emitted_write_and_syn(self, key, value):
179 key_ecode = ecodes.ecodes.get(key)
180 expected_calls = [
181 mock.call.write(ecodes.EV_KEY, key_ecode, value),
182 mock.call.syn()
183 ]
184
185 self.assertEqual(expected_calls, self.keyboard._device.mock_calls)
186
187 def test_press_key_should_append_leading_string(self):
188 self.keyboard.press('A')
189 self._assert_key_press_emitted_write_and_syn('KEY_A')
190
191 def test_press_key_should_ignore_case(self):
192 self.keyboard.press('a')
193 self._assert_key_press_emitted_write_and_syn('KEY_A')
194
195 def test_press_unexisting_key_should_raise_error(self):
196 error = self.assertRaises(
197 ValueError, self.keyboard.press, 'unexisting')
198
199 self.assertEqual('Unknown key name: unexisting.', str(error))
200
201 def test_release_not_pressed_key_should_raise_error(self):
202 error = self.assertRaises(
203 ValueError, self.keyboard.release, 'A')
204
205 self.assertEqual("Key 'A' not pressed.", str(error))
206
207 def test_release_key_should_emit_write_and_syn(self):
208 self._press_key_and_reset_mock('KEY_A')
209
210 self.keyboard.release('KEY_A')
211 self._assert_key_release_emitted_write_and_syn('KEY_A')
212
213 def _press_key_and_reset_mock(self, key):
214 self.keyboard.press(key)
215 self.keyboard._device.reset_mock()
216
217 def _assert_key_release_emitted_write_and_syn(self, key):
218 self._assert_emitted_write_and_syn(key, self._RELEASE_VALUE)
219
220 def test_release_key_should_append_leading_string(self):
221 self._press_key_and_reset_mock('KEY_A')
222
223 self.keyboard.release('A')
224 self._assert_key_release_emitted_write_and_syn('KEY_A')
225
226 def test_release_key_should_ignore_case(self):
227 self._press_key_and_reset_mock('KEY_A')
228
229 self.keyboard.release('a')
230 self._assert_key_release_emitted_write_and_syn('KEY_A')
231
232 def test_release_unexisting_key_should_raise_error(self):
233 error = self.assertRaises(
234 ValueError, self.keyboard.release, 'unexisting')
235
236 self.assertEqual('Unknown key name: unexisting.', str(error))
237
238 def test_release_pressed_keys_without_pressed_keys_should_do_nothing(self):
239 self.keyboard.release_pressed_keys()
240 self.assertEqual([], self.keyboard._device.mock_calls)
241
242 def test_release_pressed_keys_with_pressed_keys(self):
243 expected_calls = [
244 mock.call.write(
245 ecodes.EV_KEY, ecodes.ecodes.get('KEY_A'),
246 self._RELEASE_VALUE),
247 mock.call.syn(),
248 mock.call.write(
249 ecodes.EV_KEY, ecodes.ecodes.get('KEY_B'),
250 self._RELEASE_VALUE),
251 mock.call.syn()
252 ]
253
254 self._press_key_and_reset_mock('KEY_A')
255 self._press_key_and_reset_mock('KEY_B')
256
257 self.keyboard.release_pressed_keys()
258
259 self.assertEqual(expected_calls, self.keyboard._device.mock_calls)
260
261 def test_release_pressed_keys_already_released(self):
262 expected_calls = []
263 self.keyboard.press('KEY_A')
264 self.keyboard.release_pressed_keys()
265 self.keyboard._device.reset_mock()
266
267 self.keyboard.release_pressed_keys()
268 self.assertEqual(expected_calls, self.keyboard._device.mock_calls)
269
270
271class UInputKeyboardTestCase(testscenarios.TestWithScenarios, TestCase):
272 """Test UInput Keyboard helper for autopilot tests."""
273
274 scenarios = [
275 ('single key', dict(keys='a', expected_calls_args=['a'])),
276 ('upper-case letter', dict(
277 keys='A', expected_calls_args=['KEY_LEFTSHIFT', 'A'])),
278 ('key combination', dict(
279 keys='a+b', expected_calls_args=['a', 'b']))
280 ]
281
282 def setUp(self):
283 super(UInputKeyboardTestCase, self).setUp()
284 # Return to the original device after the test.
285 self.addCleanup(self._set_keyboard_device, _uinput.Keyboard._device)
286 _uinput.Keyboard._device = None
287 self.keyboard = _uinput.Keyboard(device_class=mock.Mock)
288 self.keyboard._device.mock_add_spec(
289 _uinput._UInputKeyboardDevice, spec_set=True)
290 # Mock the sleeps so we don't have to spend time actually sleeping.
291 self.addCleanup(utilities.sleep.disable_mock)
292 utilities.sleep.enable_mock()
293
294 self.keyboard._device.reset_mock()
295
296 def _set_keyboard_device(self, device):
297 _uinput.Keyboard._device = device
298
299 def test_press(self):
300 expected_calls = [
301 mock.call.press(arg) for arg in self.expected_calls_args]
302 self.keyboard.press(self.keys)
303
304 self.assertEqual(expected_calls, self.keyboard._device.mock_calls)
305
306 def test_release(self):
307 self.keyboard.press(self.keys)
308 self.keyboard._device.reset_mock()
309
310 expected_calls = [
311 mock.call.release(arg) for arg in
312 reversed(self.expected_calls_args)]
313 self.keyboard.release(self.keys)
314
315 self.assertEqual(
316 expected_calls, self.keyboard._device.mock_calls)
317
318 def test_press_and_release(self):
319 expected_press_calls = [
320 mock.call.press(arg) for arg in self.expected_calls_args]
321 expected_release_calls = [
322 mock.call.release(arg) for arg in
323 reversed(self.expected_calls_args)]
324
325 self.keyboard.press_and_release(self.keys)
326
327 self.assertEqual(
328 expected_press_calls + expected_release_calls,
329 self.keyboard._device.mock_calls)
330
331
332class UInputTouchDeviceTestCase(TestCase):
333 """Test the integration with evdev.UInput for the touch device."""
334
335 def setUp(self):
336 super(UInputTouchDeviceTestCase, self).setUp()
337 self._number_of_slots = 9
338
339 # Return to the original fingers after the test.
340 self.addCleanup(
341 self._set_fingers_in_use,
342 _uinput._UInputTouchDevice._touch_fingers_in_use,
343 _uinput._UInputTouchDevice._last_tracking_id)
344
345 # Always start the tests without fingers in use.
346 _uinput._UInputTouchDevice._touch_fingers_in_use = []
347 _uinput._UInputTouchDevice._last_tracking_id = 0
348
349 def _set_fingers_in_use(self, touch_fingers_in_use, last_tracking_id):
350 _uinput._UInputTouchDevice._touch_fingers_in_use = touch_fingers_in_use
351 _uinput._UInputTouchDevice._last_tracking_id = last_tracking_id
352
353 def test_finger_down_should_use_free_slot(self):
354 for slot in range(self._number_of_slots):
355 touch = self._get_touch_device()
356
357 touch.finger_down(0, 0)
358
359 self._assert_finger_down_emitted_write_and_syn(
360 touch, slot=slot, tracking_id=mock.ANY, x=0, y=0)
361
362 def _get_touch_device(self):
363 dummy_x_resolution = 100
364 dummy_y_resolution = 100
365 touch = _uinput._UInputTouchDevice(
366 res_x=dummy_x_resolution, res_y=dummy_y_resolution,
367 device_class=mock.Mock)
368 touch._device.mock_add_spec(uinput.UInput, spec_set=True)
369 return touch
370
371 def _assert_finger_down_emitted_write_and_syn(
372 self, touch, slot, tracking_id, x, y):
373 press_value = 1
374 expected_calls = [
375 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
376 mock.call.write(
377 ecodes.EV_ABS, ecodes.ABS_MT_TRACKING_ID, tracking_id),
378 mock.call.write(
379 ecodes.EV_KEY, ecodes.BTN_TOOL_FINGER, press_value),
380 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_X, x),
381 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_Y, y),
382 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_PRESSURE, 400),
383 mock.call.syn()
384 ]
385 self.assertEqual(expected_calls, touch._device.mock_calls)
386
387 def test_finger_down_without_free_slots_should_raise_error(self):
388 # Claim all the available slots.
389 for slot in range(self._number_of_slots):
390 touch = self._get_touch_device()
391 touch.finger_down(0, 0)
392
393 touch = self._get_touch_device()
394
395 # Try to use one more.
396 error = self.assertRaises(RuntimeError, touch.finger_down, 11, 11)
397 self.assertEqual(
398 'All available fingers have been used already.', str(error))
399
400 def test_finger_down_should_use_unique_tracking_id(self):
401 for number in range(self._number_of_slots):
402 touch = self._get_touch_device()
403 touch.finger_down(0, 0)
404
405 self._assert_finger_down_emitted_write_and_syn(
406 touch, slot=mock.ANY, tracking_id=number + 1, x=0, y=0)
407
408 def test_finger_down_should_not_reuse_tracking_ids(self):
409 # Claim and release all the available slots once.
410 for number in range(self._number_of_slots):
411 touch = self._get_touch_device()
412 touch.finger_down(0, 0)
413 touch.finger_up()
414
415 touch = self._get_touch_device()
416
417 touch.finger_down(12, 12)
418 self._assert_finger_down_emitted_write_and_syn(
419 touch, slot=mock.ANY, tracking_id=number + 2, x=12, y=12)
420
421 def test_finger_down_with_finger_pressed_should_raise_error(self):
422 touch = self._get_touch_device()
423 touch.finger_down(0, 0)
424
425 error = self.assertRaises(RuntimeError, touch.finger_down, 0, 0)
426 self.assertEqual(
427 "Cannot press finger: it's already pressed.", str(error))
428
429 def test_finger_move_without_finger_pressed_should_raise_error(self):
430 touch = self._get_touch_device()
431
432 error = self.assertRaises(RuntimeError, touch.finger_move, 10, 10)
433 self.assertEqual(
434 'Attempting to move without finger being down.', str(error))
435
436 def test_finger_move_should_use_assigned_slot(self):
437 for slot in range(self._number_of_slots):
438 touch = self._get_touch_device()
439 touch.finger_down(0, 0)
440 touch._device.reset_mock()
441
442 touch.finger_move(10, 10)
443
444 self._assert_finger_move_emitted_write_and_syn(
445 touch, slot=slot, x=10, y=10)
446
447 def _assert_finger_move_emitted_write_and_syn(self, touch, slot, x, y):
448 expected_calls = [
449 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
450 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_X, x),
451 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_POSITION_Y, y),
452 mock.call.syn()
453 ]
454 self.assertEqual(expected_calls, touch._device.mock_calls)
455
456 def test_finger_move_should_reuse_assigned_slot(self):
457 first_slot = 0
458 touch = self._get_touch_device()
459 touch.finger_down(1, 1)
460 touch._device.reset_mock()
461
462 touch.finger_move(13, 13)
463 self._assert_finger_move_emitted_write_and_syn(
464 touch, slot=first_slot, x=13, y=13)
465 touch._device.reset_mock()
466
467 touch.finger_move(14, 14)
468 self._assert_finger_move_emitted_write_and_syn(
469 touch, slot=first_slot, x=14, y=14)
470
471 def test_finger_up_without_finger_pressed_should_raise_error(self):
472 touch = self._get_touch_device()
473
474 error = self.assertRaises(RuntimeError, touch.finger_up)
475 self.assertEqual(
476 "Cannot release finger: it's not pressed.", str(error))
477
478 def test_finger_up_should_use_assigned_slot(self):
479 fingers = []
480 for slot in range(self._number_of_slots):
481 touch = self._get_touch_device()
482 touch.finger_down(0, 0)
483 touch._device.reset_mock()
484 fingers.append(touch)
485
486 for slot, touch in enumerate(fingers):
487 touch.finger_up()
488
489 self._assert_finger_up_emitted_write_and_syn(touch, slot=slot)
490
491 def _assert_finger_up_emitted_write_and_syn(self, touch, slot):
492 lift_tracking_id = -1
493 release_value = 0
494 expected_calls = [
495 mock.call.write(ecodes.EV_ABS, ecodes.ABS_MT_SLOT, slot),
496 mock.call.write(
497 ecodes.EV_ABS, ecodes.ABS_MT_TRACKING_ID, lift_tracking_id),
498 mock.call.write(
499 ecodes.EV_KEY, ecodes.BTN_TOOL_FINGER, release_value),
500 mock.call.syn()
501 ]
502 self.assertEqual(expected_calls, touch._device.mock_calls)
503
504 def test_finger_up_should_release_slot(self):
505 fingers = []
506 # Claim all the available slots.
507 for slot in range(self._number_of_slots):
508 touch = self._get_touch_device()
509 touch.finger_down(0, 0)
510 fingers.append(touch)
511
512 slot_to_reuse = 3
513 fingers[slot_to_reuse].finger_up()
514
515 touch = self._get_touch_device()
516
517 # Try to use one more.
518 touch.finger_down(15, 15)
519 self._assert_finger_down_emitted_write_and_syn(
520 touch, slot=slot_to_reuse, tracking_id=mock.ANY, x=15, y=15)
521
522 def test_pressed_with_finger_down(self):
523 touch = self._get_touch_device()
524 touch.finger_down(0, 0)
525
526 self.assertTrue(touch.pressed)
527
528 def test_pressed_without_finger_down(self):
529 touch = self._get_touch_device()
530 self.assertFalse(touch.pressed)
531
532 def test_pressed_after_finger_up(self):
533 touch = self._get_touch_device()
534 touch.finger_down(0, 0)
535 touch.finger_up()
536
537 self.assertFalse(touch.pressed)
538
539 def test_pressed_with_other_finger_down(self):
540 other_touch = self._get_touch_device()
541 other_touch.finger_down(0, 0)
542
543 touch = self._get_touch_device()
544 self.assertFalse(touch.pressed)
545
546
547class UInputTouchTestCase(TestCase):
548 """Test UInput Touch helper for autopilot tests."""
549
550 def setUp(self):
551 super(UInputTouchTestCase, self).setUp()
552 self.touch = _uinput.Touch(device_class=mock.Mock)
553 self.touch._device.mock_add_spec(
554 _uinput._UInputTouchDevice, spec_set=True)
555 # Mock the sleeps so we don't have to spend time actually sleeping.
556 self.addCleanup(utilities.sleep.disable_mock)
557 utilities.sleep.enable_mock()
558
559 def test_tap(self):
560 expected_calls = [
561 mock.call.finger_down(0, 0),
562 mock.call.finger_up()
563 ]
564
565 self.touch.tap(0, 0)
566 self.assertEqual(expected_calls, self.touch._device.mock_calls)
567
568 def test_tap_object(self):
569 object_ = type('Dummy', (object,), {'globalRect': (0, 0, 10, 10)})
570 expected_calls = [
571 mock.call.finger_down(5, 5),
572 mock.call.finger_up()
573 ]
574
575 self.touch.tap_object(object_)
576 self.assertEqual(expected_calls, self.touch._device.mock_calls)
577
578 def test_press(self):
579 expected_calls = [mock.call.finger_down(0, 0)]
580
581 self.touch.press(0, 0)
582 self.assertEqual(expected_calls, self.touch._device.mock_calls)
583
584 def test_release(self):
585 expected_calls = [mock.call.finger_up()]
586
587 self.touch.release()
588 self.assertEqual(expected_calls, self.touch._device.mock_calls)
589
590 def test_move(self):
591 expected_calls = [mock.call.finger_move(10, 10)]
592
593 self.touch.move(10, 10)
594 self.assertEqual(expected_calls, self.touch._device.mock_calls)
595
596
597class MultipleUInputTouchBackend(_uinput._UInputTouchDevice):
598
599 def __init__(self, res_x=100, res_y=100, device_class=mock.Mock):
600 super(MultipleUInputTouchBackend, self).__init__(
601 res_x, res_y, device_class)
602
603
604class MultipleUInputTouchTestCase(TestCase):
605
606 def test_with_multiple_touch(self):
607 finger1 = _uinput.Touch(device_class=MultipleUInputTouchBackend)
608 finger2 = _uinput.Touch(device_class=MultipleUInputTouchBackend)
609
610 finger1.press(0, 0)
611 self.addCleanup(finger1.release)
612
613 self.assertFalse(finger2.pressed)
154614
=== modified file 'debian/control'
--- debian/control 2014-01-21 13:02:00 +0000
+++ debian/control 2014-01-21 22:20:18 +0000
@@ -16,6 +16,7 @@
16 python-dbus,16 python-dbus,
17 python-debian,17 python-debian,
18 python-dev,18 python-dev,
19 python-evdev,
19 python-fixtures,20 python-fixtures,
20 python-gi,21 python-gi,
21 python-junitxml,22 python-junitxml,
@@ -30,6 +31,7 @@
30 python-xlib,31 python-xlib,
31 python3-all-dev (>= 3.3),32 python3-all-dev (>= 3.3),
32 python3-dbus,33 python3-dbus,
34 python3-evdev,
33 python3-fixtures,35 python3-fixtures,
34 python3-gi,36 python3-gi,
35 python3-junitxml,37 python3-junitxml,

Subscribers

People subscribed via source and target branches