Skip to content
This repository

Culebra helps you create AndroidViewClient scripts generating a working template that can be modified to suit more specific needs.

                      __    __    __    __
                     /  \  /  \  /  \  /  \ 
____________________/  __\/  __\/  __\/  __\_____________________________
___________________/  /__/  /__/  /__/  /________________________________
                   | / \   / \   / \   / \   \___
                   |/   \_/   \_/   \_/   \    o \ 
                                           \_____/--<

@author: diego @author: Jennifer E. Swofford (ascii art snake)

Introduction

culebra is another extremely valuable tool in AndroidViewClient toolbox.

Its main use is to generate templates or scripts that can later be adapted to suit the most demanding needs. It conveys the full power of AndroidViewClient and python together.

dump.py is a script that helps obtaining the dump of the content of the screen currently present on a device or emulator. dump.py provides the result as a tree of Views representing the logical content of the screen.

In a similar fashion, culebra generates a script, the logically represents and verifies the content of the screen, but instead of comparing bitmaps or using optical character recognition it uses Views properties as main component. In this script culebra can find and verify the existence of Views having a specific ID, some text or text matching some pattern or content descriptions.

Some time ago, I wrote an article about visual image comparison in monkeyrunner, which is also a valid approach for some situations, but the ability to obtain the tree of Views provides a flexibility and effectiveness very difficult to parallel using graphical methods. One of the advantages is the device, screen, resolution and density independence that allows to run the same test on different devices.

Environment

In order to run dump.py and any other AndroidViewClient script some environment variables should be provided containing the right values that allow the script to find other components.

PATH

PATH should contain the <android-sdk>/tools and <android-sdk>/platform-tools directories.

In the example above <android-sdk> represents the path to your Android SDK installation directory.

If shebang is used then it should also be in a directory contained in PATH. /usr/bin/env should be used to locate shebang as in most cases only absolute paths are allowed by shebang.

ANDROID_VIEW_CLIENT_HOME

ANDROID_VIEW_CLIENT_HOME contains the path to the AndroidViewClient directory. This is the directory containing src/ and bin/.

For example:

export ANDROID_VIEW_CLIENT_HOME=/home/user/workspace/AndroidViewClient/AndroidViewClient

ANDROID_VIEW_CLIENT_VERSION

ANDROID_VIEW_CLIENT_VERSION contains the version number of the jar file that will be loaded as plugin from $ANDROID_VIEW_CLIENT_HOME/bin directory. This is a string like 2.3.13.

For example:

export ANDROID_VIEW_CLIENT_VERSION='2.3.13'

PYTHONPATH

PYTHONPATH alternatively this variable can be used to locate modules. This is very useful when scripts are run from Eclipse using PyDev.

Usage

usage: culebra [OPTION]... [serialno]

Options:
  -H, --help                       prints this help                             
  -V, --verbose
  -I, --ignore-secure-device
  -F, --force-view-server-use
  -S, --do-not-start-view-server
  -o, --output=FILE                output to FILE
  -i, --find-views-by-id=BOOL      whether to use findViewById() in script      
  -t, --find-views-with-text=BOOL  whether to use findViewWithText() in script  
  -d, --find-views-with-content-description=BOOL whether to sue findViewWithContentDescription
  -r, --use-regexps                use regexps in matches                       
  -C, --verbose-comments
  -U, --unit-test                  generates unit test script                   
  -j, --use-jar=BOOL               use jar and appropriate shebang to run script
  -D, --use-dictionary=BOOL        use a dictionary to store the Views found    
  -R, --auto-regexps=LIST          auto regexps (i.e. clock) 

-H | --help

Prints the help message and command line usage

-V, --verbose

Sets the verbose mode for the script.

-I, --ignore-secure-device

Ignores that a device is in secured as explained in secure mode.

This is useful when used in combination with LocalViewServer.

-F | --force-view-server-use

Forces the use of ViewServer even if the conditions to use UiAutomator are present. These conditions are depicted in Select the correct implementation. Basically, if the device run on API 16 or greater it can use UiAutomator, but if for some reason you want to still use the previous ViewServer method you can force it using this option.

ViewServer is much slower than UiAutomator, so it seems there are no reasons to use it when the latter is available. However there is one reason that may have some weight and this is the availability of the same View ID's used in the layouts.

-S, --do-not-start-view-server

Avoids starting the ViewServer even when it's not present on the device.

This is useful when used in combination with LocalViewServer.

-o | --output=FILE

Saves the output of this tool to FILE. FILE execute permission is also set when this option is used to be able to run it.

-i, --find-views-by-id=BOOL

Whether to use findViewById() in the generated script.

-t, --find-views-with-text=BOOL

Whether to use findViewByWithText() in the generated script.

-d, --find-views-with-content-description=BOOL

Whether to sue findViewWithContentDescription() in the generated script.

-r, --use-regexps

Use regexps in matches. Every time a selection is done it will use a regexp match instead of text.

-C, --verbose-comments

Generates verbose comments in the script.

-U, --unit-test

Generates unit test script.

-j, --use-jar=BOOL

Whether to use the jar and appropriate shebang to run script.

-D, --use-dictionary=BOOL

Use a dictionary to store the Views found.

-R, --auto-regexps=LIST

When used in conjunction with -r or --use-regexps some cases are identified and the pattern is automatically replaced by pre-existing patterns. LIST is the list of replacements to apply separated by ,. The supported pre-existing matches are:

  • clock Matches clock values (HH:MM) that may appear in some places like the statusbar.

serialno

Specifies the serial number of the device to use if the device serial number cannot be determined automatically for some reason or if there are several devices connected.

Examples

Finding Views with specific content description (emulator, version 4.2)

This examples uses UiAutomator as the API 16 present in the emulator allows it.

Command line

The following command line creates the script not using IDs, using content description regexps to find the views and using the AndroidViewClient jar file

$ culebra -i false -d true -r -j true

the script obtained is

#! /usr/bin/env shebang monkeyrunner -plugin $ANDROID_VIEW_CLIENT_HOME/bin/androidviewclient-2.3.11.jar @!
# -*- coding: utf-8 -*-
'''
Copyright (C) 2013  Diego Torres Milano
Created on 2013-04-16 by Culebra v0.9.5

                      __    __    __    __
                     /  \  /  \  /  \  /  \ 
____________________/  __\/  __\/  __\/  __\_____________________________
___________________/  /__/  /__/  /__/  /________________________________
                   | / \   / \   / \   / \   \___
                   |/   \_/   \_/   \_/   \    o \ 
                                           \_____/--<
@author: diego
@author: Jennifer E. Swofford (ascii art snake)
'''


import re
import sys
import os


from com.dtmilano.android.viewclient import ViewClient

from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice

device, serialno = ViewClient.connectToDeviceOrExit()
vc = ViewClient(device, serialno)

no_id9 = vc.findViewWithContentDescriptionOrRaise(re.compile('Apps'))
no_id10 = vc.findViewWithContentDescriptionOrRaise(re.compile('Widgets'))
no_id15 = vc.findViewWithContentDescriptionOrRaise(re.compile('Analog clock'))
no_id20 = vc.findViewWithContentDescriptionOrRaise(re.compile('API Demos'))
no_id25 = vc.findViewWithContentDescriptionOrRaise(re.compile('ApiDemos'))
no_id30 = vc.findViewWithContentDescriptionOrRaise(re.compile('Bookmark'))
no_id35 = vc.findViewWithContentDescriptionOrRaise(re.compile('Bookmarks'))
no_id40 = vc.findViewWithContentDescriptionOrRaise(re.compile('Calendar'))

Brief description

The generated script uses shebang as shebang to be able to easily locate AndroidViewClient and simplify imports. Then the usual device connection and ViewClient instantiation. Once the ViewClient reference is obtained a series of method calls are generated locating the Views currently on the device screen. In this particular case, because we specified it in culebra command line we are generating only content description calls and using regular expressions as arguments. You can take a look at the screenshot presented before and check that the main Views, having a valid content description, are the ones in the script.

Because no serialno was specified on the command line the regular expression .* that matches any device or emulator is used. In this case the only device present is the emulator-5554.

It's not possible to determine the serial number for the emulator and thus the warning tells us that emulator-5554 was assumed.

Executing the generated script

If we save the generated script we can execute it at a later time.

$ culebra -i false -d true -r -j true -o example1.py

Now, when we execute the script, and the device contains the same screen, that is the same Views (strictly speaking Views with the same content description as before as we are not imposing any other rules in this simple example) the script will exit with no errors

$ ./example1.py

However, if we change the screen, let's say manually selecting the Apps tab and run the script again

$ ./example1.py
...
com.dtmilano.android.viewclient.ViewNotFoundException: Couldn't find View with content-desc that matches 'Analog clock' in tree with root=ROOT
...

We receive the exception telling us that a View with a content description matching 'Alarm clock' was not found.

Something went wrong with that request. Please try again.