summaryrefslogtreecommitdiffstats
path: root/Tools/unittestgui/unittestgui.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/unittestgui/unittestgui.py')
-rwxr-xr-xTools/unittestgui/unittestgui.py478
1 files changed, 0 insertions, 478 deletions
diff --git a/Tools/unittestgui/unittestgui.py b/Tools/unittestgui/unittestgui.py
deleted file mode 100755
index c3b5fa4..0000000
--- a/Tools/unittestgui/unittestgui.py
+++ /dev/null
@@ -1,478 +0,0 @@
-#!/usr/bin/env python3
-"""
-GUI framework and application for use with Python unit testing framework.
-Execute tests written using the framework provided by the 'unittest' module.
-
-Updated for unittest test discovery by Mark Roddy and Python 3
-support by Brian Curtin.
-
-Based on the original by Steve Purcell, from:
-
- http://pyunit.sourceforge.net/
-
-Copyright (c) 1999, 2000, 2001 Steve Purcell
-This module is free software, and you may redistribute it and/or modify
-it under the same terms as Python itself, so long as this copyright message
-and disclaimer are retained in their original form.
-
-IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
-SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
-THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
-AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
-SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-"""
-
-__author__ = "Steve Purcell (stephen_purcell@yahoo.com)"
-
-import sys
-import traceback
-import unittest
-
-import tkinter as tk
-from tkinter import messagebox
-from tkinter import filedialog
-from tkinter import simpledialog
-
-
-
-
-##############################################################################
-# GUI framework classes
-##############################################################################
-
-class BaseGUITestRunner(object):
- """Subclass this class to create a GUI TestRunner that uses a specific
- windowing toolkit. The class takes care of running tests in the correct
- manner, and making callbacks to the derived class to obtain information
- or signal that events have occurred.
- """
- def __init__(self, *args, **kwargs):
- self.currentResult = None
- self.running = 0
- self.__rollbackImporter = None
- self.__rollbackImporter = RollbackImporter()
- self.test_suite = None
-
- #test discovery variables
- self.directory_to_read = ''
- self.top_level_dir = ''
- self.test_file_glob_pattern = 'test*.py'
-
- self.initGUI(*args, **kwargs)
-
- def errorDialog(self, title, message):
- "Override to display an error arising from GUI usage"
- pass
-
- def getDirectoryToDiscover(self):
- "Override to prompt user for directory to perform test discovery"
- pass
-
- def runClicked(self):
- "To be called in response to user choosing to run a test"
- if self.running: return
- if not self.test_suite:
- self.errorDialog("Test Discovery", "You discover some tests first!")
- return
- self.currentResult = GUITestResult(self)
- self.totalTests = self.test_suite.countTestCases()
- self.running = 1
- self.notifyRunning()
- self.test_suite.run(self.currentResult)
- self.running = 0
- self.notifyStopped()
-
- def stopClicked(self):
- "To be called in response to user stopping the running of a test"
- if self.currentResult:
- self.currentResult.stop()
-
- def discoverClicked(self):
- self.__rollbackImporter.rollbackImports()
- directory = self.getDirectoryToDiscover()
- if not directory:
- return
- self.directory_to_read = directory
- try:
- # Explicitly use 'None' value if no top level directory is
- # specified (indicated by empty string) as discover() explicitly
- # checks for a 'None' to determine if no tld has been specified
- top_level_dir = self.top_level_dir or None
- tests = unittest.defaultTestLoader.discover(directory, self.test_file_glob_pattern, top_level_dir)
- self.test_suite = tests
- except:
- exc_type, exc_value, exc_tb = sys.exc_info()
- traceback.print_exception(*sys.exc_info())
- self.errorDialog("Unable to run test '%s'" % directory,
- "Error loading specified test: %s, %s" % (exc_type, exc_value))
- return
- self.notifyTestsDiscovered(self.test_suite)
-
- # Required callbacks
-
- def notifyTestsDiscovered(self, test_suite):
- "Override to display information about the suite of discovered tests"
- pass
-
- def notifyRunning(self):
- "Override to set GUI in 'running' mode, enabling 'stop' button etc."
- pass
-
- def notifyStopped(self):
- "Override to set GUI in 'stopped' mode, enabling 'run' button etc."
- pass
-
- def notifyTestFailed(self, test, err):
- "Override to indicate that a test has just failed"
- pass
-
- def notifyTestErrored(self, test, err):
- "Override to indicate that a test has just errored"
- pass
-
- def notifyTestSkipped(self, test, reason):
- "Override to indicate that test was skipped"
- pass
-
- def notifyTestFailedExpectedly(self, test, err):
- "Override to indicate that test has just failed expectedly"
- pass
-
- def notifyTestStarted(self, test):
- "Override to indicate that a test is about to run"
- pass
-
- def notifyTestFinished(self, test):
- """Override to indicate that a test has finished (it may already have
- failed or errored)"""
- pass
-
-
-class GUITestResult(unittest.TestResult):
- """A TestResult that makes callbacks to its associated GUI TestRunner.
- Used by BaseGUITestRunner. Need not be created directly.
- """
- def __init__(self, callback):
- unittest.TestResult.__init__(self)
- self.callback = callback
-
- def addError(self, test, err):
- unittest.TestResult.addError(self, test, err)
- self.callback.notifyTestErrored(test, err)
-
- def addFailure(self, test, err):
- unittest.TestResult.addFailure(self, test, err)
- self.callback.notifyTestFailed(test, err)
-
- def addSkip(self, test, reason):
- super(GUITestResult,self).addSkip(test, reason)
- self.callback.notifyTestSkipped(test, reason)
-
- def addExpectedFailure(self, test, err):
- super(GUITestResult,self).addExpectedFailure(test, err)
- self.callback.notifyTestFailedExpectedly(test, err)
-
- def stopTest(self, test):
- unittest.TestResult.stopTest(self, test)
- self.callback.notifyTestFinished(test)
-
- def startTest(self, test):
- unittest.TestResult.startTest(self, test)
- self.callback.notifyTestStarted(test)
-
-
-class RollbackImporter:
- """This tricky little class is used to make sure that modules under test
- will be reloaded the next time they are imported.
- """
- def __init__(self):
- self.previousModules = sys.modules.copy()
-
- def rollbackImports(self):
- for modname in sys.modules.copy().keys():
- if not modname in self.previousModules:
- # Force reload when modname next imported
- del(sys.modules[modname])
-
-
-##############################################################################
-# Tkinter GUI
-##############################################################################
-
-class DiscoverSettingsDialog(simpledialog.Dialog):
- """
- Dialog box for prompting test discovery settings
- """
-
- def __init__(self, master, top_level_dir, test_file_glob_pattern, *args, **kwargs):
- self.top_level_dir = top_level_dir
- self.dirVar = tk.StringVar()
- self.dirVar.set(top_level_dir)
-
- self.test_file_glob_pattern = test_file_glob_pattern
- self.testPatternVar = tk.StringVar()
- self.testPatternVar.set(test_file_glob_pattern)
-
- simpledialog.Dialog.__init__(self, master, title="Discover Settings",
- *args, **kwargs)
-
- def body(self, master):
- tk.Label(master, text="Top Level Directory").grid(row=0)
- self.e1 = tk.Entry(master, textvariable=self.dirVar)
- self.e1.grid(row = 0, column=1)
- tk.Button(master, text="...",
- command=lambda: self.selectDirClicked(master)).grid(row=0,column=3)
-
- tk.Label(master, text="Test File Pattern").grid(row=1)
- self.e2 = tk.Entry(master, textvariable = self.testPatternVar)
- self.e2.grid(row = 1, column=1)
- return None
-
- def selectDirClicked(self, master):
- dir_path = filedialog.askdirectory(parent=master)
- if dir_path:
- self.dirVar.set(dir_path)
-
- def apply(self):
- self.top_level_dir = self.dirVar.get()
- self.test_file_glob_pattern = self.testPatternVar.get()
-
-class TkTestRunner(BaseGUITestRunner):
- """An implementation of BaseGUITestRunner using Tkinter.
- """
- def initGUI(self, root, initialTestName):
- """Set up the GUI inside the given root window. The test name entry
- field will be pre-filled with the given initialTestName.
- """
- self.root = root
-
- self.statusVar = tk.StringVar()
- self.statusVar.set("Idle")
-
- #tk vars for tracking counts of test result types
- self.runCountVar = tk.IntVar()
- self.failCountVar = tk.IntVar()
- self.errorCountVar = tk.IntVar()
- self.skipCountVar = tk.IntVar()
- self.expectFailCountVar = tk.IntVar()
- self.remainingCountVar = tk.IntVar()
-
- self.top = tk.Frame()
- self.top.pack(fill=tk.BOTH, expand=1)
- self.createWidgets()
-
- def getDirectoryToDiscover(self):
- return filedialog.askdirectory()
-
- def settingsClicked(self):
- d = DiscoverSettingsDialog(self.top, self.top_level_dir, self.test_file_glob_pattern)
- self.top_level_dir = d.top_level_dir
- self.test_file_glob_pattern = d.test_file_glob_pattern
-
- def notifyTestsDiscovered(self, test_suite):
- discovered = test_suite.countTestCases()
- self.runCountVar.set(0)
- self.failCountVar.set(0)
- self.errorCountVar.set(0)
- self.remainingCountVar.set(discovered)
- self.progressBar.setProgressFraction(0.0)
- self.errorListbox.delete(0, tk.END)
- self.statusVar.set("Discovering tests from %s. Found: %s" %
- (self.directory_to_read, discovered))
- self.stopGoButton['state'] = tk.NORMAL
-
- def createWidgets(self):
- """Creates and packs the various widgets.
-
- Why is it that GUI code always ends up looking a mess, despite all the
- best intentions to keep it tidy? Answers on a postcard, please.
- """
- # Status bar
- statusFrame = tk.Frame(self.top, relief=tk.SUNKEN, borderwidth=2)
- statusFrame.pack(anchor=tk.SW, fill=tk.X, side=tk.BOTTOM)
- tk.Label(statusFrame, width=1, textvariable=self.statusVar).pack(side=tk.TOP, fill=tk.X)
-
- # Area to enter name of test to run
- leftFrame = tk.Frame(self.top, borderwidth=3)
- leftFrame.pack(fill=tk.BOTH, side=tk.LEFT, anchor=tk.NW, expand=1)
- suiteNameFrame = tk.Frame(leftFrame, borderwidth=3)
- suiteNameFrame.pack(fill=tk.X)
-
- # Progress bar
- progressFrame = tk.Frame(leftFrame, relief=tk.GROOVE, borderwidth=2)
- progressFrame.pack(fill=tk.X, expand=0, anchor=tk.NW)
- tk.Label(progressFrame, text="Progress:").pack(anchor=tk.W)
- self.progressBar = ProgressBar(progressFrame, relief=tk.SUNKEN,
- borderwidth=2)
- self.progressBar.pack(fill=tk.X, expand=1)
-
-
- # Area with buttons to start/stop tests and quit
- buttonFrame = tk.Frame(self.top, borderwidth=3)
- buttonFrame.pack(side=tk.LEFT, anchor=tk.NW, fill=tk.Y)
-
- tk.Button(buttonFrame, text="Discover Tests",
- command=self.discoverClicked).pack(fill=tk.X)
-
-
- self.stopGoButton = tk.Button(buttonFrame, text="Start",
- command=self.runClicked, state=tk.DISABLED)
- self.stopGoButton.pack(fill=tk.X)
-
- tk.Button(buttonFrame, text="Close",
- command=self.top.quit).pack(side=tk.BOTTOM, fill=tk.X)
- tk.Button(buttonFrame, text="Settings",
- command=self.settingsClicked).pack(side=tk.BOTTOM, fill=tk.X)
-
- # Area with labels reporting results
- for label, var in (('Run:', self.runCountVar),
- ('Failures:', self.failCountVar),
- ('Errors:', self.errorCountVar),
- ('Skipped:', self.skipCountVar),
- ('Expected Failures:', self.expectFailCountVar),
- ('Remaining:', self.remainingCountVar),
- ):
- tk.Label(progressFrame, text=label).pack(side=tk.LEFT)
- tk.Label(progressFrame, textvariable=var,
- foreground="blue").pack(side=tk.LEFT, fill=tk.X,
- expand=1, anchor=tk.W)
-
- # List box showing errors and failures
- tk.Label(leftFrame, text="Failures and errors:").pack(anchor=tk.W)
- listFrame = tk.Frame(leftFrame, relief=tk.SUNKEN, borderwidth=2)
- listFrame.pack(fill=tk.BOTH, anchor=tk.NW, expand=1)
- self.errorListbox = tk.Listbox(listFrame, foreground='red',
- selectmode=tk.SINGLE,
- selectborderwidth=0)
- self.errorListbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=1,
- anchor=tk.NW)
- listScroll = tk.Scrollbar(listFrame, command=self.errorListbox.yview)
- listScroll.pack(side=tk.LEFT, fill=tk.Y, anchor=tk.N)
- self.errorListbox.bind("<Double-1>",
- lambda e, self=self: self.showSelectedError())
- self.errorListbox.configure(yscrollcommand=listScroll.set)
-
- def errorDialog(self, title, message):
- messagebox.showerror(parent=self.root, title=title,
- message=message)
-
- def notifyRunning(self):
- self.runCountVar.set(0)
- self.failCountVar.set(0)
- self.errorCountVar.set(0)
- self.remainingCountVar.set(self.totalTests)
- self.errorInfo = []
- while self.errorListbox.size():
- self.errorListbox.delete(0)
- #Stopping seems not to work, so simply disable the start button
- #self.stopGoButton.config(command=self.stopClicked, text="Stop")
- self.stopGoButton.config(state=tk.DISABLED)
- self.progressBar.setProgressFraction(0.0)
- self.top.update_idletasks()
-
- def notifyStopped(self):
- self.stopGoButton.config(state=tk.DISABLED)
- #self.stopGoButton.config(command=self.runClicked, text="Start")
- self.statusVar.set("Idle")
-
- def notifyTestStarted(self, test):
- self.statusVar.set(str(test))
- self.top.update_idletasks()
-
- def notifyTestFailed(self, test, err):
- self.failCountVar.set(1 + self.failCountVar.get())
- self.errorListbox.insert(tk.END, "Failure: %s" % test)
- self.errorInfo.append((test,err))
-
- def notifyTestErrored(self, test, err):
- self.errorCountVar.set(1 + self.errorCountVar.get())
- self.errorListbox.insert(tk.END, "Error: %s" % test)
- self.errorInfo.append((test,err))
-
- def notifyTestSkipped(self, test, reason):
- super(TkTestRunner, self).notifyTestSkipped(test, reason)
- self.skipCountVar.set(1 + self.skipCountVar.get())
-
- def notifyTestFailedExpectedly(self, test, err):
- super(TkTestRunner, self).notifyTestFailedExpectedly(test, err)
- self.expectFailCountVar.set(1 + self.expectFailCountVar.get())
-
-
- def notifyTestFinished(self, test):
- self.remainingCountVar.set(self.remainingCountVar.get() - 1)
- self.runCountVar.set(1 + self.runCountVar.get())
- fractionDone = float(self.runCountVar.get())/float(self.totalTests)
- fillColor = len(self.errorInfo) and "red" or "green"
- self.progressBar.setProgressFraction(fractionDone, fillColor)
-
- def showSelectedError(self):
- selection = self.errorListbox.curselection()
- if not selection: return
- selected = int(selection[0])
- txt = self.errorListbox.get(selected)
- window = tk.Toplevel(self.root)
- window.title(txt)
- window.protocol('WM_DELETE_WINDOW', window.quit)
- test, error = self.errorInfo[selected]
- tk.Label(window, text=str(test),
- foreground="red", justify=tk.LEFT).pack(anchor=tk.W)
- tracebackLines = traceback.format_exception(*error)
- tracebackText = "".join(tracebackLines)
- tk.Label(window, text=tracebackText, justify=tk.LEFT).pack()
- tk.Button(window, text="Close",
- command=window.quit).pack(side=tk.BOTTOM)
- window.bind('<Key-Return>', lambda e, w=window: w.quit())
- window.mainloop()
- window.destroy()
-
-
-class ProgressBar(tk.Frame):
- """A simple progress bar that shows a percentage progress in
- the given colour."""
-
- def __init__(self, *args, **kwargs):
- tk.Frame.__init__(self, *args, **kwargs)
- self.canvas = tk.Canvas(self, height='20', width='60',
- background='white', borderwidth=3)
- self.canvas.pack(fill=tk.X, expand=1)
- self.rect = self.text = None
- self.canvas.bind('<Configure>', self.paint)
- self.setProgressFraction(0.0)
-
- def setProgressFraction(self, fraction, color='blue'):
- self.fraction = fraction
- self.color = color
- self.paint()
- self.canvas.update_idletasks()
-
- def paint(self, *args):
- totalWidth = self.canvas.winfo_width()
- width = int(self.fraction * float(totalWidth))
- height = self.canvas.winfo_height()
- if self.rect is not None: self.canvas.delete(self.rect)
- if self.text is not None: self.canvas.delete(self.text)
- self.rect = self.canvas.create_rectangle(0, 0, width, height,
- fill=self.color)
- percentString = "%3.0f%%" % (100.0 * self.fraction)
- self.text = self.canvas.create_text(totalWidth/2, height/2,
- anchor=tk.CENTER,
- text=percentString)
-
-def main(initialTestName=""):
- root = tk.Tk()
- root.title("PyUnit")
- runner = TkTestRunner(root, initialTestName)
- root.protocol('WM_DELETE_WINDOW', root.quit)
- root.mainloop()
-
-
-if __name__ == '__main__':
- if len(sys.argv) == 2:
- main(sys.argv[1])
- else:
- main()