Search code examples
androidpythonmonkeyrunner

Actions in monkeyrunner are not reliable


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:


Solution

  • 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.