Getting started with Dogtail
(Version 0.1)

Raghu Sarangapani

Last Updated

Jan 18, 2006


Table of Contents

1.      Introduction

2.      Pre requisites for dogtail

3.      Sources and setup of dogtail

4.      Scripting with dogtail

4.1.  Importing dogtail Libraries

4.2.  Load Dogtail objects

4.3.  Deleting old log files

4.4.  Launching an application

4.5.  Working with application widgets

4.6.  Get / Set  Text field values

4.7.  Simulating Widget operations

4.8.  Building Verification logic

4.9. Test Result Logging

4.10. Some Useful Tips

5.      Executing dogtail scripts

6.      Recording User actions

7.      Dogtail Configurations

8.      Further Support

9.      Sample Script Explained
    

1. Introduction:

 
dogtail is a GUI test tool and automation framework written in Python. It uses Accessibility (a11y) technology to communicate with desktop applications. dogtail scripts are written in Python and executed like any other Python program. An attempt has been made, through this document, to help beginners to get started with dogtail.

This document is based on   dogtail version 0.4.2.

TOP ^


2.
Pre-requisites for dogtail:

Dependent packages for dogtail,

TOP ^ 

3. Source and setup of dogtail:

The source code for dogtail and pyspi is managed in the GNOME project's CVS server.

Dogtail Sources:

http://cvs.gnome.org/viewcvs/dogtail/

Sample scripts can be downloaded at  http://cvs.gnome.org/viewcvs/dogtail/examples/

To build dogtail, as root executethe following commands
           
    ./setup.py build
          ./setup.py install

A source tarball for pyspi can be downloaded http://people.redhat.com/zcerza/dogtail/releases/pyspi-0.5.2.tar.gz

A source tarball for dogtail can be downloaded from  http://people.redhat.com/zcerza/dogtail/releases/dogtail-0.4.3.tar.gz

To setup ImageMagick, after untarring the file, as root, run the commands,
        ./configure,
        #make
        #make install

To Build and install ElementTree, as root, run
        ./ElementTree.py

To install pyspi, as root type,
        ./setup1.py install --prefix=/usr

To install Pyrex, Install Pyrex-0.9.3.1-1.src.rpm
             a. Build rpm from this SRPM,

            rpmbuild --rebuild Pyrex-0.9.3.1-1.src.rpm

             b. Install the resultant Pyrex-0.9.3.1-1.noarch.rpm in /usr/src/packages/RPMS/noarch/, 
                       
rpm -ivh Pyrex-0.9.3.1-1.noarch.rpm –nodeps

           Dogtail files tree (after installation):

  Dogtail gets installed under - /usr/lib/python*/site-packages/dogtail, under which all dogtail related files can be seen.
              Documents of dogtail under - /usr/share/doc/python-dogtail/*

TOP ^

4. Scripting with dogtail:

Dogtail is a test automation tool which uses accessibility feature of the underlying desktop. So it is necessary to turn-on the accessibility feature while working with Dogtail.

   Say to turn on accessibility for Gnome Desktop from the panel menu,

·   Go to the desktop Preferences menu -> Preferences -> Accessibility -> Assistive Technology Support

·   Ensure that the "Enable assistive technologies" checkbox is checked

            OR

·  Run the following command:

                gconftool-2 --set --type bool /desktop/gnome/interface/accessibility true

 
To start off with scripting,

4.1. Importing dogtail libraries

At the beginning of every dogtail script, the necessary libraries needs to be imported inorder to use the functions defined in dogtail,  

import dogtail.config
import dogtail.tc
from dogtail.procedural import *
from dogtail.utils import screenshot
from os import environ, path, remove
environ['LANG']='en_US.UTF-8'


4.2. Load Dogtail objects

The commonly used objects for verification, and configuration in any dogtail scripts need to be defined at the beginning of the script
DemoConfig = dogtail.config.Config()
TestString = dogtail.tc.TCString()
TestImage = dogtail.tc.TCImage()

TestNumber = dogtail.tc.TCNumber()

More information on how these objects are used during scripting is explained in subsequent sections

4.3. Delete old log files

Clearing of old test log files can be accomplished using the following command. This can be used at the beginning of the test script,

if path.isfile(path.join(path.expandvars("$HOME"), "Desktop", "<Filename>")):
   remove(path.join(path.expandvars("$HOME"), "Desktop", "<Filename>"))

TOP ^

4.4. Launching an application
"Run" is the command defined to launch any given application on the desktop.  Here the <application> needs to be substituted with the binary name of the application you wish  to launch

run('<application>'),

Eg: run('gedit')

4.5. Working with application widgets

Focus.application function is used to focus on the application window which is needed for further action

focus.application('<application name>'),

Eg: focus.application('gedit')

4.6. Get/Set text Field Values

Functions which can be used to work with various text fields in applications are explained below:

Focus on the text area,

focus.text()

To open a file and read contents into a variable,

from sys import path
demo = file(path[0] + '<path of file from present working directory>')


Read the file into the text area by assiging the value of demo  to  focussed widget

focus.widget.text = demo.read()

Here, demo is a variable to read character by character of the file into the text area,
          path is the current path from where script is being executed.
   
4.7. Simulating Widgets Operations


Toolbar buttons can be operated after focus.application('<application name>') by,
   
click('<button name>'),

Eg: click('Save').


Same applies for recursive for selecting the menu items,
Say for selecting  "Quit" under menu "File" menu,

focus.application('<application name>')
click('<menu name>')

Eg: focus.application('gedit')

       click('Quit')

            For dialogs,

Get the focus of the dialog so as to get all the widgets information,

focus.dialog('<Dialog Name>'),

Eg: focus.dialog('Save as...')

After getting the focus on the dialog, widget operations on the dialog is possible,

To click the expandable buttons,

activate('<Button name>'),

 Eg: activate('Browse for other folders')

To select contents of table like in a 'Save As' dialog,

activate('<Widget name>', roleName = 'role name'),

Eg: activate('Desktop', roleName = 'table cell')

To set the name in text boxes of dialogs,

focus.text()
focus.widget.text = '<filename>',

Eg: focus.widget.text = 'UTF8demo.txt'

To click on the buttons,

click('<Button filename>'), 

Eg: click('Save')

TOP ^

4.8. Building Verification logic

Verification logic refers to the inbuilt verification that needs to be incorporated in the script inorder to validate the actions performed in the script. For example, in a dogtail script eog is made to open a image file, the script needs to capture the image of the application and compare with a baseline. This comparison will  yield in "pass" / "fail" for a particular test case.

Dogtail has a few functions defined for building verification logic in the scripts  Image comparison, string comparison and number comparison.

String Comparison: Compares 2 strings to see if they are the same. The user may specify the encoding to which the two strings are to be normalized for the comparison.  Default encoding is the default system encoding.

Syntax: compare(self, label, baseline, undertest, encoding=Config.encoding)

Eg: TestString.compare(label, baseline, testfile[i], encoding='utf-8')

Here label is the line being compared with baseline, the count of which increments string by string.

Image Comparison: Use ImageMagick to compare 2 files. This Calls ImageMagick's "compare" program. Default compares are based on size but metric based comparisons are also supported with a threshold determining pass/fail criteria.

Syntax: compare(self, label, baseline, undertest, dfile='default', metric='none', threshold=0.0)

Eg: TestImage.compare(label, baseline, testfile[i])

          ImageMagick's "capture" can be used to capture images ( imagecapture function )

         Number Comparison: Compares 2 numbers to see if they are the same. The user may specify how to normalize mixed type comparisons via the type argument. Compares 2 numbers by the type provided in the type arg.
Syntax: compare(self, label, baseline, undertest, type)

           Eg: TestNumber.compare(label, baseline, testfile[i])

         File compare: Compares two text files line by line.

Open the existing file1,

try:
    file1 = open(path[0] + '<filename1>', 'r').readlines()
except IOError:
    print "File open failed"

Open the new file2,

filepath = environ['HOME'] + '<filename2>'
testfile = open(filepath, 'r').readlines()


With file1 and file2 opened as lists, compare them line by line to see if they are the same,
i = 0
for baseline in file1:
    label = "line test " + str(i + 1)
    TestString.compare(label, baseline, testfile[i], encoding='utf-8')
    i = i + 1

       TOP ^

       4.9. Test Result Logging:

            User can log results into the log file as per the conditions being checked. The various conditional operations allowed are,

            Logging library needs to be imported for log enabling .i.e., from logging import LogWriter, TimeStamp.

          If condition:

          Eg: if path.isfile(path.join(path.expandvars("$HOME"), "Desktop", "UTF8demo.txt")):

             self.result = {"File Present": "PASS"}

             LogWriter.writeResult(self.result)
In the above example, the presence of file “UTF8demo.txt” in Desktop is being checked and if present, "File Present": "PASS" will be logged to the log file. 

          Exception Handling: Any exceptions thrown in the dogtail functions (libraries) can be caught and handled. This can suitably used for logging as well.

          Eg. :

  try:

            file = open(path[0] + '/data/UTF-8-demo.txt', 'r').readlines()

  except IOError:

            print "File open failed"
In the above example, the open action of a file is placed under try:. If there are any errors resulting in file not opening, "File open failed" will be printed.

          For condition: This is used when there is a comparison to be done in a loop like a string compare.

          Eg. : file comparison using string compare.

     filepath = environ['HOME'] + '/Desktop/UTF8demo.txt'

    testfile = open(filepath, 'r').readlines()

    i = 0

    for baseline in gold:

                 label = "line test " + str(i + 1)

                 TestString.compare(label, baseline, testfile[i], encoding='utf-8')

                 i = i + 1
 

4.11. Some useful Tips

            In order to help scripting, all the dogtail classes and functions can be viewed through the web browser with the help of the pydoc. Pydoc can be made to run on any port say, type in terminal ‘pydoc –p6464’, the dogtail documents can be viewed through web browser by typing http://localhost:6464 in the address bar.

            On Gnome, "at-poke" is the  tool to get accessible information on all the widgets. User can also use at-poke to get the UI information. To invoke at-poke, type ‘at-poke’ in terminal. The at-poke window will have entry for all applications open. One can traverse through the entries of application to get information about window, toolbars, widgets etc.
The accessible information about the widgets can be used in dogtail scripting. For eg., the rolename and the accessible name of a widget can be obtained at-poke can be used in various dogtail widget functions like "activate", "click" etc.

USING Sniff

TO DO

Drag n Drop actions through dogtail:

TO DO

Writing Application Wrappers

         TO DO

TOP ^

        

5. Executing Dogtail Scripts
 

After the completion of scripting, the file is saved as “<scriptname>.py”. Make sure  python is included in the path and accessibility is enabled.

To execute the script, type in the terminalpython <scriptname>.py

            This is the playback of the actions  specified in the dogtail script.

TOP ^ 

6. Recording User Actions

In the examples folder of dogtail installation is a recorder file, recorder.py.  When executed, this records all the actions  happening  on the desktop  OR on the application  specified as the argument for  the "recorder.py".

To record user actions, invoke the application and run this script with application name as the argument.
Eg. : Launch gedit and to record, type
'recorder.py gedit'

All user actions appear on the terminal.  The terminal output can be  copied into a text  file (.py) and modified  by adding  import statements  to  get a  executable  dogtail script.

TOP ^

7. Dogtail configurations

Dogtail provides user certain configuration features to work at ease. Dogtail has a file, config.py which has all dogtail related configurations.  For detailed information on  each of these parameters refer to the comments in  config.py  installed as part of   Dogtail libraries (/usr/lib/python*/vendor-packages/dogtail).

Some configurations which a user can try setting in the config.py file are,

logdir = '/tmp/dogtail/logs/' # logs directory.

scratch = '/tmp/dogtail/' # temp directory.

basefile = '' # baseline file to load.

data = '/tmp/dogtail/data/' # location to save screenshots.

configfile = '' # The configuration file loaded.

searchBackoffDuration = 0.5 # Duration of retry.

searchCutoffCount = 20 # Number of retries for back-off algorithm.

defaultDelay = 0.5 # Delay given by default in case of wait or sleep.

Complete set of variables can be got in the config.py file.


TOP ^

8. Further Support

IRC: #dogtail on irc.freenode.net
To subscribe to dogtail mailing lists, goto http://mail.gnome.org/mailman/listinfo/dogtail-list
To post a message to all the list members, send email to dogtail-list@gnome.org.  
Bugs can be filed in http://bugzilla.gnome.org under dogtail category.

TOP ^

9. Sample Script  Explained

           Given below is one of the example scripts which comes with dogtail package for gedit testing. Each of the line in the test script has been explained to give a overview of the test flow.
            #!/usr/bin/env python

      # Preprocessor for python script

      # Importing dogtail libraries

      import dogtail.config

      import dogtail.tc

      from dogtail.procedural import *

      from dogtail.utils import screenshot


from os import environ, path, remove

      environ['LANG']='en_US.UTF-8'

      # Load our persistent Dogtail objects

      DemoConfig = dogtail.config.Config()

      TestString = dogtail.tc.TCString()

      TestImage = dogtail.tc.TCImage()

 

      # Remove the existing log file, if it's still there from a previous run

      if path.isfile(path.join(path.expandvars("$HOME"), "Desktop", "UTF8demo.txt")):

         remove(path.join(path.expandvars("$HOME"), "Desktop", "UTF8demo.txt"))

      # Invoke gedit.

      run('gedit')
 

      # Set focus on gedit application

      focus.application('gedit')

      # Focus gedit's text buffer.

      focus.text()

      # Load the UTF-8 demo file.

      from sys import path

      utfdemo = file(path[0] + '/data/UTF-8-demo.txt')

      # Load the UTF-8 demo file into the text buffer.

      focus.widget.text = utfdemo.read()

      # Take a screenshot of the window

      screenshot()

      # Click gedit's Save button.

      click('Save')

      # Focus gedit's Save As... dialog

      focus.dialog('Save as...')

      # click the Browse for other folders widget

      activate('Browse for other folders')

      # Click the Desktop widget

      activate('Desktop', roleName = 'table cell')

      # Save to the file name 'UTF8demo.txt'.

      focus.text()

      focus.widget.text = 'UTF8demo.txt'

     # Click the Save button.

      click('Save')

     # Quit gedit.

      click('Quit')

      # To check if the saved file is the same as

      # the baseline file

      # Read in the "gold" file

      try:

         gold = open(path[0] + '/data/UTF-8-demo.txt', 'r').readlines()

      except IOError:

         print "File open failed"

      # Read the test file for comparison

      filepath = environ['HOME'] + '/Desktop/UTF8demo.txt'

      testfile = open(filepath, 'r').readlines()

      # We now have the original and saved files as lists.

      # Comparing them line by line to see if they are the same

      i = 0

      for baseline in gold:

         label = "line test " + str(i + 1)

         TestString.compare(label, baseline, testfile[i], encoding='utf-8')

         i = i + 1

TOP ^