loops/agent/crawl/watsup/tools/watsup_framework.py
tschmid 41e98f0bef initial check in
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2069 fd906abe-77d9-0310-91a1-e0d9ade77398
2007-09-25 06:31:14 +00:00

653 lines
23 KiB
Python

# 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