Index: src/sas/qtgui/Utilities/UnitTesting/GridPanelTest.py
===================================================================
--- src/sas/qtgui/Utilities/UnitTesting/GridPanelTest.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
+++ src/sas/qtgui/Utilities/UnitTesting/GridPanelTest.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
@@ -0,0 +1,150 @@
+import sys
+import numpy as np
+import unittest
+from unittest.mock import mock_open, patch
+import webbrowser
+import logging
+
+from unittest.mock import MagicMock
+
+from PyQt5 import QtGui, QtWidgets
+
+# set up import paths
+import path_prepare
+
+from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy
+
+from sas.sascalc.fit.AbstractFitEngine import FResult
+from sasmodels.sasview_model import load_standard_models
+from sas.qtgui.Plotting.PlotterData import Data1D
+
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+# Local
+from sas.qtgui.Utilities.GridPanel import BatchOutputPanel
+
+if not QtWidgets.QApplication.instance():
+    app = QtWidgets.QApplication(sys.argv)
+
+class BatchOutputPanelTest(unittest.TestCase):
+    '''Test the batch output dialog'''
+    def setUp(self):
+        '''Create the dialog'''
+        # dummy perspective
+        class dummy_manager(object):
+            def communicator(self):
+                return GuiUtils.Communicate()
+            def communicate(self):
+                return GuiUtils.Communicate()
+        self.widget = BatchOutputPanel(parent=dummy_manager(), output_data=self.output_for_test())
+        test_table = {"p1":[1,2,3],
+                      "p2":[4,5,None],
+                      "":["a",credits,],
+                      "":[]}
+
+    def tearDown(self):
+        '''Destroy the GUI'''
+        self.widget.close()
+        self.widget = None
+
+    def output_for_test(self):
+        ''' define DATA1D structure for testing'''
+        # dummy parameter set
+        m = None
+        for m in load_standard_models():
+            if m.name == "core_shell_ellipsoid":
+                model = m()
+        self.assertIsNotNone(m)
+        data = Data1D(x=[1,2], y=[3,4], dx=[0.1, 0.1], dy=[0.,0.])
+        param_list = ['sld_shell', 'sld_solvent']
+        output = FResult(model=model, data=data, param_list=param_list)
+        output.theory = np.array([0.1,0.2])
+        output.pvec = np.array([0.1, 0.02])
+        output.residuals = np.array([0.01, 0.02])
+        output.fitness = 9000.0
+        output.fitter_id = 200
+        output_data = [[output],[output]]
+        return output_data
+
+    def testDefaults(self):
+        '''Test the GUI in its default state'''
+        self.assertIsInstance(self.widget, QtWidgets.QMainWindow)
+        # Default title
+        self.assertEqual(self.widget.windowTitle(), "Grid Panel")
+
+        # non-modal window
+        self.assertFalse(self.widget.isModal())
+
+    def testActionLoadData(self):
+        '''Test CSV data loader'''
+        test_csv = mock_open(read_data = ("""\
+File generated by SasView\n
+Chi2,Data,scale,background,radius_equat_core,x_core,thick_shell,x_polar_shell,sld_core,sld_shell,sld_solvent,theta,phi,\n
+1917.8,cyl_400_40.txt,1,0.001,20,3,30,1,-85.088,-97.636,-92.797,0,0,\n
+2.6169,cyl_400_20.txt,1,0.001,20,3,30,1,914.64,906.09,905.67,0,0,\n
+"""))
+        logging.info = MagicMock()
+        self.widget.setupTableFromCSV = MagicMock()
+
+        # No filename given
+        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=[""])
+        self.widget.actionLoadData()
+        # Assure parser wasn't called and logging got a message
+        self.assertTrue(logging.info.called_once())
+        self.assertIn("No data", logging.info.call_args[0][0])
+
+        # Filename given
+        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value="test")
+        with patch('builtins.open', test_csv):
+            self.widget.actionLoadData()
+            # Assure parser was called
+            self.assertTrue(self.widget.actionLoadData)
+            self.widget.setupTableFromCSV.assert_called_once()
+            self.assertIn("File generated by SasView", self.widget.setupTableFromCSV.call_args[0][0][0])
+
+    def notestPlotFits(self):
+        '''Test plot generation from selected table rows'''
+        # mock tested calls
+        #GuiUtils.Communicate.plot
+        spy_plot_signal = QtSignalSpy(self.widget.communicate, self.widget.communicate().plotFromFilenameSignal)
+        # Select row #1
+        self.widget.tblParams.selectRow(0)
+        QtWidgets.QApplication.processEvents()
+
+        # See that the signal was emitted
+        self.assertEqual(spy_plot_signal.count(), 1)
+        self.assertIn("ddd", str(spy_plot_signal.called()[0]['args'][0]))
+
+
+    def testDataFromTable(self):
+        '''Test dictionary generation from data'''
+        params = self.widget.dataFromTable(self.widget.tblParams)
+        self.assertEqual(len(params), 13)
+        self.assertEqual(params['Chi2'][0], '9000')
+        self.assertEqual(params['Data'][1], '')
+        self.assertEqual(params['sld_solvent'][1], '0.02')
+
+    def testActionSendToExcel(self):
+        '''Test Excel bindings'''
+        pass
+
+    def testActionSaveFile(self):
+        '''Test file save'''
+        self.widget.writeBatchToFile = MagicMock()
+
+        # user cancels dialog
+        QtWidgets.QFileDialog.getSaveFileName = MagicMock(return_value=("","BOOP"))
+        # write not called
+        self.widget.actionSaveFile()
+        self.widget.writeBatchToFile.assert_not_called()
+
+        # user chooses proper name
+        QtWidgets.QFileDialog.getSaveFileName = MagicMock(return_value=("plop","BOOP"))
+        # write called
+        self.widget.actionSaveFile()
+        self.widget.writeBatchToFile.assert_called_once()
+
+    def testSetupTableFromCSV(self):
+        '''Test generation of grid table rows from a CSV file'''
+        pass
+
+
Index: src/sas/qtgui/Utilities/UnitTesting/ModelEditorTest.py
===================================================================
--- src/sas/qtgui/Utilities/UnitTesting/ModelEditorTest.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
+++ src/sas/qtgui/Utilities/UnitTesting/ModelEditorTest.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
@@ -0,0 +1,51 @@
+import sys
+import unittest
+import logging
+
+from PyQt5.QtGui import *
+from PyQt5.QtCore import *
+from PyQt5.QtWidgets import *
+
+# set up import paths
+import sas.qtgui.path_prepare
+
+from UnitTesting.TestUtils import QtSignalSpy
+
+# Local
+from sas.qtgui.Utilities.ModelEditor import ModelEditor
+from sas.qtgui.Utilities.PythonSyntax import PythonHighlighter
+
+if not QApplication.instance():
+    app = QApplication(sys.argv)
+
+class ModelEditorTest(unittest.TestCase):
+    def setUp(self):
+        """
+        Prepare the editor
+        """
+        self.widget = ModelEditor(None)
+
+    def tearDown(self):
+        """Destroy the DataOperationUtility"""
+        self.widget.close()
+        self.widget = None
+
+    def testDefaults(self):
+        """Test the GUI in its default state"""
+        self.assertIsInstance(self.widget.highlight, PythonHighlighter)
+
+    def testEdit(self):
+        """Test that a signal is emitted on edit"""
+        spy_signal = QtSignalSpy(self.widget, self.widget.modelModified)
+
+        # Edit text
+        text = "test"
+        self.widget.txtEditor.setPlainText(text)
+
+        # Assure the signal got emitted
+        self.assertEqual(spy_signal.count(), 1)
+
+        # Assure getModel() returns right content
+        self.assertTrue(text, self.widget.txtEditor.toPlainText())
+
+
Index: src/sas/qtgui/Utilities/UnitTesting/PluginDefinitionTest.py
===================================================================
--- src/sas/qtgui/Utilities/UnitTesting/PluginDefinitionTest.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
+++ src/sas/qtgui/Utilities/UnitTesting/PluginDefinitionTest.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
@@ -0,0 +1,175 @@
+import sys
+import unittest
+import logging
+
+from PyQt5.QtGui import *
+from PyQt5.QtCore import *
+from PyQt5.QtWidgets import *
+
+# set up import paths
+import sas.qtgui.path_prepare
+
+from UnitTesting.TestUtils import QtSignalSpy
+
+# Local
+from sas.qtgui.Utilities.PluginDefinition import PluginDefinition
+from sas.qtgui.Utilities.PythonSyntax import PythonHighlighter
+
+#if not QApplication.instance():
+#    app = QApplication(sys.argv)
+app = QApplication(sys.argv)
+
+class PluginDefinitionTest(unittest.TestCase):
+    def setUp(self):
+        """
+        Prepare the editor
+        """
+        self.widget = PluginDefinition(None)
+
+    def tearDown(self):
+        """Destroy the DataOperationUtility"""
+        self.widget.close()
+        self.widget = None
+
+    def testDefaults(self):
+        """Test the GUI in its default state"""
+        self.assertIsInstance(self.widget.highlight, PythonHighlighter)
+        self.assertIsInstance(self.widget.parameter_dict, dict)
+        self.assertIsInstance(self.widget.pd_parameter_dict, dict)
+
+        self.assertEqual(len(self.widget.model), 6)
+        self.assertIn("filename", self.widget.model.keys())
+        self.assertIn("overwrite", self.widget.model.keys())
+        self.assertIn("description", self.widget.model.keys())
+        self.assertIn("parameters", self.widget.model.keys())
+        self.assertIn("pd_parameters", self.widget.model.keys())
+        self.assertIn("text", self.widget.model.keys())
+
+    def testOnOverwrite(self):
+        """See what happens when the overwrite checkbox is selected"""
+        spy_signal = QtSignalSpy(self.widget, self.widget.modelModified)
+
+        # check the default
+        self.assertFalse(self.widget.chkOverwrite.isChecked())
+
+        # Change the state
+        self.widget.chkOverwrite.setChecked(True)
+
+        # Check the signal
+        self.assertEqual(spy_signal.count(), 1)
+
+        # model dict updated
+        self.assertTrue(self.widget.model['overwrite'])
+
+    def testOnPluginNameChanged(self):
+        """See what happens when the name of the plugin changes"""
+        spy_signal = QtSignalSpy(self.widget, self.widget.modelModified)
+
+        # Change the name
+        new_text = "Duck season"
+        self.widget.txtName.setText(new_text)
+        self.widget.txtName.editingFinished.emit()
+
+        # Check the signal
+        self.assertEqual(spy_signal.count(), 1)
+
+        # model dict updated
+        self.assertTrue(self.widget.model['filename'] == new_text)
+
+    def testOnDescriptionChanged(self):
+        """See what happens when the description of the plugin changes"""
+        spy_signal = QtSignalSpy(self.widget, self.widget.modelModified)
+
+        # Change the name
+        new_text = "Rabbit season"
+        self.widget.txtDescription.setText(new_text)
+        self.widget.txtDescription.editingFinished.emit()
+
+        # Check the signal
+        self.assertEqual(spy_signal.count(), 1)
+
+        # model dict updated
+        self.assertTrue(self.widget.model['description'] == new_text)
+
+    def testOnParamsChanged(self):
+        """See what happens when parameters change"""
+        spy_signal = QtSignalSpy(self.widget, self.widget.modelModified)
+
+        # number of rows. default=1
+        self.assertEqual(self.widget.tblParams.rowCount(), 1)
+
+        # number of columns. default=2
+        self.assertEqual(self.widget.tblParams.columnCount(), 2)
+
+        # Change the param
+        new_param = "shoot"
+        self.widget.tblParams.setItem(0,0,QTableWidgetItem(new_param))
+        #self.widget.tblParams.editingFinished.emit()
+
+
+        # Check the signal
+        self.assertEqual(spy_signal.count(), 1)
+
+        # model dict updated
+        self.assertEqual(self.widget.model['parameters'], {0: (new_param, None)})
+
+        # Change the value
+        new_value = "BOOM"
+        self.widget.tblParams.setItem(0,1,QTableWidgetItem(new_value))
+
+        # Check the signal
+        self.assertEqual(spy_signal.count(), 2)
+
+        # model dict updated
+        self.assertEqual(self.widget.model['parameters'], {0: (new_param, new_value)})
+
+        # See that the number of rows increased
+        self.assertEqual(self.widget.tblParams.rowCount(), 2)
+
+    def testOnPDParamsChanged(self):
+        """See what happens when pd parameters change"""
+        spy_signal = QtSignalSpy(self.widget, self.widget.modelModified)
+
+        # number of rows. default=1
+        self.assertEqual(self.widget.tblParamsPD.rowCount(), 1)
+
+        # number of columns. default=2
+        self.assertEqual(self.widget.tblParamsPD.columnCount(), 2)
+
+        # Change the param
+        new_param = "shoot"
+        self.widget.tblParamsPD.setItem(0,0,QTableWidgetItem(new_param))
+
+        # Check the signal
+        self.assertEqual(spy_signal.count(), 1)
+
+        # model dict updated
+        self.assertEqual(self.widget.model['pd_parameters'], {0: (new_param, None)})
+
+        # Change the value
+        new_value = "BOOM"
+        self.widget.tblParamsPD.setItem(0,1,QTableWidgetItem(new_value))
+
+        # Check the signal
+        self.assertEqual(spy_signal.count(), 2)
+
+        # model dict updated
+        self.assertTrue(self.widget.model['pd_parameters'] == {0: (new_param, new_value)})
+
+        # See that the number of rows increased
+        self.assertEqual(self.widget.tblParamsPD.rowCount(), 2)
+
+    def testOnFunctionChanged(self):
+        """See what happens when the model text changes"""
+        spy_signal = QtSignalSpy(self.widget, self.widget.modelModified)
+
+        # Change the text
+        new_model = "1\n2\n"
+        self.widget.txtFunction.setPlainText(new_model)
+
+        # Check the signal
+        self.assertEqual(spy_signal.count(), 1)
+
+        # model dict updated
+        self.assertEqual(self.widget.model['text'], new_model.rstrip())
+
Index: src/sas/qtgui/Utilities/UnitTesting/TabbedModelEditorTest.py
===================================================================
--- src/sas/qtgui/Utilities/UnitTesting/TabbedModelEditorTest.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
+++ src/sas/qtgui/Utilities/UnitTesting/TabbedModelEditorTest.py	(revision 3b3b40bf9d5056d902b19728c621dd2a4661d65f)
@@ -0,0 +1,299 @@
+import os
+import sys
+import unittest
+import logging
+
+from unittest.mock import MagicMock
+
+from PyQt5.QtGui import *
+from PyQt5.QtCore import *
+from PyQt5.QtWidgets import *
+
+# set up import paths
+import sas.qtgui.path_prepare
+
+from UnitTesting.TestUtils import QtSignalSpy
+
+# Local
+from sas.qtgui.Utilities.TabbedModelEditor import TabbedModelEditor
+from sas.qtgui.Utilities.PluginDefinition import PluginDefinition
+from sas.qtgui.Utilities.ModelEditor import ModelEditor
+
+
+#if not QApplication.instance():
+#    app = QApplication(sys.argv)
+app = QApplication(sys.argv)
+
+class TabbedModelEditorTest(unittest.TestCase):
+    def setUp(self):
+        """
+        Prepare the editors
+        """
+        self.widget = TabbedModelEditor(None)
+        self.widget_edit = TabbedModelEditor(None, edit_only=True)
+
+    def tearDown(self):
+        """Destroy the DataOperationUtility"""
+        QMessageBox.exec = MagicMock(return_value=QMessageBox.Discard)
+        self.widget.close()
+        self.widget = None
+        self.widget_edit.close()
+        self.widget_edit = None
+
+    def testDefaults(self):
+        """Test the GUI in its default state"""
+        self.assertEqual(self.widget.filename, "")
+        self.assertEqual(self.widget.window_title, "Model Editor")
+        self.assertFalse(self.widget.is_modified)
+        self.assertFalse(self.widget.edit_only)
+        self.assertTrue(self.widget_edit.edit_only)
+
+        # Add model
+        #self.assertFalse(self.widget.cmdLoad.isVisible())
+        #self.assertTrue(self.widget_edit.cmdLoad.isVisible())
+        self.assertIsInstance(self.widget.plugin_widget, PluginDefinition)
+        self.assertIsInstance(self.widget.editor_widget, ModelEditor)
+        # tabs
+        self.assertEqual(self.widget.tabWidget.count(), 2)
+        self.assertFalse(self.widget.editor_widget.isEnabled())
+        self.assertEqual(self.widget_edit.tabWidget.count(), 1)
+        self.assertFalse(self.widget_edit.editor_widget.isEnabled())
+
+        #buttons
+        self.assertFalse(self.widget.buttonBox.button(QDialogButtonBox.Apply).isEnabled())
+        self.assertEqual(self.widget.buttonBox.button(QDialogButtonBox.Apply).text(), "Apply")
+        self.assertEqual(self.widget_edit.buttonBox.button(QDialogButtonBox.Apply).text(), "Save")
+
+    def testSetPluginActive(self):
+        """ Enables the plugin editor """
+        # by default it is on
+        self.assertTrue(self.widget.plugin_widget.isEnabled())
+        # Let's disable it
+        self.widget.setPluginActive(False)
+        self.assertFalse(self.widget.plugin_widget.isEnabled())
+        # and back to enabled
+        self.widget.setPluginActive(True)
+        self.assertTrue(self.widget.plugin_widget.isEnabled())
+
+    def notestCloseEvent(self):
+        """Test the close event wrt. saving info"""
+        event = QObject()
+        event.accept = MagicMock()
+
+        # 1. no changes to document - straightforward exit
+        self.widget.is_modified = False
+        self.widget.closeEvent(event)
+        self.assertTrue(event.accept.called_once())
+
+        # 2. document changed, cancelled
+        self.widget.is_modified = True
+        QMessageBox.exec = MagicMock(return_value=QMessageBox.Cancel)
+        self.widget.closeEvent(event)
+        self.assertTrue(QMessageBox.exec.called_once())
+        # no additional calls to event accept
+        self.assertTrue(event.accept.called_once())
+
+        # 3. document changed, save
+        QMessageBox.exec = MagicMock(return_value=QMessageBox.Save)
+        self.widget.filename = "random string #8"
+        self.widget.updateFromEditor = MagicMock()
+        self.widget.closeEvent(event)
+        self.assertTrue(QMessageBox.exec.called_once())
+        # no additional calls to event accept
+        self.assertTrue(event.accept.called_once())
+        self.assertTrue(self.widget.updateFromEditor.called_once())
+
+    def testOnApply(self):
+        """Test the Apply/Save event"""
+        # name the plugin
+        self.widget.plugin_widget.txtName.setText("Uncharacteristically eloquent filename")
+        self.widget.plugin_widget.txtName.editingFinished.emit()
+
+        # make sure the flag is set correctly
+        self.assertTrue(self.widget.is_modified)
+
+        # default tab
+        self.widget.updateFromPlugin = MagicMock()
+        self.widget.onApply()
+        self.assertTrue(self.widget.updateFromPlugin.called_once())
+
+        # switch tabs
+        self.widget.tabWidget.setCurrentIndex(1)
+        self.widget.updateFromEditor = MagicMock()
+        self.widget.onApply()
+        self.assertTrue(self.widget.updateFromEditor.called_once())
+
+    def testEditorModelModified(self):
+         """Test reaction to direct edit in the editor """
+         # Switch to the Editor tab
+         self.widget.tabWidget.setCurrentIndex(1)
+         self.assertFalse(self.widget.is_modified)
+
+         # add some text. This invokes tested method
+         self.widget.editor_widget.txtEditor.setPlainText("Plain Text")
+
+         # assure relevant functionality is invoked
+         self.assertTrue(self.widget.buttonBox.button(QDialogButtonBox.Apply).isEnabled())
+         self.assertTrue(self.widget.is_modified)
+         self.assertIn("*", self.widget.windowTitle())
+
+
+    def testPluginModelModified(self):
+        """Test reaction to direct edit in plugin wizard"""
+        self.assertFalse(self.widget.is_modified)
+
+        # Call the tested method with no filename defined
+        self.widget.pluginModelModified()
+
+        # Assure the apply button is disabled
+        self.assertFalse(self.widget.buttonBox.button(QDialogButtonBox.Apply).isEnabled())
+
+        # Modify plugin name
+        new_name = "NAME"
+        self.widget.plugin_widget.txtName.setText(new_name)
+        self.widget.plugin_widget.txtName.editingFinished.emit()
+
+        # Assure relevant functionality is invoked
+        self.assertIn("*", self.widget.windowTitle())
+        self.assertIn(new_name, self.widget.windowTitle())
+        self.assertTrue(self.widget.editor_widget.isEnabled())
+        self.assertTrue(self.widget.buttonBox.button(QDialogButtonBox.Apply).isEnabled())
+        self.assertTrue(self.widget.is_modified)
+
+    def testSetTabEdited(self):
+        """
+        Test the simple string update method
+        """
+        title = "title"
+        title_star = "title*"
+        self.widget.setWindowTitle(title)
+
+        # 1. -> edited
+        self.widget.setTabEdited(True)
+        self.assertIn("*", self.widget.windowTitle())
+        # make sure we don't get another star
+        self.widget.setWindowTitle(title_star)
+        self.widget.setTabEdited(True)
+        self.assertEqual(title_star, self.widget.windowTitle())
+
+        # 2. -> not edited
+        self.widget.setWindowTitle(title_star)
+        self.widget.setTabEdited(False)
+        self.assertEqual(title, self.widget.windowTitle())
+        # No changes when no star in title
+        self.widget.setWindowTitle(title)
+        self.widget.setTabEdited(False)
+        self.assertEqual(title, self.widget.windowTitle())
+
+    def testUpdateFromEditor(self):
+        """
+        Test the behaviour on editor window being updated
+        """
+        # Assure the test rises when no filename present
+        self.widget.filename = ""
+        with self.assertRaises(Exception):
+            self.widget.updateFromEditor()
+
+        # change the filename
+        self.widget.filename="testfile.py"
+        self.widget.writeFile = MagicMock()
+        boring_text = "so bored with unit tests"
+        self.widget.editor_widget.txtEditor.toPlainText = MagicMock(return_value=boring_text)
+        self.widget.writeFile = MagicMock()
+        #invoke the method
+        self.widget.updateFromEditor()
+
+        # Test the behaviour
+        self.assertTrue(self.widget.writeFile.called_once())
+        self.assertTrue(self.widget.writeFile.called_with('testfile.py', boring_text))
+
+    def testCanWriteModel(self):
+        """
+        Test if the model can be written to a file, given initial conditions
+        """
+        test_model = {'overwrite':False,
+                      'text':"return"}
+        test_path = "test.py"
+
+        with self.assertRaises(Exception):
+            self.widget.canWriteModel()
+
+        with self.assertRaises(Exception):
+            self.widget.canWriteModel(model=test_model)
+
+        with self.assertRaises(Exception):
+            self.widget.canWriteModel(full_path=test_path)
+
+        # 1. Overwrite box unchecked, file exists
+        os.path.isfile = MagicMock(return_value=True)
+        QMessageBox.critical = MagicMock()
+
+        ret = self.widget.canWriteModel(model=test_model, full_path=test_path)
+        self.assertFalse(ret)
+        self.assertTrue(QMessageBox.critical.called_once())
+        self.assertIn('Plugin Error', QMessageBox.critical.call_args[0][1])
+        self.assertIn('Plugin with specified name already exists', QMessageBox.critical.call_args[0][2])
+
+        # 2. Overwrite box checked, file exists, empty model
+        os.path.isfile = MagicMock(return_value=True)
+        test_model['overwrite']=True
+        test_model['text'] = ""
+        QMessageBox.critical = MagicMock()
+
+        ret = self.widget.canWriteModel(model=test_model, full_path=test_path)
+        self.assertFalse(ret)
+        self.assertTrue(QMessageBox.critical.called_once())
+        self.assertIn('Plugin Error', QMessageBox.critical.call_args[0][1])
+        self.assertIn('Error: Function is not defined', QMessageBox.critical.call_args[0][2])
+
+        # 3. Overwrite box unchecked, file doesn't exists, model with no 'return'
+        os.path.isfile = MagicMock(return_value=False)
+        test_model['overwrite']=False
+        test_model['text'] = "I am a simple model"
+        QMessageBox.critical = MagicMock()
+
+        ret = self.widget.canWriteModel(model=test_model, full_path=test_path)
+        self.assertFalse(ret)
+        self.assertTrue(QMessageBox.critical.called_once())
+        self.assertIn('Plugin Error', QMessageBox.critical.call_args[0][1])
+        self.assertIn('Error: The func(x) must', QMessageBox.critical.call_args[0][2])
+
+        # 4. Overwrite box unchecked, file doesnt exist, good model
+        os.path.isfile = MagicMock(return_value=False)
+        test_model['text'] = "return"
+        ret = self.widget.canWriteModel(model=test_model, full_path=test_path)
+        self.assertTrue(ret)
+
+    def testGenerateModel(self):
+        """
+        Test the model text creation from the dictionary
+        """
+        pass
+
+    def notestCheckModel(self):
+        """test the sasmodel model checker """
+        pass
+
+    def testGetParamHelper(self):
+        """
+        Test the convenience method for converting 
+        GUI parameter representation into sasmodel comprehensible string
+        """
+        pass
+
+    def testStrFromParamDict(self):
+        """
+        Test Conversion from dict to param string
+        """
+        test_dict = {0: ('a', '1'),
+                     1: ('eee',''),
+                     2: ('bob', None),
+                     3: ('',-1),
+                     4: (None, None)}
+
+        #{0: ('variable','value'),
+        # 1: ('variable','value'),
+
+        test_str = self.widget.strFromParamDict(test_dict)
+        self.assertIn("", test_str)
+
