0

Question

My application has a map-like canvas where the user can move by dragging the map around. What I'm trying to accomplish is move the map to the right of 100px and then to the left of 100px and check if the position in the center is the same.

The code is as follows:

device.drag((640, 360), (640 - 100, 360))
device.drag((640, 360), (640 + 100, 360))
# check

This doesn't reliably bring the map to the same place every time. Sometimes the device hangs or is slow and it moves few extra pixels; some other times in the last move step it goes faster, giving it momentum.

Is there any way to precisely drag the screen of a certain amount of pixels? If the device hangs or is slow it doesn't matter, as long as the movement in the end is correct.

My attempts

I've tried to tweak the third (duration) and fourth (steps) argument to no avail.


I've also tried to implement my custom drag code with:

# Touch down screen                                                                               
device.touch(100, 500, MonkeyDevice.DOWN)                                           

# Move from 100, 500 to 200, 500
for i in range(1, 11):                                                              
    device.touch(100 + 10 * i, 500, MonkeyDevice.MOVE)
    time.sleep(0.1)

# Extra sleep to avoid momentum
time.sleep(0.2)

# Remove finger from screen
device.touch(200, 500, MonkeyDevice.UP)

and then to the other side:

# Touch down screen                                                                               
device.touch(200, 500, MonkeyDevice.DOWN)                                           

# Move from 200, 500 to 100, 500
for i in range(1, 11):                                                              
    device.touch(200 - 10 * i, 500, MonkeyDevice.MOVE)
    time.sleep(0.1)

# Extra sleep to avoid momentum
time.sleep(0.2)

# Remove finger from screen
device.touch(100, 500, MonkeyDevice.UP)

In a similar fashion I've also tried to test my game pad keys with:

for _ in range(0, 10):
    device.press('KEYCODE_DPAD_RIGHT', MonkeyDevice.DOWN_AND_UP)
    time.sleep(0.1)

for _ in range(0, 10):
    device.press('KEYCODE_DPAD_LEFT', MonkeyDevice.DOWN_AND_UP)
    time.sleep(0.1)

and even then sometimes monkeyrunner will either skip events or not consider the up event and therefore causing a long press (which is equivalent to "keep moving on the map").

Useful references:

Community
  • 1
  • 1
Shoe
  • 74,840
  • 36
  • 166
  • 272

2 Answers2

1

Very good question. After reading it I was also intrigued and I wonder if the problem affects AndroidViewClient/culebra too (which is more reliable).

However, as the folowing steps can tell it might be more related to the way Maps moves than the reliability of the different protocols sending envents. Also, a methond of automating the test is given, as you mentioned that you were doing a visual comparison of the results.

I think it would be of great help to share this and it may give you additional ideas to test your app.

Started by creating a test using culebra GUI.

enter image description here

Then I slightly edited the test to factor the points and add the opposite drag.

#! /usr/bin/env python
# -*- coding: utf-8 -*-
'''
Copyright (C) 2013-2016  Diego Torres Milano
Created on 2017-03-04 by Culebra
                      __    __    __    __
                     /  \  /  \  /  \  /  \ 
____________________/  __\/  __\/  __\/  __\_____________________________
___________________/  /__/  /__/  /__/  /________________________________
                   | / \   / \   / \   / \   \___
                   |/   \_/   \_/   \_/   \    o \ 
                                           \_____/--<
@author: Diego Torres Milano
@author: Jennifer E. Swofford (ascii art snake)
'''


import re
import sys
import os


import unittest
try:
    sys.path.insert(0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src'))
except:
    pass

import pkg_resources
pkg_resources.require('androidviewclient>=13.0.0')
from com.dtmilano.android.viewclient import ViewClient, CulebraTestCase
from com.dtmilano.android.uiautomator.uiautomatorhelper import UiAutomatorHelper, UiScrollable, UiObject, UiObject2

TAG = 'CULEBRA'


class CulebraTests(CulebraTestCase):

    @classmethod
    def setUpClass(cls):
        cls.kwargs1 = {'ignoreversioncheck': False, 'verbose': False, 'ignoresecuredevice': False}
        cls.kwargs2 = {'forceviewserveruse': False, 'useuiautomatorhelper': False, 'ignoreuiautomatorkilled': True, 'autodump': False, 'startviewserver': True, 'compresseddump': True}
        cls.options = {'start-activity': None, 'concertina': False, 'device-art': None, 'use-jar': False, 'multi-device': False, 'unit-test-class': True, 'save-screenshot': None, 'use-dictionary': False, 'glare': False, 'dictionary-keys-from': 'id', 'scale': 1, 'find-views-with-content-description': True, 'window': -1, 'orientation-locked': None, 'save-view-screenshots': None, 'find-views-by-id': True, 'log-actions': False, 'use-regexps': False, 'null-back-end': False, 'auto-regexps': None, 'do-not-verify-screen-dump': True, 'verbose-comments': False, 'gui': False, 'find-views-with-text': True, 'prepend-to-sys-path': False, 'install-apk': None, 'drop-shadow': False, 'output': None, 'unit-test-method': None, 'interactive': False}
        cls.sleep = 5

    def setUp(self):
        super(CulebraTests, self).setUp()

    def tearDown(self):
        super(CulebraTests, self).tearDown()

    def preconditions(self):
        if not super(CulebraTests, self).preconditions():
            return False
        return True

    def testSomething(self):
        if not self.preconditions():
            self.fail('Preconditions failed')

        _s = CulebraTests.sleep
        _v = CulebraTests.verbose

        d = '/tmp/'

        p = (377.14, 380.86)
        q = (175.14, 380.86)

        self.vc.writeImageToFile(d + 'map-start.png')

        # Each step execution is throttled to 5ms per step
        # So for a 400 steps, the swipe will take about 2 second to complete
        steps = 400
        duration = 2000
        # Let's give some extra delay.
        sleep = 3

        for n in range(10):
            print n
            self.device.dragDip(p, q, duration, steps)
            self.vc.sleep(sleep)
            self.device.dragDip(q, p, duration, steps)
            self.vc.sleep(sleep)

        self.vc.writeImageToFile(d + 'map-finish.png')

        self.device.compare(d + 'map-finish.png', d + 'map-start.png', d + 'map-compare.png')

if __name__ == '__main__':
    CulebraTests.main()

Once run it, these are the results before and after the iterations

enter image description here enter image description here enter image description here

In this last image, you can see the visual comparison and it reveals there a slight movement. However, I think this might be just the way Maps do it.

I also used CulebraTester which uses a completely different backend based on UiAutomator and the same problem shows up. So I don't think is something related to drag.

Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
1

I have measured the visual difference with our appetizer-toolkit and discover similar issues here. Just some thoughts and previous experiences.

For timing, we've tried several backends, such as sendevent, MonkeyDevice/Chimp. As I mentioned, sendevent would create a process per point, which totally kills the timing.MonkeyDevice agent on the device is just not as reliable as it should be and we have some initial attempts but later abandoned. Check this if you are interested. The current acceptable backend is openstf/minitouch, which is with current toolkit. After calibrating and some tuning, I believe the toolkit would get better (aimed for 1.0.5 release for now)

I doubt that one can achieve actual "deterministic replays" with whatever those input recording and replaying tools. From our experiences, even if we can enforce perfect determinism for input events (x,y and timing), the app would still have other non-determinism, notably lags, network activities and Canvas view responsiveness. I suspect the problem you encountered is an add-up of the imprecision of the MonkeyDevice and the Canvas View's problem. Thus for your higher level design, treat these tools, as well as our toolkit, as a "input automation tool" and have some tolerance of the error.

mingyuan-xia
  • 265
  • 4
  • 10