Tuesday, December 22, 2015

AndroidViewClient/culebra vs. MonkeyRunner

More than 2 years ago I took a crucial decision in AndroidViewClient/culebra development plan and that was to free it from `monkeyrunner`, Jython and Chimpchat.

AndroidViewClient/culebra was liberated and starting with version 4.0.0 it does not require any other runtime environment than python 2.x (read announcement). It can be installed and upgraded using the corresponding platform tools like easy_install or pip and can be easily integrated into IDEs like Eclipse PyDev or Pycharm. It also improves speed, solves chimpchat bugs, and even provides a GUI whre you can automatically create tests or scripts without writing a single line of code.

Nonetheless, from time to time, I receive some questions or reports about problems with scripts created with `culebra` that are attempted to run with `monkeyrunner` or some other combinations. I take the blame for it. I failed at communicating that AndroidViewClient/culebra is a complete replacement and should not be used together.

In order to improve the situation I gave a very detailed, easy to follow, step-by-step answer to
Error of Script with MonkeyRunner and AndroidViewClient (Touch) on Stackoverflow, showing how you can create a test case that automatically starts.and Activity (Duolingo) , checks if some Views are on the screen, touches them and finally take the screenshot. All from the GUI.



I hope you find this explanation useful.

Monday, August 24, 2015

culebra: concertina mode

One of the latest additions to culebra is the concertina mode. Named after the concertina movement occurring in snakes and other legless organisms that consists of gripping or anchoring with portions of the body while pulling/pushing other sections in the direction of movement, this mode allows culebra to move across the UI.

You may have used android's monkey before. monkey is a UI/Application exerciser that generates pseudo-random streams of user events such as clicks, touches, or gestures, as well as a number of system-level events. It is commonly used to stress-test application. While it may serve this purpose, it's overly complicated to be used in other scenarios.


Then, it comes culebra concertina mode that instead of sending pseudo-random events, analyzes the content of the screen and randomly selects a suitable event or action for the also randomly selected target, normally a View.

For example, if the randomly selected view is an EditText, culebra enters some random text. However if the EditText turns to be a password, it sends random passwords, that is instead of just entering a normal sentence it selected sample passwords from a list like the infamous "querty".
Furthermore, if it detects the EditText is expecting an email address, by inspecting the id, the hint or anything else that may suggest that, it sends random email addresses like user@example.com. Of course, other Views, receive other treatment, like Buttons being clicked, Scrollables being scrolled, etc.

Additionally, if the content description suggest the application may be awaiting for you to talk, like 'Tap to Speak' or 'Voice Search', culebra will actually speak random text to the app (this feature is only available on Linux and OSX).

You should check this aspect, it's sometimes hilarious.


Taking advantage of the culebra GUI already described in previous posts, while it's running the randomly selected Views are highlighted. At the same time, the python script is being generated. You can save it, modify it and run the same tests again and again. It's worth to mention that commonly the script eliminates the random selection of components and thus it runs deterministically.

Run culebra in concertina mode

$ culebra --gui --concertina --scale=0.5

and you can obtain something like this, where the nickname of a new contact was randomly selected from the available Views and the random text "chaos reigned" was entered.


BTW, this screenshot was taken via dump as described in android: obtaining beautiful screenshots automatically.


Friday, May 29, 2015

Google Cloud Test Lab

Yesterday, at Google I/O 2015, Cloud Test Lab was announced. One of its promoted features is: "you can run all of your tests across all devices, all at the same time--giving you access to massively parallel testing, bringing you deep and scaled insight".
Wow, that's pretty much the same I described in my previous post,
android: culebra multi-device capabilities, minus the devices of course. You have to provide them.

Wouldn't it be great if Cloud Test Lab allows you to create your personalized tests, in addition to the automatically generated ones, using something like culebra GUI, where you can just point and click?

Tuesday, May 19, 2015

android: culebra multi-device capabilities

culebra and its GUI both include multi-device capabilities in auto-generated test cases and scripts.

The test case or script is created as usual, but when the --multi-device option is present in the command line, the statements generated will be slightly different, still you can easily recognize them. These statements will include support for several devices using python's list comprehension.

For example, a typical self.vc.dump(window=-1) will become

 [vc.dump(window=-1) for vc in self.allVcs()]

that is, for all the ViewClient's dump the content of the default window. You may wonder where the list of all ViewClient's obtained by self.allVcs() comes from. CulebraTestCase class handles multiple devices in its setUp() method and it's there where the lists of all devices, serial numbers and ViewClient's are handled

self.devices.append(ConnectedDevice(serialno=serialno, device=device, vc=vc))

Everything is handled automatically, so there's nothing to worry about. If only one device is connected to adb or a single serial number is passed to the test case as an argument, then the test case behaves exactly as single-device. However, if more than one device is connected to adb and they are specified on the command line the multi-device capabilities kicks-in and the test is run on all devices at the same time.
The special command line keyword all can be used to mean all the connected devices.

This trivial test for Calculator generated using

$ culebra --gui --multi-device \
    --unit-test-class \
    --do-not-verify-screen-dump \
    -o ~/tmp/multi.py

shows multi-device capabilities.


#! /usr/bin/env python
# -*- coding: utf-8 -*-
'''
Copyright (C) 2013-2014  Diego Torres Milano
Created on 2015-05-19 by Culebra v10.5.1
                      __    __    __    __
                     /  \  /  \  /  \  /  \ 
____________________/  __\/  __\/  __\/  __\_____________________________
___________________/  /__/  /__/  /__/  /________________________________
                   | / \   / \   / \   / \   \___
                   |/   \_/   \_/   \_/   \    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

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 = {'compresseddump': True, 'startviewserver': True, 'forceviewserveruse': False, 'autodump': False, 'ignoreuiautomatorkilled': True}
        cls.options = {'start-activity': None, 'device-art': None, 'use-jar': False, 'multi-device': True, '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': True, 'find-views-with-text': True, 'prepend-to-sys-path': True, 'drop-shadow': False, 'output': '/Users/diego/tmp/multi2.py', '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

        [_vc.dump(window=-1) for _vc in self.allVcs()]
        [_vc.findViewWithTextOrRaise(u'2').touch() for _vc in self.allVcs()]
        [_vc.sleep(_s) for _vc in self.allVcs()]
        [_vc.dump(window=-1) for _vc in self.allVcs()]
        [_vc.findViewWithContentDescriptionOrRaise(u'''plus''').touch() for _vc in self.allVcs()]
        [_vc.sleep(_s) for _vc in self.allVcs()]
        [_vc.dump(window=-1) for _vc in self.allVcs()]
        [_vc.findViewWithTextOrRaise(u'1').touch() for _vc in self.allVcs()]
        [_vc.sleep(_s) for _vc in self.allVcs()]
        [_vc.dump(window=-1) for _vc in self.allVcs()]
        [_vc.findViewWithContentDescriptionOrRaise(u'''equals''').touch() for _vc in self.allVcs()]
        [_vc.sleep(_s) for _vc in self.allVcs()]
        [_vc.dump(window=-1) for _vc in self.allVcs()]


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


Then, when you run the test with several devices connected as

$ ~/tmp/multi.py -s all

you will obtain something like this



Wednesday, May 13, 2015

android: beautiful screenshots, take 2

In my previous post, android: obtaining beautiful screenshots automatically, I introduced an automated way of obtaining screenshots of your application perhaps after doing some actions that would take considerably time to do manually over time.

We described how culebra intuitive UI could help you create the automation script even if you don't know anything about programing or python.

However, for simpler cases or when you just want to take one screenshot and an automation script is not worth the effort, dump, another tool in AndroidViewClient's toolbox can be used.

Simply, to take a screenshot and add device art, shadow and glare:


$ dump -f /path/to/image -ZBA auto

or, with the long option which are easier to remember

$ dump --save-screenshot=/path/to/file \
    --drop-shadow \
    --glare \
    --device-art=auto

Now taking advantage of this opportunity. I would like to introduce some new device art support added in the latest version, like this Samsung Galaxy S4 (this screenshot has been taken with the previous command)




Sunday, April 26, 2015

android: obtaining beautiful screenshots automatically

How much time have you invested obtaining screenshots for your app and then making them look nicer framing them in the corresponding device artwork?

You have to install the app, manually do the steps needed to get to the point where you want to take the screenshot to produce the most effective communication of the idea you are trying to convey, actually taking the screenshot, saving it to a file and then probably using Device Art Generator to frame it using the corresponding artwork and orientation that you have to decide and finally downloading the result.

Android Studio may simplify these steps a bit, but still is annoyingly slow and time consuming to have to check the desired options every time.



The model (i.e. Nexus 5) has to be selected every time as well as Drop Shadow and Screen Glare. The orientation is not detected and you have to Rotate it manually if incorrect.

Wouldn't it be great if you can automate all of these steps and get the screenshots without human intervention?

I have wondered the same many times and finally support for device artwork was added to AndroidViewClient/culebra.

Task automation using culebra

As an example, just let's pretend we want to prepare some screenshots to be included in the documentation or presentation about our app, which in this case would be Calculator.
Every time we create a new version of the app we want to reproduce the same scenario an take the beautiful screenshot. For the sake of simplicity, this scenario is the simple calculation "2+1=3".

Let's run

$ culebra --gui --do-not-verify-screen-dump --scale=0.5 \
    --device-art=auto --drop-shadow --glare \
    -o ~/tmp/calculator.py

This runs `culebra` GUI.
Once the window reproducing your device screen is shown, do:

  1. Press HOME, to start from a known state
  2. Touch on All Apps
  3. Touch on Calculator
  4. Click with the secondary button to open the context menu and select Long touch point using PX (the shortcut for this action is ^L)
  5. Touch on Calculator's DEL, to completely clear the screen
  6. Touch 2
  7. Touch +
  8. Touch 1
  9. Click with the secondary button to open the context menu and select Take snapshot and save to file (the shortcut is ^F)
  10. Close the window
Now we have the `calculator.py` screen generated, that every time we run it will reproduce the steps, detect the device and orientation, take the screenshot and frame it with the corresponding device artwork and will add the drop shadow and glare.



Analyzing the generated script

Let's take a closer look at the script generated by `culebra`.


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


import re
import sys
import os


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

from com.dtmilano.android.viewclient import ViewClient

TAG = 'CULEBRA'

_s = 5
_v = '--verbose' in sys.argv


kwargs1 = {'ignoreversioncheck': False, 'verbose': False, 'ignoresecuredevice': False}
device, serialno = ViewClient.connectToDeviceOrExit(**kwargs1)
kwargs2 = {'compresseddump': True, 'startviewserver': True, 'forceviewserveruse': False, 'autodump': False, 'ignoreuiautomatorkilled': True}
vc = ViewClient(device, serialno, **kwargs2)
#vc.dump(window='-1') # FIXME: seems not needed

vc.dump(window=-1)
device.press('HOME')
vc.dump(window=-1)
vc.findViewWithContentDescriptionOrRaise(u'''Apps''').touch()
vc.sleep(_s)
vc.dump(window=-1)
vc.findViewWithContentDescriptionOrRaise(u'''Calculator''').touch()
vc.sleep(_s)
vc.dump(window=-1)
device.longTouch(912.0, 838.0, 2000, 0)
vc.sleep(5)
vc.dump(window=-1)
vc.findViewWithTextOrRaise(u'2').touch()
vc.sleep(_s)
vc.dump(window=-1)
vc.findViewWithContentDescriptionOrRaise(u'''plus''').touch()
vc.sleep(_s)
vc.dump(window=-1)
vc.findViewWithTextOrRaise(u'1').touch()
vc.sleep(_s)
vc.dump(window=-1)
vc.writeImageToFile('/tmp/${serialno}-${focusedwindowname}-${timestamp}.png', 'PNG', 'auto', True, True)


The first part is the same for all culebra scripts. Some global variables, like TAG, are defined and can lately be used as parameters for some method calls like `Log.d()`.
Then the ViewClient object is created connecting to the selected device. If you don't specify any `serialno` the default device will be selected.
Once the ViewClient object is created we see the method invocations that correspond to the action we did on the GUI, closed by the invocation to `vc.writeImageToFile()` which will be creating the screenshot. In this case, and determined by the command line arguments used when we launched `culebra`, device art will be `auto` selected, and drop shadow (True) and glare (True) will be added.
The filename generation for the taken screenshot uses some keywords, replaced at save time, to avoid overwriting old ones if any.

Here is where the magic begins

As a proof-of-concept, let's physically rotate the device and run the generated script again

$ ~/tmp/calculator.py

once it runs we will obtain the correct screenshot for landscape orientation



No human intervention!



Monday, January 26, 2015

New culebra logo

culebra logo
Dean Britto of deanbritto.com has graciously created this awesome culebra logo and release it under a Creative Commons Attribution 4.0 International License.

This will be incorporated in future culebra versions and documentation.


Thanks Dean for your fine work!

Thursday, January 22, 2015

AndroidViewClient/culebra 10K/month download mark

I proudly announce that last month we achieved the 10K/month download.




Thank you all collaborator, contributors, testers and users that helped in the creation and evolution of one of the best and most versatile Android automated testing tool.

Since culebra's GUI inception some months ago, we closed the gap between programmers and testers. Even though the auto-generated test scripts are the same as the ones that could be manually created using AndroidViewClient as a library  with the GUI we lowered the bar and the tool can be used even you don't know how to program in python and even if you don't program at all.

In retrospective last months we have seen several fundamental additions, like as I mentioned

  • culebra GUI
  • multi-device test generation and run capability (more coming soon)
  • orientation locked tests
  • log test steps to file, screen and adb logcat
  • screen and View snapshots with filename generated from pattern
  • added WiFiManager to mimic Android device's
  • calculation of distance between different View trees
  • drag using PX or DIP
  • and many, many more

Stay in touch. You comments and suggestions are welcome, and http://stackoverflow.com/questions/tagged/androidviewclient is a great way of getting your questions answered.
Install from pypi, clone from github and read the documentation from the wiki (yes, it's been updated and cleaned up lately, however some still needs some love, anyone?).

See you at the 100K/month post ;-)