Merge lp:~elopio/autopilot/fix1266601-Pointer-pressed-move into lp:autopilot

Proposed by Leo Arias
Status: Superseded
Proposed branch: lp:~elopio/autopilot/fix1266601-Pointer-pressed-move
Merge into: lp:autopilot
Diff against target: 418 lines (+270/-22)
4 files modified
autopilot/input/_X11.py (+3/-3)
autopilot/input/__init__.py (+9/-4)
autopilot/input/_uinput.py (+23/-13)
autopilot/tests/unit/test_input.py (+235/-2)
To merge this branch: bzr merge lp:~elopio/autopilot/fix1266601-Pointer-pressed-move
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Autopilot Hackers Pending
Review via email: mp+200617@code.launchpad.net

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

Commit message

Do a touch move when the Pointer is pressed.

To post a comment you must log in.
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)
410. By Leo Arias

Merged with trunk.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
411. By Leo Arias

Merged with prerequisite branch.

412. By Leo Arias

Merged with prerequisite branch.

413. By Leo Arias

Merged with prerequisite branch.

414. By Leo Arias

Merged with prerequisite branch.

415. By Leo Arias

Use the mock touch so now real mouse events are used.

416. By Leo Arias

Merged with prerequisite.

417. By Leo Arias

Cleaned up the tests.

418. By Leo Arias

Cleaned up the tests.

419. By Leo Arias

Merged with prerequisite.

420. By Leo Arias

Updated prerequisite.

421. By Leo Arias

Use the device pressed property.

422. By Leo Arias

Fixed the tests backend.

423. By Leo Arias

Cleanup.

424. By Leo Arias

Finger move with transition.

425. By Leo Arias

Be consistent on the use of attributes.

426. By Leo Arias

Fixed pep8.

Unmerged revisions

426. By Leo Arias

Fixed pep8.

425. By Leo Arias

Be consistent on the use of attributes.

424. By Leo Arias

Finger move with transition.

423. By Leo Arias

Cleanup.

422. By Leo Arias

Fixed the tests backend.

421. By Leo Arias

Use the device pressed property.

420. By Leo Arias

Updated prerequisite.

419. By Leo Arias

Merged with prerequisite.

418. By Leo Arias

Cleaned up the tests.

417. By Leo Arias

Cleaned up the tests.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'autopilot/input/_X11.py'
2--- autopilot/input/_X11.py 2013-11-07 05:53:36 +0000
3+++ autopilot/input/_X11.py 2014-01-19 01:47:17 +0000
4@@ -1,7 +1,7 @@
5 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
6 #
7 # Autopilot Functional Test Tool
8-# Copyright (C) 2012-2013 Canonical
9+# Copyright (C) 2012, 2013, 2014 Canonical
10 #
11 # This program is free software: you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13@@ -455,7 +455,7 @@
14 x, y = coord["root_x"], coord["root_y"]
15 return x, y
16
17- def drag(self, x1, y1, x2, y2):
18+ def drag(self, x1, y1, x2, y2, rate=10):
19 """Performs a press, move and release.
20
21 This is to keep a common API between Mouse and Finger as long as
22@@ -464,7 +464,7 @@
23 """
24 self.move(x1, y1)
25 self.press()
26- self.move(x2, y2)
27+ self.move(x2, y2, rate=rate)
28 self.release()
29
30 @classmethod
31
32=== modified file 'autopilot/input/__init__.py'
33--- autopilot/input/__init__.py 2013-09-20 19:01:27 +0000
34+++ autopilot/input/__init__.py 2014-01-19 01:47:17 +0000
35@@ -369,7 +369,7 @@
36 """
37 raise NotImplementedError("You cannot use this class directly.")
38
39- def drag(self, x1, y1, x2, y2):
40+ def drag(self, x1, y1, x2, y2, rate=10):
41 """Performs a press, move and release.
42
43 This is to keep a common API between Mouse and Finger as long as
44@@ -466,7 +466,7 @@
45 """Release a previously pressed finger"""
46 raise NotImplementedError("You cannot use this class directly.")
47
48- def drag(self, x1, y1, x2, y2):
49+ def drag(self, x1, y1, x2, y2, rate=10):
50 """Perform a drag gesture from (x1,y1) to (x2,y2)"""
51 raise NotImplementedError("You cannot use this class directly.")
52
53@@ -502,6 +502,7 @@
54 self._device = device
55 self._x = 0
56 self._y = 0
57+ self._pressed = False
58
59 @property
60 def x(self):
61@@ -544,6 +545,7 @@
62 raise ValueError(
63 "Touch devices do not have button %d" % (button))
64 self._device.press(self._x, self._y)
65+ self._pressed = True
66
67 def release(self, button=1):
68 """Releases the pointer at it's current location.
69@@ -560,6 +562,7 @@
70 raise ValueError(
71 "Touch devices do not have button %d" % (button))
72 self._device.release()
73+ self._pressed = False
74
75 def click(self, button=1, press_duration=0.10):
76 """Press and release at the current pointer location.
77@@ -589,6 +592,8 @@
78 if isinstance(self._device, Mouse):
79 self._device.move(x, y)
80 else:
81+ if self._pressed:
82+ self._device.move(x, y)
83 self._x = x
84 self._y = y
85
86@@ -641,9 +646,9 @@
87 else:
88 return (self._x, self._y)
89
90- def drag(self, x1, y1, x2, y2):
91+ def drag(self, x1, y1, x2, y2, rate=10):
92 """Performs a press, move and release."""
93- self._device.drag(x1, y1, x2, y2)
94+ self._device.drag(x1, y1, x2, y2, rate=rate)
95 if isinstance(self._device, Touch):
96 self._x = x2
97 self._y = y2
98
99=== modified file 'autopilot/input/_uinput.py'
100--- autopilot/input/_uinput.py 2013-11-07 05:53:36 +0000
101+++ autopilot/input/_uinput.py 2014-01-19 01:47:17 +0000
102@@ -1,7 +1,7 @@
103 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
104 #
105 # Autopilot Functional Test Tool
106-# Copyright (C) 2012-2013 Canonical
107+# Copyright (C) 2012, 2013, 2014 Canonical
108 #
109 # This program is free software: you can redistribute it and/or modify
110 # it under the terms of the GNU General Public License as published by
111@@ -361,23 +361,33 @@
112 raise RuntimeError("Attempting to move without finger being down.")
113 self._finger_move(x, y)
114
115- def drag(self, x1, y1, x2, y2):
116+ def drag(self, x1, y1, x2, y2, rate=10):
117 """Perform a drag gesture from (x1,y1) to (x2,y2)"""
118 logger.debug("Dragging from %d,%d to %d,%d", x1, y1, x2, y2)
119 self._finger_down(x1, y1)
120
121- # Let's drag in 100 steps for now...
122- dx = 1.0 * (x2 - x1) / 100
123- dy = 1.0 * (y2 - y1) / 100
124- cur_x = x1 + dx
125- cur_y = y1 + dy
126- for i in range(0, 100):
127- self._finger_move(int(cur_x), int(cur_y))
128+ current_x, current_y = x1, y1
129+ while current_x != x2 or current_y != y2:
130+ dx = abs(x2 - current_x)
131+ dy = abs(y2 - current_y)
132+
133+ intx = float(dx) / max(dx, dy)
134+ inty = float(dy) / max(dx, dy)
135+
136+ step_x = min(rate * intx, dx)
137+ step_y = min(rate * inty, dy)
138+
139+ if x2 < current_x:
140+ step_x *= -1
141+ if y2 < current_y:
142+ step_y *= -1
143+
144+ current_x += step_x
145+ current_y += step_y
146+ self._finger_move(current_x, current_y)
147+
148 sleep(0.002)
149- cur_x += dx
150- cur_y += dy
151- # Make sure we actually end up at target
152- self._finger_move(x2, y2)
153+
154 self._finger_up()
155
156 def _finger_down(self, x, y):
157
158=== modified file 'autopilot/tests/unit/test_input.py'
159--- autopilot/tests/unit/test_input.py 2013-12-10 03:10:11 +0000
160+++ autopilot/tests/unit/test_input.py 2014-01-19 01:47:17 +0000
161@@ -1,7 +1,7 @@
162 # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
163 #
164 # Autopilot Functional Test Tool
165-# Copyright (C) 2013 Canonical
166+# Copyright (C) 2013, 2014 Canonical
167 #
168 # This program is free software: you can redistribute it and/or modify
169 # it under the terms of the GNU General Public License as published by
170@@ -17,10 +17,14 @@
171 # along with this program. If not, see <http://www.gnu.org/licenses/>.
172 #
173
174-from mock import patch
175+import testscenarios
176+
177+from mock import call, Mock, patch
178 from testtools import TestCase
179 from testtools.matchers import raises
180
181+import autopilot.input
182+from autopilot.input import _uinput, _X11
183 from autopilot.input._common import get_center_point
184
185
186@@ -151,3 +155,232 @@
187
188 self.assertEqual(123, x)
189 self.assertEqual(345, y)
190+
191+
192+class PartialMock(object):
193+ """Mock some of the methods of an object, and record their calls."""
194+
195+ def __init__(self, real_object, *args):
196+ super(PartialMock, self).__init__()
197+ self._mock_manager = Mock()
198+ self._real_object = real_object
199+ self.patched_attributes = args
200+
201+ def __getattr__(self, name):
202+ """Forward all the calls to the real object."""
203+ return self._real_object.__getattribute__(name)
204+
205+ @property
206+ def mock_calls(self):
207+ """Return the calls recorded for the mocked attributes."""
208+ return self._mock_manager.mock_calls
209+
210+ def __enter__(self):
211+ self._start_patchers()
212+ return self
213+
214+ def _start_patchers(self):
215+ self._patchers = []
216+ for attribute in self.patched_attributes:
217+ patcher = patch.object(self._real_object, attribute)
218+ self._patchers.append(patcher)
219+
220+ self._mock_manager.attach_mock(patcher.start(), attribute)
221+
222+ def __exit__(self, exc_type, exc_val, exc_tb):
223+ self._stop_patchers()
224+
225+ def _stop_patchers(self):
226+ for patcher in self._patchers:
227+ patcher.stop()
228+
229+
230+class MockX11Mouse(PartialMock):
231+ """Mock for the X11 Mouse Touch.
232+
233+ It records the calls to press, release and move, but doesn't perform them.
234+
235+ """
236+
237+ def __init__(self):
238+ super(MockX11Mouse, self).__init__(
239+ _X11.Mouse(), 'press', 'release', 'move')
240+
241+ def get_move_call_args_list(self):
242+ return self._mock_manager.move.call_args_list
243+
244+
245+class X11MouseTestCase(TestCase):
246+
247+ def test_drag_should_call_move_with_rate(self):
248+ expected_first_move_call = call(0, 0)
249+ expected_second_move_call = call(100, 100, rate=1)
250+ with MockX11Mouse() as mock_mouse:
251+ mock_mouse.drag(0, 0, 100, 100, rate=1)
252+
253+ self.assertEqual(
254+ [expected_first_move_call, expected_second_move_call],
255+ mock_mouse.get_move_call_args_list())
256+
257+ def test_drag_with_default_rate(self):
258+ expected_first_move_call = call(0, 0)
259+ expected_second_move_call = call(100, 100, rate=10)
260+ with MockX11Mouse() as mock_mouse:
261+ mock_mouse.drag(0, 0, 100, 100)
262+
263+ self.assertEqual(
264+ [expected_first_move_call, expected_second_move_call],
265+ mock_mouse.get_move_call_args_list())
266+
267+
268+class MockUinputTouch(PartialMock):
269+ """Mock for the uinput Touch.
270+
271+ It records the calls to _finger_down, _finger_up and _finger_move, but
272+ doesn't perform them.
273+
274+ """
275+
276+ def __init__(self):
277+ super(MockUinputTouch, self).__init__(
278+ _uinput.Touch(), '_finger_down', '_finger_up', '_finger_move')
279+
280+ def get_finger_move_call_args_list(self):
281+ return self._mock_manager._finger_move.call_args_list
282+
283+
284+class UinputTouchTestCase(TestCase):
285+
286+ def test_drag_finger_actions(self):
287+ expected_finger_calls = [
288+ call._finger_down(0, 0),
289+ call._finger_move(10, 10),
290+ call._finger_up()
291+ ]
292+ with MockUinputTouch() as mock_touch:
293+ mock_touch.drag(0, 0, 10, 10)
294+ self.assertEqual(mock_touch.mock_calls, expected_finger_calls)
295+
296+ def test_drag_should_call_move_with_rate(self):
297+ expected_move_calls = [call(5, 5), call(10, 10), call(15, 15)]
298+ with MockUinputTouch() as mock_touch:
299+ mock_touch.drag(0, 0, 15, 15, rate=5)
300+
301+ self.assertEqual(
302+ expected_move_calls, mock_touch.get_finger_move_call_args_list())
303+
304+ def test_drag_with_default_rate(self):
305+ expected_move_calls = [call(10, 10), call(20, 20)]
306+ with MockUinputTouch() as mock_touch:
307+ mock_touch.drag(0, 0, 20, 20)
308+
309+ self.assertEqual(
310+ expected_move_calls, mock_touch.get_finger_move_call_args_list())
311+
312+ def test_drag_to_same_place_should_not_move(self):
313+ expected_finger_calls = [
314+ call._finger_down(0, 0),
315+ call._finger_up()
316+ ]
317+ with MockUinputTouch() as mock_touch:
318+ mock_touch.drag(0, 0, 0, 0)
319+ self.assertEqual(mock_touch.mock_calls, expected_finger_calls)
320+
321+
322+class DragUinputTouchTestCase(testscenarios.TestWithScenarios, TestCase):
323+
324+ scenarios = [
325+ ('drag to top', dict(
326+ start_x=50, start_y=50, stop_x=50, stop_y=30,
327+ expected_moves=[call(50, 40), call(50, 30)])),
328+ ('drag to bottom', dict(
329+ start_x=50, start_y=50, stop_x=50, stop_y=70,
330+ expected_moves=[call(50, 60), call(50, 70)])),
331+ ('drag to left', dict(
332+ start_x=50, start_y=50, stop_x=30, stop_y=50,
333+ expected_moves=[call(40, 50), call(30, 50)])),
334+ ('drag to right', dict(
335+ start_x=50, start_y=50, stop_x=70, stop_y=50,
336+ expected_moves=[call(60, 50), call(70, 50)])),
337+
338+ ('drag to top-left', dict(
339+ start_x=50, start_y=50, stop_x=30, stop_y=30,
340+ expected_moves=[call(40, 40), call(30, 30)])),
341+ ('drag to top-right', dict(
342+ start_x=50, start_y=50, stop_x=70, stop_y=30,
343+ expected_moves=[call(60, 40), call(70, 30)])),
344+ ('drag to bottom-left', dict(
345+ start_x=50, start_y=50, stop_x=30, stop_y=70,
346+ expected_moves=[call(40, 60), call(30, 70)])),
347+ ('drag to bottom-right', dict(
348+ start_x=50, start_y=50, stop_x=70, stop_y=70,
349+ expected_moves=[call(60, 60), call(70, 70)])),
350+
351+ ('drag less than rate', dict(
352+ start_x=50, start_y=50, stop_x=55, stop_y=55,
353+ expected_moves=[call(55, 55)])),
354+
355+ ('drag with last move less than rate', dict(
356+ start_x=50, start_y=50, stop_x=65, stop_y=65,
357+ expected_moves=[call(60, 60), call(65, 65)])),
358+ ]
359+
360+ def test_drag_moves(self):
361+ with MockUinputTouch() as mock_touch:
362+ mock_touch.drag(
363+ self.start_x, self.start_y, self.stop_x, self.stop_y)
364+
365+ self.assertEqual(
366+ self.expected_moves, mock_touch.get_finger_move_call_args_list())
367+
368+
369+class PointerTestCase(TestCase):
370+
371+ def setUp(self):
372+ super(PointerTestCase, self).setUp()
373+ self.pointer = autopilot.input.Pointer(autopilot.input.Mouse.create())
374+
375+ def test_drag_with_rate(self):
376+ with patch.object(self.pointer._device, 'drag') as mock_drag:
377+ self.pointer.drag(0, 0, 20, 20, rate=5)
378+
379+ mock_drag.assert_called_once_with(0, 0, 20, 20, rate=5)
380+
381+ def test_drag_with_default_rate(self):
382+ with patch.object(self.pointer._device, 'drag') as mock_drag:
383+ self.pointer.drag(0, 0, 20, 20)
384+
385+ mock_drag.assert_called_once_with(0, 0, 20, 20, rate=10)
386+
387+
388+class PointerWithTouchBackendTestCase(TestCase):
389+
390+ def setUp(self):
391+ super(PointerWithTouchBackendTestCase, self).setUp()
392+ self.pointer = autopilot.input.Pointer(autopilot.input.Touch.create())
393+
394+ def test_not_pressed_move_should_not_move_pointing_finger(self):
395+ with patch.object(_uinput.Touch, 'move') as mock_move:
396+ self.pointer.move(1, 1)
397+
398+ self.assertFalse(mock_move.called)
399+
400+ def test_pressed_move_should_move_pointing_finger(self):
401+ with MockUinputTouch() as mock_touch:
402+ self.pointer._device = mock_touch
403+ with patch.object(_uinput.Touch, 'move') as mock_move:
404+ self.pointer.press()
405+ self.pointer.move(1, 1)
406+
407+ mock_move.assert_called_once_with(1, 1)
408+
409+ def test_released_move_should_not_move_pointing_finger(self):
410+ with MockUinputTouch() as mock_touch:
411+ self.pointer._device = mock_touch
412+ with patch.object(_uinput.Touch, 'move') as mock_move:
413+ self.pointer.press()
414+ self.pointer.release()
415+
416+ self.pointer.move(1, 1)
417+
418+ self.assertFalse(mock_move.called)

Subscribers

People subscribed via source and target branches