Index: src/sas/qtgui/MainWindow/UnitTesting/AboutBoxTest.py
===================================================================
--- src/sas/qtgui/MainWindow/UnitTesting/AboutBoxTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/MainWindow/UnitTesting/AboutBoxTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
@@ -0,0 +1,103 @@
+import sys
+import unittest
+import webbrowser
+
+from PyQt4.QtGui import *
+from PyQt4.QtTest import QTest
+from PyQt4.QtCore import *
+from mock import MagicMock
+
+# set up import paths
+import path_prepare
+
+# Local
+from sas.qtgui.MainWindow.AboutBox import AboutBox
+import sas.qtgui.Utilities.LocalConfig as LocalConfig
+
+app = QApplication(sys.argv)
+
+class AboutBoxTest(unittest.TestCase):
+    '''Test the AboutBox'''
+    def setUp(self):
+        '''Create the AboutBox'''
+        self.widget = AboutBox(None)
+
+    def tearDown(self):
+        '''Destroy the AboutBox'''
+        self.widget.close()
+        self.widget = None
+
+    def testDefaults(self):
+        '''Test the GUI in its default state'''
+        self.assertIsInstance(self.widget, QWidget)
+        self.assertEqual(self.widget.windowTitle(), "About")
+        self.assertEqual(self.widget.cmdOK.text(), "OK")
+
+        self.assertIn("SasView", self.widget.label_2.text())
+        # Link buttons pixmaps don't contain image filenames, so can't check this.
+        # self.assertEqual(self.widget.cmdLinkUT.icon().name(), "utlogo.gif")
+
+
+    def testVersion(self):
+        """
+        Assure the version number is as expected
+        """
+        version = self.widget.lblVersion
+        self.assertIsInstance(version, QLabel)
+        self.assertEqual(str(version.text()), str(LocalConfig.__version__))
+
+    def testAbout(self):
+        """
+        Assure the about label is filled properly
+        """
+        about = self.widget.lblAbout
+        self.assertIsInstance(about, QLabel)
+        # build version
+        self.assertIn(str(LocalConfig.__build__), about.text())
+        # License
+        self.assertIn(str(LocalConfig._copyright), about.text())
+        # URLs
+        self.assertIn(str(LocalConfig._homepage), about.text())
+        self.assertIn(str(LocalConfig.__download_page__), about.text())
+        self.assertIn(str(LocalConfig._license), about.text())
+
+        # Are links enabled?
+        self.assertTrue(about.openExternalLinks())
+
+    def testAddActions(self):
+        """
+        Assure link buttons are set up correctly
+        """
+        webbrowser.open = MagicMock()
+        all_hosts = [
+                LocalConfig._nist_url,
+                LocalConfig._umd_url,
+                LocalConfig._sns_url,
+                LocalConfig._nsf_url,
+                LocalConfig._isis_url,
+                LocalConfig._ess_url,
+                LocalConfig._ill_url,
+                LocalConfig._ansto_url,
+                LocalConfig._inst_url]
+
+        # Press the buttons
+        buttonList = self.widget.findChildren(QPushButton)
+        for button in buttonList:
+            QTest.mouseClick(button, Qt.LeftButton)
+            #open_link = webbrowser.open.call_args
+            args, _ = webbrowser.open.call_args
+            # args[0] contains the actual argument sent to open()
+            self.assertIn(args[0], all_hosts)
+
+        # The above test also greedily catches the OK button,
+        # so let's test it separately.
+        # Show the widget
+        self.widget.show()
+        self.assertTrue(self.widget.isVisible())
+        # Click on the OK button
+        QTest.mouseClick(self.widget.cmdOK, Qt.LeftButton)
+        # assure the widget is no longer seen
+        self.assertFalse(self.widget.isVisible())
+
+if __name__ == "__main__":
+    unittest.main()
Index: src/sas/qtgui/MainWindow/UnitTesting/DataExplorerTest.py
===================================================================
--- src/sas/qtgui/MainWindow/UnitTesting/DataExplorerTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/MainWindow/UnitTesting/DataExplorerTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
@@ -0,0 +1,807 @@
+import sys
+import unittest
+
+from PyQt4.QtGui import *
+from PyQt4.QtTest import QTest
+from PyQt4.QtCore import *
+from mock import MagicMock
+from mock import patch
+from mpl_toolkits.mplot3d import Axes3D
+
+# set up import paths
+import path_prepare
+
+# Local
+from sas.sasgui.guiframe.dataFitting import Data1D
+from sas.sascalc.dataloader.loader import Loader
+from sas.sasgui.guiframe.data_manager import DataManager
+
+from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow
+from sas.qtgui.MainWindow.GuiManager import GuiManager
+from sas.qtgui.Utilities.GuiUtils import *
+from UnitTesting.TestUtils import QtSignalSpy
+from sas.qtgui.Plotting.Plotter import Plotter
+from sas.qtgui.Plotting.Plotter2D import Plotter2D
+import sas.qtgui.Plotting.PlotHelper as PlotHelper
+
+app = QApplication(sys.argv)
+
+class DataExplorerTest(unittest.TestCase):
+    '''Test the Data Explorer GUI'''
+    def setUp(self):
+        '''Create the GUI'''
+        class MyPerspective(object):
+            def communicator(self):
+                return Communicate()
+            def allowBatch(self):
+                return False
+            def setData(self, data_item=None):
+                return None
+            def title(self):
+                return "Dummy Perspective"
+
+        class dummy_manager(object):
+            def communicator(self):
+                return Communicate()
+            def perspective(self):
+                return MyPerspective()
+            def workspace(self):
+                return None
+
+        self.form = DataExplorerWindow(None, dummy_manager())
+
+    def tearDown(self):
+        '''Destroy the GUI'''
+        self.form.close()
+        self.form = None
+
+    def testDefaults(self):
+        '''Test the GUI in its default state'''
+        # Tab widget
+        self.assertIsInstance(self.form, QTabWidget)
+        self.assertEqual(self.form.count(), 2)
+
+        # Buttons - data tab
+        self.assertEqual(self.form.cmdLoad.text(), "Load data")
+        self.assertEqual(self.form.cmdDeleteData.text(), "Delete")
+        self.assertEqual(self.form.cmdDeleteTheory.text(), "Delete")
+        self.assertEqual(self.form.cmdFreeze.text(), "Freeze Theory")
+        self.assertEqual(self.form.cmdSendTo.text(), "Send data to")
+        self.assertEqual(self.form.cmdSendTo.iconSize(), QSize(48, 48))
+        self.assertIsInstance(self.form.cmdSendTo.icon(), QIcon)
+        self.assertEqual(self.form.chkBatch.text(), "Batch mode")
+        self.assertFalse(self.form.chkBatch.isChecked())
+
+        # Buttons - theory tab
+
+        # Combo boxes
+        self.assertEqual(self.form.cbSelect.count(), 6)
+        self.assertEqual(self.form.cbSelect.currentIndex(), 0)
+
+        # Models - data
+        self.assertIsInstance(self.form.model, QStandardItemModel)
+        self.assertEqual(self.form.treeView.model().rowCount(), 0)
+        self.assertEqual(self.form.treeView.model().columnCount(), 0)
+        self.assertEqual(self.form.model.rowCount(), 0)
+        self.assertEqual(self.form.model.columnCount(), 0)
+        self.assertIsInstance(self.form.data_proxy, QSortFilterProxyModel)
+        self.assertEqual(self.form.data_proxy.sourceModel(), self.form.model)
+        self.assertEqual("[^()]", str(self.form.data_proxy.filterRegExp().pattern()))
+        self.assertIsInstance(self.form.treeView, QTreeView)
+
+        # Models - theory
+        self.assertIsInstance(self.form.theory_model, QStandardItemModel)
+        self.assertEqual(self.form.freezeView.model().rowCount(), 0)
+        self.assertEqual(self.form.freezeView.model().columnCount(), 0)
+        self.assertEqual(self.form.theory_model.rowCount(), 0)
+        self.assertEqual(self.form.theory_model.columnCount(), 0)
+        self.assertIsInstance(self.form.theory_proxy, QSortFilterProxyModel)
+        self.assertEqual(self.form.theory_proxy.sourceModel(), self.form.theory_model)
+        self.assertEqual("[^()]", str(self.form.theory_proxy.filterRegExp().pattern()))
+        self.assertIsInstance(self.form.freezeView, QTreeView)
+
+    def testWidgets(self):
+        """
+        Test if all required widgets got added
+        """
+    def testLoadButton(self):
+        loadButton = self.form.cmdLoad
+
+        filename = "cyl_400_20.txt"
+        # Initialize signal spy instances
+        spy_file_read = QtSignalSpy(self.form, self.form.communicator.fileReadSignal)
+
+        # Return no files.
+        QtGui.QFileDialog.getOpenFileNames = MagicMock(return_value=None)
+
+        # Click on the Load button
+        QTest.mouseClick(loadButton, Qt.LeftButton)
+
+        # Test the getOpenFileName() dialog called once
+        self.assertTrue(QtGui.QFileDialog.getOpenFileNames.called)
+        QtGui.QFileDialog.getOpenFileNames.assert_called_once()
+
+        # Make sure the signal has not been emitted
+        self.assertEqual(spy_file_read.count(), 0)
+
+        # Now, return a single file
+        QtGui.QFileDialog.getOpenFileNames = MagicMock(return_value=filename)
+
+        # Click on the Load button
+        QTest.mouseClick(loadButton, Qt.LeftButton)
+        QtGui.qApp.processEvents()
+
+        # Test the getOpenFileName() dialog called once
+        self.assertTrue(QtGui.QFileDialog.getOpenFileNames.called)
+        QtGui.QFileDialog.getOpenFileNames.assert_called_once()
+
+        # Expected one spy instance
+        #self.assertEqual(spy_file_read.count(), 1)
+        #self.assertIn(filename, str(spy_file_read.called()[0]['args'][0]))
+
+    def testLoadFiles(self):
+        """
+        Test progress bar update while loading of multiple files
+        """
+        # Set up the spy on progress bar update signal
+        spy_progress_bar_update = QtSignalSpy(self.form,
+            self.form.communicator.progressBarUpdateSignal)
+
+        # Populate the model
+        filename = ["cyl_400_20.txt", "Dec07031.ASC", "cyl_400_20.txt"]
+        self.form.readData(filename)
+
+        # 0, 0, 33, 66, -1 -> 5 signals reaching progressBar
+        self.assertEqual(spy_progress_bar_update.count(), 5)
+
+        expected_list = [0, 0, 33, 66, -1]
+        spied_list = [spy_progress_bar_update.called()[i]['args'][0] for i in xrange(5)]
+        self.assertEqual(expected_list, spied_list)
+        
+    def testDeleteButton(self):
+        """
+        Functionality of the delete button
+        """
+        deleteButton = self.form.cmdDeleteData
+
+        # Mock the confirmation dialog with return=No
+        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.No)
+
+        # Populate the model
+        filename = ["cyl_400_20.txt", "Dec07031.ASC", "cyl_400_20.txt"]
+        self.form.readData(filename)
+
+        # Assure the model contains three items
+        self.assertEqual(self.form.model.rowCount(), 3)
+
+        # Assure the checkboxes are on
+        item1 = self.form.model.item(0)
+        item2 = self.form.model.item(1)
+        item3 = self.form.model.item(2)
+        self.assertTrue(item1.checkState() == QtCore.Qt.Checked)
+        self.assertTrue(item2.checkState() == QtCore.Qt.Checked)
+        self.assertTrue(item3.checkState() == QtCore.Qt.Checked)
+
+        # Click on the delete  button
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+        # Test the warning dialog called once
+        self.assertTrue(QtGui.QMessageBox.question.called)
+
+        # Assure the model still contains the items
+        self.assertEqual(self.form.model.rowCount(), 3)
+
+        # Now, mock the confirmation dialog with return=Yes
+        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.Yes)
+
+        # Click on the delete  button
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+        # Test the warning dialog called once
+        self.assertTrue(QtGui.QMessageBox.question.called)
+
+        # Assure the model contains no items
+        self.assertEqual(self.form.model.rowCount(), 0)
+
+        # Click delete once again to assure no nasty behaviour on empty model
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+    def testDeleteTheory(self):
+        """
+        Test that clicking "Delete" in theories tab removes selected indices
+        """
+        deleteButton = self.form.cmdDeleteTheory
+
+        # Mock the confirmation dialog with return=No
+        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.No)
+
+        # Populate the model
+        item1 = QtGui.QStandardItem(True)
+        item1.setCheckable(True)
+        item1.setCheckState(QtCore.Qt.Checked)
+        item1.setText("item 1")
+        self.form.theory_model.appendRow(item1)
+        item2 = QtGui.QStandardItem(True)
+        item2.setCheckable(True)
+        item2.setCheckState(QtCore.Qt.Unchecked)
+        item2.setText("item 2")
+        self.form.theory_model.appendRow(item2)
+
+        # Assure the model contains two items
+        self.assertEqual(self.form.theory_model.rowCount(), 2)
+
+        # Assure the checkboxes are on
+        self.assertTrue(item1.checkState() == QtCore.Qt.Checked)
+        self.assertTrue(item2.checkState() == QtCore.Qt.Unchecked)
+
+        # Click on the delete  button
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+        # Test the warning dialog called once
+        self.assertTrue(QtGui.QMessageBox.question.called)
+
+        # Assure the model still contains the items
+        self.assertEqual(self.form.theory_model.rowCount(), 2)
+
+        # Now, mock the confirmation dialog with return=Yes
+        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.Yes)
+
+        # Click on the delete  button
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+        # Test the warning dialog called once
+        self.assertTrue(QtGui.QMessageBox.question.called)
+
+        # Assure the model contains 1 item
+        self.assertEqual(self.form.theory_model.rowCount(), 1)
+
+        # Set the remaining item to checked
+        self.form.theory_model.item(0).setCheckState(QtCore.Qt.Checked)
+
+        # Click on the delete button again
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+        # Assure the model contains no items
+        self.assertEqual(self.form.theory_model.rowCount(), 0)
+
+        # Click delete once again to assure no nasty behaviour on empty model
+        QTest.mouseClick(deleteButton, Qt.LeftButton)
+
+
+    def testSendToButton(self):
+        """
+        Test that clicking the Send To button sends checked data to a perspective
+        """
+        # Send empty data
+        mocked_perspective = self.form.parent.perspective()
+        mocked_perspective.setData = MagicMock()
+
+        # Click on the Send To  button
+        QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton)
+
+        # The set_data method not called
+        self.assertFalse(mocked_perspective.setData.called)
+               
+        # Populate the model
+        filename = ["cyl_400_20.txt"]
+        self.form.readData(filename)
+
+        # setData is the method we want to see called
+        mocked_perspective = self.form.parent.perspective()
+        mocked_perspective.setData = MagicMock(filename)
+
+        # Assure the checkbox is on
+        self.form.cbSelect.setCurrentIndex(0)
+
+        # Click on the Send To  button
+        QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton)
+
+        # Test the set_data method called once
+        #self.assertTrue(mocked_perspective.setData.called)
+
+        # open another file
+        filename = ["cyl_400_20.txt"]
+        self.form.readData(filename)
+
+        # Mock the warning message
+        QtGui.QMessageBox = MagicMock()
+
+        # Click on the button
+        QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton)
+
+        # Assure the message box popped up
+        QtGui.QMessageBox.assert_called_once()
+
+    def testDataSelection(self):
+        """
+        Tests the functionality of the Selection Option combobox
+        """
+        # Populate the model with 1d and 2d data
+        filename = ["cyl_400_20.txt", "Dec07031.ASC"]
+        self.form.readData(filename)
+
+        # Unselect all data
+        self.form.cbSelect.setCurrentIndex(1)
+
+        # Test the current selection
+        item1D = self.form.model.item(0)
+        item2D = self.form.model.item(1)
+        self.assertTrue(item1D.checkState() == QtCore.Qt.Unchecked)
+        self.assertTrue(item2D.checkState() == QtCore.Qt.Unchecked)        
+
+        # Select all data
+        self.form.cbSelect.setCurrentIndex(0)
+
+        # Test the current selection
+        self.assertTrue(item1D.checkState() == QtCore.Qt.Checked)
+        self.assertTrue(item2D.checkState() == QtCore.Qt.Checked)        
+
+        # select 1d data
+        self.form.cbSelect.setCurrentIndex(2)
+
+        # Test the current selection
+        self.assertTrue(item1D.checkState() == QtCore.Qt.Checked)
+        self.assertTrue(item2D.checkState() == QtCore.Qt.Unchecked)        
+
+        # unselect 1d data
+        self.form.cbSelect.setCurrentIndex(3)
+
+        # Test the current selection
+        self.assertTrue(item1D.checkState() == QtCore.Qt.Unchecked)
+        self.assertTrue(item2D.checkState() == QtCore.Qt.Unchecked)        
+
+        # select 2d data
+        self.form.cbSelect.setCurrentIndex(4)
+
+        # Test the current selection
+        self.assertTrue(item1D.checkState() == QtCore.Qt.Unchecked)
+        self.assertTrue(item2D.checkState() == QtCore.Qt.Checked)        
+
+        # unselect 2d data
+        self.form.cbSelect.setCurrentIndex(5)
+
+        # Test the current selection
+        self.assertTrue(item1D.checkState() == QtCore.Qt.Unchecked)
+        self.assertTrue(item2D.checkState() == QtCore.Qt.Unchecked)        
+
+        # choose impossible index and assure the code raises
+        #with self.assertRaises(Exception):
+        #    self.form.cbSelect.setCurrentIndex(6)
+
+    def testFreezeTheory(self):
+        """
+        Assure theory freeze functionality works
+        """
+        # Not yet tested - agree on design first.
+        pass
+
+    def testRecursivelyCloneItem(self):
+        """
+        Test the rescursive QAbstractItem/QStandardItem clone
+        """
+        # Create an item with several branches
+        item1 = QtGui.QStandardItem()
+        item2 = QtGui.QStandardItem()
+        item3 = QtGui.QStandardItem()
+        item4 = QtGui.QStandardItem()
+        item5 = QtGui.QStandardItem()
+        item6 = QtGui.QStandardItem()
+
+        item4.appendRow(item5)
+        item2.appendRow(item4)
+        item2.appendRow(item6)
+        item1.appendRow(item2)
+        item1.appendRow(item3)
+
+        # Clone
+        new_item = self.form.recursivelyCloneItem(item1)
+
+        # assure the trees look identical
+        self.assertEqual(item1.rowCount(), new_item.rowCount())
+        self.assertEqual(item1.child(0).rowCount(), new_item.child(0).rowCount())
+        self.assertEqual(item1.child(1).rowCount(), new_item.child(1).rowCount())
+        self.assertEqual(item1.child(0).child(0).rowCount(), new_item.child(0).child(0).rowCount())
+
+    def testReadData(self):
+        """
+        Test the low level readData() method
+        """
+        filename = ["cyl_400_20.txt"]
+        self.form.manager.add_data = MagicMock()
+
+        # Initialize signal spy instances
+        spy_status_update = QtSignalSpy(self.form, self.form.communicator.statusBarUpdateSignal)
+        spy_data_received = QtSignalSpy(self.form, self.form.communicator.fileDataReceivedSignal)
+
+        # Read in the file
+        self.form.readData(filename)
+
+        # Expected two status bar updates
+        self.assertEqual(spy_status_update.count(), 2)
+        self.assertIn(filename[0], str(spy_status_update.called()[0]['args'][0]))
+
+
+        # Check that the model contains the item
+        self.assertEqual(self.form.model.rowCount(), 1)
+        self.assertEqual(self.form.model.columnCount(), 1)
+
+        # The 0th item header should be the name of the file
+        model_item = self.form.model.index(0,0)
+        model_name = str(self.form.model.data(model_item).toString())
+        self.assertEqual(model_name, filename[0])
+
+    def skip_testDisplayHelp(self): # Skip due to help path change
+        """
+        Test that the Help window gets shown correctly
+        """
+        partial_url = "sasgui/guiframe/data_explorer_help.html"
+        button1 = self.form.cmdHelp
+        button2 = self.form.cmdHelp_2
+
+        # Click on the Help button
+        QTest.mouseClick(button1, Qt.LeftButton)
+        qApp.processEvents()
+
+        # Check the browser
+        self.assertIn(partial_url, str(self.form._helpView.url()))
+        # Close the browser
+        self.form._helpView.close()
+
+        # Click on the Help_2 button
+        QTest.mouseClick(button2, Qt.LeftButton)
+        qApp.processEvents()
+        # Check the browser
+        self.assertIn(partial_url, str(self.form._helpView.url()))
+
+    def testLoadFile(self):
+        """
+        Test the threaded call to readData()
+        """
+        #self.form.loadFile()
+        pass
+
+    def testGetWList(self):
+        """
+        Test the list of known extensions
+        """
+        w_list = self.form.getWlist()
+
+        defaults = 'All (*.*);;canSAS files (*.xml);;SESANS files' +\
+            ' (*.ses);;ASCII files (*.txt);;IGOR 2D files (*.asc);;' +\
+            'IGOR/DAT 2D Q_map files (*.dat);;IGOR 1D files (*.abs);;'+\
+            'HFIR 1D files (*.d1d);;DANSE files (*.sans);;NXS files (*.nxs)'
+        default_list = defaults.split(';;')
+
+        for def_format in default_list:
+            self.assertIn(def_format, w_list)
+       
+    def testLoadComplete(self):
+        """
+        Test the callback method updating the data object
+        """
+        message="Loading Data Complete"
+        data_dict = {"a1":Data1D()}
+        output_data = (data_dict, message)
+
+        self.form.manager.add_data = MagicMock()
+
+        # Initialize signal spy instances
+        spy_status_update = QtSignalSpy(self.form, self.form.communicator.statusBarUpdateSignal)
+        spy_data_received = QtSignalSpy(self.form, self.form.communicator.fileDataReceivedSignal)
+
+        # Read in the file
+        self.form.loadComplete(output_data)
+
+        # "Loading data complete" no longer sent in LoadFile but in callback
+        self.assertIn("Loading Data Complete", str(spy_status_update.called()[0]['args'][0]))
+
+        # Expect one Data Received signal
+        self.assertEqual(spy_data_received.count(), 1)
+
+        # Assure returned dictionary has correct data
+        # We don't know the data ID, so need to iterate over dict
+        data_dict = spy_data_received.called()[0]['args'][0]
+        for data_key, data_value in data_dict.iteritems():
+            self.assertIsInstance(data_value, Data1D)
+
+        # Assure add_data on data_manager was called (last call)
+        self.assertTrue(self.form.manager.add_data.called)
+
+    def testNewPlot1D(self):
+        """
+        Creating new plots from Data1D/2D
+        """
+        loader = Loader()
+        manager = DataManager()
+        PlotHelper.clear()
+        self.form.enableGraphCombo(None)
+
+        # Make sure the controls are disabled
+        self.assertFalse(self.form.cbgraph.isEnabled())
+        self.assertFalse(self.form.cmdAppend.isEnabled())
+
+        # get Data1D
+        p_file="cyl_400_20.txt"
+        output_object = loader.load(p_file)
+        new_data = [manager.create_gui_data(output_object, p_file)]
+
+        # Mask retrieval of the data
+        self.form.plotsFromCheckedItems = MagicMock(return_value=new_data)
+
+        # Mask plotting
+        self.form.parent.workspace = MagicMock()
+
+        # Call the plotting method
+        self.form.newPlot()
+
+        # The plot was registered
+        self.assertEqual(len(PlotHelper.currentPlots()), 1)
+
+        self.assertTrue(self.form.cbgraph.isEnabled())
+        self.assertTrue(self.form.cmdAppend.isEnabled())
+
+    def testNewPlot2D(self):
+        """
+        Creating new plots from Data1D/2D
+        """
+        loader = Loader()
+        manager = DataManager()
+        PlotHelper.clear()
+        self.form.enableGraphCombo(None)
+
+        # Make sure the controls are disabled
+        self.assertFalse(self.form.cbgraph.isEnabled())
+        self.assertFalse(self.form.cmdAppend.isEnabled())
+
+        # get Data2D
+        p_file="Dec07031.ASC"
+        output_object = loader.load(p_file)
+        new_data = [manager.create_gui_data(output_object, p_file)]
+
+        # Mask retrieval of the data
+        self.form.plotsFromCheckedItems = MagicMock(return_value=new_data)
+
+        # Mask plotting
+        self.form.parent.workspace = MagicMock()
+
+        # Call the plotting method
+        self.form.newPlot()
+
+        # The plot was registered
+        self.assertEqual(len(PlotHelper.currentPlots()), 1)
+
+        self.assertTrue(self.form.cbgraph.isEnabled())
+        self.assertTrue(self.form.cmdAppend.isEnabled())
+
+    @patch('sas.qtgui.Utilities.GuiUtils.plotsFromCheckedItems')
+    def testAppendPlot(self, test_patch):
+        """
+        Creating new plots from Data1D/2D
+        """
+        loader = Loader()
+        manager = DataManager()
+
+        PlotHelper.clear()
+        self.form.enableGraphCombo(None)
+
+        # Make sure the controls are disabled
+        self.assertFalse(self.form.cbgraph.isEnabled())
+        self.assertFalse(self.form.cmdAppend.isEnabled())
+
+        # get Data1D
+        p_file="cyl_400_20.txt"
+        output_object = loader.load(p_file)
+        output_item = QtGui.QStandardItem()
+        new_data = [(output_item, manager.create_gui_data(output_object, p_file))]
+
+        # Mask plotting
+        self.form.parent.workspace = MagicMock()
+
+        # Mask the plot show call
+        Plotter.show = MagicMock()
+
+        # Mask retrieval of the data
+        test_patch.return_value = new_data
+
+        # Call the plotting method
+        self.form.newPlot()
+
+        # Call the plotting method again, so we have 2 graphs
+        self.form.newPlot()
+
+        # See that we have two plots
+        self.assertEqual(len(PlotHelper.currentPlots()), 2)
+
+        # Add data to plot #1
+        self.form.cbgraph.setCurrentIndex(1)
+        self.form.appendPlot()
+
+        # See that we still have two plots
+        self.assertEqual(len(PlotHelper.currentPlots()), 2)
+
+    def testUpdateGraphCombo(self):
+        """
+        Test the combo box update
+        """
+        PlotHelper.clear()
+
+        graph_list=["1","2","3"]
+        self.form.updateGraphCombo(graph_list)
+
+        self.assertEqual(self.form.cbgraph.count(), 3)
+        self.assertEqual(self.form.cbgraph.currentText(), '1')
+
+        graph_list=[]
+        self.form.updateGraphCombo(graph_list)
+        self.assertEqual(self.form.cbgraph.count(), 0)
+
+    def testUpdateModelFromPerspective(self):
+        """
+        Assure the model update is correct
+        """
+        good_item = QtGui.QStandardItem()
+        bad_item = "I'm so bad"
+
+        self.form.model.reset = MagicMock()
+
+        self.form.updateModelFromPerspective(good_item)
+
+        # See that the model got reset
+        self.form.model.reset.assert_called_once()
+
+        # See that the bad item causes raise
+        with self.assertRaises(Exception):
+            self.form.updateModelFromPerspective(bad_item)
+
+    def testContextMenu(self):
+        """
+        See if the context menu is present
+        """
+        # get Data1D
+        p_file=["cyl_400_20.txt"]
+        # Read in the file
+        output, message = self.form.readData(p_file)
+        self.form.loadComplete((output, message))
+
+        # Pick up the treeview index corresponding to that file
+        index = self.form.treeView.indexAt(QtCore.QPoint(5,5))
+        self.form.show()
+
+        # Find out the center pointof the treeView row
+        rect = self.form.treeView.visualRect(index).center()
+
+        self.form.context_menu.exec_ = MagicMock()
+
+        # Move the mouse pointer to the first row
+        QTest.mouseMove(self.form.treeView.viewport(), pos=rect)
+
+        # This doesn't invoke the action/signal. Investigate why?
+        # QTest.mouseClick(self.form.treeView.viewport(), Qt.RightButton, pos=rect)
+
+        # Instead, send the signal directly
+        self.form.treeView.customContextMenuRequested.emit(rect)
+
+        # app.exec_() # debug
+
+        # See that the menu has been shown
+        self.form.context_menu.exec_.assert_called_once()
+
+    def testShowDataInfo(self):
+        """
+        Test of the showDataInfo method
+        """
+        # get Data1D
+        p_file=["cyl_400_20.txt"]
+        # Read in the file
+        output, message = self.form.readData(p_file)
+        self.form.loadComplete((output, message))
+
+        # select the data
+        self.form.treeView.selectAll()
+
+        # Call the tested method
+        self.form.showDataInfo()
+
+        # Test the properties
+        self.assertTrue(self.form.txt_widget.isReadOnly())
+        self.assertEqual(self.form.txt_widget.windowTitle(), "Data Info: cyl_400_20.txt")
+        self.assertIn("Waveln_max", self.form.txt_widget.toPlainText())
+
+        # Slider moved all the way up
+        self.assertEqual(self.form.txt_widget.verticalScrollBar().sliderPosition(), 0)
+
+    def testSaveDataAs(self):
+        """
+        Test the Save As context menu action
+        """
+        # get Data1D
+        p_file=["cyl_400_20.txt"]
+        # Read in the file
+        output, message = self.form.readData(p_file)
+        self.form.loadComplete((output, message))
+
+        # select the data
+        self.form.treeView.selectAll()
+
+        QFileDialog.getSaveFileName = MagicMock()
+
+        # Call the tested method
+        self.form.saveDataAs()
+        QFileDialog.getSaveFileName.assert_called_with(
+                                caption="Save As",
+                                directory='cyl_400_20_out.txt',
+                                filter='Text files (*.txt);;CanSAS 1D files(*.xml)',
+                                parent=None)
+        QFileDialog.getSaveFileName.assert_called_once()
+
+        # get Data2D
+        p_file=["Dec07031.ASC"]
+        # Read in the file
+        output, message = self.form.readData(p_file)
+        self.form.loadComplete((output, message))
+
+        # select the data
+        index = self.form.model.index(1, 0)
+        selmodel = self.form.treeView.selectionModel()
+        selmodel.setCurrentIndex(index, QItemSelectionModel.NoUpdate)
+        selmodel.select(index, QItemSelectionModel.Select|QItemSelectionModel.Rows)
+
+        QFileDialog.getSaveFileName = MagicMock()
+
+        # Call the tested method
+        self.form.saveDataAs()
+        QFileDialog.getSaveFileName.assert_called_with(
+                                caption="Save As",
+                                directory='Dec07031_out.dat',
+                                filter='IGOR/DAT 2D file in Q_map (*.dat)',
+                                parent=None)
+        QFileDialog.getSaveFileName.assert_called_once()
+
+    def testQuickDataPlot(self):
+        """
+        Quick data plot generation.
+        """
+        # get Data1D
+        p_file=["cyl_400_20.txt"]
+        # Read in the file
+        output, message = self.form.readData(p_file)
+        self.form.loadComplete((output, message))
+
+        # select the data
+        self.form.treeView.selectAll()
+
+        Plotter.show = MagicMock() # for masking the display
+
+        self.form.quickDataPlot()
+        self.assertTrue(Plotter.show.called)
+
+    def testQuickData3DPlot(self):
+        """
+        Slow(er) 3D data plot generation.
+        """
+        # get Data1D
+        p_file=["Dec07031.ASC"]
+        # Read in the file
+        output, message = self.form.readData(p_file)
+        self.form.loadComplete((output, message))
+
+        # select the data
+        self.form.treeView.selectAll()
+
+        Plotter2D.show = MagicMock() # for masking the display
+
+        self.form.quickData3DPlot()
+
+        self.assertTrue(Plotter2D.show.called)
+
+    def testShowEditMask(self):
+        """
+        Edit mask on a 2D plot.
+
+        TODO: add content once plotting finalized
+        """
+        pass
+
+
+if __name__ == "__main__":
+    unittest.main()
Index: src/sas/qtgui/MainWindow/UnitTesting/DroppableDataLoadWidgetTest.py
===================================================================
--- src/sas/qtgui/MainWindow/UnitTesting/DroppableDataLoadWidgetTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/MainWindow/UnitTesting/DroppableDataLoadWidgetTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
@@ -0,0 +1,75 @@
+import sys
+import unittest
+
+from PyQt4.QtGui import QApplication
+from PyQt4.QtTest import QTest
+from PyQt4 import QtCore
+
+# set up import paths
+import path_prepare
+
+from sas.qtgui.MainWindow.DroppableDataLoadWidget import DroppableDataLoadWidget
+from sas.qtgui.Utilities.GuiUtils import *
+from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy
+
+app = QApplication(sys.argv)
+
+class DroppableDataLoadWidgetTest(unittest.TestCase):
+    '''Test the DroppableDataLoadWidget GUI'''
+    def setUp(self):
+        '''Create the GUI'''
+        class dummy_manager(object):
+            def communicator(self):
+                return Communicate()
+            def perspective(self):
+                return MyPerspective()
+
+        self.form = DroppableDataLoadWidget(None, guimanager=dummy_manager())
+
+        # create dummy mime object
+        self.mime_data = QtCore.QMimeData()
+        self.testfile = 'testfile.txt'
+        self.mime_data.setUrls([QtCore.QUrl(self.testfile)])
+
+    def testDragIsOK(self):
+        """
+        Test the item being dragged over the load widget
+        """
+        good_drag_event = QtGui.QDragMoveEvent(QtCore.QPoint(0,0),
+                                               QtCore.Qt.CopyAction,
+                                               self.mime_data,
+                                               QtCore.Qt.LeftButton,
+                                               QtCore.Qt.NoModifier)
+        mime_data = QtCore.QMimeData()
+        bad_drag_event = QtGui.QDragMoveEvent(QtCore.QPoint(0,0),
+                                               QtCore.Qt.CopyAction,
+                                               mime_data,
+                                               QtCore.Qt.LeftButton,
+                                               QtCore.Qt.NoModifier)
+
+        # Call the drag handler with good event
+        self.assertTrue(self.form.dragIsOK(good_drag_event))
+
+        # Call the drag handler with bad event
+        self.assertFalse(self.form.dragIsOK(bad_drag_event))
+
+    def testDropEvent(self):
+        """
+        Test what happens if an object is dropped onto the load widget
+        """
+        spy_file_read = QtSignalSpy(self.form, self.form.communicator.fileReadSignal)
+
+        drop_event = QtGui.QDropEvent(QtCore.QPoint(0,0),
+                                           QtCore.Qt.CopyAction,
+                                           self.mime_data,
+                                           QtCore.Qt.LeftButton,
+                                           QtCore.Qt.NoModifier)
+
+        self.form.dropEvent(drop_event)
+        QtGui.qApp.processEvents()
+        self.assertEqual(spy_file_read.count(), 1)
+        self.assertIn(self.testfile, str(spy_file_read.signal(index=0)))
+
+
+if __name__ == "__main__":
+    unittest.main()
Index: src/sas/qtgui/MainWindow/UnitTesting/GuiManagerTest.py
===================================================================
--- src/sas/qtgui/MainWindow/UnitTesting/GuiManagerTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/MainWindow/UnitTesting/GuiManagerTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
@@ -0,0 +1,332 @@
+import sys
+import subprocess
+import unittest
+import webbrowser
+import logging
+
+from PyQt4.QtGui import *
+from PyQt4.QtTest import QTest
+from PyQt4.QtCore import *
+from PyQt4.QtWebKit import *
+from mock import MagicMock
+
+# set up import paths
+import path_prepare
+
+# Local
+from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow
+from sas.qtgui.MainWindow.AboutBox import AboutBox
+from sas.qtgui.MainWindow.WelcomePanel import WelcomePanel
+from sas.qtgui.Utilities.IPythonWidget import IPythonWidget
+
+from sas.qtgui.MainWindow.GuiManager import Acknowledgements, GuiManager
+from sas.qtgui.MainWindow.MainWindow import MainSasViewWindow
+from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy
+
+app = QApplication(sys.argv)
+
+class GuiManagerTest(unittest.TestCase):
+    '''Test the Main Window functionality'''
+    def setUp(self):
+        '''Create the tested object'''
+        class MainWindow(MainSasViewWindow):
+            # Main window of the application
+            def __init__(self, reactor, parent=None):
+                super(MainWindow, self).__init__(parent)
+        
+                # define workspace for dialogs.
+                self.workspace = QWorkspace(self)
+                self.setCentralWidget(self.workspace)
+
+        self.manager = GuiManager(MainWindow(None))
+
+    def tearDown(self):
+        '''Destroy the GUI'''
+        self.manager = None
+
+    def testDefaults(self):
+        """
+        Test the object in its default state
+        """
+        self.assertIsInstance(self.manager.filesWidget, DataExplorerWindow)
+        self.assertIsInstance(self.manager.dockedFilesWidget, QDockWidget)
+        self.assertIsInstance(self.manager.dockedFilesWidget.widget(), DataExplorerWindow)
+        self.assertEqual(self.manager.dockedFilesWidget.features(), QDockWidget.NoDockWidgetFeatures)
+        self.assertEqual(self.manager._workspace.dockWidgetArea(self.manager.dockedFilesWidget), Qt.LeftDockWidgetArea)
+
+        self.assertIsInstance(self.manager.logDockWidget, QDockWidget)
+        self.assertIsInstance(self.manager.logDockWidget.widget(), QTextBrowser)
+        self.assertEqual(self.manager._workspace.dockWidgetArea(self.manager.logDockWidget), Qt.BottomDockWidgetArea)
+
+        self.assertIsInstance(self.manager.ackWidget, Acknowledgements)
+        self.assertIsInstance(self.manager.aboutWidget, AboutBox)
+        self.assertIsInstance(self.manager.welcomePanel, WelcomePanel)
+
+    def skip_testLogging(self):
+        """
+        Test logging of stdout, stderr and log messages
+        """
+        # See if the log window is empty
+        self.assertEqual(self.manager.logDockWidget.widget().toPlainText(), "")
+
+        # Now, send some message to stdout.
+        # We are in the MainWindow scope, so simple 'print' will work
+        message = "from stdout"
+        print message
+        self.assertIn(message, self.manager.logDockWidget.widget().toPlainText())
+
+        # Send some message to stderr
+        message = "from stderr"
+        sys.stderr.write(message)
+        self.assertIn(message, self.manager.logDockWidget.widget().toPlainText())
+
+        # And finally, send a log message
+        import logging
+        message = "from logging"
+        message_logged = "ERROR: " + message
+        logging.error(message)
+        self.assertIn(message_logged, self.manager.logDockWidget.widget().toPlainText())
+
+    def testConsole(self):
+        """
+        Test the docked QtConsole
+        """
+        # Invoke the console action
+        self.manager.actionPython_Shell_Editor()
+
+        # Test the widegt properties
+        self.assertIsInstance(self.manager.ipDockWidget, QDockWidget)
+        self.assertIsInstance(self.manager.ipDockWidget.widget(), IPythonWidget)
+        self.assertEqual(self.manager._workspace.dockWidgetArea(self.manager.ipDockWidget), Qt.RightDockWidgetArea)
+
+    def testUpdatePerspective(self):
+        """
+        """
+        pass
+
+    def testUpdateStatusBar(self):
+        """
+        """
+        pass
+
+    def testSetData(self):
+        """
+        """
+        pass
+
+    def testQuitApplication(self):
+        """
+        Test that the custom exit method is called on shutdown
+        """
+        self.manager._workspace.show()
+
+        # Must mask sys.exit, otherwise the whole testing process stops.
+        sys.exit = MagicMock()
+
+        # Say No to the close dialog
+        QMessageBox.question = MagicMock(return_value=QMessageBox.No)
+
+        # Open, then close the manager
+        self.manager.quitApplication()
+
+        # See that the MessageBox method got called
+        #self.assertTrue(QMessageBox.question.called)
+
+        # Say Yes to the close dialog
+        QMessageBox.question = MagicMock(return_value=QMessageBox.Yes)
+
+        # Open, then close the manager
+        self.manager.quitApplication()
+
+        # See that the MessageBox method got called
+        #self.assertTrue(QMessageBox.question.called)
+
+    def testCheckUpdate(self):
+        """
+        Tests the SasView website version polling
+        """
+        self.manager.processVersion = MagicMock()
+        version = {'update_url'  : 'http://www.sasview.org/sasview.latestversion', 
+                   'version'     : '4.1.0',
+                   'download_url': 'https://github.com/SasView/sasview/releases'}
+        self.manager.checkUpdate()
+
+        self.manager.processVersion.assert_called_with(version)
+
+        pass
+
+    def testProcessVersion(self):
+        """
+        Tests the version checker logic
+        """
+        # 1. version = 0.0.0
+        version_info = {u'version' : u'0.0.0'}
+        spy_status_update = QtSignalSpy(self.manager, self.manager.communicate.statusBarUpdateSignal)
+
+        self.manager.processVersion(version_info)
+
+        self.assertEqual(spy_status_update.count(), 1)
+        message = 'Could not connect to the application server. Please try again later.'
+        self.assertIn(message, str(spy_status_update.signal(index=0)))
+
+        # 2. version < LocalConfig.__version__
+        version_info = {u'version' : u'0.0.1'}
+        spy_status_update = QtSignalSpy(self.manager, self.manager.communicate.statusBarUpdateSignal)
+
+        self.manager.processVersion(version_info)
+
+        self.assertEqual(spy_status_update.count(), 1)
+        message = 'You have the latest version of SasView'
+        self.assertIn(message, str(spy_status_update.signal(index=0)))
+
+        # 3. version > LocalConfig.__version__
+        version_info = {u'version' : u'999.0.0'}
+        spy_status_update = QtSignalSpy(self.manager, self.manager.communicate.statusBarUpdateSignal)
+        webbrowser.open = MagicMock()
+
+        self.manager.processVersion(version_info)
+
+        self.assertEqual(spy_status_update.count(), 1)
+        message = 'Version 999.0.0 is available!'
+        self.assertIn(message, str(spy_status_update.signal(index=0)))
+
+        webbrowser.open.assert_called_with("https://github.com/SasView/sasview/releases")
+
+        # 4. couldn't load version
+        version_info = {}
+        logging.error = MagicMock()
+        spy_status_update = QtSignalSpy(self.manager, self.manager.communicate.statusBarUpdateSignal)
+
+        self.manager.processVersion(version_info)
+
+        # Retrieve and compare arguments of the mocked call
+        message = "guiframe: could not get latest application version number"
+        args, _ = logging.error.call_args
+        self.assertIn(message, args[0])
+
+        # Check the signal message
+        message = 'Could not connect to the application server.'
+        self.assertIn(message, str(spy_status_update.signal(index=0)))
+
+    def testActions(self):
+        """
+        """
+        pass
+
+    #### FILE ####
+    def testActionLoadData(self):
+        """
+        Menu File/Load Data File(s)
+        """
+        # Mock the system file open method
+        QFileDialog.getOpenFileNames = MagicMock(return_value=None)
+
+        # invoke the action
+        self.manager.actionLoadData()
+
+        # Test the getOpenFileName() dialog called once
+        self.assertTrue(QFileDialog.getOpenFileNames.called)
+
+    def testActionLoadDataFolder(self):
+        """
+        Menu File/Load Data Folder
+        """
+        # Mock the system file open method
+        QFileDialog.getExistingDirectory = MagicMock(return_value=None)
+
+        # invoke the action
+        self.manager.actionLoad_Data_Folder()
+
+        # Test the getOpenFileName() dialog called once
+        self.assertTrue(QFileDialog.getExistingDirectory.called)
+
+    #### VIEW ####
+    def testActionHideToolbar(self):
+        """
+        Menu View/Hide Toolbar
+        """
+        # Need to display the main window to initialize the toolbar.
+        self.manager._workspace.show()
+
+        # Check the initial state
+        self.assertTrue(self.manager._workspace.toolBar.isVisible())
+        self.assertEqual('Hide Toolbar', self.manager._workspace.actionHide_Toolbar.text())
+
+        # Invoke action
+        self.manager.actionHide_Toolbar()
+
+        # Assure changes propagated correctly
+        self.assertFalse(self.manager._workspace.toolBar.isVisible())
+        self.assertEqual('Show Toolbar', self.manager._workspace.actionHide_Toolbar.text())
+
+        # Revert
+        self.manager.actionHide_Toolbar()
+
+        # Assure the original values are back
+        self.assertTrue(self.manager._workspace.toolBar.isVisible())
+        self.assertEqual('Hide Toolbar', self.manager._workspace.actionHide_Toolbar.text())
+
+
+    #### HELP ####
+    def testActionDocumentation(self):
+        """
+        Menu Help/Documentation
+        """
+        #Mock the QWebView method
+        QWebView.show = MagicMock()
+
+        # Assure the filename is correct
+        self.assertIn("index.html", self.manager._helpLocation)
+
+        # Invoke the action
+        self.manager.actionDocumentation()
+
+        # Check if show() got called
+        self.assertTrue(QWebView.show.called)
+
+    def skip_testActionTutorial(self):
+        """
+        Menu Help/Tutorial
+        """
+        # Mock subprocess.Popen
+        subprocess.Popen = MagicMock()
+
+        tested_location = self.manager._tutorialLocation
+
+        # Assure the filename is correct
+        self.assertIn("Tutorial.pdf", tested_location)
+
+        # Invoke the action
+        self.manager.actionTutorial()
+
+        # Check if popen() got called
+        self.assertTrue(subprocess.Popen.called)
+
+        #Check the popen() call arguments
+        subprocess.Popen.assert_called_with([tested_location], shell=True)
+
+    def testActionAcknowledge(self):
+        """
+        Menu Help/Acknowledge
+        """
+        self.manager.actionAcknowledge()
+
+        # Check if the window is actually opened.
+        self.assertTrue(self.manager.ackWidget.isVisible())
+        self.assertIn("developers@sasview.org", self.manager.ackWidget.label.text())
+
+    def testActionCheck_for_update(self):
+        """
+        Menu Help/Check for update
+        """
+        # Just make sure checkUpdate is called.
+        self.manager.checkUpdate = MagicMock()
+
+        self.manager.actionCheck_for_update()
+
+        self.assertTrue(self.manager.checkUpdate.called)
+             
+       
+if __name__ == "__main__":
+    unittest.main()
+
Index: src/sas/qtgui/MainWindow/UnitTesting/MainWindowTest.py
===================================================================
--- src/sas/qtgui/MainWindow/UnitTesting/MainWindowTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/MainWindow/UnitTesting/MainWindowTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
@@ -0,0 +1,55 @@
+import sys
+import unittest
+
+from PyQt4 import QtGui
+from PyQt4 import QtTest
+from PyQt4 import QtCore
+from mock import MagicMock
+
+# set up import paths
+import path_prepare
+
+# Local
+from sas.qtgui.MainWindow.MainWindow import MainSasViewWindow
+from sas.qtgui.MainWindow.MainWindow import SplashScreen
+
+app = QtGui.QApplication(sys.argv)
+
+class MainWindowTest(unittest.TestCase):
+    """Test the Main Window GUI"""
+    def setUp(self):
+        """Create the GUI"""
+
+        self.widget = MainSasViewWindow(None)
+
+    def tearDown(self):
+        """Destroy the GUI"""
+        self.widget = None
+
+    def testDefaults(self):
+        """Test the GUI in its default state"""
+        self.assertIsInstance(self.widget, QtGui.QMainWindow)
+        self.assertIsInstance(self.widget.centralWidget(), QtGui.QWorkspace)
+        
+    def testSplashScreen(self):
+        """ Test the splash screen """
+        splash = SplashScreen()
+        self.assertIsInstance(splash, QtGui.QSplashScreen)
+
+    def testExit(self):
+        """
+        Test that the custom exit method is called on shutdown
+        """
+        # Must mask sys.exit, otherwise the whole testing process stops.
+        sys.exit = MagicMock()
+        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.Yes)
+
+        # Open, then close the main window
+        tmp_main = MainSasViewWindow(None)
+        tmp_main.close()
+
+        # See that the MessageBox method got called
+        self.assertTrue(QtGui.QMessageBox.question.called)
+       
+if __name__ == "__main__":
+    unittest.main()
Index: src/sas/qtgui/MainWindow/UnitTesting/WelcomePanelTest.py
===================================================================
--- src/sas/qtgui/MainWindow/UnitTesting/WelcomePanelTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/MainWindow/UnitTesting/WelcomePanelTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
@@ -0,0 +1,41 @@
+import sys
+import unittest
+
+from PyQt4 import QtGui
+
+# set up import paths
+import path_prepare
+
+# Local
+from sas.qtgui.MainWindow.WelcomePanel import WelcomePanel
+
+app = QtGui.QApplication(sys.argv)
+
+class WelcomePanelTest(unittest.TestCase):
+    '''Test the WelcomePanel'''
+    def setUp(self):
+        '''Create the WelcomePanel'''
+
+        self.widget = WelcomePanel(None)
+
+    def tearDown(self):
+        '''Destroy the GUI'''
+        self.widget.close()
+        self.widget = None
+
+    def testDefaults(self):
+        '''Test the GUI in its default state'''
+        self.assertIsInstance(self.widget, QtGui.QDialog)
+        self.assertEqual(self.widget.windowTitle(), "Welcome")
+        
+    def testVersion(self):
+        '''Test the version string'''
+        version = self.widget.lblVersion
+        self.assertIsInstance(version, QtGui.QLabel)
+
+        self.assertIn("SasView", version.text())
+        self.assertIn("Build:", version.text())
+        self.assertIn("UTK, UMD, NIST, ORNL, ISIS, ESS, ILL and ANSTO", version.text())
+       
+if __name__ == "__main__":
+    unittest.main()
