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