Skip to content
Snippets Groups Projects
main_window.py 28.6 KiB
Newer Older
Janik Zikovsky's avatar
Janik Zikovsky committed
#!/usr/bin/env python
# -*- coding: utf-8 -*-
Janik Zikovsky's avatar
Janik Zikovsky committed

import sys
import time
from PyQt4 import QtGui, QtCore
import PyQt4.QtCore
from PyQt4.QtCore import *
import PyQt4.QtGui
from PyQt4.QtGui import *
Janik Zikovsky's avatar
Janik Zikovsky committed
import ui_main_window
import test_info
from test_info import TestSuite, TestSingle, TestProject
from test_tree import TestTreeModel, TreeFilterProxyModel
#==================================================================================================
#==================================================================================================
Janik Zikovsky's avatar
Janik Zikovsky committed
class TestWorker(QtCore.QThread):
    """ Class to run the tests in the background of the GUI"""

    def __init__(self, parent = None):
        QtCore.QThread.__init__(self, parent)
        self.exiting = False
    #-----------------------------------------------------------------------------
    def set_parameters(self, mainWindow, selected_only=False, make_tests=True, parallel=False):
Janik Zikovsky's avatar
Janik Zikovsky committed
        """Set the parameters of how the tests will be built and run;
        Returns: the number of steps in the runner.
        """
Janik Zikovsky's avatar
Janik Zikovsky committed
        self.selected_only = selected_only
        self.make_tests = make_tests
        self.parallel = parallel
        return test_info.all_tests.run_tests_computation_steps(selected_only, make_tests)
    #-----------------------------------------------------------------------------
    def test_run_callback(self, obj):
        """ Simple callback for running tests. This is called into the MainProcess.
        Parameters:
            obj :: the object, either a TestSuite or TestProject that was just calculated
        """
        if isinstance(obj, TestSuite):
            suite = obj
            text = "%s done." % suite.classname
            test_info.all_tests.replace_suite(suite)
        elif isinstance(obj, TestProject):
            pj = obj
            # Replace the project in THIS thread!
            test_info.all_tests.replace_project( pj )
            text = "Made project %s" % pj.name
Janik Zikovsky's avatar
Janik Zikovsky committed
        else:
        self.mainWindow.emit( QtCore.SIGNAL("testRun"), text, obj)
Janik Zikovsky's avatar
Janik Zikovsky committed
    #-----------------------------------------------------------------------------
    def run(self):
        test_info.all_tests.run_tests_in_parallel(self.selected_only, self.make_tests,
                          self.parallel, callback_func=self.test_run_callback)



#==================================================================================================
#==================================================================================================
class TestMonitorFilesWorker(QtCore.QThread):
    """ Worker that runs in the background
    and looks for modified files. """

    def __init__(self, mainWindow, parent = None):
        QtCore.QThread.__init__(self, parent)
        self.mainWindow = mainWindow
        self.exiting = False
        self.monitor_tests = False
        # Hoy many seconds to wait before checking again
        self.delay = 1.0

    def set_monitor_tests(self, state):
        self.monitor_tests = state

    #-----------------------------------------------------------------------------
    def run(self):
        print "Beginning to monitor files and/or libraries..."
            if not self.mainWindow.running:
                # Don't monitor files while a test suite is building or running
                if self.monitor_tests:
                    if test_info.all_tests.is_source_modified(selected_only=True):
                        self.mainWindow.emit( QtCore.SIGNAL("testMonitorChanged()") )
#==================================================================================================
#==================================================================================================
class TestViewerMainWindow(QtGui.QMainWindow, ui_main_window.Ui_MainWindow):
Janik Zikovsky's avatar
Janik Zikovsky committed
    """
        The main GUI window of the test viewer.
    """

    def __init__(self, settings=None, bin_folder="", source_folder="", parent=None):
Janik Zikovsky's avatar
Janik Zikovsky committed
        QtGui.QWidget.__init__(self, parent)
        # Save the settings object
        self.settings = settings
        self.bin_folder = bin_folder
        self.source_folder = source_folder
Janik Zikovsky's avatar
Janik Zikovsky committed
        # Populate all GUI elements
        self.setupUi(self)
        self.buttonTest.hide()
Janik Zikovsky's avatar
Janik Zikovsky committed
        # Create the worker
        self.worker = TestWorker()
        # Now the file monitor
        self.monitor_worker = TestMonitorFilesWorker(self)
        self.monitor_worker.start()
        self.connect(self.worker, QtCore.SIGNAL("finished()"), self.complete_run)
        self.connect(self.worker, QtCore.SIGNAL("terminated()"), self.complete_run)
Janik Zikovsky's avatar
Janik Zikovsky committed
        # --- Menu Commands ---
        self.connect(self.action_Quit, QtCore.SIGNAL("triggered()"), self.quit)

        # -- Checkboxes toggle  ----
        self.connect(self.checkShowFailedOnly, QtCore.SIGNAL("stateChanged(int)"), self.checked_show_fail_only)
        self.connect(self.checkShowSelected, QtCore.SIGNAL("stateChanged(int)"), self.checked_show_selected_only)
        self.connect(self.checkMonitorTests, QtCore.SIGNAL("stateChanged(int)"), self.checked_monitor_tests)
        # -- Button commands ----
Janik Zikovsky's avatar
Janik Zikovsky committed
        self.connect(self.buttonRunAll, QtCore.SIGNAL("clicked()"), self.run_all)
        self.connect(self.buttonRunSelected, QtCore.SIGNAL("clicked()"), self.run_selected)
        self.connect(self.buttonAbort, QtCore.SIGNAL("clicked()"), self.abort)
        self.connect(self.buttonRefresh, QtCore.SIGNAL("clicked()"), self.setup_tree) #self.update_tree)
        self.connect(self.buttonExpandProjects, QtCore.SIGNAL("clicked()"), self.expand_tree_to_projects)
        self.connect(self.buttonExpandSuites, QtCore.SIGNAL("clicked()"), self.expand_tree_to_suites)
        self.connect(self.buttonExpandAll, QtCore.SIGNAL("clicked()"), self.expand_tree_to_all)
        self.connect(self.buttonTest, QtCore.SIGNAL("clicked()"), self.test)
        self.connect(self.buttonSelectAll, QtCore.SIGNAL("clicked()"), self.select_all)
        self.connect(self.buttonSelectNone, QtCore.SIGNAL("clicked()"), self.select_none)
        self.connect(self.buttonSelectFailed, QtCore.SIGNAL("clicked()"), self.select_failed)
        self.connect(self.buttonSelectSVN, QtCore.SIGNAL("clicked()"), self.select_svn)
        self.connect(self.buttonSelectByString, QtCore.SIGNAL("clicked()"), self.select_by_string)
        self.connect(self.textTimeout, QtCore.SIGNAL("textChanged()"), self.text_settings_changed)
        self.connect(self.textMemory, QtCore.SIGNAL("textChanged()"), self.text_settings_changed)

        # --- Tree commands ----
        #self.connect(self.treeTests, QtCore.SIGNAL("doubleClicked(QModelIndex)"), self.tree_doubleClicked)
        self.connect(self.treeTests, QtCore.SIGNAL("doubleClicked(QModelIndex)"), self.tree_doubleClicked)
        self.connect(self.treeTests, QtCore.SIGNAL("customContextMenuRequested(QPoint)"), self.tree_contextMenu)
        # Signal that will be called by the worker thread
        self.connect(self, QtCore.SIGNAL("testRun"), self.test_run_callback)
        # Signal called by the monitor thread
        self.connect(self, QtCore.SIGNAL("testMonitorChanged()"), self.run_selected)
        self.last_tree_update = time.time()
        self.last_console_update = time.time()
        # The accumulated stdoutput from commands
        self.stdout = ""
        self.current_results = None
        self.show_results()
    #-----------------------------------------------------------------------------
Janik Zikovsky's avatar
Janik Zikovsky committed
    def setup_tree(self):
        """ Set up the QTreeWidget of the tree """
        # Re-discover all the projects
        # TODO: Use arguments for the source paths
        test_info.all_tests.discover_CXX_projects(self.bin_folder, self.source_folder)
Janik Zikovsky's avatar
Janik Zikovsky committed
        tree = self.treeTests
        #@type tree QTreeWidget

        # Create the tree model and put it in there
        # It is important to save the model in self otherwise I think it gets garbage collected without error!
        self.model = TestTreeModel()
        # It is probably smart to hold a ref. to the proxy object too
        self.proxy = TreeFilterProxyModel()
        self.proxy.setSourceModel(self.model)
        self.proxy.setFilterWildcard("*G*")
        # Set the tree to use the SORTING/FILTERING proxy of the real model
        tree.setModel(self.proxy)
        tree.setAlternatingRowColors(True)
        tree.header().setResizeMode(0,QHeaderView.Stretch)
        tree.header().setResizeMode(1,QHeaderView.Interactive)
        tree.header().setResizeMode(2,QHeaderView.Interactive)
        tree.connect( tree, QtCore.SIGNAL("clicked (QModelIndex)"), self.tree_clicked)
        tree.connect( tree, QtCore.SIGNAL("activated (QModelIndex)"), self.tree_clicked)
        self.connect( tree.selectionModel(), QtCore.SIGNAL("currentChanged  (const QModelIndex &, const QModelIndex &)"), self.tree_selected)
    #-----------------------------------------------------------------------------
    #-----------------------------------------------------------------------------
    def tree_doubleClicked(self, index):
        """ Double-clicked on tree at that QModelIndex """
        print "CLICK ON MOUSE"
        try:
            object = self.treeTests.model().data( index, Qt.UserRole).toPyObject()
        except:
            return
        if isinstance(object, TestSingle):
            print "Running single test:", object.fullname
            object.parent.run_tests(self.test_run_callback, object.name)
    def tree_contextMenu(self, point):
        """ Open the context menu (if on an appropriate index
        point :: location clicked on the tree.
        """
        try:
            index = self.treeTests.indexAt(point)
            object = self.treeTests.model().data( index, Qt.UserRole).toPyObject()
        except:
            return

        if isinstance(object, TestSuite):
            menu = QMenu()
            runTestsAction = menu.addAction("Run Tests Individually")
            action = menu.exec_(self.treeTests.mapToGlobal(point))
            if action == runTestsAction:
                print "Running every test in %s individually" % object.name
                for test in object.tests:
                    print "Running %s" % test.fullname
                    object.run_tests(self.test_run_callback, test.name)
        elif isinstance(object, TestSingle):
            menu = QMenu()
            runTestsAction = menu.addAction("Run Single Test")
            action = menu.exec_(self.treeTests.mapToGlobal(point))
            if action == runTestsAction:
                print "Running single test:", object.fullname
                object.parent.run_tests(self.test_run_callback, object.name)

    #-----------------------------------------------------------------------------
    def readSettings(self):
        s = self.settings
        self.checkInParallel.setChecked( s.value("checkInParallel", False).toBool() )
        self.checkMonitorTests.setChecked( s.value("checkMonitorTests", False).toBool() )
        if s.contains("splitter"): self.splitter.restoreState( s.value("splitter").toByteArray() )
        self.resize( s.value("TestViewerMainWindow.width", 1500).toInt()[0],
                     s.value("TestViewerMainWindow.height", 900).toInt()[0] )
        self.move( s.value("TestViewerMainWindow.x", 0).toInt()[0],
                   s.value("TestViewerMainWindow.y", 0).toInt()[0] )
        column_default_width = [200, 230, 100]
            self.treeTests.setColumnWidth( i, s.value("treeTests.columnWidth(%d)"%i, column_default_width[i]).toInt()[0] )
            if self.treeTests.columnWidth(i) < 50:
                self.treeTests.setColumnWidth( i, 50)
        # --- Text settings ---
        memory = s.value("memory_limit_MB", 8000).toInt()[0]
        test_info.memory_limit_kb = memory*1024
        self.textMemory.setPlainText("%d" % memory)

        timeout = s.value("process_timeout_sec", 30).toInt()[0]
        test_info.process_timeout_sec = timeout
        self.textTimeout.setPlainText("%d" % timeout)
        self.select_by_string_lastValue = str(s.value("select_by_string_lastValue", "-Performance").toString())
    #-----------------------------------------------------------------------------
    def saveSettings(self):
        s = self.settings
        s.setValue("checkInParallel", self.checkInParallel.isChecked() )
        s.setValue("checkMonitorTests", self.checkMonitorTests.isChecked() )
        s.setValue("splitter", self.splitter.saveState())
            s.setValue("treeTests.columnWidth(%d)"%i, self.treeTests.columnWidth(i) )
        s.setValue("TestViewerMainWindow.width", self.width())
        s.setValue("TestViewerMainWindow.height", self.height())
        s.setValue("TestViewerMainWindow.x", self.x())
        s.setValue("TestViewerMainWindow.y", self.y())
        s.setValue("select_by_string_lastValue", self.select_by_string_lastValue)
    #-----------------------------------------------------------------------------
    def get_int_from_text(self, plainTextBox):
        """ Get a value from a text box. Color it red and return None if it is bad."""
        try:
            s = plainTextBox.toPlainText()
            value = int(s)
            plainTextBox.setStyleSheet("QPlainTextEdit { background-color: white; }")
            plainTextBox.setStyleSheet("QPlainTextEdit { background-color: tomato; }")
    #-----------------------------------------------------------------------------
    def text_settings_changed(self):
        """ called when one of the text boxes changes """
        s = self.settings
        memory = self.get_int_from_text(self.textMemory)
            test_info.memory_limit_kb = memory*1024
        timeout = self.get_int_from_text(self.textTimeout)
            test_info.process_timeout_sec = timeout
    #-----------------------------------------------------------------------------
    def closeEvent(self, event):
        """Window is closing """
        # Save all settings that haven't been saved before
        self.saveSettings()
    #-----------------------------------------------------------------------------
    def quit(self):
        """ Exit the program """
        test_info.all_tests.abort()
        print "Exiting TestViewer. Happy coding!"
        self.close()
    #-----------------------------------------------------------------------------
    def expand_tree_to_projects(self):
        """ Collapse suites so that only the top projects are shown """
        # Number of root projects
        rowcount = self.treeTests.model().rowCount( QModelIndex() )
        for i in xrange(rowcount):
            # Collapse each project-level one
            self.treeTests.setExpanded( self.treeTests.model().index(i, 0, QModelIndex() ), False)
    #-----------------------------------------------------------------------------
    def expand_tree_to_suites(self):
        """ Collapse single test results but expand projects """
        # Number of root projects
        rowcount = self.treeTests.model().rowCount( QModelIndex() )
        for i in xrange(rowcount):
            # Expand each project-level one
            indx = self.treeTests.model().index(i, 0, QModelIndex() )
            self.treeTests.setExpanded( indx, True)
            # And go through the suites and collapse those
            num_suites = self.treeTests.model().rowCount( indx)
            for j in xrange(num_suites):
                suite_indx = self.treeTests.model().index(j, 0, indx )
                self.treeTests.setExpanded( suite_indx, False)
    #-----------------------------------------------------------------------------
    def expand_tree_to_all(self):
        """ Expand all tree items """
        self.treeTests.expandAll()
    #-----------------------------------------------------------------------------
    def markup_console(self, in_line):
        """Return a marked-up (with HTML) version of a line
        from the consolue. """
        # Replace any < with HTML goodness
        line = test_info.html_escape(in_line)
        if ("Error" in line) or ("error:" in line) \
            or ("terminate called after throwing an instance" in line) \
            or ("Segmentation fault" in line) \
            or ("  what(): " in line) \
            or ("Assertion" in line and " failed." in line) \
            :
            #An error line!
            color = "red"
        elif "warning:" in line or "Warning:" in line:
            #Just a warning
            color = "orange"
        else:
            #Print normally
            color = ""
        # Bolden any lines with --- in them
        if line.startswith("---"):
            line = "<b>%s</b>" % line
        # Add the color tag
        if color != "":
            line = '<font color="%s">%s</font>' % (color, line)
        return line + "<br>\n"
    #-----------------------------------------------------------------------------
    def test_stdout_callback(self, line):
        """ Add a line to stdoutput from test running """
        pass
    #-----------------------------------------------------------------------------
    def test_run_callback(self, text, obj):
        """ Update the progress bar and label when a calculation is done (callback from test runner.
            obj: either a TestSuite or a TestProject;
            if it is a string, then it is the stdout of the make command"""
        # Update the tree's data for the suite
        if not obj is None:
            # For test results, increment the progress bar
            if isinstance(obj, TestSingle) or isinstance(obj, TestSuite) or isinstance(obj, TestProject):
                val = self.progTest.value()+1
                self.progTest.setValue( val )
                self.progTest.setFormat("%p% : " + text)
                # Try to update the current results shown
                if not self.current_results is None:
                    if obj.get_fullname() == self.current_results.get_fullname():
                #print "Updating GUI of TestSuite", obj.name
                self.model.update_suite(obj)
            elif isinstance(obj, TestProject):
                # Updating the project is now redundant since the test status string is made dynamically
                pass
                #self.model.update_project(obj.name)
            elif isinstance(obj, basestring):
                # String was returned
                if obj == test_info.MSG_ALL_BUILDS_SUCCESSFUL:
                    # Special message that all builds were good
                    # Go back to the test results tab; unless you are NOT running in parallel.
                    if self.checkInParallel.isChecked():
                        self.tabWidgetRight.setCurrentIndex(0)
                    self.stdout +=  self.markup_console( unicode(obj) )
                    if (time.time() - self.last_console_update) > 1.5: # Dont update more often than this:
                        self.textConsole.setText( u'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n' +
                                                  self.stdout )
                        sb = self.textConsole.verticalScrollBar()
                        sb.setValue(sb.maximum())
                        self.last_console_update = time.time()
    #-----------------------------------------------------------------------------
    def quick_update_tree(self):
        """ Update the tree view without resetting all the model data """
        self.treeTests.model().update_all()
    #-----------------------------------------------------------------------------
    def set_running(self, running):
        """ Sets whether a test suite is running. Adjusts the GUI as needed"""
        self.running=running
        self.buttonAbort.setEnabled(running)
        self.buttonRunAll.setEnabled(not running)
        self.buttonRunSelected.setEnabled(not running)
Janik Zikovsky's avatar
Janik Zikovsky committed
    #-----------------------------------------------------------------------------
        """ Event called when completing/aborting a test run """
        self.progTest.setValue(0)
        self.progTest.setFormat("")
        self.update_tree()
        failed = test_info.all_tests.failed
        num_run = test_info.all_tests.num_run
        if failed > 0:
            self.labelResult.setStyleSheet("QLabel { background-color: red }")
            self.labelResult.setText("FAILURE! %d of %d tests failed." % (failed, num_run))
        else:
            self.labelResult.setStyleSheet("QLabel { background-color: green }")
            self.labelResult.setText("All Passed! %d of %d tests failed." % (failed, num_run))
    #-----------------------------------------------------------------------------
    def update_tree(self):
        """ Update the tree view with whatever the current results are """
        self.model.setupModelData()
        self.treeTests.update()
Janik Zikovsky's avatar
Janik Zikovsky committed
    #-----------------------------------------------------------------------------
    def tree_clicked(self, index):
        """ A row of the tree was clicked. Show the test results.
        @param index :: QAbstracItemModel index into the tree.
        """
        # Retrieve the item at that index
        # (returns a QVariant that you have to bring back to a python object)
        item = self.treeTests.model().data( index, Qt.UserRole).toPyObject()
        self.current_results = item
        self.show_results()
    #-----------------------------------------------------------------------------
    def tree_selected(self, current_index, previous_index):
        """ Selection in the tree changed from previous_index to current_index """
        item = self.treeTests.model().data( current_index, Qt.UserRole).toPyObject()
        self.current_results = item
        self.show_results()

    #-----------------------------------------------------------------------------
        """ Show the test results.
        @param res :: either TestProject, TestSuite, or TestSingle object containing the results to show."""
        if self.current_results is None:
            self.labelTestType.setText("Test Project Results:")
            self.labelTestName.setText( "" )
            self.textResults.setText( "" )
            return
        res = self.current_results
        if isinstance(res, TestProject):
            self.labelTestType.setText("Test Project Results:")
        elif isinstance(res, TestSuite):
            self.labelTestType.setText("Test Suite Results:")
        elif isinstance(res, TestSingle):
            self.labelTestType.setText("Single Test Results:")
            raise RuntimeError("Incorrect object passed to show_results; should be TestProject, TestSuite, or TestSingle.")
        self.labelTestName.setText( res.get_fullname() )
        self.labelTestName.hide()
        self.textResults.setText( u'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n'
    #-----------------------------------------------------------------------------
        """ Toggle the filtering """
        self.proxy.set_filter_failed_only(state > 0)
    def checked_show_selected_only(self, state):
        """ Toggle the filtering """
        self.proxy.set_filter_selected_only(state > 0)
    def checked_monitor_libraries(self, state):
        self.monitor_worker.set_monitor_libraries( state > 0 )
    def checked_monitor_tests(self, state):
        self.monitor_worker.set_monitor_tests( state > 0 )
Janik Zikovsky's avatar
Janik Zikovsky committed
    #-----------------------------------------------------------------------------
    def run_all(self):
Janik Zikovsky's avatar
Janik Zikovsky committed

    def run_selected(self):
        """ Start a parallelized test running with the given parameters """
        parallel = self.checkInParallel.isChecked()
        # Do some setup of the worker and GUI
        num_steps = self.worker.set_parameters(self, selected_only=selected_only, make_tests=True, parallel=parallel)
        if num_steps < 1:
            num_steps = 1
        self.progTest.setValue(0)
        self.progTest.setMaximum( num_steps )
        # Select the console output tab (for the make output)        self.running=True

        self.tabWidgetRight.setCurrentIndex(1)
        # Begin the thread in the background
        self.worker.start()
    #-----------------------------------------------------------------------------
    def abort(self):
        test_info.all_tests.abort()
    #-----------------------------------------------------------------------------
    def select_all(self):
        """ Select all tests """
        test_info.all_tests.select_all(True)
        self.proxy.invalidateFilter()
        self.treeTests.update()
    def select_none(self):
        """ De-Select all tests """
        test_info.all_tests.select_all(False)
        self.proxy.invalidateFilter()
        self.treeTests.update()
    def select_failed(self):
        """ Select all failing tests """
        test_info.all_tests.select_failed()
        self.proxy.invalidateFilter()
        self.treeTests.update()
        """ Select all files modified by SVN/git st """
        test_info.all_tests.select_svn()
        self.proxy.invalidateFilter()
        self.treeTests.update()
    def select_by_string(self):
        """ Use a string to select """
        dlg = QtGui.QInputDialog(self)
        dlg.setLabelText("Select tests by strings, separated by spaces. Use a '-' before a word to select all except that word.")
        dlg.setTextValue(self.select_by_string_lastValue)
        dlg.setModal(True)
        dlg.exec_()
        self.select_by_string_lastValue = str(dlg.textValue())
        test_info.all_tests.select_by_string(self.select_by_string_lastValue)
        self.proxy.invalidateFilter()
        self.treeTests.update()
    # Start the settings object.
    # TODO: Change the company name here and in MantidPlot
    settings = QSettings("ISIS", "MantidTestViewer")
    settings_bin_folder = str(settings.value("bin_folder", "../../Mantid/bin").toString())
    settings_source_folder = str(settings.value("source_folder", "../../Mantid/Framework").toString())
    if len(sys.argv) > 1:
        bin_folder = sys.argv[1]
    if len(sys.argv) > 2:
        source_folder = sys.argv[2]
    if bin_folder == "--help":
        print """TestViewer.py [BINFOLDER] [SOURCEFOLDER]
 GUI to run and view Mantid tests.

   BINFOLDER       Path to the bin/ folder that contains the test executables.
                   Will use the last path used if not specified:
   SOURCEFOLDER    Path to the root of the framework source folder.
                   Will use the last path used if not specified:
                   %s
""" % (settings_bin_folder, settings_source_folder)
        sys.exit()
    # No command line arguments? Get them from the settings
    if bin_folder == "":
        bin_folder = settings_bin_folder
        source_folder = settings_source_folder
    else:
        # Use whatever was given.
        settings.setValue("bin_folder", bin_folder)
        if source_folder == "":
            # No source folder given? Use the old one
            if settings.contains("source_folder"):
                source_folder = str(settings.value("source_folder", "../../Mantid/Framework").toString())
            else:
                # Try a relative source folder if not specified
                source_folder = os.path.join( bin_folder, "/../Framework" )
            settings.setValue("source_folder", source_folder)
    print "Starting TestViewer ..."
    print "... bin folder is", bin_folder
    print "... source folder is", source_folder
    print "."
    app = QtGui.QApplication([])
Janik Zikovsky's avatar
Janik Zikovsky committed
    app.setOrganizationName("Mantid")
    app.setOrganizationDomain("mantidproject.org")
    app.setApplicationName("Mantid Test Viewer")
    main = TestViewerMainWindow(settings, bin_folder, source_folder)
Janik Zikovsky's avatar
Janik Zikovsky committed
    main.show()
if __name__ == '__main__':
    start()