Example #1
0
def main():
	TermColor.init()

	# Check whether to load the default testbench
	if not argvContains("--bench=") and os.path.isfile("testbench.py"):
		sys.argv.append("--bench=testbench.py")

	if "-h" in sys.argv or "--help" in sys.argv or "-?" in sys.argv:
		printHelp()
	if "--no-gui" in sys.argv:
		# Capt. Obvious: We're running in console mode
		runner = TestRunner()
		runner.parseArgv()
		suite = runner.loadSuite()
		for testcase in runner.run():
			pass
		if not runner.lengthOnly and not runner.infoOnly and runner.test == -1:
			print "{:2.2f}%".format(suite.getRate())
		sys.exit(suite.lastResult if suite.lastResult not in [TestState.Waiting,TestState.InfoOnly] else 0)
	else:
		from pyTestGui import TestRunnerGui
		if len(sys.argv) > 1 and not sys.argv[1].startswith("-") and os.path.exists(sys.argv[1]):
			sys.argv[1] = '--bench='+sys.argv[1]
		gui = TestRunnerGui()	
		gui.buildWindow()
		gui.show()
Example #2
0
 def __init__(self):
     """Initialise the gui"""
     wx.App.__init__(self, redirect=False, useBestVisual=True)
     logger.logListener = self.addLog
     TermColor.active = False
     self.wHnd = None
     self.testthread = None
     self.editForm = None
     self.log = []
     self.logForm = None
     self.runner = TestRunner()
     self.runner.options['quiet'] = True
     self.runner.parseArgv()
     self.runner.options['mode'] = TestSuiteMode.Continuous
Example #3
0
	def __init__(self):
		"""Initialise the gui"""
		wx.App.__init__(self, redirect = False, useBestVisual=True)
		self.runner = TestRunner()
		self.runner.quiet = True
		self.runner.parseArgv()
		self.runner.mode = TestSuiteMode.Continuous
		self.testthread = None
Example #4
0
 def __init__(self):
     """Initialise the gui"""
     wx.App.__init__(self, redirect=False, useBestVisual=True)
     logger.logListener = self.addLog
     TermColor.active = False
     self.wHnd = None
     self.testthread = None
     self.editForm = None
     self.log = []
     self.logForm = None
     self.runner = TestRunner()
     self.runner.options['quiet'] = True
     self.runner.parseArgv()
     self.runner.options['mode'] = TestSuiteMode.Continuous
Example #5
0
def main():
    # Check whether wxpython is installed or not
    try:
        import wx
        # Has the exe been double clicked? -> Try GUI
        # Allow at max 1 parameter if a testbench has been dropped onto the
        # the exe.
        if sys.argv[0].endswith(".exe") and len(sys.argv) < 2:
            sys.argv.append("--gui")
    except ImportError:
        if "--no-gui" not in sys.argv:
            sys.argv.append("--no-gui")

    if "--no-color" in sys.argv:
        TermColor.active = False
    if "--version" in sys.argv:
        runner = TestRunner(flush=True)
    elif "--gui" in sys.argv:
        # Capt. Obvious: We're running the GUI
        from pyTestGui import TestRunnerGui
        if len(sys.argv) > 1 and not sys.argv[1].startswith(
                "-") and os.path.exists(sys.argv[1]):
            sys.argv[1] = '--bench=' + sys.argv[1]
        gui = TestRunnerGui()
        gui.buildWindow()
        gui.show()
    else:
        # Capt. Obvious: We're running in console mode
        runner = TestRunner()
        runner.parseArgv()
        suite = runner.loadSuite()
        if suite is not None:
            for testcase in runner.run():
                pass
            if not runner.options['info'] and not runner.options[
                    'length'] and not runner.options['quiet']:
                print "{:2.2f}%".format(suite.getRate())
            sys.exit(suite.lastResult if suite.lastResult not in
                     [TestState.Waiting, TestState.InfoOnly] else 0)
        else:
            sys.exit(1)
Example #6
0
def main():
    # Check whether wxpython is installed or not
    try:
        import wx
        # Has the exe been double clicked? -> Try GUI
        # Allow at max 1 parameter if a testbench has been dropped onto the
        # the exe.
        if sys.argv[0].endswith(".exe") and len(sys.argv) < 2:
            sys.argv.append("--gui")
    except ImportError:
        if "--no-gui" not in sys.argv:
            sys.argv.append("--no-gui")

    if "--no-color" in sys.argv:
        TermColor.active = False
    if "--version" in sys.argv:
        runner = TestRunner(flush=True)
    elif "--gui" in sys.argv:
        # Capt. Obvious: We're running the GUI
        from pyTestGui import TestRunnerGui
        if len(sys.argv) > 1 and not sys.argv[1].startswith("-") and os.path.exists(sys.argv[1]):
            sys.argv[1] = '--bench=' + sys.argv[1]
        gui = TestRunnerGui()
        gui.buildWindow()
        gui.show()
    else:
        # Capt. Obvious: We're running in console mode
        runner = TestRunner()
        runner.parseArgv()
        suite = runner.loadSuite()
        if suite is not None:
            for testcase in runner.run():
                pass
            if not runner.options['info'] and not runner.options['length'] and not runner.options['quiet']:
                print "{:2.2f}%".format(suite.getRate())
            sys.exit(suite.lastResult if suite.lastResult not in [TestState.Waiting, TestState.InfoOnly] else 0)
        else:
            sys.exit(1)
Example #7
0
class TestRunnerGui(wx.App):
    """Graphical User Interface"""
    modes = ["Continuous", "Halt on Fail", "Halt on Error"]
    benchtypes = [('nightmare', 'py'), ('All Files', '*')]
    duttypes = [('All Files', '*'), ('Executables', '*.exe')]

    def suiteSave(self, fn):
        self.runner.saveToFile(fn)

    def loadIcon(self, frame):
        try:
            if sys.platform == 'win32':
                exeName = sys.executable
                icon = wx.Icon(exeName + ';0', wx.BITMAP_TYPE_ICO)
                frame.SetIcon(icon)
        except:
            pass

    def saveSuite(self):
        """ Savedialog execution before saving the suite"""
        fname = self.saveFileDialog(fileTypes=TestRunnerGui.benchtypes)
        if fname is not None:
            self.suiteSave(fname)

    def loadSuite(self):
        """ Load a testsuite"""
        fname = self.loadFileDialog(fileTypes=TestRunnerGui.benchtypes)
        if fname is not None:
            self.edtFile.SetValue(os.path.relpath(fname))
            self.runner.options['bench'] = fname
            self.updateFromRunner()

    def updateFromRunner(self):
        self.runner.loadSuite()
        self.lstTests.DeleteAllItems()
        self.applyToList(self.runner.getSuite().getTests(), self.insertTest)
        self.edtDUT.SetValue(str(self.runner.options['dut']))
        self.edtTests.SetValue(str(self.runner.countTests()))
        self.grpMode.SetSelection(self.runner.getSuite().mode)

    def selectDut(self):
        """Show filedialog and set the result as DUT"""
        fname = self.loadFileDialog(fileTypes=TestRunnerGui.duttypes)
        if fname is not None:
            self.runner.setDUT(fname)
            self.edtDUT.SetValue(os.path.relpath(fname))

    def applyToList(self, l, f, gauge=True):
        cnt = self.runner.countTests()
        if gauge:
            self.prgGauge.SetRange(cnt - 1)
            self.prgGauge.SetValue(0)
        lastIdx = 0
        for idx, test in itertools.izip(xrange(cnt), l):
            lastIdx = idx
            if gauge:
                self.prgGauge.SetValue(idx)
                self.prgGauge.Update()
            if idx < self.lstTests.GetItemCount() - 1:
                self.lstTests.SetStringItem(idx + 1, 2, "RUNNING")
                self.lstTests.Update()
            f(idx, test)
            self.lstTests.Update()
        for i in range(lastIdx + 1, len(self.runner.getSuite())):
            self.lstTests.SetStringItem(i, 2, "CANCELED")

    def insertTest(self, idx, test):
        """Insert a new test into the test-list

        @type    idx: int
        @param     idx: The index of the test to add
        @type    test: pyTestCore.test.Test
        @param     test: The test to add
        """
        self.lstTests.InsertStringItem(idx, test.name)
        self.lstTests.CheckItem(idx)
        self.updateTest(idx, test)

    def updateTest(self, idx, test):
        """Update the information on one test in the test-list

        @type    idx: int
        @param     idx: The index of the test to change
        @type    test: pyTestCore.test.Test
        @param     test: The test to change
        """
        if test.state == TestState.Error:
            self.lstTests.CheckItem(idx, False)
            self.lstTests.SetItemBackgroundColour(idx, 'yellow')
        elif test.state == TestState.Success:
            self.lstTests.SetItemBackgroundColour(idx, 'green')
        elif test.state == TestState.Fail:
            self.lstTests.SetItemBackgroundColour(idx, 'red')
        elif test.state == TestState.Timeout:
            self.lstTests.SetItemBackgroundColour(idx, 'purple')
        elif test.state == TestState.Waiting:
            self.lstTests.SetItemBackgroundColour(idx, 'white')
        elif test.state == TestState.Disabled:
            self.lstTests.SetItemBackgroundColour(idx, 'gray')
        TermColor.active = False
        self.lstTests.SetStringItem(idx, 0, test.name)
        self.lstTests.SetStringItem(idx, 1, test.descr)
        self.lstTests.SetStringItem(idx, 2, TestState.toString(test.state))

    def setTestState(self, test, idx, state):
        """Update the state of one test, but only if the test is not enabled"""
        if test.state != TestState.Disabled:
            test.state = state
        self.updateTest(idx, test)

    def onListCheck(self, idx, flag):
        """Eventhandler for changes on the checkboxes in the list"""
        test = self.runner.getSuite()[idx]
        test.state = TestState.Waiting if flag else TestState.Disabled
        self.updateTest(idx, test)

    def __runthread(self, testIdx=None):
        """Run tests"""
        if testIdx is None:
            self.applyToList(
                self.runner.getSuite().getTests(),
                lambda i, t: self.setTestState(t, i, TestState.Waiting),
                gauge=False)
            self.applyToList(self.runner.run(), self.updateTest)
        else:
            test, = self.runner.getSuite().run(tests=[testIdx])
            self.updateTest(testIdx, test)
        self.testthread = None
        """Unset yourself for further processing"""

    def run(self, testIdx=None):
        """start running thread"""
        if self.testthread is None:
            self.testthread = threading.Thread(target=self.__runthread,
                                               args=(testIdx, ))
            self.testthread.start()
        else:
            self.displayError("Test is already running!")

    def addTest(self):
        newIdx = len(self.runner.getSuite())
        newTest = self.runner.addTest()
        self.insertTest(newIdx, newTest)
        self.editTest(newIdx)

    def selectTest(self, event):
        self.editTest(event.GetIndex())

    def editTest(self, testIdx):
        test = self.runner.getSuite()[testIdx]
        if self.editForm is None:
            self.editForm = TestEditForm(self.wHnd, testIdx, test, self.runner,
                                         self)
            self.loadIcon(self.editForm)
        else:
            self.editForm.updateTest(testIdx)
        self.editForm.Show()

    def __init__(self):
        """Initialise the gui"""
        wx.App.__init__(self, redirect=False, useBestVisual=True)
        logger.logListener = self.addLog
        TermColor.active = False
        self.wHnd = None
        self.testthread = None
        self.editForm = None
        self.log = []
        self.logForm = None
        self.runner = TestRunner()
        self.runner.options['quiet'] = True
        self.runner.parseArgv()
        self.runner.options['mode'] = TestSuiteMode.Continuous

    def addLog(self, line):
        self.log.append(line)
        if self.logForm is not None:
            self.logForm.add(line)

    def showLog(self):
        if self.logForm is None:
            self.logForm = LogWindow(self.wHnd, self.log)
        if self.logForm.IsShown():
            self.logForm.Hide()
        else:
            self.logForm.Show()

    def messageDialog(self,
                      message,
                      caption=wx.MessageBoxCaptionStr,
                      style=wx.OK | wx.ICON_INFORMATION):
        dial = wx.MessageDialog(None, message, caption, style)
        return dial.ShowModal()

    def fileDialog(self, mode, message, fileTypes=None, dir=wx.EmptyString):
        if fileTypes is None:
            wc = wx.FileSelectorDefaultWildcardStr
        else:
            wc = " | ".join([
                descr + " (*" + ext + ") | *" + ext for descr, ext in fileTypes
            ])
        diag = wx.FileDialog(self.wHnd,
                             message,
                             defaultDir=dir,
                             wildcard=wc,
                             style=mode)
        diag.ShowModal()
        return os.path.join(
            diag.Directory,
            path=diag.Filename) if diag.Filename != "" else None

    def displayError(self, message, caption='An error occured'):
        return self.messageDialog(message, caption,
                                  wx.OK | wx.ICON_ERROR) == wx.OK

    def displayInformation(self, message, caption='Warning'):
        return self.messageDialog(message, caption,
                                  wx.OK | wx.ICON_INFORMATION) == wx.OK

    def displayQuestion(self, message, caption='Question'):
        return self.messageDialog(message, caption,
                                  wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)

    def loadFileDialog(self,
                       message="Load File",
                       fileTypes=None,
                       dir=wx.EmptyString):
        return self.fileDialog(mode=wx.FD_OPEN,
                               message=message,
                               fileTypes=fileTypes,
                               dir=dir)

    def saveFileDialog(self,
                       message="Save File",
                       fileTypes=None,
                       dir=wx.EmptyString):
        return self.fileDialog(mode=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
                               message=message,
                               fileTypes=fileTypes,
                               dir=dir)

    def buildWindow(self):
        """Creates the window with all its components"""
        self.wHnd = wx.Frame(None,
                             style=wx.DEFAULT_FRAME_STYLE,
                             title="nightmare GUI-Modus",
                             size=(600, 400))
        self.SetTopWindow(self.wHnd)
        self.loadIcon(self.wHnd)

        panel = wx.Panel(self.wHnd)
        sizer = wx.GridBagSizer(3, 3)
        # Create Controls
        self.btnLoad = wx.Button(panel, label="Load", id=wx.ID_OPEN)
        self.btnSave = wx.Button(panel, label="Save", id=wx.ID_SAVE)
        self.lblDUT = wx.StaticText(panel, label="DUT")
        self.lblFile = wx.StaticText(panel, label="File")
        self.lblTests = wx.StaticText(panel, label="Tests")
        self.edtDUT = wx.TextCtrl(panel)
        self.edtFile = wx.TextCtrl(panel)
        self.edtTests = wx.TextCtrl(panel)
        self.btnSelect = wx.Button(panel, label="...")
        self.btnAdd = wx.Button(panel, label=" + ", id=wx.ID_ADD)
        self.grpMode = wx.RadioBox(panel,
                                   choices=TestRunnerGui.modes,
                                   style=wx.RA_VERTICAL)
        self.prgGauge = wx.Gauge(panel)
        self.btnRun = wx.Button(panel, label="Run")
        self.lstTests = CheckListCtrl(panel)
        # Feature for wxPython 2.9 (currently in development)
        if hasattr(self.btnSave, 'SetBitmap'):
            self.btnSave.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE))
            self.btnLoad.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN))
        # Disable TextCtrl
        self.edtFile.Disable()
        self.edtTests.Disable()
        # Insert Columns into list and hook up the checkboxes
        self.lstTests.InsertColumn(0, 'Test', width=140)
        self.lstTests.InsertColumn(1, 'Description', width=220)
        self.lstTests.InsertColumn(2, 'State', width=100)
        self.lstTests.OnCheckItem = self.onListCheck
        # Create Layout
        sizer.Add(self.btnLoad,
                  pos=(0, 0),
                  span=(3, 1),
                  border=5,
                  flag=wx.LEFT | wx.TOP | wx.EXPAND)
        sizer.Add(self.btnSave,
                  pos=(0, 1),
                  span=(3, 1),
                  border=5,
                  flag=wx.TOP | wx.EXPAND)
        sizer.Add(self.lblDUT,
                  pos=(0, 2),
                  span=(1, 1),
                  border=5,
                  flag=wx.RIGHT | wx.LEFT | wx.TOP | wx.EXPAND)
        sizer.Add(self.lblFile,
                  pos=(1, 2),
                  span=(1, 1),
                  border=5,
                  flag=wx.RIGHT | wx.LEFT | wx.EXPAND)
        sizer.Add(self.lblTests,
                  pos=(2, 2),
                  span=(1, 1),
                  border=5,
                  flag=wx.RIGHT | wx.LEFT | wx.EXPAND)
        sizer.Add(self.edtDUT,
                  pos=(0, 3),
                  span=(1, 1),
                  border=5,
                  flag=wx.TOP | wx.EXPAND)
        sizer.Add(self.edtFile,
                  pos=(1, 3),
                  span=(1, 1),
                  border=5,
                  flag=wx.EXPAND)
        sizer.Add(self.edtTests,
                  pos=(2, 3),
                  span=(1, 1),
                  border=5,
                  flag=wx.EXPAND)
        sizer.Add(self.btnSelect,
                  pos=(0, 4),
                  span=(1, 1),
                  border=5,
                  flag=wx.TOP)
        sizer.Add(
            self.btnAdd,
            pos=(2, 4),
            span=(1, 1),
            border=5,
        )
        sizer.Add(self.grpMode,
                  pos=(0, 5),
                  span=(3, 1),
                  border=5,
                  flag=wx.TOP | wx.RIGHT | wx.EXPAND)
        sizer.Add(self.prgGauge,
                  pos=(3, 0),
                  span=(1, 5),
                  border=5,
                  flag=wx.LEFT | wx.EXPAND)
        sizer.Add(self.btnRun,
                  pos=(3, 5),
                  span=(1, 1),
                  border=5,
                  flag=wx.RIGHT | wx.EXPAND)
        sizer.Add(self.lstTests,
                  pos=(4, 0),
                  span=(1, 6),
                  border=5,
                  flag=wx.ALL | wx.EXPAND)
        sizer.AddGrowableCol(3)
        sizer.AddGrowableRow(4)
        panel.SetSizerAndFit(sizer)
        # Hook up window events
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.loadSuite(),
                       id=self.btnLoad.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.saveSuite(),
                       id=self.btnSave.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.run(),
                       id=self.btnRun.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.selectDut(),
                       id=self.btnSelect.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.addTest(),
                       id=self.btnAdd.GetId())
        self.wHnd.Bind(wx.EVT_RADIOBOX,
                       lambda e: self.runner.options.__setitem__(
                           "mode", self.grpMode.GetSelection()),
                       id=self.grpMode.GetId())
        self.wHnd.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        self.lstTests.Bind(wx.EVT_LIST_ITEM_ACTIVATED,
                           self.selectTest,
                           id=self.lstTests.GetId())
        # Some Shortcuts
        shortcuts = [
            (wx.ACCEL_CTRL, ord('q'), self.OnCloseWindow),
            (wx.ACCEL_CTRL, ord('r'), lambda e: self.run()),
            (wx.ACCEL_CTRL, ord('o'), lambda e: self.loadSuite()),
            (wx.ACCEL_CTRL, ord('d'), lambda e: self.selectDut()),
            (wx.ACCEL_CTRL, ord('n'), lambda e: self.addTest()),
            (wx.ACCEL_CTRL, ord('e'), lambda e: self.addTest()),
            (wx.ACCEL_CTRL, ord('l'), lambda e: self.showLog()),
        ]
        entries = []
        for special, key, func in shortcuts:
            id = wx.NewId()
            entries.append((special, key, id))
            self.wHnd.Bind(wx.EVT_MENU, func, id=id)
        self.acceleratorTable = wx.AcceleratorTable(entries)
        self.wHnd.SetAcceleratorTable(self.acceleratorTable)
        # Change the DUT after the user left the associated TextCtrl
        self.edtDUT.Bind(wx.EVT_KILL_FOCUS,
                         lambda e: self.runner.setDUT(self.edtDUT.GetValue()),
                         id=self.edtDUT.GetId())
        #
        self.updateFromRunner()

    def OnCloseWindow(self, e):
        """Handler to make sure the window doesn't gets close by accident"""
        ret = self.displayQuestion('Are you sure, you want to quit?')
        if ret == wx.ID_YES:
            self.wHnd.Destroy()
        else:
            e.Veto()

    def show(self):
        self.wHnd.Centre()
        self.wHnd.Show()
        self.MainLoop()
Example #8
0
class TestRunnerGui(wx.App):
	"""Graphical User Interface"""
	modes = ["Continuous", "Halt on Fail", "Halt on Error"]
	filetypes = [('PyTest', 'py'), ('All Files', '*')]
	
	def suiteSave(self, fn):
		"""
		Save the testsuite into a file
		
		@type	fn: String
		@param 	fn: The filename
		"""
		fHnd = open(fn,"w")
		fHnd.write("#!/usr/bin/env python\n\n")
		fHnd.write("# pyTest - Testbench\n")
		fHnd.write("# Saved at {}\n".format(time.strftime("%H:%M:%S")))
		fHnd.write("# \n\n")
		#fHnd.write("# Author: {}\n".format())
		if self.runner.DUT is not None:
			fHnd.write("# Device Under Test\n")
			fHnd.write("DUT = \"{}\"\n\n".format(os.path.relpath(self.runner.DUT)))
		fHnd.write("# Test definitions\n")
		fHnd.write("suite = [\n")
		tests = []
		for test in self.runner.getSuite().getTests():
			tests.append("\t{}".format(test.toString()))
		fHnd.write(",\n".join(tests))
		fHnd.write("\n]\n")
		fHnd.close()
		
	def saveSuite(self):
		""" Savedialog execution before saving the suite"""
		fname = self.saveFileDialog(fileTypes = TestRunnerGui.filetypes)
		if fname is not None:
			self.suiteSave(fname)
	
	def loadSuite(self):
		""" Load a testsuite"""
		fname = self.loadFileDialog(fileTypes = TestRunnerGui.filetypes)
		if fname is not None:
			self.edtFile.SetValue(os.path.relpath(fname))
			self.runner.file = fname
			self.updateFromRunner()
			
	def updateFromRunner(self):
		self.runner.loadSuite()
		self.lstTests.DeleteAllItems()
		self.applyToList(self.runner.getSuite().getTests(), self.insertTest )
		self.edtDUT.SetValue(str(self.runner.DUT))
		self.edtTests.SetValue(str(self.runner.countTests()))
		self.grpMode.SetSelection(self.runner.getSuite().mode)
			
	def selectDut(self):
		"""Show filedialog and set the result as DUT"""
		fname = self.loadFileDialog(fileTypes = TestRunnerGui.filetypes)
		if fname is not None:
			self.runner.setDUT(fname)
			self.edtDUT.SetValue(os.path.relpath(fname))
		
			
	def applyToList(self, l, f, gauge = True):
		cnt = self.runner.countTests()
		if gauge:
			self.prgGauge.SetRange(cnt-1)
			self.prgGauge.SetValue(0)
		lastIdx = 0
		for idx, test in itertools.izip(xrange(cnt), l):
			lastIdx = idx
			if gauge:
				self.prgGauge.SetValue(idx)
				self.prgGauge.Update()
			if idx < self.lstTests.GetItemCount()-1:
				self.lstTests.SetStringItem(idx+1, 2, "RUNNING")
				self.lstTests.Update()
			f(idx, test)
			self.lstTests.Update()
		for i in  range(lastIdx+1,len(self.runner.getSuite())):
			self.lstTests.SetStringItem(i, 2, "CANCELED")
				
	def insertTest(self, idx, test):
		"""Insert a new test into the test-list
		
		@type	idx: int
		@param 	idx: The index of the test to add
		@type	test: pyTestCore.test.Test
		@param 	test: The test to add
		"""
		self.lstTests.InsertStringItem(idx, test.name)
		self.lstTests.CheckItem(idx)
		self.updateTest(idx, test)
		
	def updateTest(self, idx, test):
		"""Update the information on one test in the test-list
		
		@type	idx: int
		@param 	idx: The index of the test to change
		@type	test: pyTestCore.test.Test
		@param 	test: The test to change
		"""
		if test.state == TestState.Error:
			self.lstTests.CheckItem(idx, False)
			self.lstTests.SetItemBackgroundColour(idx, 'yellow')
		elif test.state == TestState.Success:
			self.lstTests.SetItemBackgroundColour(idx, 'green')
		elif test.state == TestState.Fail:
			self.lstTests.SetItemBackgroundColour(idx, 'red')
		elif test.state == TestState.Timeout:
			self.lstTests.SetItemBackgroundColour(idx, 'purple')
		elif test.state == TestState.Waiting:
			self.lstTests.SetItemBackgroundColour(idx, 'white')
		elif test.state == TestState.Disabled:
			self.lstTests.SetItemBackgroundColour(idx, 'gray')
		TermColor.active = False
		self.lstTests.SetStringItem(idx, 0, test.name)
		self.lstTests.SetStringItem(idx, 1, test.descr)
		self.lstTests.SetStringItem(idx, 2, TestState.toString(test.state))
		TermColor.active = True
		
	def setTestState(self, test, idx, state):
		"""Update the state of one test, but only if the test is not enabled"""
		if test.state != TestState.Disabled:
			test.state = state
		self.updateTest(idx, test)
	
	def onListCheck(self, idx, flag):
		"""Eventhandler for changes on the checkboxes in the list"""
		test = self.runner.getSuite()[idx]
		test.state = TestState.Waiting if flag else TestState.Disabled
		self.updateTest(idx, test)
		
	def __runthread(self, testIdx = None):
		"""Run tests"""
		if testIdx is None:
			self.applyToList(self.runner.getSuite().getTests(), lambda i,t: self.setTestState(t, i, TestState.Waiting), gauge = False)
			self.applyToList(self.runner.run() , self.updateTest)
		else:
			test = self.runner.getSuite().runOne(testIdx)
			self.updateTest(testIdx, test)
		self.testthread = None
		"""Unset yourself for further processing"""
		
		
	def run(self, testIdx = None):
		"""start running thread"""
		if self.testthread is None:
			self.testthread = threading.Thread(target=self.__runthread,args=(testIdx,))
			self.testthread.start()
		else:
			self.displayError("Test is already running!")
			
			
	def addTest(self):
		newIdx = len(self.runner.getSuite())
		newTest = self.runner.addTest()
		self.insertTest(newIdx, newTest)
		TestEditForm(self.wHnd, newIdx, newTest, self.runner, self).show()
	
	def selectTest(self, event):
		idx = event.GetIndex()
		test = self.runner.getSuite()[idx]
		TestEditForm(self.wHnd, idx, test, self.runner, self).show()
	
	def __init__(self):
		"""Initialise the gui"""
		wx.App.__init__(self, redirect = False, useBestVisual=True)
		self.runner = TestRunner()
		self.runner.quiet = True
		self.runner.parseArgv()
		self.runner.mode = TestSuiteMode.Continuous
		self.testthread = None
		
	def messageDialog(self, message, caption=wx.MessageBoxCaptionStr, style=wx.OK | wx.ICON_INFORMATION):
		dial = wx.MessageDialog(None, message, caption, style)
		return dial.ShowModal()
		
	def fileDialog(self, mode, message, fileTypes = None, dir = wx.EmptyString):
		if fileTypes is None:
			wc = wx.FileSelectorDefaultWildcardStr
		else:
			wc = "|".join([ descr + " (*" +  ext + ")|*" + ext for descr, ext in fileTypes])
		diag = wx.FileDialog(self.wHnd, message, defaultDir=dir, wildcard = wc, style=mode)
		diag.ShowModal()
		return os.path.join(diag.Directory,diag.Filename) if diag.Filename != "" else None
		
	def displayError(self, message, caption = 'An error occured'):
		return self.messageDialog(message, caption, wx.OK | wx.ICON_ERROR) == wx.OK
		
	def displayInformation(self, message, caption='Warning'):
		return self.messageDialog(message, caption, wx.OK | wx.ICON_INFORMATION) == wx.OK
		
	def displayQuestion(self, message, caption='Question'):
		return self.messageDialog(message, caption, wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
		
	def loadFileDialog(self, message = "Load File", fileTypes = None, dir = wx.EmptyString):
		return self.fileDialog(mode = wx.FD_OPEN, message= message, fileTypes = fileTypes, dir = dir)
		
	def saveFileDialog(self, message = "Save File", fileTypes = None, dir = wx.EmptyString):
		return self.fileDialog(mode = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, message= message, fileTypes = fileTypes, dir = dir)
		
	def buildWindow(self):
		"""Creates the window with all its components"""
		self.wHnd = wx.Frame(None, wx.DEFAULT_FRAME_STYLE, title = "pyTest GUI", size=(500,400))
		self.SetTopWindow(self.wHnd)
		
		panel = wx.Panel(self.wHnd)
		sizer = wx.GridBagSizer(3, 3)
		# Create Controls
		self.btnLoad   = wx.Button(panel, label="Load", id = wx.ID_OPEN)
		self.btnSave   = wx.Button(panel, label="Save", id = wx.ID_SAVE)
		self.lblDUT    = wx.StaticText(panel, label="DUT")
		self.lblFile   = wx.StaticText(panel, label="File")
		self.lblTests  = wx.StaticText(panel, label="Tests")
		self.edtDUT    = wx.TextCtrl(panel)
		self.edtFile   = wx.TextCtrl(panel)
		self.edtTests  = wx.TextCtrl(panel)
		self.btnSelect = wx.Button(panel, label="...")
		self.btnAdd    = wx.Button(panel, label="+", id = wx.ID_ADD)
		self.grpMode   = wx.RadioBox(panel, choices=TestRunnerGui.modes, style=wx.RA_VERTICAL)
		self.prgGauge  = wx.Gauge(panel)
		self.btnRun    = wx.Button(panel, label="Run")
		self.lstTests  = CheckListCtrl(panel)
		# Feature for wxPython 2.9 (currently in development)
		if hasattr(self.btnSave, 'SetBitmap'):
			self.btnSave.SetBitmap(wx.ART_FILE_OPEN)
			self.btnLoad.SetBitmap(wx.ART_FILE_LOAD)
		# Disable TextCtrl
		self.edtFile.Disable()
		self.edtTests.Disable()
		# Insert Columns into list and hook up the checkboxes
		self.lstTests.InsertColumn(0, 'Test', width=140)
		self.lstTests.InsertColumn(1, 'Description', width=220)
		self.lstTests.InsertColumn(2, 'State', width=100)
		self.lstTests.OnCheckItem = self.onListCheck 
		# Create Layout
		sizer.Add(self.btnLoad,   pos=(0,0), span=(3,1), border=5, flag=wx.LEFT|wx.TOP|wx.EXPAND)
		sizer.Add(self.btnSave,   pos=(0,1), span=(3,1), border=5, flag=wx.TOP|wx.EXPAND)
		sizer.Add(self.lblDUT,    pos=(0,2), span=(1,1), border=5, flag=wx.RIGHT|wx.LEFT|wx.TOP|wx.EXPAND)
		sizer.Add(self.lblFile,   pos=(1,2), span=(1,1), border=5, flag=wx.RIGHT|wx.LEFT|wx.EXPAND)
		sizer.Add(self.lblTests,  pos=(2,2), span=(1,1), border=5, flag=wx.RIGHT|wx.LEFT|wx.EXPAND)
		sizer.Add(self.edtDUT,    pos=(0,3), span=(1,1), border=5, flag=wx.TOP|wx.EXPAND)
		sizer.Add(self.edtFile,   pos=(1,3), span=(1,1), border=5, flag=wx.EXPAND)
		sizer.Add(self.edtTests,  pos=(2,3), span=(1,1), border=5, flag=wx.EXPAND)
		sizer.Add(self.btnSelect, pos=(0,4), span=(1,1), border=5, flag=wx.TOP)
		sizer.Add(self.btnAdd,    pos=(2,4), span=(1,1), border=5, )
		sizer.Add(self.grpMode,   pos=(0,5), span=(3,1), border=5, flag=wx.TOP|wx.RIGHT|wx.EXPAND)
		sizer.Add(self.prgGauge,  pos=(3,0), span=(1,5), border=5, flag=wx.LEFT|wx.EXPAND)
		sizer.Add(self.btnRun,    pos=(3,5), span=(1,1), border=5, flag=wx.RIGHT|wx.EXPAND)
		sizer.Add(self.lstTests,  pos=(4,0), span=(1,6), border=5, flag=wx.ALL|wx.EXPAND)
		sizer.AddGrowableCol(3)
		sizer.AddGrowableRow(4)
		panel.SetSizerAndFit(sizer)
		# Hook up window events
		self.wHnd.Bind(wx.EVT_BUTTON, lambda e:self.loadSuite(), id = self.btnLoad.GetId())
		self.wHnd.Bind(wx.EVT_BUTTON, lambda e:self.saveSuite(), id = self.btnSave.GetId())
		self.wHnd.Bind(wx.EVT_BUTTON, lambda e:self.run(), id = self.btnRun.GetId())
		self.wHnd.Bind(wx.EVT_BUTTON, lambda e:self.selectDut(), id = self.btnSelect.GetId())
		self.wHnd.Bind(wx.EVT_BUTTON, lambda e:self.addTest(), id = self.btnAdd.GetId())
		self.wHnd.Bind(wx.EVT_RADIOBOX, lambda e:self.runner.__setattr__("mode",self.grpMode.GetSelection()), id = self.grpMode.GetId())
		self.wHnd.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
		self.wHnd.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
		self.lstTests.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.selectTest, id = self.lstTests.GetId())
		# Change the DUT after the user left the associated TextCtrl
		self.edtDUT.Bind(wx.EVT_KILL_FOCUS, lambda e: self.runner.setDUT(self.edtDUT.GetValue()), id = self.edtDUT.GetId())
		#
		self.updateFromRunner()

	def OnKeyDown(self, e):
		"""Handler for key events"""
		if e.CmdDown and e.GetUniChar() == 'q':
			self.Destroy()
		e.Skip()
		
	def OnCloseWindow(self, e):
		"""Handler to make sure the window doesn't gets close by accident"""
		ret = self.displayQuestion('Are you sure, you want to quit?')
		if ret == wx.ID_YES:
			self.wHnd.Destroy()
		else:
			e.Veto()
	
	def show(self):
		self.wHnd.Centre()
		self.wHnd.Show()
		self.MainLoop()
Example #9
0
class TestRunnerGui(wx.App):
    """Graphical User Interface"""
    modes = ["Continuous", "Halt on Fail", "Halt on Error"]
    benchtypes = [('nightmare', 'py'), ('All Files', '*')]
    duttypes = [('All Files', '*'), ('Executables', '*.exe')]

    def suiteSave(self, fn):
        self.runner.saveToFile(fn)

    def loadIcon(self, frame):
        try:
            if sys.platform == 'win32':
                exeName = sys.executable
                icon = wx.Icon(exeName + ';0', wx.BITMAP_TYPE_ICO)
                frame.SetIcon(icon)
        except:
            pass

    def saveSuite(self):
        """ Savedialog execution before saving the suite"""
        fname = self.saveFileDialog(fileTypes=TestRunnerGui.benchtypes)
        if fname is not None:
            self.suiteSave(fname)

    def loadSuite(self):
        """ Load a testsuite"""
        fname = self.loadFileDialog(fileTypes=TestRunnerGui.benchtypes)
        if fname is not None:
            self.edtFile.SetValue(os.path.relpath(fname))
            self.runner.options['bench'] = fname
            self.updateFromRunner()

    def updateFromRunner(self):
        self.runner.loadSuite()
        self.lstTests.DeleteAllItems()
        self.applyToList(self.runner.getSuite().getTests(), self.insertTest)
        self.edtDUT.SetValue(str(self.runner.options['dut']))
        self.edtTests.SetValue(str(self.runner.countTests()))
        self.grpMode.SetSelection(self.runner.getSuite().mode)

    def selectDut(self):
        """Show filedialog and set the result as DUT"""
        fname = self.loadFileDialog(fileTypes=TestRunnerGui.duttypes)
        if fname is not None:
            self.runner.setDUT(fname)
            self.edtDUT.SetValue(os.path.relpath(fname))

    def applyToList(self, l, f, gauge=True):
        cnt = self.runner.countTests()
        if gauge:
            self.prgGauge.SetRange(cnt - 1)
            self.prgGauge.SetValue(0)
        lastIdx = 0
        for idx, test in itertools.izip(xrange(cnt), l):
            lastIdx = idx
            if gauge:
                self.prgGauge.SetValue(idx)
                self.prgGauge.Update()
            if idx < self.lstTests.GetItemCount() - 1:
                self.lstTests.SetStringItem(idx + 1, 2, "RUNNING")
                self.lstTests.Update()
            f(idx, test)
            self.lstTests.Update()
        for i in range(lastIdx + 1, len(self.runner.getSuite())):
            self.lstTests.SetStringItem(i, 2, "CANCELED")

    def insertTest(self, idx, test):
        """Insert a new test into the test-list

        @type    idx: int
        @param     idx: The index of the test to add
        @type    test: pyTestCore.test.Test
        @param     test: The test to add
        """
        self.lstTests.InsertStringItem(idx, test.name)
        self.lstTests.CheckItem(idx)
        self.updateTest(idx, test)

    def updateTest(self, idx, test):
        """Update the information on one test in the test-list

        @type    idx: int
        @param     idx: The index of the test to change
        @type    test: pyTestCore.test.Test
        @param     test: The test to change
        """
        if test.state == TestState.Error:
            self.lstTests.CheckItem(idx, False)
            self.lstTests.SetItemBackgroundColour(idx, 'yellow')
        elif test.state == TestState.Success:
            self.lstTests.SetItemBackgroundColour(idx, 'green')
        elif test.state == TestState.Fail:
            self.lstTests.SetItemBackgroundColour(idx, 'red')
        elif test.state == TestState.Timeout:
            self.lstTests.SetItemBackgroundColour(idx, 'purple')
        elif test.state == TestState.Waiting:
            self.lstTests.SetItemBackgroundColour(idx, 'white')
        elif test.state == TestState.Disabled:
            self.lstTests.SetItemBackgroundColour(idx, 'gray')
        TermColor.active = False
        self.lstTests.SetStringItem(idx, 0, test.name)
        self.lstTests.SetStringItem(idx, 1, test.descr)
        self.lstTests.SetStringItem(idx, 2, TestState.toString(test.state))

    def setTestState(self, test, idx, state):
        """Update the state of one test, but only if the test is not enabled"""
        if test.state != TestState.Disabled:
            test.state = state
        self.updateTest(idx, test)

    def onListCheck(self, idx, flag):
        """Eventhandler for changes on the checkboxes in the list"""
        test = self.runner.getSuite()[idx]
        test.state = TestState.Waiting if flag else TestState.Disabled
        self.updateTest(idx, test)

    def __runthread(self, testIdx=None):
        """Run tests"""
        if testIdx is None:
            self.applyToList(self.runner.getSuite().getTests(), lambda i, t: self.setTestState(t, i, TestState.Waiting), gauge=False)
            self.applyToList(self.runner.run(), self.updateTest)
        else:
            test, = self.runner.getSuite().run(tests=[testIdx])
            self.updateTest(testIdx, test)
        self.testthread = None
        """Unset yourself for further processing"""

    def run(self, testIdx=None):
        """start running thread"""
        if self.testthread is None:
            self.testthread = threading.Thread(target=self.__runthread, args=(testIdx,))
            self.testthread.start()
        else:
            self.displayError("Test is already running!")

    def addTest(self):
        newIdx = len(self.runner.getSuite())
        newTest = self.runner.addTest()
        self.insertTest(newIdx, newTest)
        self.editTest(newIdx)

    def selectTest(self, event):
        self.editTest(event.GetIndex())

    def editTest(self, testIdx):
        test = self.runner.getSuite()[testIdx]
        if self.editForm is None:
            self.editForm = TestEditForm(self.wHnd, testIdx, test, self.runner, self)
            self.loadIcon(self.editForm)
        else:
            self.editForm.updateTest(testIdx)
        self.editForm.Show()

    def __init__(self):
        """Initialise the gui"""
        wx.App.__init__(self, redirect=False, useBestVisual=True)
        logger.logListener = self.addLog
        TermColor.active = False
        self.wHnd = None
        self.testthread = None
        self.editForm = None
        self.log = []
        self.logForm = None
        self.runner = TestRunner()
        self.runner.options['quiet'] = True
        self.runner.parseArgv()
        self.runner.options['mode'] = TestSuiteMode.Continuous

    def addLog(self, line):
        self.log.append(line)
        if self.logForm is not None:
            self.logForm.add(line)

    def showLog(self):
        if self.logForm is None:
            self.logForm = LogWindow(self.wHnd, self.log)
        if self.logForm.IsShown():
            self.logForm.Hide()
        else:
            self.logForm.Show()

    def messageDialog(self, message, caption=wx.MessageBoxCaptionStr, style=wx.OK | wx.ICON_INFORMATION):
        dial = wx.MessageDialog(None, message, caption, style)
        return dial.ShowModal()

    def fileDialog(self, mode, message, fileTypes=None, dir=wx.EmptyString):
        if fileTypes is None:
            wc = wx.FileSelectorDefaultWildcardStr
        else:
            wc = " | ".join([descr + " (*" + ext + ") | *" + ext for descr, ext in fileTypes])
        diag = wx.FileDialog(self.wHnd, message, defaultDir=dir, wildcard=wc, style=mode)
        diag.ShowModal()
        return os.path.join(diag.Directory, path=diag.Filename) if diag.Filename != "" else None

    def displayError(self, message, caption='An error occured'):
        return self.messageDialog(message, caption, wx.OK | wx.ICON_ERROR) == wx.OK

    def displayInformation(self, message, caption='Warning'):
        return self.messageDialog(message, caption, wx.OK | wx.ICON_INFORMATION) == wx.OK

    def displayQuestion(self, message, caption='Question'):
        return self.messageDialog(message, caption, wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)

    def loadFileDialog(self, message="Load File", fileTypes=None, dir=wx.EmptyString):
        return self.fileDialog(mode=wx.FD_OPEN, message=message, fileTypes=fileTypes, dir=dir)

    def saveFileDialog(self, message="Save File", fileTypes=None, dir=wx.EmptyString):
        return self.fileDialog(mode=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, message=message, fileTypes=fileTypes, dir=dir)

    def buildWindow(self):
        """Creates the window with all its components"""
        self.wHnd = wx.Frame(None, style=wx.DEFAULT_FRAME_STYLE, title="nightmare GUI-Modus", size=(600, 400))
        self.SetTopWindow(self.wHnd)
        self.loadIcon(self.wHnd)

        panel = wx.Panel(self.wHnd)
        sizer = wx.GridBagSizer(3, 3)
        # Create Controls
        self.btnLoad   = wx.Button(panel, label="Load", id=wx.ID_OPEN)
        self.btnSave   = wx.Button(panel, label="Save", id=wx.ID_SAVE)
        self.lblDUT    = wx.StaticText(panel, label="DUT")
        self.lblFile   = wx.StaticText(panel, label="File")
        self.lblTests  = wx.StaticText(panel, label="Tests")
        self.edtDUT    = wx.TextCtrl(panel)
        self.edtFile   = wx.TextCtrl(panel)
        self.edtTests  = wx.TextCtrl(panel)
        self.btnSelect = wx.Button(panel, label="...")
        self.btnAdd    = wx.Button(panel, label=" + ", id=wx.ID_ADD)
        self.grpMode   = wx.RadioBox(panel, choices=TestRunnerGui.modes, style=wx.RA_VERTICAL)
        self.prgGauge  = wx.Gauge(panel)
        self.btnRun    = wx.Button(panel, label="Run")
        self.lstTests  = CheckListCtrl(panel)
        # Feature for wxPython 2.9 (currently in development)
        if hasattr(self.btnSave, 'SetBitmap'):
            self.btnSave.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE))
            self.btnLoad.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN))
        # Disable TextCtrl
        self.edtFile.Disable()
        self.edtTests.Disable()
        # Insert Columns into list and hook up the checkboxes
        self.lstTests.InsertColumn(0, 'Test', width=140)
        self.lstTests.InsertColumn(1, 'Description', width=220)
        self.lstTests.InsertColumn(2, 'State', width=100)
        self.lstTests.OnCheckItem = self.onListCheck
        # Create Layout
        sizer.Add(self.btnLoad,   pos=(0, 0), span=(3, 1), border=5, flag=wx.LEFT | wx.TOP | wx.EXPAND)
        sizer.Add(self.btnSave,   pos=(0, 1), span=(3, 1), border=5, flag=wx.TOP | wx.EXPAND)
        sizer.Add(self.lblDUT,    pos=(0, 2), span=(1, 1), border=5, flag=wx.RIGHT | wx.LEFT | wx.TOP | wx.EXPAND)
        sizer.Add(self.lblFile,   pos=(1, 2), span=(1, 1), border=5, flag=wx.RIGHT | wx.LEFT | wx.EXPAND)
        sizer.Add(self.lblTests,  pos=(2, 2), span=(1, 1), border=5, flag=wx.RIGHT | wx.LEFT | wx.EXPAND)
        sizer.Add(self.edtDUT,    pos=(0, 3), span=(1, 1), border=5, flag=wx.TOP | wx.EXPAND)
        sizer.Add(self.edtFile,   pos=(1, 3), span=(1, 1), border=5, flag=wx.EXPAND)
        sizer.Add(self.edtTests,  pos=(2, 3), span=(1, 1), border=5, flag=wx.EXPAND)
        sizer.Add(self.btnSelect, pos=(0, 4), span=(1, 1), border=5, flag=wx.TOP)
        sizer.Add(self.btnAdd,    pos=(2, 4), span=(1, 1), border=5, )
        sizer.Add(self.grpMode,   pos=(0, 5), span=(3, 1), border=5, flag=wx.TOP | wx.RIGHT | wx.EXPAND)
        sizer.Add(self.prgGauge,  pos=(3, 0), span=(1, 5), border=5, flag=wx.LEFT | wx.EXPAND)
        sizer.Add(self.btnRun,    pos=(3, 5), span=(1, 1), border=5, flag=wx.RIGHT | wx.EXPAND)
        sizer.Add(self.lstTests,  pos=(4, 0), span=(1, 6), border=5, flag=wx.ALL | wx.EXPAND)
        sizer.AddGrowableCol(3)
        sizer.AddGrowableRow(4)
        panel.SetSizerAndFit(sizer)
        # Hook up window events
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.loadSuite(), id=self.btnLoad.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.saveSuite(), id=self.btnSave.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.run(), id=self.btnRun.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.selectDut(), id=self.btnSelect.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.addTest(), id=self.btnAdd.GetId())
        self.wHnd.Bind(wx.EVT_RADIOBOX, lambda e: self.runner.options.__setitem__("mode", self.grpMode.GetSelection()), id=self.grpMode.GetId())
        self.wHnd.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        self.lstTests.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.selectTest, id=self.lstTests.GetId())
        # Some Shortcuts
        shortcuts = [
            (wx.ACCEL_CTRL,  ord('q'), self.OnCloseWindow),
            (wx.ACCEL_CTRL,  ord('r'), lambda e:self.run()),
            (wx.ACCEL_CTRL,  ord('o'), lambda e:self.loadSuite()),
            (wx.ACCEL_CTRL,  ord('d'), lambda e:self.selectDut()),
            (wx.ACCEL_CTRL,  ord('n'), lambda e:self.addTest()),
            (wx.ACCEL_CTRL,  ord('e'), lambda e:self.addTest()),
            (wx.ACCEL_CTRL,  ord('l'), lambda e:self.showLog()),
        ]
        entries = []
        for special, key, func in shortcuts:
            id = wx.NewId()
            entries.append((special, key, id))
            self.wHnd.Bind(wx.EVT_MENU, func, id=id)
        self.acceleratorTable = wx.AcceleratorTable(entries)
        self.wHnd.SetAcceleratorTable(self.acceleratorTable)
        # Change the DUT after the user left the associated TextCtrl
        self.edtDUT.Bind(wx.EVT_KILL_FOCUS, lambda e: self.runner.setDUT(self.edtDUT.GetValue()), id=self.edtDUT.GetId())
        #
        self.updateFromRunner()

    def OnCloseWindow(self, e):
        """Handler to make sure the window doesn't gets close by accident"""
        ret = self.displayQuestion('Are you sure, you want to quit?')
        if ret == wx.ID_YES:
            self.wHnd.Destroy()
        else:
            e.Veto()

    def show(self):
        self.wHnd.Centre()
        self.wHnd.Show()
        self.MainLoop()