initial check in

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2069 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
tschmid 2007-09-25 06:31:14 +00:00
parent cd18b551dc
commit 41e98f0bef
37 changed files with 3877 additions and 0 deletions

View file

@ -0,0 +1,152 @@
""" Class and functions to find the windowText and className for a given executable
"""
# Author : Tim Couper - tim@2wave.net
# Date : 22 July 2004
# Version : 1.1
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
# Notes : Requires Python 2.3, win32all, ctypes & watsup package
import os.path
from watsup.winGuiAuto import findTopWindows,findControls,dumpWindow,\
dumpTopWindows
from watsup.utils import dumpHwnd,tupleHwnd
from watsup.launcher import AppThread,terminateApp
from watsup.OrderedDict import OrderedDict
from time import time,sleep
from types import ListType
THREAD_TIMEOUT_SECS=2.0
INDENT=2
#wstruct is of the form [hwnd,wclass,wtext] or
# [hwnd,wclass,wtext,[winstruct]
##def printwstruct(wstruct):
## print 'printFormat had trouble with:\n%s' % str(wstruct[3])
## import pprint
## print '------'
## pprint.pprint(wstruct)
## print '------'
def printFormat(wstruct,indent=0):
def printwstruct(wstruct):
print 'printFormat had trouble with:\n%s' % str(wstruct[3])
import pprint
print '------'
pprint.pprint(wstruct)
print '------'
"""wstruct is either a wintuple, or a recursive list of wintuples
"""
# type 1
if type(wstruct[1]) is not ListType:
print '%s%s: %s' % (' '*indent,wstruct[2],wstruct[1])
if len(wstruct)>3 and wstruct[3]<>None:
try:
for item in wstruct[3]:
printFormat(item,indent+INDENT)
except:
print_wstruct(wstruct)
# type 2:
else:
for item in wstruct[1]:
printFormat(item,indent+INDENT)
class AppControls(object):
def __init__(self,program,wantedText=None,wantedClass=None,selectionFunction=None,verbose=False):
self.wantedText=wantedText
self.wantedClass=wantedClass
self.selectionFunction=selectionFunction
self.verbose=verbose
topHwnds,unwantedHwnds,self.appThread=findAppTopWindows(program,verbose=verbose) #should only be one, but you never know
self.topHwnds=[]
self.unwantedHwnds=unwantedHwnds
def run(self):
while self.appThread.isAlive():
results=findNewTopWindows(self.unwantedHwnds,self.verbose)
if results:
self.process(results) # update the list of topWindows and unwanted TopWindows
def process(self,results):
for hwnd in results:
ctHwnds=findControls(hwnd)
if ctHwnds:
# we only add hwnd if there are controlHwnds
# as there may be a form which exists
# as an hwnd, but has not controls materialised yet
self.unwantedHwnds.append(hwnd)
self.write(hwnd)
for ctHwnd in ctHwnds:
self.unwantedHwnds.append(ctHwnd)
def write(self,hwnd):
h=tupleHwnd(hwnd)
t=[h[0],h[1],h[2],dumpWindow(hwnd)]
printFormat(t)
def findNewTopWindows(unwantedHwnds=[],verbose=False):
# returns a list of all top windows' hwnds we haven't
# found yet
htuples=dumpTopWindows()
if verbose:
print '..%d windows found (%d windows in unwanted)' % (len(htuples),len(unwantedHwnds))
results=[]
for htuple in htuples:
hwnd=htuple[0]
if hwnd not in unwantedHwnds:
if verbose:
print '..adding %s' % dumpHwnd(hwnd)
results.append(hwnd)
return results
def findAppTopWindows(program,verbose=False):
"""returns the hwnds for the program, along with the hwnds for the
stuff that are to be ignored
"""
# first we run findTopWindows before launching the program; store the hwnds found
# (note that it doesn't matter if an instance of the program IS running
# as we'll start another one whose hwnds will be new)
# run findTopWindows, and remove from the list any which are stored
unwantedHwnds=findNewTopWindows() # list of topWindow hwnds that exist
if verbose:
print '..%d original window(s)' % len(unwantedHwnds)
# run the program
appThread=AppThread(program,verbose)
appThread.start()
# give the thread a chance to get going:
results=[]
t=time()
while not results and ((time()-t)<THREAD_TIMEOUT_SECS):
sleep(0.2) # means that the thread hasn't launched; give it a chance
results=findNewTopWindows(unwantedHwnds,verbose)
if not results:
# stop the program
terminateApp()
raise Exception, 'Failed to find any new windows!'
if verbose:
print '..%d additional (new) non-trivial window(s)' % len(results)
if verbose:
for hwnd in results:
print '..%s: %s' % tupleHwnd(hwnd)[:2]
return results,unwantedHwnds,appThread
if __name__=='__main__':
pass

View file

@ -0,0 +1,61 @@
class OrderedDict(dict):
def __init__(self,dict=None):
""" currently, dict parameter does not work """
self._keys = []
dict.__init__(self,dict)
def __delitem__(self, key):
dict.__delitem__(self, key)
self._keys.remove(key)
def __setitem__(self, key, item):
dict.__setitem__(self, key, item)
if key not in self._keys: self._keys.append(key)
def clear(self):
dict.clear(self)
self._keys = []
def copy(self):
dict = dict.copy(self)
dict._keys = self._keys[:]
return dict
def items(self):
return zip(self._keys, self.values())
def keys(self):
return self._keys
def popitem(self):
try:
key = self._keys[-1]
except IndexError:
raise KeyError('dictionary is empty')
val = self[key]
del self[key]
return (key, val)
def setdefault(self, key, failobj = None):
dict.setdefault(self, key, failobj)
if key not in self._keys: self._keys.append(key)
def update(self, dict):
dict.update(self, dict)
for key in dict.keys():
if key not in self._keys: self._keys.append(key)
def values(self):
return map(self.get, self._keys)
def __str(self):
return self.__repr__()
def __repr__(self):
itemList=[]
for item in self.items():
itemList.append('%s: %s' % item)
return '{'+', '.join(itemList)+'}'

View file

@ -0,0 +1,389 @@
from watsup.winGuiAuto import findControl,findControls,findTopWindow, \
WinGuiAutoError,getEditText,clickButton, \
activateMenuItem,getMenuInfo, \
getListboxItems,getComboboxItems, \
selectListboxItem,selectComboboxItem,\
setEditText,getTopMenu, setCheckBox,\
getCheckBox
from watsup.launcher import launchApp,terminateApp
import win32gui
import win32con
#from watsup.Knowledge import getKnowledge
from types import ListType,TupleType
verbose=False
CONTROL_MAX_WAIT_SECS=3
def PLaunchApp(program,wantedText=None,wantedClass=None):
hwnd=launchApp(program,wantedText,wantedClass,verbose)
return PWindow(hwnd=hwnd)
class PWinControl(object):
"""Abstract base class for PWindows and PControls"""
def __init__(self,parent):
self.parent=parent
def findPControl(self,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None):
"""Factory method returning a PControl instance, or a subclass thereof,
within a PWinControl instance,
find a unique control - raises exception if non-unique
"""
# if wantedClass is not given, let's try and find out what it is;
# then we should be better able to assign the right PControl subclass
# to it
if wantedClass==None:
#find the wantedClass anyway
p=PControl(self.parent,hwnd,wantedText,wantedClass,selectionFunction)
wantedClass=p.className
else:
p=None
# if this is a known class, return the instance of the specific
# subclass of PControl
if KNOWN_CLASSES.has_key(wantedClass):
if verbose:
print KNOWN_CLASSES[wantedClass],(self.parent,hwnd,wantedText,wantedClass,selectionFunction)
return KNOWN_CLASSES[wantedClass](self.parent,hwnd,wantedText,wantedClass,selectionFunction)
# in all other cases, return a PControl (we may have already calculated it above)
if p:
return p
else:
return PControl(self.parent,hwnd,wantedText,wantedClass,selectionFunction)
def findPControls(self,wantedText=None,wantedClass=None,selectionFunction=None):
# returns a list of PControl instances which match the criteria
hwnds=findControls(wantedText,wantedClass,selectionFunction)
controlList=[]
for hwnd in hwnds:
controlList.append(self.findPControl(self,hwnd=hwnd))
return controlList
##class Menu(object):
## # container for menu items
## def __init__(self,mwnd):
## self.mwnd=mwnd
# not sure we need this entity, as we can address MenuItems directly
class MenuItem(object):
def __init__(self,parentWindow,*menuItems):
self.parentWindow=parentWindow
#acceept either a tuple/list or *args-type values
if type(menuItems[0]) in (ListType,TupleType):
self.menuItemTuple=menuItems[0]
else:
self.menuItemTuple=menuItems
def activate(self):
activateMenuItem(self.parentWindow.hwnd,self.menuItemTuple)
#-------------------------------------------------------------------------------
# accessors and properties
def getInfo(self):
return getMenuInfo(self.parentWindow.hwnd,self.menuItemTuple)
def getName(self):
return menuInfo.name
def getItemCount(self):
return menuInfo.itemCount
def getIsChecked(self):
return menuInfo.IsChecked
def getIsSeparator(self):
return menuInfo.IsSeparator
def getIsDisabled(self):
return menuInfo.IsDisabled
def getIsGreyed(self):
return menuInfo.IsGreyed
name=property(getName)
itemCount=property(getItemCount)
isChecked=property(getIsChecked)
isDisabled = property(getIsDisabled)
isGreyed = property(getIsGreyed)
isSeparator = property(getIsSeparator)
menuInfo=property(getInfo)
#-------------------------------------------------------------------------------
class PWindowError(Exception): pass
class PWindow(PWinControl):
def __init__(self,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None,controlParameters=None):
PWinControl.__init__(self,self)
if hwnd:
self.hwnd=hwnd
else:
try:
self.hwnd=findTopWindow(wantedText=wantedText,
wantedClass=wantedClass,
selectionFunction=selectionFunction)
except WinGuiAutoError,e:
raise PWindowError,e
# controlParameters is the list of dictionaries with unique
# definitions of the controls within this Window
# eg controlParameters=[{'wantedClass':'TButton','wantedText':'Button1'},
# {'wantedClass':'TRadioButton','selectionFunction':chooseIt}]
self.controls=[]
if controlParameters<>None:
for cp in controlParameters:
hwnd=cp.get('hwnd',None)
wantedClass=cp.get('wantedClass',None)
wantedText=cp.get('wantedTest',None)
selectionFunction=cp.get('selectionFunction',None)
clist=self.findControls(hwnd=hwnd,
wantedText=wantedText,
wantedClass=wantedClass,
selectionFunction=selectionFunction)
self.controls.extend(clist)
self._mainMenuHandle=None
def activateMenuItem(self,menuItem):
menuItem.activateMenuItem()
def terminate(self):
terminateApp(self.hwnd)
#-------------------------------------------------------------------------------
#Accessors & properties
### top menu item
## def getMainMenu(self):
## if self._mainMenuHandle:
## return self._mainMenuHandle
## else:
## return getTopMenu(self.hwnd)
##
## mainMenu=property(getMainMenu)
#-------------------------------------------------------------------------------
class PControlError(Exception): pass
class PControl(PWinControl):
def __init__(self,parent,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None):
"Constructor takes either hwnd directly, or others in a controlParameter set"
PWinControl.__init__(self,parent)
if hwnd:
self.hwnd=hwnd
else:
try:
self.hwnd=findControl(parent.hwnd,
wantedText=wantedText,
wantedClass=wantedClass,
selectionFunction=selectionFunction,
maxWait=CONTROL_MAX_WAIT_SECS)
except WinGuiAutoError,e:
raise PControlError,e
## def addKnowledge(self,attrName):
## knowledge=getKnowledge()
## knowledge.add(self.className,attrName)
#-------------------------------------------------------------------------------
# general winctrl actions which, if acted upon by a user, might tell us sth about
# this unknown control
def getItems(self):
# This PControl of unknown class can get items
# at least the user thinks so
#self.addKnowledge('getItems')
res=getComboboxItems(self.hwnd)
if not res:
res=getListboxItems(self.hwnd)
return res
def selectItem(self,item):
# This PControl of unknown class can select items
# at least the user thinks so
#self.addKnowledge('selectItem')
res= selectListboxItem(self.hwnd, item)
if not res:
res=selectComboBoxItem(self.hwnd,item)
return res
def click(self):
# This PControl of unknown class is clickable
# at least the user thinks so
#self.addKnowledge('click')
clickButton(self.hwnd)
def getCaption(self):
# This PControl of unknown class has a caption,
# at least the user thinks so
#self.addKnowledge('caption')
return self.getEditText()
def setCheckBox(self, state=3DTrue):
setCheckBox(self.hwnd, state)
def getCheckBox(self):
return getCheckBox(self.hwnd)
#-------------------------------------------------------------------------------
#Accessors and properties
def getText(self):
# returns a list of strings which make up the text
return getEditText(self.hwnd)
def setText(self,text,append=False):
setEditText(text,append)
def getClassName(self):
return win32gui.GetClassName(self.hwnd)
text=property(getText)
className=property(getClassName)
caption=property(getCaption)
items=property(getItems)
#-------------------------------------------------------------------------------
class PEdit(PControl):
def __init__(self,parent,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None):
PControl.__init__(self,parent,hwnd,wantedText,wantedClass,selectionFunction)
#-------------------------------------------------------------------------------
#Accessors and properties
def getText(self):
# returns a simple string - PEdit controls only have one value
p=PControl.getText(self)
if p:
return p[0]
else:
return ''
text=property(getText)
caption=None #undefine the caption property
#-------------------------------------------------------------------------------
class PText(PControl):
# multi-line text control
caption=None
class PComboBox(PControl):
def selectItem(self,item):
selectComboboxItem(self.hwnd,item)
#-------------------------------------------------------------------------------
#Accessors and properties
def getItems(self):
return getComboboxItems(self.hwnd)
items=property(getItems)
#-------------------------------------------------------------------------------
class PDelphiComboBox(PComboBox):
# The Delphi Combo box has a control of class Edit within
# it, which contains the text
def __init__(self,parent,hwnd=None,wantedText=None,wantedClass=None,selectionFunction=None):
PControl.__init__(self,parent,hwnd,wantedText,wantedClass,selectionFunction)
self.editCtrl=self.findPControl(wantedClass='Edit')
#-------------------------------------------------------------------------------
#Accessors and properties
def getText(self):
# get the content from the control Edit:
return self.editCtrl.getText()
text=property(getText)
#-------------------------------------------------------------------------------
class PButton(PControl):
def click(self):
clickButton(self.hwnd)
#-------------------------------------------------------------------------------
#Accessors and properties
caption=property(PControl.getText)
#-------------------------------------------------------------------------------
class PListBox(PControl):
def selectItem(self,item):
return selectListboxItem(self.hwnd, item)
#-------------------------------------------------------------------------------
#Accessors and properties
def getItems(self):
return getListboxItems(self.hwnd)
items=property(getItems)
#-------------------------------------------------------------------------------
class PCheckBox(PControl):
#-------------------------------------------------------------------------------
#Accessors and properties
caption=property(PControl.getText)
def getCheckStatus(self):
return self.getCheckBox()
def isChecked(self):
return self.getCheckStatus()#=3D=3D win32con.BST_INDETERMINATE
def isIndeterminate(self):
return self.getCheckStatus() #=3D=3D win32con.BST_INDETERMINATE
=20
def isNotChecked(self):
return self.getCheckStatus() #=3D=3D win32con.BST_UNCHECKED
=20
def setChecked(self):
setCheckBox(hwnd, state = True)
def setUnChecked(self):
setCheckBox(hwnd, state = False)
#-------------------------------------------------------------------------------
KNOWN_CLASSES={'TEdit': PEdit,
'TComboBox': PDelphiComboBox,
'ComboBox': PComboBox,
'TButton': PButton,
'TListBox': PListBox,
'ListBox': PListBox,
'CheckBox': PCheckBox,
'TCheckBox': PCheckBox,
}

View file

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,10 @@
from watsup.winGuiAuto import findTopWindow,findControl,setEditText
from time import sleep
# Locate notepad's edit area, and enter various bits of text.
notepadWindow = findTopWindow(wantedClass='Notepad')
editArea = findControl(notepadWindow,wantedClass="Edit")
setEditText(editArea, "Hello, again!")
sleep(0.8)
setEditText(editArea, " You still there?",True)

View file

@ -0,0 +1,27 @@
from watsup.winGuiAuto import findControl,setEditText, findTopWindow,clickButton
import os
import os.path
FILENAME='atestfile.txt'
def main():
# delete any occurrence of this file from the disk
if os.path.exists(FILENAME):
os.remove(FILENAME)
form=findTopWindow(wantedText='Simple Form')
button=findControl(form,wantedText='Create file')
editbox=findControl(form,wantedClass='TEdit')
# enter a filename:
setEditText(editbox,[FILENAME])
clickButton(button)
# now check that the file is there
if os.path.exists(FILENAME):
print 'file %s is present' %FILENAME
else:
print "file %s isn't there" % FILENAME
if __name__=='__main__':
main()

View file

@ -0,0 +1,17 @@
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows
import example2
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText='Simple Form')
if forms:
form=forms[0]
else:
form=launchApp('simple.exe',wantedText='Simple Form')
example2.main()
# and terminate the form
terminateApp(form)

View file

@ -0,0 +1,51 @@
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows, findControl,getEditText,clickButton
from watsup.performance import PerformanceCheck,PerformanceCheckError
from time import sleep,time
def main(myExecutable,myWantedText):
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText=myWantedText)
if forms:
form=forms[0]
else:
form=launchApp(myExecutable,wantedText=myWantedText)
button=findControl(form,wantedText='Click me')
editbox=findControl(form,wantedClass='TEdit')
#start a performance check instance
p=PerformanceCheck()
clickButton(button)
# belts and braces to avoid infinite waiting!
maxWaitTime=2.0
startTime=time()
while time()-startTime<maxWaitTime:
t=getEditText(editbox)
if t:
break
else:
sleep(0.1)
else:
raise Exception,'Failed to get value after maxWaitTime of %s secs' % maxWaitTime
try:
try:
#do the check/recording step, identifying this step with the wantedtext
p.check(myWantedText,1.0)
except PerformanceCheckError,e:
print '** Failed: %s' % e
# and terminate the form
finally:
terminateApp(form)
from watsup.performance import nicePrint
nicePrint()
if __name__=='__main__':
print ' please run example4a or 4b'

View file

@ -0,0 +1,3 @@
from example4 import main
main('perform/perform.exe','Performance Form 1')

View file

@ -0,0 +1,3 @@
from example4 import main
main('perform/perform2.exe','Performance Form 2')

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View file

@ -0,0 +1,343 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Windows Application Test System Using Python</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link href="../tac.css" rel="stylesheet" type="text/css">
</head>
<body>
<img src="../images/tizmoi.jpg">
<H2>WATSUP - Windows Application Test System Using Python</H2>
The WATSUP toolkit is designed to allow the automated test of Windows applications.
The system uses the "object-based" mechanism for identifying and invoking actions
on controls and menu items.
<P>So much has been written about the scope, robustness, scalability and outstanding
usability of Python that I'll go no further with it here, only to say that if
you haven't yet had a chance to use this comprehensive, open source language,
don't miss out on the opportunity to take a serious look at it!</P>
<p>The examples in this document assume a basic familiarity with Python.</p>
<H2>Functional Tests</H2>
Testers/developers write automated functional tests which follow a prescriptive,
possibly branching, possibly dynamic "user workflow". The script can check for
changes in the gui itself, operating system environment, file system, database
table and records, network, internet or extranet urls/pages/web services ... - in
fact anywhere that there could be changes.
<p>
Examination of the functions in module autoWinGui.py within the watsup package shows the variety of windows control items
that can be checked/modified. These include:
<ul>
<li>Get and set text in editable controls</li>
<li>Edit and select items from controls supporting lists</li>
<li>Click and double-click controls to invoke their actions</li>
<li>Determine the state of menu items and invoke them</li>
</ul>
<p> The system also provides tools for finding windows by caption and/or class,
controls by text/caption and/or class, and menu items by text or position. (One
of the aspirations of this project is to continue to extend the list to include
as many controls as possible) .</p>
<H3>Example 1 - automated writing on Notepad</H3>
<p>Here's a simple example of the control over applications that you can have with watsup.
First, launch notepad from:</p> <p>Windows Start Menu - All Programs - Accessories - Notepad</p>
Then run the following script (<a href="code/example1.py">Example 1</a>)
<code>
<pre>
from watsup.winGuiAuto import findTopWindow,findControl,setEditText
from time import sleep
# Locate notepad's edit area, and enter various bits of text.
notepadWindow = findTopWindow(wantedClass='Notepad')
editArea = findControl(notepadWindow,wantedClass="Edit")
setEditText(editArea, "Hello, again!")
sleep(0.8)
setEditText(editArea, " You still there?",True)
</pre></code>
Finally, close notepad.<p></p>
<h3>Example 2 - testing a simple example </h3>
In functional tests, the tester wants to ensure that
the cause - invoking a sequence of windows events (button clicks, menu item activation)
has the predicted effect of, for example, a change in a value of a sindows control,
the creation of a file, or the entry of a new database record.
See the directory watsup/examples/simple directory for the executable simple.exe.
If you run the application, you see a button and a text box.
Enter a valid filename into the box, say xyz, and
click the button;
after the file is created, a message box appears containing a success message,
and investigation of the directory watsup/examples/simple will show a file
called 'xyz.txt' has been created (or overwritten).
<p>Now let's script a test to automate this functionality. </p>
First find and launch the application.
Then run the following script (<a href="code/example2.py">Example 2</a>)
<code><pre>
from watsup.winGuiAuto import findControl,setEditText, findTopWindow,clickButton
import os
import os.path
FILENAME='atestfile.txt'
def main():
# delete any occurrence of this file from the disk
if os.path.exists(FILENAME):
os.remove(FILENAME)
form=findTopWindow(wantedText='Simple Form')
button=findControl(form,wantedText='Create file')
editbox=findControl(form,wantedClass='TEdit')
# enter a filename:
setEditText(editbox,[FILENAME])
print 'clicking button to create file'
clickButton(button)
# now check that the file is there
if os.path.exists(FILENAME):
print 'file %s is present' %FILENAME
else:
print "file %s isn't there" % FILENAME
if __name__=='__main__':
main()
</pre>
</code>
<h3>Example 3 - automating program launch and termination</h3>
<p>It's a bit tedious having to start and close the application each time.
<a href="code/example3.py">Example 3</a> launches the application,
if it isn't already running, and terminates it on
completion of the test</p>
<code><pre>
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows
import example2
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText='Simple Form')
if forms:
form=forms[0]
else:
form=launchApp('simple.exe',wantedText='Simple Form')
example2.main()
# and terminate the form
terminateApp(form)
</pre></code>
launchApp starts the application in a separate thread,
and looks for a window with caption containing "Simple Form",
returning the window handle of the recovered form.
terminateApp attempts to close the form, by trying to activate menu item File-Exit, or, failing that,
sending Alt + F4.
<H3>Example 4 - finding windows and controls</H3>
<p>In building scripts, we need to be able to find the class and/or text of the many windows and controls
to be investigated or invoked.</p>
<p>In the tools directory, there's a tool - ShowWindows.bat - to assist us with this.</p>
<img src="images/ShowWindows1.jpg" alt="Show Windows 1" />
<p>Clicking the "Register" button persists information about
the existing windows running on the system (and it tells you how many, FYI).
Clicking the "Find new" button will report all non-trivial windows and all their
constituent controls which have appeared in the windows environment
since the last "Register" click.
So to test our program simple.exe, launch ShowWindows, click Register.
Then launch simple.exe and
and click the Find New button.
The associated text box shows n/m, where m is the total number of new windows found,
and n is the number of those which are significant (ie have any controls) and are reported. </p>
<img src="images/ShowWindows2.jpg" alt="Show Windows 2" />
<H2>Performance Tests</H2>
<p>Performance tests, in this definition, are single-client scripts,
which are similar in operation to the functional tests above,
but for which certain steps of the tests
must either be done within an acceptable timeframe ("CHECKING")
and/or the time taken for those steps
should be recorded for subsequent analysis ("RECORDING").</p>
<p>WATSUP provides a simple mechanism to add such tests to existing functional tests.
In examples 4a & 4b, we launch almost identical applications,
perform.exe and perform2.exe respectively.
When the button is clicked, the text "Finished" is written
to the edit box. The difference between the programs is that the former is
coded to wait for 1/2 second before "Finished"
appears; in the latter case, the delay is 1.5 seconds.</p>
<p>In both cases, we are setting a performance test that the process
take no more than 1 second.
Clearly, we should expect example 4a to be ok and example 4b to fail.
</p>
<p>So we have <a href="code/example4a.py">Example 4a</a></p>
<code><pre>
from example4 import main
main('perform.exe','Performance Form 1')
</pre></code>
<p>and <a href="code/example4b.py">Example 4b</a></p>
<code><pre>
from example4 import main
main('perform2.exe','Performance Form 2')
</pre></code>
<p>which reference <a href="code/example4.py">Example 4</a>:</p>
<code><pre>
from watsup.launcher import launchApp,terminateApp
from watsup.winGuiAuto import findTopWindows, findControl,getEditText,clickButton
from watsup.performance import PerformanceCheck,PerformanceCheckError
from time import sleep,time
def main(myExecutable,myWantedText):
# find an instance of SimpleForm. If one isn't there, launch it
forms=findTopWindows(wantedText=myWantedText)
if forms:
form=forms[0]
else:
form=launchApp(myExecutable,wantedText=myWantedText)
button=findControl(form,wantedText='Click me')
editbox=findControl(form,wantedClass='TEdit')
#start a performance check instance
p=PerformanceCheck()
clickButton(button)
# belts and braces to avoid infinite waiting!
maxWaitTime=2.0
startTime=time()
while time()-startTime&lt;maxWaitTime:
t=getEditText(editbox)
if t:
break
else:
sleep(0.1)
else:
raise Exception,'Failed to get value after maxWaitTime of %s secs' % maxWaitTime
try:
try:
#do the check/recording step, identifying this step with the wantedtext
p.check(myWantedText,1.0)
except PerformanceCheckError,e:
print '** Failed: %s' % e
# and terminate the form
finally:
terminateApp(form)
if __name__=='__main__':
print ' please run example4a or 4b'
</pre></code>
<h4>Key points in example4.py</h4>
<p>Immediately prior to clicking the button, we establish a PerformanceCheck instance,
which, among other things, establises a timer.
Every time the check() method is called on a PerformanceCheck instance, the following occurs:</p>
<p>Also if we are CHECKING (the default condition),
that time is checked against the number of seconds added as the
second parameter.
If the elapsed time exceeds the value of the 2nd parameter, an exception is raised.</p>
<p>So in example4.py, we see that the check method requires a 1 second acceptance.
Hence example4a succeeds and example4b fails.
</p>
<H2>Regression Testing</H2>
<p>Functional and performance scripts such as those above are
immediately useable within a test framework -
the excellent python unit test framework is highly recommended
(unittest.py, which comes along with your python installation).
This then enables the tester to develop complete regression tests
involving any combination of Functional & Performance testing. </p>
<p>For an example of the use of functional and performance tests within
the unit test framework, run the program framework.bat in the tools subdirectory.
This will launch a nice user interface from which sets of test cases can be run:
</p>
<img src="images/framework1.jpg">
<p>Select the File menu option, and you have the option to load files which
contain testcases, or directories/directory trees. If select the "Load files"
option, and navigate up one directory, and then down through examples and the unittests
directories, you should find, and select, exampleTests.py.
The Framework program examines the selected file(s) and extracts the TestCases,
presents in the top frame</p>
<img src="images/framework2.jpg">
<p>Selecting Menu option Testing - Run Tests (or the long button labelled Run Tests),
will cause each test to be run; success or failure is shown in the lower frame</p>
<img src="images/framework3.jpg">
<p>For more information on python's unit test module, refer to the python documentation.
Finally, it is worth noting that this framework will work with any unit tests, not just
those specifically testing windows application, so you can test
elements of the logic that you have written in other applications.
<H2>Downloads</H2>
Download <a href="../downloads/watsup-0.4.zip">watsup</a> here
<p>(This package should be unzipped in the site-packages directory in your python installation)</p>
<H3> Dependencies</H3>
<ul>
<li><a href="http://www.python.org/download"> Python</a> (version at least 2.3)</li>
<li><a href="http://sourceforge.net/projects/pywin32">pywin32 </a></li>
<li><a href="http://sourceforge.net/project/showfiles.php?group_id=71702">ctypes </a></li>
<li><a href="http://www.rutherfurd.net/python/sendkeys/#binaries" >SendKeys </a></li>
<li><a href="http://www.wxpython.org/download.php" >wxPython library</a> (for tools)</li>
</ul>
<h3>Credits</h3>
<p> The framework test tool was built from parts in unittestgui.py by Chris Liechti.
Much of the core functionality in WATSUP derives from the important work by
<a href="http://www.brunningonline.net/simon/python/index.html">Simon Brunning</a>,
who, in his module winGuiAuto.py provided concise, accessible mechanisms to
access and "click" windows controls & menus; Simon's work recognises the huge
contribution that Mark Hammond has made to the python community (and wider)
in providing pywin32, comprehensive win32 API modules for python.
</p>
Dr Tim Couper<br/>
<a href="mailto:timc@tizmoi.net">timc@tizmoi.net</a>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -0,0 +1,7 @@
body {font-family: "Trebuchet MS", Verdana, sans-serif}
code {font: Courier New; size 12}
h1 {color: navy}
h2 {color: navy}
h3 {color: navy}
h4 {color: navy}

View file

@ -0,0 +1,116 @@
""" launch & terminate a windows application
"""
# Author : Tim Couper - tim@2wave.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
# Notes : Requires Python 2.3, win32all, ctypes, SendKeys & winGuiAuto
import os.path
import threading
import win32gui
#import win32api
#import win32con
import SendKeys
from watsup.utils import WatsupError
from watsup.winGuiAuto import findTopWindow,findTopWindows, \
WinGuiAutoError,activateMenuItem
MENU_TERMINATIONS=[('file', 'exit'),('file', 'quit')]
def launchApp(program,wantedText=None,wantedClass=None,verbose=False):
global p
p=AppThread(program,verbose)
p.start()
try:
return findTopWindow(wantedText=wantedText,wantedClass=wantedClass)
except WinGuiAutoError,e:
pass
# find all the Windows that are lurking about, and terminate them
# this I can't do until terminateApp can find a window and bring it to focus
# for now, find one top window and return that; if there are none,
# it should raise an exception
try:
return findTopWindows(wantedText=wantedText,wantedClass=wantedClass)[0]
except IndexError:
raise WatsupError,'Failed to find any windows'
class AppThread(threading.Thread):
def __init__(self,program,verbose=False):
threading.Thread.__init__(self,name=program)
self.program=program
self.verbose=verbose
def run(self):
"""main control loop"""
#check the given program exists, and is a file:
# if the program has no type, make it .exe:
if os.path.splitext(self.program)[1]=='':
prog='%s.exe' % self.program
else:
prog=self.program
prog=os.path.abspath(prog)
if os.path.isfile(prog):
# launch the new application in a separate thread
if self.verbose:
print '..launching "%s"' % prog
os.system('"%s"' % prog)
else:
print 'AppThread: program not found: "%s"\n' % prog
if self.verbose:
print '..terminating "%s"' % prog
def terminateApp(hwnd=None):
"""Terminate the application by:
If there's a file-exit menu, click that
If there's a file-Quit menu, click that
Otherwise, send Alt-F4 to the current active window
** it's the calling program responsibility to get the right active window
"""
if hwnd:
for fileExit in MENU_TERMINATIONS:
try:
activateMenuItem(hwnd, fileExit)
return
except WinGuiAutoError:
pass
# blast the current window with ALT F4 ..
SendKeys.SendKeys("%{F4}")
#win32gui.PumpWaitingMessages()
## from watsup.winGuiAuto import findControl,findTopWindows
## topHs=findTopWindows(wantedText='MyTestForm')
## for topH in topHs:
## #hwnd=findControl(topH)
## a=win32gui.findWindow(topH)
## print a, topH
## win32gui.SetFocus(topH)
## win32gui.PumpWaitingMessages()
## SendKeys.SendKeys("%{F4}")
##
## if not hwnd and (wantedText or wantedClass):
## hwnds=findTopWindows(wantedText=wantedText,wantedClass=wantedClass)
## elif hwnd:
## hwnds=[hwnd]
##
## if hwnds:
## for hwnd in hwnds:
## #win32gui.SetFocus(hwnd)
## #win32gui.SetActiveWindow(hwnd)
## # mm the above don;t work, perhaps because the
## # window is actually to be found in another thread
##
## SendKeys.SendKeys("%{F4}")
if __name__=='__main__':
pass

View file

@ -0,0 +1,181 @@
# Author : Tim Couper - timc@tizmoi.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
from time import time,ctime
import sys
import cPickle
#-------------------------------------------------------------------------------
# hidden globals
_PerformanceDataThisRun=None
_PerformanceDataStore=None
_doPerformanceChecks=True
_recordPerformanceData=False
#-------------------------------------------------------------------------------
# externals
def isRecording():
return _recordPerformanceData
def isChecking():
return _doPerformanceChecks
def doRecording(doIt=True):
global _recordPerformanceData
_recordPerformanceData=doIt
def doChecking(doIt=True):
global _doPerformanceChecks
_doPerformanceChecks=doIt
def onOff(bool):
if bool:
return 'On'
else:
return 'Off'
def getPerformanceDataThisRun():
"returns the Performance data for this run of the tests"
global _PerformanceDataThisRun
if _PerformanceDataThisRun==None:
# create an instance, and load up what was already there
_PerformanceDataThisRun=PerformanceDataThisRun()
return _PerformanceDataThisRun
def getPerformanceDataStore():
"returns the all Performance data for all runs of the tests"
global _PerformanceDataStore
if _PerformanceDataStore==None:
# create an instance, and load up what was already there
_PerformanceDataStore=PerformanceDataStore()
try:
_PerformanceDataStore=cPickle.load(open(_PerformanceDataStore.filename))
except IOError,EOFError:
pass
return _PerformanceDataStore
PERFORMANCE_STORE_FILENAME='PerformanceDataStore.dat'
class PerformanceDataCollectionError(Exception): pass
class PerformanceDataCollection(dict):
def __str__(self):
lines=[]
keys=self.keys()
keys.sort()
for msg in keys:
lines.append(str(msg))
pdiList=self[msg]
pdiList.sort()
for pdi in pdiList:
lines.append(' %s' % pdi)
return '\n'.join(lines)
#class PerformanceDataThisRunError(Exception): pass
class PerformanceDataThisRun(PerformanceDataCollection): pass
class PerformanceDataStoreError(Exception): pass
class PerformanceDataStore(PerformanceDataCollection):
'performs persistent store of PerformanceDataItems'
def __init__(self, filename=PERFORMANCE_STORE_FILENAME):
self.filename=filename
def addItem(self,msg,pdi):
'adds a pdi item to items, in the right place'
# msg must be immutable
if isinstance(pdi,PerformanceDataItem):
if not self.has_key(msg):
self[msg]=[]
self[msg].append(pdi)
else:
e='addItem can only accept PerformanceDataItem instances (%s: %s)' % (msg,str(pdi))
raise PerformanceDataStoreError,e
#-------------------------------------------------------------------------------
class PerformanceCheckError(AssertionError): pass
class PerformanceCheck(object):
def __init__(self):
self.reset()
def reset(self):
self.startTime=time()
def check(self,msg,maxDelay=None):
res=time()-self.startTime
# if we're storing the data
if isRecording():
pdi=PerformanceDataItem(self.startTime,res,maxDelay)
pd=getPerformanceDataThisRun()
if not pd.has_key(msg):
pd[msg]=[] #list of PerformanceData instances, we hope
# add it to the current data being processed
pd[msg].append(pdi)
# and add it to the store that we're going to persist
pds=getPerformanceDataStore()
pds.addItem(msg,pdi)
cPickle.dump(pds,open(pds.filename,'w'))
# if we're acting on performance tests:
if isChecking() and maxDelay<>None and (res>float(maxDelay)):
res=round(res,2)
msg='%s [Delay > %s secs (%s secs)]' % (msg,maxDelay,res)
raise PerformanceCheckError,msg
class PerformanceDataItem(object):
"holds a result (as elapsed time value in secs) and the time of invocation"
def __init__(self,startTime,elapsedTime,maxDelay=None):
self.startTime=startTime # time of run
self.elapsedTime=round(elapsedTime,2) # elapsed time in secs
self.maxDelay=maxDelay # user defined delay for check
# if None, then there is no upper threshhold
def __str__(self):
if self.maxDelay==None:
ok='OK'
md=''
else:
if self.elapsedTime<=self.maxDelay:
ok='OK'
else:
ok='Fail'
md= ' (%s)' % self.maxDelay
return '%s: %s%s %s' % (ctime(self.startTime), self.elapsedTime, md, ok)
def __cmp__(self,other):
if self.startTime<other.startTime:
return -1
elif self.startTime>other.startTime:
return 1
else:
return 0
def nicePrint(filename=sys.stdout):
def wout(f,text):
f.write('%s\n' % text)
if filename==sys.stdout:
f=sys.stdout
else:
f=open(filename,'w')
## from watsup.timedunittest import getPerformanceDataStore,getPerformanceDataThisRun, \
## isRecording
if isRecording():
for func in (getPerformanceDataThisRun,getPerformanceDataStore):
fn=func.__name__
wout(f,'\n%s\n%s\n' % (fn,'-'*len(fn)))
wout(f,str(func()))

View file

@ -0,0 +1,279 @@
# Author : Tim Couper - tim@2wave.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
from unittest import TestCase
from time import time,sleep,ctime
import sys
if sys.platform=='win32':
import win32gui
def pump():
# clear down any waiting messages (it all helps)
win32gui.PumpWaitingMessages()
else:
pump=None
###-------------------------------------------------------------------------------
### hidden globals
##
##_PerformanceDataThisRun=None
##_PerformanceDataStore=None
##_doPerformanceChecks=True
##_recordPerformanceData=True
###-------------------------------------------------------------------------------
##
###-------------------------------------------------------------------------------
### externals
##
##def isRecording():
## return _recordPerformanceData
##
##def isChecking():
## return _doPerformanceChecks
##
##def doRecording(doIt=True):
## global _recordPerformanceData
## _recordPerformanceData=doIt
##
##def doChecking(doIt=True):
## global _doPerformanceChecks
## _doPerformanceChecks=doIt
##
##def getPerformanceDataThisRun():
## "returns the Performance data for this run of the tests"
## global _PerformanceDataThisRun
## if _PerformanceDataThisRun==None:
## # create an instance, and load up what was already there
## _PerformanceDataThisRun=PerformanceDataThisRun()
## return _PerformanceDataThisRun
##
##def getPerformanceDataStore():
## "returns the all Performance data for all runs of the tests"
## global _PerformanceDataStore
## if _PerformanceDataStore==None:
## # create an instance, and load up what was already there
## _PerformanceDataStore=PerformanceDataStore()
## try:
## _PerformanceDataStore=cPickle.load(open(_PerformanceDataStore.filename))
## except IOError,EOFError:
## pass
## return _PerformanceDataStore
##
##PERFORMANCE_STORE_FILENAME='PerformanceDataStore.dat'
##
##class PerformanceDataCollectionError(Exception): pass
##class PerformanceDataCollection(dict):
## def __str__(self):
## lines=[]
## keys=self.keys()
## keys.sort()
## for msg in keys:
## lines.append(str(msg))
## pdiList=self[msg]
## pdiList.sort()
## for pdi in pdiList:
## lines.append(' %s' % pdi)
## return '\n'.join(lines)
##
###class PerformanceDataThisRunError(Exception): pass
##class PerformanceDataThisRun(PerformanceDataCollection): pass
##
##class PerformanceDataStoreError(Exception): pass
##class PerformanceDataStore(PerformanceDataCollection):
## 'performs persistent store of PerformanceDataItems'
## def __init__(self, filename=PERFORMANCE_STORE_FILENAME):
## self.filename=filename
##
## def addItem(self,msg,pdi):
## 'adds a pdi item to items, in the right place'
## # msg must be immutable
## if isinstance(pdi,PerformanceDataItem):
## if not self.has_key(msg):
## self[msg]=[]
## self[msg].append(pdi)
## else:
## e='addItem can only accept PerformanceDataItem instances (%s: %s)' % (msg,str(pdi))
## raise PerformanceDataStoreError,e
##
###-------------------------------------------------------------------------------
##
##class PerformanceCheckError(AssertionError): pass
##
##class PerformanceCheck(object):
##
## def __init__(self):
## self.reset()
##
## def reset(self):
## self.startTime=time()
##
## def check(self,msg,maxDelay=None):
## res=time()-self.startTime
##
## # if we're storing the data
## if isRecording():
## pdi=PerformanceDataItem(self.startTime,res,maxDelay)
## pd=getPerformanceDataThisRun()
## if not pd.has_key(msg):
## pd[msg]=[] #list of PerformanceData instances, we hope
##
## # add it to the current data being processed
## pd[msg].append(pdi)
##
## # and add it to the store that we're going to persist
## pds=getPerformanceDataStore()
## pds.addItem(msg,pdi)
## cPickle.dump(pds,open(pds.filename,'w'))
##
## # if we're acting on performance tests:
## if isChecking() and maxDelay<>None and (res>float(maxDelay)):
## res=round(res,2)
## msg='%s [Delay > %s secs (%s secs)]' % (msg,maxDelay,res)
## raise PerformanceCheckError,msg
##
##class PerformanceDataItem(object):
## "holds a result (as elapsed time value in secs) and the time of invocation"
## def __init__(self,startTime,elapsedTime,maxDelay=None):
## self.startTime=startTime # time of run
## self.elapsedTime=round(elapsedTime,2) # elapsed time in secs
## self.maxDelay=maxDelay # user defined delay for check
## # if None, then there is no upper threshhold
##
## def __str__(self):
## if self.maxDelay==None:
## ok='OK'
## md=''
## else:
## if self.elapsedTime<=self.maxDelay:
## ok='OK'
## else:
## ok='Fail'
## md= ' (%s)' % self.maxDelay
##
## return '%s: %s%s %s' % (ctime(self.startTime), self.elapsedTime, md, ok)
##
## def __cmp__(self,other):
## if self.startTime<other.startTime:
## return -1
## elif self.startTime>other.startTime:
## return 1
## else:
## return 0
##
maxWaitSecsDefault=3.0
retrySecsDefault=0.2
class TimedOutError(AssertionError): pass
class TimedTestCase(TestCase):
# extends the functionality of unittest.TestCase to
# allow multiple attempts at assertion, failif, for
# a specific length of time. If the test is not successful by the time
# that Then it fails
def __init__(self, methodName='runTest',maxWaitSecsDefault=1.0,retrySecsDefault=0.2):
TestCase.__init__(self,methodName)
self.maxWaitSecsDefault=float(maxWaitSecsDefault)
self.retrySecsDefault=float(retrySecsDefault)
def doTimeTest(self,func,*args):
# remove the last 2 args, as they're out timing values:
maxWaitSecs=args[-2]
retrySecs=args[-1]
args=args[:-2]
if maxWaitSecs==None:
maxWaitSecs=self.maxWaitSecsDefault
else:
try:
maxWaitSecs=float(maxWaitSecs)
except TypeError,e:
e='%s (maxWaitSecs "%s")' % (e,maxWaitSecs)
raise TypeError,e
if retrySecs==None:
retrySecs=self.retrySecsDefault
else:
try:
retrySecs=float(retrySecs)
except TypeError,e:
e='%s (retrySecs "%s")' % (e,retrySecs)
raise TypeError,e
retrySecs=max(0,retrySecs-.001) # allow for the pump,etc below
t=time()
while (time()-t)<maxWaitSecs :
try:
func(self,*args)
break
except self.failureException:
if pump:
pump()
sleep(retrySecs)
else:
# timed out, and still not worked!
try:
func(self,*args)
except self.failureException,e:
raise TimedOutError,e
def fail(self,msg=None,maxWaitSecs=None,retrySecs=None):
self.doTimeTest(TestCase.fail,maxWaitSecs,retrySecs)
def failIf(self, expr, msg=None,maxWaitSecs=None,retrySecs=None):
"Fail the test if the expression is true."
self.doTimeTest(TestCase.failIf,expr,msg,maxWaitSecs,retrySecs)
def failUnless(self, expr, msg=None,maxWaitSecs=None,retrySecs=None):
"""Fail the test unless the expression is true."""
self.doTimeTest(TestCase.failUnless,expr,msg,maxWaitSecs,retrySecs)
def failUnlessEqual(self, first, second, msg=None,maxWaitSecs=None,retrySecs=None):
"""Fail if the two objects are unequal as determined by the '=='
operator.
"""
self.doTimeTest(TestCase.failUnlessEqual,first,second,msg,maxWaitSecs,retrySecs)
def failIfEqual(self, first, second, msg=None):
"""Fail if the two objects are equal as determined by the '=='
operator.
"""
self.doTimeTest(TestCase.failIfEqual,first,second,msg,maxWaitSecs,retrySecs)
def failUnlessAlmostEqual(self, first, second, places=7, msg=None):
"""Fail if the two objects are unequal as determined by their
difference rounded to the given number of decimal places
(default 7) and comparing to zero.
Note that decimal places (from zero) is usually not the same
as significant digits (measured from the most signficant digit).
"""
self.doTimeTest(TestCase.failUnlessAlmostEqual,first,second,places,msg,maxWaitSecs,retrySecs)
def failIfAlmostEqual(self, first, second, places=7, msg=None):
"""Fail if the two objects are equal as determined by their
difference rounded to the given number of decimal places
(default 7) and comparing to zero.
Note that decimal places (from zero) is usually not the same
as significant digits (measured from the most signficant digit).
"""
self.doTimeTest(TestCase.failIfAlmostEqual,first,second,places,msg,maxWaitSecs,retrySecs)
assertEqual = assertEquals = failUnlessEqual
assertNotEqual = assertNotEquals = failIfEqual
assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual
assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual
assert_ = failUnless

View file

@ -0,0 +1 @@
pythonw wxShowWindows.py

View file

View file

@ -0,0 +1,96 @@
import unittest
import os.path, imp, os
from types import ListType, TupleType
def buildSuite(paths,recurse_directories=False):
# returns a suite of TestCase classes from the list of paths,
# These may be files and directories. If directories,
# there's the opportunity to recurse down all
def visit(args,dirname,allnames):
for aname in allnames:
fullname=os.path.join(dirname,aname)
if os.path.isdir(fullname):
res=buildSuitesFromDirectory(fullname)
if res:
suites.extend(res)
#ensure paths is a list:
if type(paths) not in (ListType, TupleType):
paths=[paths]
suites=[]
for path in paths:
if os.path.isfile(path):
res=buildSuiteFromFile(path)
if res:
suites.append(res)
elif os.path.isdir(path):
#print 'Directory: %s' % path
# find all the TestCases in this directory:
res=buildSuitesFromDirectory(path)
if res:
suites.extend(res)
if recurse_directories:
os.path.walk(path,visit,suites)
t=unittest.TestSuite()
for suite in suites:
for test in suite._tests:
t.addTest(test)
if len(t._tests):
return t
else:
return None
def buildSuiteFromFile(filename):
modname, modtype = os.path.splitext(os.path.basename(filename))
if modtype.lower() == '.py':
moduleToTest = imp.load_source(modname, filename, file(filename))
#elif modtype.lower() in {'.pyc':0, '.pyo':0}:
# moduleToTest = imp.load_compiled(modname, filename, file(filename, 'rb'))
else:
return None
suite= unittest.defaultTestLoader.loadTestsFromModule(moduleToTest)
for test in suite._tests: # tests is a TestSuite class
# remove any which are null:
if len(test._tests)==0:
suite._tests.remove(test)
if len(suite._tests): #not interested if no tests!
return suite
else:
return None
def buildSuitesFromDirectory(dirname):
filenames=os.listdir(dirname)
suites=[]
for filename in filenames:
fullname=os.path.join(dirname,filename)
res=buildSuiteFromFile(fullname)
if res:
suites.append(res)
return suites
if __name__=='__main__':
dir='c:/MyProducts/watsup/examples/unittests'
fs=[]
for fname in ('ExampleTestsAgain.py','ExampleTests.py'):
fs.append(os.path.join(dir,fname))
myfile=os.path.abspath( __file__)
#print buildSuite(myfile)
mydir=os.path.split(myfile)[0]
suite= buildSuite(dir,True)
print '------------'
print suite._tests

View file

@ -0,0 +1,42 @@
""" Lists the windowText and className for a given executable on the system.
Usage:
c:>findAppControls.py example/example1
c:>findAppControls.py -v example/example1
- this does it in verbose mode
"""
# Author : Tim Couper - tim@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires watsup
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-v", "--verbose",
action="store_true", dest="verbose", default=False,
help="print verbose messages")
(options, args) = parser.parse_args()
if len(args)<1:
print 'Usage: findAppControls.py <path_to_executable>'
else:
from watsup.AppControls import AppControls
try:
print 'passing',args[0]
a=AppControls(args[0],verbose=options.verbose)
a.run()
except:
import sys
print '\n** %s **\n' % sys.exc_info()[1]
import traceback
traceback.print_tb(sys.exc_info()[2])

View file

@ -0,0 +1,127 @@
#Boa:Frame:ShowWindows
# Author : Tim Couper - tim@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires watsup, wxPython
from wxPython.wx import *
from wxPython.lib.anchors import LayoutAnchors
from watsup.tools.showWindows import findAll,findNew,readPickle
from watsup.winGuiAuto import dumpWindow
from watsup.utils import tupleHwnd
import pprint
def create(parent):
return ShowWindows(parent)
[wxID_SHOWWINDOWS, wxID_SHOWWINDOWSFINDNEW, wxID_SHOWWINDOWSNEWWINDOWS,
wxID_SHOWWINDOWSPANEL1, wxID_SHOWWINDOWSREGISTER, wxID_SHOWWINDOWSREGISTERED,
wxID_SHOWWINDOWSTEXT,
] = map(lambda _init_ctrls: wxNewId(), range(7))
class ShowWindows(wxFrame):
def _init_ctrls(self, prnt):
# generated method, don't edit
wxFrame.__init__(self, id=wxID_SHOWWINDOWS, name='ShowWindows',
parent=prnt, pos=wxPoint(424, 184), size=wxSize(456, 433),
style=wxMINIMIZE_BOX | wxSTATIC_BORDER | wxCAPTION | wxSYSTEM_MENU,
title='ShowWindows 1.0')
self.SetClientSize(wxSize(448, 399))
self.SetToolTipString('ShowWindow')
self.Center(wxBOTH)
self.Enable(True)
self.SetSizeHints(-1, -1, -1, -1)
self.SetThemeEnabled(False)
self.panel1 = wxPanel(id=wxID_SHOWWINDOWSPANEL1, name='panel1',
parent=self, pos=wxPoint(0, 350), size=wxSize(450, 50),
style=wxTAB_TRAVERSAL)
self.panel1.SetConstraints(LayoutAnchors(self.panel1, True, True, False,
False))
self.Register = wxButton(id=wxID_SHOWWINDOWSREGISTER, label='Register',
name='Register', parent=self.panel1, pos=wxPoint(32, 13),
size=wxSize(75, 23), style=0)
self.Register.SetToolTipString('Register all windows info')
EVT_BUTTON(self.Register, wxID_SHOWWINDOWSREGISTER,
self.OnRegisterButton)
self.FindNew = wxButton(id=wxID_SHOWWINDOWSFINDNEW, label='Find New',
name='FindNew', parent=self.panel1, pos=wxPoint(304, 13),
size=wxSize(75, 23), style=0)
EVT_BUTTON(self.FindNew, wxID_SHOWWINDOWSFINDNEW, self.OnFindNewButton)
self.Text = wxTextCtrl(id=wxID_SHOWWINDOWSTEXT, name='Text',
parent=self, pos=wxPoint(0, 0), size=wxSize(450, 350),
style=wxRAISED_BORDER | wxTE_WORDWRAP | wxTE_MULTILINE, value='')
self.Registered = wxTextCtrl(id=wxID_SHOWWINDOWSREGISTERED,
name='Registered', parent=self.panel1, pos=wxPoint(110, 16),
size=wxSize(40, 16), style=wxTE_CENTER | wxTE_READONLY, value='')
self.Registered.SetToolTipString('No of windows registered on system')
self.Registered.SetBackgroundColour(wxColour(175, 175, 175))
self.NewWindows = wxTextCtrl(id=wxID_SHOWWINDOWSNEWWINDOWS,
name='NewWindows', parent=self.panel1, pos=wxPoint(382, 16),
size=wxSize(40, 16), style=wxTE_CENTER | wxTE_READONLY, value='')
self.NewWindows.SetToolTipString('No of new windows found')
self.NewWindows.SetBackgroundColour(wxColour(175, 175, 175))
def __init__(self, parent):
self._init_ctrls(parent)
#load up the last value for your info
ws=readPickle()
if ws:
self.Registered.SetValue(str(len(ws)))
def OnRegisterButton(self, event):
ws=findAll()
self.Registered.SetBackgroundColour(wxColour(255, 255, 255))
self.Registered.SetValue(str(len(ws)))
def OnFindNewButton(self, event):
ws=findNew()
self.NewWindows.SetBackgroundColour(wxColour(255, 255, 255))
# write the details to the text box
withControls=self.writeNewWindows(ws)
self.NewWindows.SetValue('%s/%d' % (withControls,len(ws)))
def writeNewWindows(self,ws):
noControls=0
withControls=0
txt=[]
for w in ws:
dw=dumpWindow(w)
if not dw:
noControls+=1
continue
t=tupleHwnd(w)
# don't bother with ShowWindows application:
wclass=t[2]
wtext=t[1]
if wclass=='wxWindowClass' and wtext.startswith('ShowWindows'):
noControls+=1
continue
if wtext:
wtext="%s" % wtext
else:
wtext=''
withControls+=1
# write the heading window
#txt.append('%d. %s %s' % (withControls,wclass,wtext))
txt.append('%d. %s' % (withControls,str(list(t))))
txt.append(pprint.pformat(dw))
txt.append('')
self.Text.SetValue('\n'.join(txt))
return withControls

View file

@ -0,0 +1,4 @@
[main]
checking = True
current_directory = C:\Python23\Lib\site-packages\watsup\examples\unittests

View file

@ -0,0 +1,34 @@
# logic for wxShowWindows
# Author : Tim Couper - timc@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires watsup
import cPickle
from watsup.AppControls import findNewTopWindows
from watsup.winGuiAuto import dumpWindow
from watsup.utils import dumpHwnd
import os.path
from types import ListType
PICKLE_FILE='findall.pkl'
def readPickle(pickle_file=PICKLE_FILE):
#reads the list in the pickle file if possible
if os.path.exists(pickle_file):
return cPickle.load(open(pickle_file))
else:
return []
def findAll(pickle_file=PICKLE_FILE):
# get all the top windows:
res=findNewTopWindows()
cPickle.dump(res,open(pickle_file,'w'))
return res
def findNew(pickle_file=PICKLE_FILE):
# get all the top windows, and return any new ones
olds=readPickle(pickle_file)
return findNewTopWindows(olds)

View file

@ -0,0 +1 @@
python watsup_framework.py

View file

@ -0,0 +1,653 @@
# Author : Tim Couper - timc@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires wxPython,watsup
# Based heavily on unittestgui.py by Chris Liechti
import unittest, os, imp, time, traceback,sys
## import all of the wxPython GUI package
from wxPython.wx import *
from wxPython.grid import *
from watsup.performance import doChecking,onOff,isChecking
from watsup.tools.buildSuite import buildSuite
from time import ctime
BASE_TITLE="Watsup Framework"
INI_FILE='./runTests.ini'
from ConfigParser import ConfigParser,NoOptionError, NoSectionError
#specify editor, make it return immediately, otherwise the GUI will wait
#until its closed. on win use "start cmd", on un*x "cmd&"
EDITOR = r'start c:\tools\wscite\scite "%(filename)s" -goto:%(lineno)s'
def starteditor(filename, lineno=1):
if os.path.exists(filename):
os.system(EDITOR % {'filename':filename, 'lineno':lineno})
else:
wxMessageBox("Cannot locate sourcefile:\n%s" % filename, "Can't start editor...", wxOK)
#----------------------------------------------------------------
class GUITestResult(unittest.TestResult):
"""A test result class that can print formatted text results to a stream.
"""
separator1 = '=' * 70
separator2 = '-' * 70
def __init__(self, listview, progress, descriptions, verbosity):
unittest.TestResult.__init__(self)
self.listview = listview
self.showAll = verbosity > 1
self.descriptions = descriptions
self.progress = progress
self.testdescr = None
self.stream = sys.stdout
def getDescription(self, test):
if self.descriptions:
return test.shortDescription() or str(test)
else:
return str(test)
def startTest(self, test):
unittest.TestResult.startTest(self, test)
self.testdescr = self.getDescription(test)
if self.showAll:
self.stream.write(self.getDescription(test))
self.stream.write(" ... ")
def _correctFilename(self, filename):
if filename[-4:] in ('.pyc', '.pyo'):
return filename[:-1]
return filename
def addSuccess(self, test):
unittest.TestResult.addSuccess(self, test)
if self.showAll:
self.stream.write("ok\n")
self.listview.Append( (
'Ok',
self.testdescr,
'',
None,
None
) )
self.progress.tick()
def addError(self, test, err):
unittest.TestResult.addError(self, test, err)
if self.showAll:
self.stream.write("ERROR\n")
try:
filename = self._correctFilename(err[2].tb_frame.f_globals['__file__'])
except KeyError:
filename = None
lineno = err[2].tb_lineno
self.listview.Append( (
'Error',
self.testdescr,
traceback.format_exception(*err)[-1].rstrip(),
self._exc_info_to_string(err),
(filename, lineno)
) )
self.progress.tick()
def addFailure(self, test, err):
unittest.TestResult.addFailure(self, test, err)
if self.showAll:
self.stream.write("FAIL\n")
try:
filename = self._correctFilename(err[2].tb_frame.f_globals['__file__'])
except KeyError:
filename = None
lineno = err[2].tb_lineno
self.listview.Append( (
'Fail',
self.testdescr,
traceback.format_exception(*err)[-1].rstrip(),
self._exc_info_to_string(err),
(filename, lineno)
) )
self.progress.tick()
def printErrors(self):
if self.showAll:
self.stream.write("\n")
self.printErrorList('ERROR', self.errors)
self.printErrorList('FAIL', self.failures)
def printErrorList(self, flavour, errors):
for test, err in errors:
self.stream.write(self.separator1)
self.stream.write("\n%s: %s\n" % (flavour,self.getDescription(test)))
self.stream.write(self.separator2)
self.stream.write("\n%s\n" % err)
class GUITestRunner:
"""A test runner class that displays results in textual form.
It prints out the names of tests as they are run, errors as they
occur, and a summary of the results at the end of the test run.
"""
def __init__(self, listview, progress, stream=sys.stderr, descriptions=1, verbosity=2):
self.listview = listview
self.progress = progress
self.stream = unittest._WritelnDecorator(stream)
self.descriptions = descriptions
self.verbosity = verbosity
def _makeResult(self):
return GUITestResult(self.listview, self.progress, self.descriptions, self.verbosity)
def run(self, test):
"Run the given test case or test suite."
result = self._makeResult()
startTime = time.time()
test(result)
stopTime = time.time()
timeTaken = float(stopTime - startTime)
result.printErrors()
self.stream.writeln(result.separator2)
run = result.testsRun
self.stream.writeln("Ran %d test%s in %.3fs" %
(run, run == 1 and "" or "s", timeTaken))
self.stream.writeln()
if not result.wasSuccessful():
self.stream.write("FAILED (")
failed, errored = map(len, (result.failures, result.errors))
if failed:
self.stream.write("failures=%d" % failed)
if errored:
if failed: self.stream.write(", ")
self.stream.write("errors=%d" % errored)
self.stream.writeln(")")
else:
self.stream.writeln("OK")
return result
#----------------------------------------------------------------
def lastline(t):
t = t.rstrip()
n = t.rfind('\n')
if n >= 0:
return t[n+1:]
return t
#----------------------------------------------------------------
class ResultView(wxListCtrl):
PM_DETAIL = wxNewId()
PM_LOCATE = wxNewId()
def __init__(self, parent):
wxListCtrl.__init__(self, parent, -1,
style=wxLC_REPORT|wxSUNKEN_BORDER|wxLC_VIRTUAL|wxLC_VRULES,#|wxLC_HRULES,
size=(500,200))
self.InsertColumn(0, '-Ok-')
self.InsertColumn(1, 'Description')
self.InsertColumn(2, 'Details')
self.SetColumnWidth(0, 40)
w = self.GetSize()[0] - self.GetColumnWidth(0)
self.SetColumnWidth(1, w/2)
self.SetColumnWidth(2, w/2)
EVT_RIGHT_DOWN(self, self.OnRightClick)
EVT_LIST_ITEM_SELECTED(self, self.GetId(), self.OnItemSelected)
EVT_LIST_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated)
self.red = wxListItemAttr()
self.red.SetBackgroundColour(wxRED)
self.green = wxListItemAttr()
self.green.SetBackgroundColour(wxColour(200,255,200)) #light green
self.orange = wxListItemAttr()
self.orange.SetBackgroundColour(wxColour(255,200,200)) #light red
self.result = []
self.currentItem = None
EVT_SIZE(self, self.OnSize)
#node menu
self.nodemenu = wxMenu()
self.nodemenu.Append(self.PM_DETAIL, "Show Details")
#self.nodemenu.AppendSeparator()
#self.nodemenu.Append(self.PM_LOCATE, "Locate Source")
EVT_MENU(self, self.PM_DETAIL, self.OnDetail)
#EVT_MENU(self, self.PM_LOCATE, self.OnLocate)
def OnDetail(self, event=None):
if self.result[self.currentItem][3]:
wxMessageBox(self.result[self.currentItem][3], "Result details", wxOK)
def OnLocate(self, event=None):
filename, lineno = self.result[self.currentItem][4]
print "locate",filename, lineno
starteditor(filename, lineno)
def OnSize(self, event):
w = self.GetSize()[0] - self.GetColumnWidth(0) - 30
if w < 180: w = 180
self.SetColumnWidth(1, w/2)
self.SetColumnWidth(2, w/2)
def Append(self, item):
self.result.append(item)
self.SetItemCount(len(self.result))
#wxYield()
#self.Refresh()
def DeleteAllItems(self):
self.result = []
wxListCtrl.DeleteAllItems(self)
def OnItemSelected(self, event):
self.currentItem = event.m_itemIndex
def OnItemActivated(self, event):
self.currentItem = event.m_itemIndex
self.OnDetail()
def OnRightClick(self, event):
pt = event.GetPosition()
item, flags = self.HitTest(pt)
if not flags & wxLIST_HITTEST_NOWHERE:
if self.currentItem is not None:
self.SetItemState(self.currentItem, 0, wxLIST_STATE_SELECTED )
self.currentItem = item
self.SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED )
if self.result[self.currentItem][3]:
self.PopupMenu(self.nodemenu, pt) #display popup menu
def OnCopyAsText(self, all=0):
res = []
item = -1;
while 1:
if all:
item = self.GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_DONTCARE);
else:
item = self.GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
text = '\t'.join(map(str, self.result[item]))
res.append(text)
if item == -1:
break
clip = wxClipboard()
clip.Open()
clip.SetData( wxTextDataObject('\n'.join(res)) )
clip.Close()
#---------------------------------------------------
# These methods are callbacks for implementing the
# "virtualness" of the list...
def OnGetItemText(self, row, col):
#print row, col
try:
return str(self.result[row][col])
except IndexError:
return ''
def OnGetItemImage(self, item):
return 0
def OnGetItemAttr(self, item):
if self.result[item][0] == 'Error':
return self.red
elif self.result[item][0] == 'Fail':
return self.orange
elif self.result[item][0] == 'Ok':
return self.green
return None
class TreeView(wxTreeCtrl):
PM_RUNSEL = wxNewId()
PM_LOCATE = wxNewId()
def __init__(self, *args, **kwargs):
wxTreeCtrl.__init__(self, *args, **kwargs)
EVT_LEFT_DCLICK(self, self.OnLeftDClick)
EVT_RIGHT_DOWN(self, self.OnRightClick)
EVT_RIGHT_UP(self, self.OnRightUp)
EVT_TREE_BEGIN_LABEL_EDIT(self, self.GetId(), self.OnBeginEdit)
EVT_TREE_END_LABEL_EDIT (self, self.GetId(), self.OnEndEdit)
#node menu
## self.nodemenu = wxMenu()
## self.nodemenu.Append(self.PM_RUNSEL, "Run selected")
## self.nodemenu.AppendSeparator()
## self.nodemenu.Append(self.PM_LOCATE, "Locate Source")
##
## EVT_MENU(self, self.PM_RUNSEL, self.OnRunSel)
## EVT_MENU(self, self.PM_LOCATE, self.OnLocate)
#don't allow editing of node names
def OnBeginEdit(self, event): event.Veto()
def OnEndEdit(self, event): event.Veto()
def OnLeftDClick(self, event):
## pt = event.GetPosition();
## item, flags = self.HitTest(pt)
## #print("OnLeftDClick: %s" % self.GetItemText(item))
## parent = self.GetItemParent(item)
## #self.SortChildren(parent)
event.Skip()
def OnRightClick(self, event):
pt = event.GetPosition()
item, flags = self.HitTest(pt)
if not flags & wxTREE_HITTEST_NOWHERE:
self.SelectItem(item)
#self.PopupMenu(self.nodemenu, pt) #display node menu
def OnRightUp(self, event):
pt = event.GetPosition();
item, flags = self.HitTest(pt)
#self.tree.EditLabel(item)
def OnCompareItems(self, item1, item2):
t1 = self.GetItemText(item1)
t2 = self.GetItemText(item2)
#print('compare: ' + t1 + ' <> ' + t2)
return cmp(t1,t2)
def OnRunSel(self, event):
pass
def OnLocate(self, event):
pass
#----------------------------------------------------------------
class ProgressBar(wxGauge):
def __init__(self, *args, **kwargs):
wxGauge.__init__(self, *args, **kwargs)
self.SetBezelFace(5)
self.SetShadowWidth(5)
self.increment = 1
def tick(self):
self.SetValue(self.GetValue() + self.increment)
#----------------------------------------------------------------
class Frame(wxFrame):
ID_BUTTON = wxNewId()
#M_NEW = wxNewId()
M_OPENFILES = wxNewId()
M_OPENDIR = wxNewId()
M_OPENTREE = wxNewId()
M_EXIT = wxNewId()
M_COPYALL = wxNewId()
M_COPYSEL = wxNewId()
M_RUN = wxNewId()
M_CHECKING = wxNewId()
def __init__(self, parent, id, title = BASE_TITLE):
# First, call the base class' __init__ method to create the frame
wxFrame.__init__(self, parent, id, title, wxPoint(100, 100), wxSize(100, 100))
#variables
self.suite = None
self.loadConfig()
#menu
menuBar = wxMenuBar()
menu = wxMenu()
#menu.Append(self.M_NEW, "&New")
menu.Append(self.M_OPENFILES, "&Load Test File(s)")
menu.Append(self.M_OPENDIR, "&Load Test Directory")
menu.Append(self.M_OPENTREE, "&Load Test Directory Tree")
menu.AppendSeparator()
menu.Append(self.M_EXIT, "E&xit")
#EVT_MENU(self, self.M_NEW, self.OnNew)
EVT_MENU(self, self.M_OPENFILES, self.OnMenuOpenFiles)
EVT_MENU(self, self.M_OPENDIR, self.OnMenuOpenDir)
EVT_MENU(self, self.M_OPENTREE, self.OnMenuOpenTree)
EVT_MENU(self, self.M_EXIT, self.OnCloseWindow)
menuBar.Append(menu, "&File");
menu = wxMenu()
menu.Append(self.M_COPYALL, "&Copy all results")
menu.Append(self.M_COPYSEL, "&Copy selected results")
EVT_MENU(self, self.M_COPYALL, self.OnCopyAll)
EVT_MENU(self, self.M_COPYSEL, self.OnCopySelection)
menuBar.Append(menu, "&Edit");
menu = wxMenu()
menu.Append(self.M_RUN, "&Run all")
menu.AppendSeparator()
menu.AppendCheckItem(self.M_CHECKING,"&Checking")
menu.Check(self.M_CHECKING,isChecking())
EVT_MENU(self, self.M_RUN, self.OnRun)
EVT_MENU(self, self.M_CHECKING, self.OnChecking)
self.testing_menu=menu
menuBar.Append(menu, "&Testing")
self.SetMenuBar(menuBar)
#GUI
panel = wxPanel(self, 0)
button = wxButton(panel, self.ID_BUTTON, "Run Tests" )
button.SetDefault()
EVT_BUTTON(panel, self.ID_BUTTON, self.OnRun )
self.tree = TreeView(panel, -1, size=(300,250), style=wxTR_HAS_BUTTONS | wxTR_EDIT_LABELS)# | wxTR_MULTIPLE
self.progress = ProgressBar(panel, -1, 100, style=wxGA_HORIZONTAL|wxGA_SMOOTH)
self.list = ResultView(panel)
sizer = wxBoxSizer(wxVERTICAL)
sizer.Add(self.tree, 1, wxEXPAND)
sizer.Add(button, 0, wxEXPAND)
sizer.Add(self.progress, 0, wxEXPAND)
sizer.Add(self.list, 1, wxEXPAND)
panel.SetAutoLayout(1)
panel.SetSizer(sizer)
sizer.Fit(panel)
basesizer = wxBoxSizer(wxVERTICAL)
basesizer.Add(panel, 1, wxEXPAND)
self.SetAutoLayout(1)
self.SetSizer(basesizer)
self.Fit()
#create a statusbar
sb = self.CreateStatusBar(1)
sb.SetStatusWidths([-1])
self.SetStatusText('Please load file(s), a directory or tree',0)
#update controls
self.OnNew()
def __del__(self):
# persist any values in .ini file
cfg=ConfigParser()
cfg.add_section('main')
cfg.set('main','current_directory',self.getCurrentDirectory())
cfg.set('main','checking',isChecking())
cfg.write(open(INI_FILE,'w'))
def loadConfig(self):
# load settings from the config file
cfg=ConfigParser()
cfg.read(INI_FILE)
self._currentDirectory='.'
try:
self.setCurrentDirectory(cfg.get('main','current_directory'))
except NoSectionError: # none of the other options will be available
return
except NoOptionError:
pass
try:
doChecking(cfg.getboolean('main','checking'))
except (NoOptionError, NoSectionError):
doChecking(True)
def UpdateTree(self, root=None, testlist=None):
if root is None:
root = self.root
if testlist is None and self.suite:
testlist = self.suite._tests #grmpf accessing a _ member, oh well
if root and testlist:
for testcase in testlist:
if isinstance(testcase, unittest.TestSuite):
testname=testcase._tests[0]
#str(testname): atestcasename (module.class)
# we want module.class here, with no brackets
# this should do it
label= str(testname).split('(')[1]
label=label[:-1]
child = self.tree.AppendItem(root, "%s (%d)" % (label,len(testcase._tests)))
self.tree.SetPyData(child, None)
self.UpdateTree(child, testcase._tests)
self.tree.SortChildren(child)
self.tree.Collapse(child)
else:
label="%s" % testcase
label=label.split('(')[0]
child = self.tree.AppendItem(root,label)
self.tree.SetPyData(child, None)
##self.tree.SetItemImage(child, idx2, wxTreeItemIcon_Expanded)
##self.tree.SetItemSelectedImage(child, idx3)
self.tree.Expand(root)
def OnRun(self, event=None):
""" """
if self.suite:
runner = GUITestRunner(self.list, self.progress)
self.SetStatusText('Running tests...',0)
ln = self.suite.countTestCases()
self.progress.SetValue(0)
self.progress.SetRange(ln)
self.list.DeleteAllItems()
result = runner.run(self.suite)
self.SetStatusText('Ran %d tests, %d errors, %d failures' % (ln, len(result.errors), len(result.failures)), 0)
else:
self.SetStatusText('No tests found', 0)
def OnMenuOpenFiles(self, event=None):
""" """
dlg = wxFileDialog(self, "Choose one or more modules",
self.getCurrentDirectory(),"",
'Python files (*.py)|*.py|All files (*.*)|*.*',
wxOPEN|wxMULTIPLE)
if dlg.ShowModal() == wxID_OK:
## for path in dlg.GetPaths():
## log.WriteText('You selected: %s\n' % path)
self.OnNew()
filenames = dlg.GetPaths() #dlg.GetPath()
self.setCurrentDirectory(dlg.GetDirectory())
self.suite=buildSuite(filenames)
#print self.suite
dlg.Destroy()
self.UpdateTree()
def OnMenuOpenDir(self,event=None):
self.loadDirectory('Choose the test directory')
def OnMenuOpenTree(self,event=None):
self.loadDirectory('Choose the top test directory',True)
def loadDirectory(self,msg,tree=False):
dlg=wxDirDialog(self,msg, self.getCurrentDirectory())
if dlg.ShowModal() == wxID_OK:
self.OnNew()
dir = dlg.GetPath()
self.setCurrentDirectory(dir)
self.suite=buildSuite(dir,tree)
#print self.suite
dlg.Destroy()
self.UpdateTree()
def OnNew(self, event=None):
self.list.DeleteAllItems()
self.tree.DeleteAllItems()
self.root = self.tree.AddRoot("Watsup Tests:")
self.tree.SetPyData(self.root, None)
self.progress.SetValue(0)
self.suite = None
#self.filename = None
def OnCopySelection(self,event=None):
self.list.OnCopyAsText()
def OnCopyAll(self,event=None):
self.list.OnCopyAsText(all=1)
def OnCloseWindow(self, event=None):
self.Destroy()
def OnChecking(self,event=None):
# toggle self._checking:
bool=not isChecking()
self.testing_menu.Check(self.M_CHECKING,bool)
doChecking(bool)
self.SetStatusText('Checking %s' % onOff(bool),0)
def getCurrentDirectory(self):
if os.path.isdir(self._currentDirectory):
return self._currentDirectory
else:
return '.'
def setCurrentDirectory(self,value):
if self._currentDirectory<>value:
if os.path.isdir(value):
self._currentDirectory=value
# update the title
if value=='.':
text=''
else:
text='(%s)' % value
self.SetTitle('%s %s'.strip() % (BASE_TITLE,text))
else:
self._currentDirectory='.'
#---------------------------------------------------------------------------
# Every wxWindows application must have a class derived from wxApp
class App(wxApp):
# wxWindows calls this method to initialize the application
def OnInit(self):
# Create an instance of our customized Frame class
frame = Frame(NULL, -1)
frame.Show(true)
# Tell wxWindows that this is our main window
self.SetTopWindow(frame)
# Return a success flag
return true
#---------------------------------------------------------------------------
if __name__ == "__main__":
app = App(0) # Create an instance of the application class
app.MainLoop() # Tell it to start processing events

View file

@ -0,0 +1,29 @@
#!/usr/bin/env python
#Boa:App:BoaApp
# Author : Tim Couper - tim@tizmoi.net
# Date : 1 August 2004
# Copyright : Copyright TAC Software Ltd, under Python-like licence.
# Provided as-is, with no warranty.
# Notes : Requires watsup,wxPython
from wxPython.wx import *
import fmShowWindows
modules ={'fmShowWindows': [1, 'Main frame of Application', 'fmShowWindows.py']}
class BoaApp(wxApp):
def OnInit(self):
wxInitAllImageHandlers()
self.main = fmShowWindows.create(None)
self.main.Show()
self.SetTopWindow(self.main)
return True
def main():
application = BoaApp(0)
application.MainLoop()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,37 @@
""" watsup system utilities
"""
# Author : Tim Couper - timc@tizmoi.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
# Notes : Requires win32all
import win32gui
import win32api
import sys
class WatsupError(Exception): pass
##def kill(pid):
## """kill function for Win32"""
## handle = win32api.OpenProcess(1, 0, pid)
## return (0 != win32api.TerminateProcess(handle, 0))
def dumpHwnd(hwnd):
t=list(tupleHwnd(hwnd))
t.reverse()
return '%s:"%s" (%d)' % tuple(t) #(win32gui.GetClassName(hwnd),win32gui.GetWindowText(hwnd),hwnd)
def tupleHwnd(hwnd):
return (hwnd,win32gui.GetWindowText(hwnd),win32gui.GetClassName(hwnd))
def pump():
win32gui.PumpWaitingMessages()
if __name__=='__main__':
pass

45
agent/crawl/watsup/wie.py Normal file
View file

@ -0,0 +1,45 @@
# provides a testing interface to web applications via the PAMIE module
# Author : Tim Couper - timc@tizmoi.net
# Date : 22 July 2004
# Version : 1.0
# Copyright : Copyright TAC Software Ltd, under Python-style licence.
# Provided as-is, with no warranty.
from cPAMIE import PAMIE
def findRunningIE():
from win32com.client import Dispatch
from win32gui import GetClassName
ShellWindowsCLSID = '{9BA05972-F6A8-11CF-A442-00A0C90A8F39}'
ShellWindows = Dispatch ( ShellWindowsCLSID )
# try to get an ie instance from the window
for shellwindow in ShellWindows :
if GetClassName ( shellwindow.HWND ) == 'IEFrame' :
return shellwindow
class WatsupIE(PAMIE):
def __init__(self,url=None, timeOut=1000, useExistingIfPossible=False):
self._ie=None
if useExistingIfPossible:
self._ie=findRunningIE()
if self._ie:
# this case can only arise if we've located a running IE;
# the code below should be everything else in PAMIE.__init__,
# apart from instantiation of the new ie instance:
if url:
self._ie.Navigate(url)
else:
self._ie.Navigate('about:blank')
self._timeOut = timeOut
self._ie.Visible = 1
else:
PAMIE.__init__(self,url,timeOut)

File diff suppressed because it is too large Load diff

Binary file not shown.