Search code examples
androidpython-2.7monkeyrunnerandroidviewclient

Error of Script with MonkeyRunner and AndroidViewClient (Touch)


I'm tryng to test some app using monkeyrunner and AndroidViewClient, also I have used culebra to generate code, But I have this erro:

from com.dtmilano.android.viewclient import ViewClient, TextView, EditText
SyntaxError: ("mismatched input 'as' expecting COLON", ('/home/experts/AndroidViewClient-master/src/com/dtmilano/android/viewclient.py', 3795, 35, ''))

This is my script whit monkeyrunner and AndroidViewClient

import re
import sys
import os

try:
   sys.path.append(os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src'))
except:
   pass

from com.dtmilano.android.viewclient import ViewClient, TextView, EditText
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage

def main():
# Connects to the current device, returning a MonkeyDevice object
   emulatorid = 'emulator-5558'
   device = MonkeyRunner.waitForConnection('',emulatorid)
   print "waiting for connection...\n"

   package = "com.duolingo"
   activity = "app.LoginActivity"

   # sets the name of the component to start
   runComponent = package + "/." + activity

   #Runs the component
   print "Start Component"
   device.startActivity(component=runComponent)
   MonkeyRunner.sleep(10)

   vc = ViewClient(device)
   vc.dump()
   vc.sleep(_s)
   vc.dump(window=-1)
   vc.findViewWithTextOrRaise(u'GET STARTED').touch()
   vc.sleep(_s)
   vc.dump(window=-1)
   vc.findViewWithTextOrRaise(u'Spanish', root=self.vc.findViewByIdOrRaise('id/no_id/3')).touch()

   # Takes a screenshot
   screenshot = device.takeSnapshot()

   # Writes the screenshot to a file
   screenshot.writeToFile('./device1.png','png')

   print "end"
   if __name__ == '__main__':
   main()

If I execute this wthiout monkeyrunner it show an erro about "ViewClient(device)", This script is inside an loop (for).

EDIT:

I executed culebra and now I have my scripts running fine!! But, it works by CommandLine ($ myscripttest.py emulator-5554) If I call it using subprocess.call, where emulatorid='emulator-5554'

subprocess.call('path/script_files/myscripttest.py' + ' ' + emulatorid, shell=True)

it shows an error:

  File "/path/script_files/myscripttest.py", line 9, in <module>
from com.dtmilano.android.viewclient import ViewClient
File "/usr/local/lib/python2.7/dist-packages/androidviewclient-11.0.7-py2.7.egg/com/dtmilano/android/viewclient.py", line 43, in <module>
import xml.parsers.expat
File "/usr/lib/python2.7/xml/parsers/expat.py", line 4, in <module>
from pyexpat import *
ImportError: /usr/lib/python2.7/lib-dynload/pyexpat.x86_64-linux-gnu.so: undefined symbol: XML_SetHashSalt

I verify with this code: Where is the error?

$ ldd /usr/lib/python2.7/lib-dynload/pyexpat.x86_64-linux-gnu.so 
linux-vdso.so.1 =>  (0x00007fffe5dc2000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f55d2fd4000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f55d2c0f000)
libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f55d29e5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f55d3403000)

Solution

  • AndroidViewClient/culebra is a monkeyrunner replacement. You should use one or the other, not both together in a script.

    culerba or Culebra GUI will generate the correct test or script template for you that you can further customize.

    All the tests and scripts generated by these tools should be executed with python 2.x.

    I think I could guess what you are trying to achieve with your script and I'm going to give a step-by-step example using culebra so you can understand how easy it is.

    Step 1

    Launch culebra telling that you also want to start a specific Activity

    $ culebra -GuU --start-activity=com.duolingo/.app.LoginActivity --scale=0.5 -o ~/tmp/duolingo-screenshot.py
    

    this runs the GUI (-G), does not verify the Views in the dump (-u), create a unit test (-U), start the Activity (--start-activity), scales the window 50% so it fits on the screen (--scale) and saves the test in a file (-o).

    If everything goes well, you will see Culebra GUI.

    enter image description here

    Step 2

    Click on the GET STARTED button on the window.

    enter image description here

    Step 3

    Click on Spanish

    enter image description here

    Step 4

    Take the desired screenshot using the context menu (alternate click on the window) or keyboard shortcut

    enter image description here

    Step 5

    Close the window.

    At this point the test is generated and it contains something like this

    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    '''
    Copyright (C) 2013-2014  Diego Torres Milano
    Created on 2015-12-22 by Culebra v11.0.8
                          __    __    __    __
                         /  \  /  \  /  \  /  \ 
    ____________________/  __\/  __\/  __\/  __\_____________________________
    ___________________/  /__/  /__/  /__/  /________________________________
                       | / \   / \   / \   / \   \___
                       |/   \_/   \_/   \_/   \    o \ 
                                               \_____/--<
    @author: Diego Torres Milano
    @author: Jennifer E. Swofford (ascii art snake)
    '''
    
    
    import re
    import sys
    import os
    
    
    import unittest
    
    from com.dtmilano.android.viewclient import ViewClient, CulebraTestCase
    
    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': 'com.duolingo/.app.LoginActivity', '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': 0.5, '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': True, '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
    
            self.vc.dump(window=-1)
            self.vc.findViewWithTextOrRaise(u'GET STARTED').touch()
            self.vc.sleep(_s)
            self.vc.dump(window=-1)
            self.vc.findViewWithTextOrRaise(u'Spanish', root=self.vc.findViewByIdOrRaise('id/no_id/5')).touch()
            self.vc.sleep(_s)
            self.vc.dump(window=-1)
            self.vc.writeImageToFile('/tmp/${serialno}-${focusedwindowname}-${timestamp}.png', 'PNG', 'None', False, False)
    
    
    if __name__ == '__main__':
        CulebraTests.main()
    

    So when you run the test again, you will obtain a new screenshot.

    IMPORTANT: because the test does not contain any specific information about the device or its characteristics like screen size and such and it's not using coordinates to touch but View properties like text or id you can run the same test on a very different device, like let's say a table, and it will run successfully. Even the screenshots are saved prefixed by the device's serial number so you can run all of them at the same time and on the same CI server if you like.

    Extra tip

    If you run the test more than once without reinstalling the app perhaps the second time you are not presented with the GET STARTED screen but with Pick a Daily Goal. If we wanted to consider this alternative too we can just edit the script and add the corresponding condition

    def testSomething(self):
        if not self.preconditions():
            self.fail('Preconditions failed')
    
        _s = CulebraTests.sleep
        _v = CulebraTests.verbose
    
        self.vc.dump(window=-1)
        if not self.vc.findViewWithText(u'Pick a Daily Goal'):
            self.vc.findViewWithTextOrRaise(u'GET STARTED').touch()
            self.vc.sleep(_s)
            self.vc.dump(window=-1)
            self.vc.findViewWithTextOrRaise(u'Spanish', root=self.vc.findViewByIdOrRaise('id/no_id/5')).touch()
            self.vc.sleep(_s)
            self.vc.dump(window=-1)
        self.vc.writeImageToFile('/tmp/${serialno}-${focusedwindowname}-${timestamp}.png', 'PNG', 'None', False, False)
    

    Even if you want to frame the screenshot in a device image you can do it much easier than with Android Studio where you have to re-select the options again and again. Just change the parameters of takeScreenshot, set the STUDIO_DIR environment variable pointing to your Studio installation

    self.vc.writeImageToFile('/tmp/${serialno}-${focusedwindowname}-${timestamp}.png', 'PNG', 'nexus_5', True, True)
    

    and voila!

    enter image description here