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
=== modified file 'autopilot/input/_X11.py'
--- autopilot/input/_X11.py 2013-11-07 05:53:36 +0000
+++ autopilot/input/_X11.py 2014-01-19 01:47:17 +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
@@ -455,7 +455,7 @@
455 x, y = coord["root_x"], coord["root_y"]455 x, y = coord["root_x"], coord["root_y"]
456 return x, y456 return x, y
457457
458 def drag(self, x1, y1, x2, y2):458 def drag(self, x1, y1, x2, y2, rate=10):
459 """Performs a press, move and release.459 """Performs a press, move and release.
460460
461 This is to keep a common API between Mouse and Finger as long as461 This is to keep a common API between Mouse and Finger as long as
@@ -464,7 +464,7 @@
464 """464 """
465 self.move(x1, y1)465 self.move(x1, y1)
466 self.press()466 self.press()
467 self.move(x2, y2)467 self.move(x2, y2, rate=rate)
468 self.release()468 self.release()
469469
470 @classmethod470 @classmethod
471471
=== modified file 'autopilot/input/__init__.py'
--- autopilot/input/__init__.py 2013-09-20 19:01:27 +0000
+++ autopilot/input/__init__.py 2014-01-19 01:47:17 +0000
@@ -369,7 +369,7 @@
369 """369 """
370 raise NotImplementedError("You cannot use this class directly.")370 raise NotImplementedError("You cannot use this class directly.")
371371
372 def drag(self, x1, y1, x2, y2):372 def drag(self, x1, y1, x2, y2, rate=10):
373 """Performs a press, move and release.373 """Performs a press, move and release.
374374
375 This is to keep a common API between Mouse and Finger as long as375 This is to keep a common API between Mouse and Finger as long as
@@ -466,7 +466,7 @@
466 """Release a previously pressed finger"""466 """Release a previously pressed finger"""
467 raise NotImplementedError("You cannot use this class directly.")467 raise NotImplementedError("You cannot use this class directly.")
468468
469 def drag(self, x1, y1, x2, y2):469 def drag(self, x1, y1, x2, y2, rate=10):
470 """Perform a drag gesture from (x1,y1) to (x2,y2)"""470 """Perform a drag gesture from (x1,y1) to (x2,y2)"""
471 raise NotImplementedError("You cannot use this class directly.")471 raise NotImplementedError("You cannot use this class directly.")
472472
@@ -502,6 +502,7 @@
502 self._device = device502 self._device = device
503 self._x = 0503 self._x = 0
504 self._y = 0504 self._y = 0
505 self._pressed = False
505506
506 @property507 @property
507 def x(self):508 def x(self):
@@ -544,6 +545,7 @@
544 raise ValueError(545 raise ValueError(
545 "Touch devices do not have button %d" % (button))546 "Touch devices do not have button %d" % (button))
546 self._device.press(self._x, self._y)547 self._device.press(self._x, self._y)
548 self._pressed = True
547549
548 def release(self, button=1):550 def release(self, button=1):
549 """Releases the pointer at it's current location.551 """Releases the pointer at it's current location.
@@ -560,6 +562,7 @@
560 raise ValueError(562 raise ValueError(
561 "Touch devices do not have button %d" % (button))563 "Touch devices do not have button %d" % (button))
562 self._device.release()564 self._device.release()
565 self._pressed = False
563566
564 def click(self, button=1, press_duration=0.10):567 def click(self, button=1, press_duration=0.10):
565 """Press and release at the current pointer location.568 """Press and release at the current pointer location.
@@ -589,6 +592,8 @@
589 if isinstance(self._device, Mouse):592 if isinstance(self._device, Mouse):
590 self._device.move(x, y)593 self._device.move(x, y)
591 else:594 else:
595 if self._pressed:
596 self._device.move(x, y)
592 self._x = x597 self._x = x
593 self._y = y598 self._y = y
594599
@@ -641,9 +646,9 @@
641 else:646 else:
642 return (self._x, self._y)647 return (self._x, self._y)
643648
644 def drag(self, x1, y1, x2, y2):649 def drag(self, x1, y1, x2, y2, rate=10):
645 """Performs a press, move and release."""650 """Performs a press, move and release."""
646 self._device.drag(x1, y1, x2, y2)651 self._device.drag(x1, y1, x2, y2, rate=rate)
647 if isinstance(self._device, Touch):652 if isinstance(self._device, Touch):
648 self._x = x2653 self._x = x2
649 self._y = y2654 self._y = y2
650655
=== modified file 'autopilot/input/_uinput.py'
--- autopilot/input/_uinput.py 2013-11-07 05:53:36 +0000
+++ autopilot/input/_uinput.py 2014-01-19 01:47:17 +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
@@ -361,23 +361,33 @@
361 raise RuntimeError("Attempting to move without finger being down.")361 raise RuntimeError("Attempting to move without finger being down.")
362 self._finger_move(x, y)362 self._finger_move(x, y)
363363
364 def drag(self, x1, y1, x2, y2):364 def drag(self, x1, y1, x2, y2, rate=10):
365 """Perform a drag gesture from (x1,y1) to (x2,y2)"""365 """Perform a drag gesture from (x1,y1) to (x2,y2)"""
366 logger.debug("Dragging from %d,%d to %d,%d", x1, y1, x2, y2)366 logger.debug("Dragging from %d,%d to %d,%d", x1, y1, x2, y2)
367 self._finger_down(x1, y1)367 self._finger_down(x1, y1)
368368
369 # Let's drag in 100 steps for now...369 current_x, current_y = x1, y1
370 dx = 1.0 * (x2 - x1) / 100370 while current_x != x2 or current_y != y2:
371 dy = 1.0 * (y2 - y1) / 100371 dx = abs(x2 - current_x)
372 cur_x = x1 + dx372 dy = abs(y2 - current_y)
373 cur_y = y1 + dy373
374 for i in range(0, 100):374 intx = float(dx) / max(dx, dy)
375 self._finger_move(int(cur_x), int(cur_y))375 inty = float(dy) / max(dx, dy)
376
377 step_x = min(rate * intx, dx)
378 step_y = min(rate * inty, dy)
379
380 if x2 < current_x:
381 step_x *= -1
382 if y2 < current_y:
383 step_y *= -1
384
385 current_x += step_x
386 current_y += step_y
387 self._finger_move(current_x, current_y)
388
376 sleep(0.002)389 sleep(0.002)
377 cur_x += dx390
378 cur_y += dy
379 # Make sure we actually end up at target
380 self._finger_move(x2, y2)
381 self._finger_up()391 self._finger_up()
382392
383 def _finger_down(self, x, y):393 def _finger_down(self, x, y):
384394
=== 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-19 01:47:17 +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,10 +17,14 @@
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 testscenarios
21
22from mock import call, Mock, patch
21from testtools import TestCase23from testtools import TestCase
22from testtools.matchers import raises24from testtools.matchers import raises
2325
26import autopilot.input
27from autopilot.input import _uinput, _X11
24from autopilot.input._common import get_center_point28from autopilot.input._common import get_center_point
2529
2630
@@ -151,3 +155,232 @@
151155
152 self.assertEqual(123, x)156 self.assertEqual(123, x)
153 self.assertEqual(345, y)157 self.assertEqual(345, y)
158
159
160class PartialMock(object):
161 """Mock some of the methods of an object, and record their calls."""
162
163 def __init__(self, real_object, *args):
164 super(PartialMock, self).__init__()
165 self._mock_manager = Mock()
166 self._real_object = real_object
167 self.patched_attributes = args
168
169 def __getattr__(self, name):
170 """Forward all the calls to the real object."""
171 return self._real_object.__getattribute__(name)
172
173 @property
174 def mock_calls(self):
175 """Return the calls recorded for the mocked attributes."""
176 return self._mock_manager.mock_calls
177
178 def __enter__(self):
179 self._start_patchers()
180 return self
181
182 def _start_patchers(self):
183 self._patchers = []
184 for attribute in self.patched_attributes:
185 patcher = patch.object(self._real_object, attribute)
186 self._patchers.append(patcher)
187
188 self._mock_manager.attach_mock(patcher.start(), attribute)
189
190 def __exit__(self, exc_type, exc_val, exc_tb):
191 self._stop_patchers()
192
193 def _stop_patchers(self):
194 for patcher in self._patchers:
195 patcher.stop()
196
197
198class MockX11Mouse(PartialMock):
199 """Mock for the X11 Mouse Touch.
200
201 It records the calls to press, release and move, but doesn't perform them.
202
203 """
204
205 def __init__(self):
206 super(MockX11Mouse, self).__init__(
207 _X11.Mouse(), 'press', 'release', 'move')
208
209 def get_move_call_args_list(self):
210 return self._mock_manager.move.call_args_list
211
212
213class X11MouseTestCase(TestCase):
214
215 def test_drag_should_call_move_with_rate(self):
216 expected_first_move_call = call(0, 0)
217 expected_second_move_call = call(100, 100, rate=1)
218 with MockX11Mouse() as mock_mouse:
219 mock_mouse.drag(0, 0, 100, 100, rate=1)
220
221 self.assertEqual(
222 [expected_first_move_call, expected_second_move_call],
223 mock_mouse.get_move_call_args_list())
224
225 def test_drag_with_default_rate(self):
226 expected_first_move_call = call(0, 0)
227 expected_second_move_call = call(100, 100, rate=10)
228 with MockX11Mouse() as mock_mouse:
229 mock_mouse.drag(0, 0, 100, 100)
230
231 self.assertEqual(
232 [expected_first_move_call, expected_second_move_call],
233 mock_mouse.get_move_call_args_list())
234
235
236class MockUinputTouch(PartialMock):
237 """Mock for the uinput Touch.
238
239 It records the calls to _finger_down, _finger_up and _finger_move, but
240 doesn't perform them.
241
242 """
243
244 def __init__(self):
245 super(MockUinputTouch, self).__init__(
246 _uinput.Touch(), '_finger_down', '_finger_up', '_finger_move')
247
248 def get_finger_move_call_args_list(self):
249 return self._mock_manager._finger_move.call_args_list
250
251
252class UinputTouchTestCase(TestCase):
253
254 def test_drag_finger_actions(self):
255 expected_finger_calls = [
256 call._finger_down(0, 0),
257 call._finger_move(10, 10),
258 call._finger_up()
259 ]
260 with MockUinputTouch() as mock_touch:
261 mock_touch.drag(0, 0, 10, 10)
262 self.assertEqual(mock_touch.mock_calls, expected_finger_calls)
263
264 def test_drag_should_call_move_with_rate(self):
265 expected_move_calls = [call(5, 5), call(10, 10), call(15, 15)]
266 with MockUinputTouch() as mock_touch:
267 mock_touch.drag(0, 0, 15, 15, rate=5)
268
269 self.assertEqual(
270 expected_move_calls, mock_touch.get_finger_move_call_args_list())
271
272 def test_drag_with_default_rate(self):
273 expected_move_calls = [call(10, 10), call(20, 20)]
274 with MockUinputTouch() as mock_touch:
275 mock_touch.drag(0, 0, 20, 20)
276
277 self.assertEqual(
278 expected_move_calls, mock_touch.get_finger_move_call_args_list())
279
280 def test_drag_to_same_place_should_not_move(self):
281 expected_finger_calls = [
282 call._finger_down(0, 0),
283 call._finger_up()
284 ]
285 with MockUinputTouch() as mock_touch:
286 mock_touch.drag(0, 0, 0, 0)
287 self.assertEqual(mock_touch.mock_calls, expected_finger_calls)
288
289
290class DragUinputTouchTestCase(testscenarios.TestWithScenarios, TestCase):
291
292 scenarios = [
293 ('drag to top', dict(
294 start_x=50, start_y=50, stop_x=50, stop_y=30,
295 expected_moves=[call(50, 40), call(50, 30)])),
296 ('drag to bottom', dict(
297 start_x=50, start_y=50, stop_x=50, stop_y=70,
298 expected_moves=[call(50, 60), call(50, 70)])),
299 ('drag to left', dict(
300 start_x=50, start_y=50, stop_x=30, stop_y=50,
301 expected_moves=[call(40, 50), call(30, 50)])),
302 ('drag to right', dict(
303 start_x=50, start_y=50, stop_x=70, stop_y=50,
304 expected_moves=[call(60, 50), call(70, 50)])),
305
306 ('drag to top-left', dict(
307 start_x=50, start_y=50, stop_x=30, stop_y=30,
308 expected_moves=[call(40, 40), call(30, 30)])),
309 ('drag to top-right', dict(
310 start_x=50, start_y=50, stop_x=70, stop_y=30,
311 expected_moves=[call(60, 40), call(70, 30)])),
312 ('drag to bottom-left', dict(
313 start_x=50, start_y=50, stop_x=30, stop_y=70,
314 expected_moves=[call(40, 60), call(30, 70)])),
315 ('drag to bottom-right', dict(
316 start_x=50, start_y=50, stop_x=70, stop_y=70,
317 expected_moves=[call(60, 60), call(70, 70)])),
318
319 ('drag less than rate', dict(
320 start_x=50, start_y=50, stop_x=55, stop_y=55,
321 expected_moves=[call(55, 55)])),
322
323 ('drag with last move less than rate', dict(
324 start_x=50, start_y=50, stop_x=65, stop_y=65,
325 expected_moves=[call(60, 60), call(65, 65)])),
326 ]
327
328 def test_drag_moves(self):
329 with MockUinputTouch() as mock_touch:
330 mock_touch.drag(
331 self.start_x, self.start_y, self.stop_x, self.stop_y)
332
333 self.assertEqual(
334 self.expected_moves, mock_touch.get_finger_move_call_args_list())
335
336
337class PointerTestCase(TestCase):
338
339 def setUp(self):
340 super(PointerTestCase, self).setUp()
341 self.pointer = autopilot.input.Pointer(autopilot.input.Mouse.create())
342
343 def test_drag_with_rate(self):
344 with patch.object(self.pointer._device, 'drag') as mock_drag:
345 self.pointer.drag(0, 0, 20, 20, rate=5)
346
347 mock_drag.assert_called_once_with(0, 0, 20, 20, rate=5)
348
349 def test_drag_with_default_rate(self):
350 with patch.object(self.pointer._device, 'drag') as mock_drag:
351 self.pointer.drag(0, 0, 20, 20)
352
353 mock_drag.assert_called_once_with(0, 0, 20, 20, rate=10)
354
355
356class PointerWithTouchBackendTestCase(TestCase):
357
358 def setUp(self):
359 super(PointerWithTouchBackendTestCase, self).setUp()
360 self.pointer = autopilot.input.Pointer(autopilot.input.Touch.create())
361
362 def test_not_pressed_move_should_not_move_pointing_finger(self):
363 with patch.object(_uinput.Touch, 'move') as mock_move:
364 self.pointer.move(1, 1)
365
366 self.assertFalse(mock_move.called)
367
368 def test_pressed_move_should_move_pointing_finger(self):
369 with MockUinputTouch() as mock_touch:
370 self.pointer._device = mock_touch
371 with patch.object(_uinput.Touch, 'move') as mock_move:
372 self.pointer.press()
373 self.pointer.move(1, 1)
374
375 mock_move.assert_called_once_with(1, 1)
376
377 def test_released_move_should_not_move_pointing_finger(self):
378 with MockUinputTouch() as mock_touch:
379 self.pointer._device = mock_touch
380 with patch.object(_uinput.Touch, 'move') as mock_move:
381 self.pointer.press()
382 self.pointer.release()
383
384 self.pointer.move(1, 1)
385
386 self.assertFalse(mock_move.called)

Subscribers

People subscribed via source and target branches