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:
parent
cd18b551dc
commit
41e98f0bef
37 changed files with 3877 additions and 0 deletions
152
agent/crawl/watsup/AppControls.py
Normal file
152
agent/crawl/watsup/AppControls.py
Normal 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
|
61
agent/crawl/watsup/OrderedDict.py
Normal file
61
agent/crawl/watsup/OrderedDict.py
Normal 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)+'}'
|
389
agent/crawl/watsup/PControl.py
Normal file
389
agent/crawl/watsup/PControl.py
Normal 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,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
0
agent/crawl/watsup/__init__.py
Normal file
0
agent/crawl/watsup/__init__.py
Normal file
BIN
agent/crawl/watsup/__init__.pyc
Normal file
BIN
agent/crawl/watsup/__init__.pyc
Normal file
Binary file not shown.
BIN
agent/crawl/watsup/docs/WatsupIE methods.doc
Normal file
BIN
agent/crawl/watsup/docs/WatsupIE methods.doc
Normal file
Binary file not shown.
10
agent/crawl/watsup/docs/html/code/example1.py
Normal file
10
agent/crawl/watsup/docs/html/code/example1.py
Normal 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)
|
||||||
|
|
27
agent/crawl/watsup/docs/html/code/example2.py
Normal file
27
agent/crawl/watsup/docs/html/code/example2.py
Normal 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()
|
17
agent/crawl/watsup/docs/html/code/example3.py
Normal file
17
agent/crawl/watsup/docs/html/code/example3.py
Normal 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)
|
||||||
|
|
||||||
|
|
51
agent/crawl/watsup/docs/html/code/example4.py
Normal file
51
agent/crawl/watsup/docs/html/code/example4.py
Normal 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'
|
||||||
|
|
3
agent/crawl/watsup/docs/html/code/example4a.py
Normal file
3
agent/crawl/watsup/docs/html/code/example4a.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from example4 import main
|
||||||
|
|
||||||
|
main('perform/perform.exe','Performance Form 1')
|
3
agent/crawl/watsup/docs/html/code/example4b.py
Normal file
3
agent/crawl/watsup/docs/html/code/example4b.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from example4 import main
|
||||||
|
|
||||||
|
main('perform/perform2.exe','Performance Form 2')
|
BIN
agent/crawl/watsup/docs/html/images/ShowWindows1.jpg
Normal file
BIN
agent/crawl/watsup/docs/html/images/ShowWindows1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
BIN
agent/crawl/watsup/docs/html/images/ShowWindows2.jpg
Normal file
BIN
agent/crawl/watsup/docs/html/images/ShowWindows2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
BIN
agent/crawl/watsup/docs/html/images/framework1.jpg
Normal file
BIN
agent/crawl/watsup/docs/html/images/framework1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
BIN
agent/crawl/watsup/docs/html/images/framework2.jpg
Normal file
BIN
agent/crawl/watsup/docs/html/images/framework2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
BIN
agent/crawl/watsup/docs/html/images/framework3.jpg
Normal file
BIN
agent/crawl/watsup/docs/html/images/framework3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 124 KiB |
343
agent/crawl/watsup/docs/html/intro.html
Normal file
343
agent/crawl/watsup/docs/html/intro.html
Normal 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<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>
|
BIN
agent/crawl/watsup/docs/images/tizmoi.jpg
Normal file
BIN
agent/crawl/watsup/docs/images/tizmoi.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
7
agent/crawl/watsup/docs/tac.css
Normal file
7
agent/crawl/watsup/docs/tac.css
Normal 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}
|
||||||
|
|
116
agent/crawl/watsup/launcher.py
Normal file
116
agent/crawl/watsup/launcher.py
Normal 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
|
181
agent/crawl/watsup/performance.py
Normal file
181
agent/crawl/watsup/performance.py
Normal 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()))
|
||||||
|
|
279
agent/crawl/watsup/timedunittest.py
Normal file
279
agent/crawl/watsup/timedunittest.py
Normal 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
|
||||||
|
|
1
agent/crawl/watsup/tools/ShowWindows.bat
Normal file
1
agent/crawl/watsup/tools/ShowWindows.bat
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pythonw wxShowWindows.py
|
0
agent/crawl/watsup/tools/__init__.py
Normal file
0
agent/crawl/watsup/tools/__init__.py
Normal file
96
agent/crawl/watsup/tools/buildSuite.py
Normal file
96
agent/crawl/watsup/tools/buildSuite.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
42
agent/crawl/watsup/tools/findAppControls.py
Normal file
42
agent/crawl/watsup/tools/findAppControls.py
Normal 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])
|
||||||
|
|
127
agent/crawl/watsup/tools/fmShowWindows.py
Normal file
127
agent/crawl/watsup/tools/fmShowWindows.py
Normal 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
|
||||||
|
|
4
agent/crawl/watsup/tools/runTests.ini
Normal file
4
agent/crawl/watsup/tools/runTests.ini
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[main]
|
||||||
|
checking = True
|
||||||
|
current_directory = C:\Python23\Lib\site-packages\watsup\examples\unittests
|
||||||
|
|
34
agent/crawl/watsup/tools/showWindows.py
Normal file
34
agent/crawl/watsup/tools/showWindows.py
Normal 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)
|
1
agent/crawl/watsup/tools/watsup_framework.bat
Normal file
1
agent/crawl/watsup/tools/watsup_framework.bat
Normal file
|
@ -0,0 +1 @@
|
||||||
|
python watsup_framework.py
|
653
agent/crawl/watsup/tools/watsup_framework.py
Normal file
653
agent/crawl/watsup/tools/watsup_framework.py
Normal 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
|
||||||
|
|
29
agent/crawl/watsup/tools/wxShowWindows.py
Normal file
29
agent/crawl/watsup/tools/wxShowWindows.py
Normal 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()
|
37
agent/crawl/watsup/utils.py
Normal file
37
agent/crawl/watsup/utils.py
Normal 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
45
agent/crawl/watsup/wie.py
Normal 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)
|
||||||
|
|
||||||
|
|
1169
agent/crawl/watsup/winGuiAuto.py
Normal file
1169
agent/crawl/watsup/winGuiAuto.py
Normal file
File diff suppressed because it is too large
Load diff
BIN
agent/crawl/watsup/winGuiAuto.pyc
Normal file
BIN
agent/crawl/watsup/winGuiAuto.pyc
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue