From 337352e9a4c731ff1ed368b91ecdd84461c92d3c Mon Sep 17 00:00:00 2001
From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com>
Date: Thu, 23 Jan 2025 17:02:23 +0100
Subject: [PATCH 01/23] commit
---
scripts/install_from_local.bat | 72 +++++++++++++++++++---------------
1 file changed, 40 insertions(+), 32 deletions(-)
diff --git a/scripts/install_from_local.bat b/scripts/install_from_local.bat
index d80d891f..662b0ffc 100644
--- a/scripts/install_from_local.bat
+++ b/scripts/install_from_local.bat
@@ -1,39 +1,47 @@
-:: This batch script will install python 3.8, pytentiostat and it's necessary packages
+:: This batch script will install Python 3.12, pytentiostat, and its necessary packages
@echo off
cd ../local_install
-echo Installing python
-START /WAIT python-3.8.1-amd64
+echo Installing Python 3.12
+START /WAIT python-3.12.8-amd64.exe
python --version
if errorlevel 1 goto NoPython
-pip install whls/pytz-2019.3-py2.py3-none-any.whl
-pip install whls/kiwisolver-1.1.0-cp38-none-win_amd64.whl
-pip install whls/six-1.14.0-py2.py3-none-any.whl
-pip install whls/pyparsing-2.4.6-py2.py3-none-any.whl
-pip install whls/python_dateutil-2.8.1-py2.py3-none-any.whl
-pip install whls/pyserial-3.4-py2.py3-none-any.whl
-pip install whls/pyFirmata-1.1.0-py2.py3-none-any.whl
-pip install whls/numpy-1.18.1-cp38-cp38-win_amd64.whl
-pip install whls/pandas-0.25.3-cp38-cp38-win_amd64.whl
-pip install whls/PyYAML-5.3-cp38-cp38-win_amd64.whl
-pip install whls/urllib3-1.25.8-py2.py3-none-any.whl
-pip install whls/py-1.8.1-py2.py3-none-any.whl
-pip install whls/idna-2.8-py2.py3-none-any.whl
-pip install whls/chardet-3.0.4-py2.py3-none-any.whl
-pip install whls/certifi-2019.11.28-py2.py3-none-any.whl
-pip install whls/pluggy-0.13.1-py2.py3-none-any.whl
-pip install whls/packaging-20.0-py2.py3-none-any.whl
-pip install whls/more_itertools-8.1.0-py3-none-any.whl
-pip install whls/wcwidth-0.1.8-py2.py3-none-any.whl
-pip install whls/atomicwrites-1.3.0-py2.py3-none-any.whl
-pip install whls/attrs-19.3.0-py2.py3-none-any.whl
-pip install whls/cycler-0.10.0-py2.py3-none-any.whl
-pip install whls/matplotlib-3.1.2-cp38-cp38-win_amd64.whl
-pip install whls/coverage-5.0.3.tar.gz
-pip install whls/colorama-0.4.3-py2.py3-none-any.whl
-pip install whls/requests-2.22.0-py2.py3-none-any.whl
-pip install whls/pytest-5.3.4-py3-none-any.whl
-pip install whls/mock-3.0.5-py2.py3-none-any.whl
-pip install whls/codecov-2.0.15-py2.py3-none-any.whl
+
+:: Upgrade pip to the latest version
+python -m pip install --upgrade pip
+
+:: Install necessary packages
+pip install pytentiostat
+pip install pytz
+pip install kiwisolver
+pip install six
+pip install pyparsing
+pip install python_dateutil
+pip install pyserial
+pip install pyFirmata
+pip install numpy
+pip install pandas
+pip install PyYAML
+pip install urllib3
+pip install py
+pip install idna
+pip install chardet
+pip install certifi
+pip install pluggy
+pip install packaging
+pip install more_itertools
+pip install wcwidth
+pip install atomicwrites
+pip install attrs
+pip install cycler
+pip install matplotlib
+pip install coverage
+pip install colorama
+pip install requests
+pip install pytest
+pip install mock
+pip install codecov
+pip install PySide6
+
cd ..
python setup.py install
@pause
From 83893617fcc740352de712df2b0b7457e68cb690 Mon Sep 17 00:00:00 2001
From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com>
Date: Thu, 23 Jan 2025 17:09:16 +0100
Subject: [PATCH 02/23] commit
---
scripts/install_from_local.bat | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/install_from_local.bat b/scripts/install_from_local.bat
index 662b0ffc..c4d67d6f 100644
--- a/scripts/install_from_local.bat
+++ b/scripts/install_from_local.bat
@@ -40,7 +40,7 @@ pip install requests
pip install pytest
pip install mock
pip install codecov
-pip install PySide6
+pip install pyside6
cd ..
python setup.py install
From b8707941713a2923135e39077f780f9962690c80 Mon Sep 17 00:00:00 2001
From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com>
Date: Thu, 23 Jan 2025 17:35:31 +0100
Subject: [PATCH 03/23] commit
---
src/pytentiostat/version.py | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/src/pytentiostat/version.py b/src/pytentiostat/version.py
index 1156a65b..9e2b88fe 100644
--- a/src/pytentiostat/version.py
+++ b/src/pytentiostat/version.py
@@ -5,7 +5,7 @@
# All rights reserved.
#
# File coded by: Simon J. L. Billinge, Michael Spencer, Yao Tong, Austin
-# Plymill, eremy Hitt, Weizi Yuan, and JUAMI community contributors.u
+# Plymill, Jeremy Hitt, Weizi Yuan, and JUAMI community contributors.
#
# See GitHub contributions for a more detailed list of contributors.
# https://github.com/juami/pytentiostat/graphs/contributors
@@ -14,13 +14,33 @@
#
##############################################################################
"""Definition of __version__."""
-
-# We do not use the other three variables, but can be added back if needed.
-# __all__ = ["__date__", "__git_commit__", "__timestamp__", "__version__"]
-
-# obtain version information
+import argparse
from importlib.metadata import version
+# Obtain version information
__version__ = version("pytentiostat")
-# End of file
+
+def main():
+ # Set up argument parsing
+ parser = argparse.ArgumentParser(
+ description="Pytentiostat Command Line Interface"
+ )
+ parser.add_argument(
+ "--version",
+ action="store_true",
+ help="Show the version of pytentiostat",
+ )
+
+ # Parse the arguments
+ args = parser.parse_args()
+
+ # Check if the version flag is set
+ if args.version:
+ print(f"Pytentiostat version: {__version__}")
+ else:
+ print("No command provided. Use --version to get the version.")
+
+
+if __name__ == "__main__":
+ main()
From 2734cd0ca09ca70d09cb65be5da410eece1bcd82 Mon Sep 17 00:00:00 2001
From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com>
Date: Thu, 23 Jan 2025 17:46:21 +0100
Subject: [PATCH 04/23] commit
---
src/pytentiostat/version.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/pytentiostat/version.py b/src/pytentiostat/version.py
index 9e2b88fe..a2b9193c 100644
--- a/src/pytentiostat/version.py
+++ b/src/pytentiostat/version.py
@@ -40,7 +40,3 @@ def main():
print(f"Pytentiostat version: {__version__}")
else:
print("No command provided. Use --version to get the version.")
-
-
-if __name__ == "__main__":
- main()
From f797e17c430f68874a755f4fc52538c05e0360b6 Mon Sep 17 00:00:00 2001
From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com>
Date: Thu, 23 Jan 2025 18:03:26 +0100
Subject: [PATCH 05/23] commit
---
src/pytentiostat/version.py | 40 ++++++++++++++++++++-----------------
1 file changed, 22 insertions(+), 18 deletions(-)
diff --git a/src/pytentiostat/version.py b/src/pytentiostat/version.py
index a2b9193c..9f468efd 100644
--- a/src/pytentiostat/version.py
+++ b/src/pytentiostat/version.py
@@ -15,28 +15,32 @@
##############################################################################
"""Definition of __version__."""
import argparse
-from importlib.metadata import version
+from importlib.metadata import PackageNotFoundError, version
# Obtain version information
__version__ = version("pytentiostat")
+# Obtain version information
+try:
+ __version__ = version("pytentiostat")
+except PackageNotFoundError:
+ print("Error: pytentiostat package is not installed.")
-def main():
- # Set up argument parsing
- parser = argparse.ArgumentParser(
- description="Pytentiostat Command Line Interface"
- )
- parser.add_argument(
- "--version",
- action="store_true",
- help="Show the version of pytentiostat",
- )
+# Set up argument parsing
+parser = argparse.ArgumentParser(
+ description="Pytentiostat Command Line Interface"
+)
+parser.add_argument(
+ "--version",
+ action="store_true",
+ help="Show the version of pytentiostat",
+)
- # Parse the arguments
- args = parser.parse_args()
+# Parse the arguments
+args = parser.parse_args()
- # Check if the version flag is set
- if args.version:
- print(f"Pytentiostat version: {__version__}")
- else:
- print("No command provided. Use --version to get the version.")
+# Check if the version flag is set
+if args.version:
+ print(f"Pytentiostat version: {__version__}")
+else:
+ print("No command provided. Use --version to get the version.")
From a9f23fa37ebe2d28033dde31c9add5f8b974c6b1 Mon Sep 17 00:00:00 2001
From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com>
Date: Thu, 23 Jan 2025 18:05:26 +0100
Subject: [PATCH 06/23] commit
---
src/pytentiostat/version.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/pytentiostat/version.py b/src/pytentiostat/version.py
index 9f468efd..59fd9585 100644
--- a/src/pytentiostat/version.py
+++ b/src/pytentiostat/version.py
@@ -17,9 +17,6 @@
import argparse
from importlib.metadata import PackageNotFoundError, version
-# Obtain version information
-__version__ = version("pytentiostat")
-
# Obtain version information
try:
__version__ = version("pytentiostat")
From 3a82caeec35e34f00a06a8f15d5f5f4b23288614 Mon Sep 17 00:00:00 2001
From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com>
Date: Thu, 23 Jan 2025 18:08:00 +0100
Subject: [PATCH 07/23] commit
---
src/pytentiostat/version.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/pytentiostat/version.py b/src/pytentiostat/version.py
index 59fd9585..26f47aa6 100644
--- a/src/pytentiostat/version.py
+++ b/src/pytentiostat/version.py
@@ -27,6 +27,7 @@
parser = argparse.ArgumentParser(
description="Pytentiostat Command Line Interface"
)
+
parser.add_argument(
"--version",
action="store_true",
From 31cda153112e020ba29c63e38300e53dc6881c9c Mon Sep 17 00:00:00 2001
From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com>
Date: Sat, 25 Jan 2025 00:05:32 +0100
Subject: [PATCH 08/23] commit
---
news/version-implemented.rst | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 news/version-implemented.rst
diff --git a/news/version-implemented.rst b/news/version-implemented.rst
new file mode 100644
index 00000000..67da92da
--- /dev/null
+++ b/news/version-implemented.rst
@@ -0,0 +1,23 @@
+**Added:**
+
+* Conversion Factor Average Number Shunt Resistor /mohm Advanced Parameters Setpoint Gain Setpoint Offset
Time Step /s
') + + def test_line_edits_exist(self): + """Test if all line edit fields are created.""" + self.assertIsNotNone(self.ui.experiment_setpoint_gain) + self.assertIsNotNone(self.ui.experiment_shunt_resistor) + self.assertIsNotNone(self.ui.experiment_setpoint_offset) + self.assertIsNotNone(self.ui.experiment_time_step) + self.assertIsNotNone(self.ui.experiment_averag_number) + self.assertIsNotNone(self.ui.experiment_conversion_factor) + + def test_save_button_exists(self): + """Test if the save button is created.""" + self.assertIsNotNone(self.ui.save_experiment_file_button) + self.assertEqual(self.ui.save_experiment_file_button.text(), "Save ") + + def tearDown(self): + """Clean up after each test.""" + self.window.close() + + +if __name__ == "__main__": + unittest.main() From a5b945ac08d1abcfb2fe6f57ae79ca4bafe803d3 Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:38:32 +0100 Subject: [PATCH 13/23] commit --- src/GUI/gui_test/test_ca_gui.py | 76 +++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/GUI/gui_test/test_ca_gui.py diff --git a/src/GUI/gui_test/test_ca_gui.py b/src/GUI/gui_test/test_ca_gui.py new file mode 100644 index 00000000..401dd512 --- /dev/null +++ b/src/GUI/gui_test/test_ca_gui.py @@ -0,0 +1,76 @@ +import unittest + +from PySide6 import QtWidgets + +from src.GUI.code.CA_GUI import Ui_CA + + +class TestUiCA(unittest.TestCase): + def setUp(self): + """Set up the application and the UI before each test.""" + self.app = QtWidgets.QApplication([]) + self.window = QtWidgets.QMainWindow() + self.ui = Ui_CA() + self.ui.setupUi(self.window) + + def test_window_title(self): + """Test if the window title is set correctly.""" + self.assertEqual(self.window.windowTitle(), "CA Experiment Creator") + + def test_labels_exist(self): + """Test if all labels are created and have the correct text.""" + self.assertIsNotNone(self.ui.rest_time_label) + self.assertIsNotNone(self.ui.experiment_type_label) + self.assertIsNotNone(self.ui.experiment_time_label) + self.assertIsNotNone(self.ui.general_parameters_label) + self.assertIsNotNone(self.ui.output_filename_label) + self.assertIsNotNone(self.ui.step_number_label) + self.assertIsNotNone(self.ui.experiment_parameters_label) + self.assertIsNotNone(self.ui.voltage_label) + + self.assertEqual(self.ui.rest_time_label.text(), + 'Rest Time
') + self.assertEqual(self.ui.experiment_type_label.text(), + 'Experiment Type
') + self.assertEqual(self.ui.experiment_time_label.text(), + 'Experiment Time
') + self.assertEqual(self.ui.general_parameters_label.text(), + 'General Parameters
') + self.assertEqual(self.ui.output_filename_label.text(), + 'Output Filename
') + self.assertEqual(self.ui.step_number_label.text(), + 'Step Number
') + self.assertEqual(self.ui.experiment_parameters_label.text(), + 'Experiment Parameters
') + self.assertEqual(self.ui.voltage_label.text(), + 'Voltage
') + + def test_line_edits_exist(self): + """Test if all line edit fields are created.""" + self.assertIsNotNone(self.ui.experiment_step_number) + self.assertIsNotNone(self.ui.experiment_time) + self.assertIsNotNone(self.ui.experiment_voltage) + self.assertIsNotNone(self.ui.experiment_rest_time) + self.assertIsNotNone(self.ui.experiment_file_name) + self.assertIsNotNone(self.ui.experiment_file_path) + self.assertIsNotNone(self.ui.experiment_duration) + + def test_buttons_exist(self): + """Test if all buttons are created.""" + self.assertIsNotNone(self.ui.select_output_filepath_button) + self.assertIsNotNone(self.ui.advanced_parameters_button) + self.assertIsNotNone(self.ui.save_experiment_file_button) + self.assertIsNotNone(self.ui.generate_preview_button) + + self.assertEqual(self.ui.select_output_filepath_button.text(), "Output Filepath") + self.assertEqual(self.ui.advanced_parameters_button.text(), "Advanced Parameters") + self.assertEqual(self.ui.save_experiment_file_button.text(), "Save Experiment File") + self.assertEqual(self.ui.generate_preview_button.text(), "Generate Preview") + + def tearDown(self): + """Clean up after each test.""" + self.window.close() + + +if __name__ == "__main__": + unittest.main() From a83741a33841f32ec620101a03dbe3aef4b77d7a Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:38:45 +0100 Subject: [PATCH 14/23] commit --- src/GUI/gui_test/test_cv_gui.py | 84 +++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/GUI/gui_test/test_cv_gui.py diff --git a/src/GUI/gui_test/test_cv_gui.py b/src/GUI/gui_test/test_cv_gui.py new file mode 100644 index 00000000..cb38094f --- /dev/null +++ b/src/GUI/gui_test/test_cv_gui.py @@ -0,0 +1,84 @@ +import unittest + +from PySide6 import QtWidgets + +from src.GUI.code.CV_GUI import Ui_CV + + +class TestUiCV(unittest.TestCase): + def setUp(self): + """Set up the application and the UI before each test.""" + self.app = QtWidgets.QApplication([]) + self.window = QtWidgets.QMainWindow() + self.ui = Ui_CV() + self.ui.setupUi(self.window) + + def test_window_title(self): + """Test if the window title is set correctly.""" + self.assertEqual(self.window.windowTitle(), "CV Experiment Creator") + + def test_labels_exist(self): + """Test if all labels are created and have the correct text.""" + self.assertIsNotNone(self.ui.rest_time_label) + self.assertIsNotNone(self.ui.experiment_type_label) + self.assertIsNotNone(self.ui.output_filename_label) + self.assertIsNotNone(self.ui.step_number_label) + self.assertIsNotNone(self.ui.sweep_rate_label) + self.assertIsNotNone(self.ui.first_vertex_voltage_label) + self.assertIsNotNone(self.ui.second_vertex_voltage_label) + self.assertIsNotNone(self.ui.start_voltage_label) + self.assertIsNotNone(self.ui.cycle_number_label) + self.assertIsNotNone(self.ui.experiment_parameters_label) + + self.assertEqual(self.ui.rest_time_label.text(), + 'Rest Time
') + self.assertEqual(self.ui.experiment_type_label.text(), + 'Experiment Type
') + self.assertEqual(self.ui.output_filename_label.text(), + 'Output Filename
') + self.assertEqual(self.ui.step_number_label.text(), + 'Step Number
') + self.assertEqual(self.ui.sweep_rate_label.text(), + 'Sweep Rate
') + self.assertEqual(self.ui.first_vertex_voltage_label.text(), + '1st Vertex Voltage
') + self.assertEqual(self.ui.second_vertex_voltage_label.text(), + '2nd Vertex Voltage
') + self.assertEqual(self.ui.start_voltage_label.text(), + 'Start Voltage
') + self.assertEqual(self.ui.cycle_number_label.text(), + 'Cycle Number
') + self.assertEqual(self.ui.experiment_parameters_label.text(), + 'Experiment Parameters
') + + def test_line_edits_exist(self): + """Test if all line edit fields are created.""" + self.assertIsNotNone(self.ui.experiment_file_name) + self.assertIsNotNone(self.ui.experiment_file_path) + self.assertIsNotNone(self.ui.experiment_sweep_rate) + self.assertIsNotNone(self.ui.experiment_rest_time) + self.assertIsNotNone(self.ui.experiment_cycle_number) + self.assertIsNotNone(self.ui.experiment_step_number) + self.assertIsNotNone(self.ui.experiment_start_voltage) + self.assertIsNotNone(self.ui.experiment_first_vertex_voltage) + self.assertIsNotNone(self.ui.experiment_second_vertex_voltage) + + def test_buttons_exist(self): + """Test if all buttons are created.""" + self.assertIsNotNone(self.ui.select_output_filepath_button) + self.assertIsNotNone(self.ui.advanced_parameters_button) + self.assertIsNotNone(self.ui.save_experiment_file_button) + self.assertIsNotNone(self.ui.generate_preview_button) + + self.assertEqual(self.ui.select_output_filepath_button.text(), "Output Filepath") + self.assertEqual(self.ui.advanced_parameters_button.text(), "Advanced Parameters") + self.assertEqual(self.ui.save_experiment_file_button.text(), "Save Experiment File") + self.assertEqual(self.ui.generate_preview_button.text(), "Generate Preview") + + def tearDown(self): + """Clean up after each test.""" + self.window.close() + + +if __name__ == "__main__": + unittest.main() From 2087efd3d8c035064b311843201d57642ad9dcd5 Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:48:32 +0100 Subject: [PATCH 15/23] commit --- src/GUI/gui_test/test_gui_ca_exp_creator.py | 249 ++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 src/GUI/gui_test/test_gui_ca_exp_creator.py diff --git a/src/GUI/gui_test/test_gui_ca_exp_creator.py b/src/GUI/gui_test/test_gui_ca_exp_creator.py new file mode 100644 index 00000000..2eb8e184 --- /dev/null +++ b/src/GUI/gui_test/test_gui_ca_exp_creator.py @@ -0,0 +1,249 @@ +import unittest +from unittest.mock import MagicMock, patch + +from src.GUI.code.GUI_CA_exp_creator import ( + CA_main, + CA_window_writer, + check, + config_writer_CA, + get_AP_parameters, + get_parameters, + load_APwindow, + load_folder, + plot, + preview_CA, + save_file, + time_converter, + write_to_file_CA, +) + + +class TestGUICAExpCreator(unittest.TestCase): + + @patch('MultipleFiles.GUI_CA_exp_creator.QFileDialog.getExistingDirectory') + def test_load_folder(self): + """Test load_folder function.""" + mock_CA = MagicMock() + mock_CA.load_folder_name.return_value = "/mock/path" + load_folder(mock_CA) + mock_CA.experiment_file_path.setText.assert_called_with("/mock/path") + + def test_get_AP_parameters(self): + """Test get_AP_parameters function.""" + mock_exp = MagicMock() + mock_exp.experiment_conversion_factor.text.return_value = "1.0" + mock_exp.experiment_setpoint_gain.text.return_value = "2.0" + mock_exp.experiment_setpoint_offset.text.return_value = "0.0" + mock_exp.experiment_shunt_resistor.text.return_value = "0.1" + mock_exp.experiment_time_step.text.return_value = "0.01" + mock_exp.experiment_averag_number.text.return_value = "10" + + expected_params = [1.0, 2.0, 0.0, 0.1, 0.01, 10] + self.assertEqual(get_AP_parameters(mock_exp), expected_params) + + @patch('MultipleFiles.GUI_CA_exp_creator.warning') + def test_load_APwindow(self, mock_warning): + """Test load_APwindow function.""" + mock_CA = MagicMock() + mock_AP_params = [1.0, 2.0, 0.0, 0.1, 0.01, 10] + load_APwindow(mock_CA, mock_AP_params) + + # Check if the parameters are set correctly + self.assertEqual(mock_CA.AP_window().experiment_conversion_factor.text(), "1.0") + self.assertEqual(mock_CA.AP_window().experiment_setpoint_gain.text(), "2.0") + self.assertEqual(mock_CA.AP_window().experiment_setpoint_offset.text(), "0.0") + self.assertEqual(mock_CA.AP_window().experiment_shunt_resistor.text(), "0.1") + self.assertEqual(mock_CA.AP_window().experiment_time_step.text(), "0.01") + self.assertEqual(mock_CA.AP_window().experiment_averag_number.text(), "10") + + def test_time_converter(self): + """Test time_converter function.""" + self.assertEqual(time_converter(3661), "1:01:01") + self.assertEqual(time_converter(-1), "0:00:00") + + def test_plot(self): + """Test plot function.""" + x = [0, 1, 2] + y = [0, 1, 4] + canvas = plot(x, y) + self.assertIsNotNone(canvas) + + @patch('MultipleFiles.GUI_CA_exp_creator.warning') + def test_preview_CA(self, mock_warning): + """Test preview_CA function.""" + mock_exp = MagicMock() + mock_grid = MagicMock() + mock_exp.experiment_step_number.text.return_value = "10" + mock_exp.experiment_voltage.text.return_value = "5.0" + mock_exp.experiment_time.text.return_value = "10" + + preview_CA(mock_exp, mock_grid) + + # Check if the grid is updated + mock_grid.addWidget.assert_called() + + @patch('MultipleFiles.GUI_CA_exp_creator.yaml.dump') + def test_save_file(self, mock_yaml_dump): + """Test save_file function.""" + config_path_name = "/mock/path/config.yml" + config_data = {"key": "value"} + line_skipper = 0 + + save_file(config_path_name, config_data, line_skipper) + mock_yaml_dump.assert_called_once() + + def test_check(self): + """Test check function.""" + self.assertTrue(check("123")) + self.assertFalse(check("abc")) + self.assertTrue(check("-123.45")) + + @patch('MultipleFiles.GUI_CA_exp_creator.warning') + def test_get_parameters(self, mock_warning): + """Test get_parameters function.""" + mock_exp = MagicMock() + mock_exp.experiment_file_name.text.return_value = "test_experiment" + mock_exp.experiment_file_path.text.return_value = "/mock/path" + mock_exp.experiment_rest_time.text.return_value = "5" + mock_exp.experiment_step_number.text.return_value = "10" + mock_exp.experiment_voltage.text.return_value = "1.0" + mock_exp.experiment_time.text.return_value = "10" + + AP_parameters = [1.0, 2.0, 0.0, 0.1, 0.01, 10] # Mock advanced parameters + expected_params = [ + "CA", # Experiment type + 5, # Rest time + 10, # Step number + "test_experiment", # Output filename + "/mock/path", # Output path + 1.0, # Voltage + 10 # Time + ] + + result = get_parameters(mock_exp, "CA", AP_parameters) + self.assertEqual(result, expected_params) + + @patch('MultipleFiles.GUI_CA_exp_creator.save_file') + def test_config_writer_CA(self, mock_save_file): + """Test config_writer_CA function.""" + # Define the input parameters + exp_type = "CA" + rt = 5 + stp_num = 10 + data_out_name = "test_experiment" + data_out_path = "/mock/path" + conv_fact = 1.0 + set_gain = 2.0 + set_offset = 0.0 + sr = 0.1 + ts = 0.01 + avg_num = 10 + ca_v = 1.0 + ca_time = 10 + + # Call the function + config_writer_CA( + exp_type, + rt, + stp_num, + data_out_name, + data_out_path, + conv_fact, + set_gain, + set_offset, + sr, + ts, + avg_num, + ca_v, + ca_time, + ) + + # Check if save_file was called with the correct parameters + expected_config_data = { + "general_parameters": { + "experiment_type": exp_type, + "rest_time": rt, + "step_number": stp_num, + "data_output_filename": data_out_name, + "data_output_path": data_out_path, + }, + "chronoamperometry": { + "voltage": ca_v, + "time": ca_time, + }, + "advanced_parameters": { + "conversion_factor": conv_fact, + "setpoint_gain": set_gain, + "setpoint_offset": set_offset, + "shunt_resistor": sr, + "time_step": ts, + "average_number": avg_num, + }, + } + + # Verify that save_file was called with the correct parameters + mock_save_file.assert_called_once() + args, kwargs = mock_save_file.call_args[0] + self.assertEqual(args[0], f"{data_out_path}/{data_out_name}_{exp_type}_config.yml") + self.assertEqual(kwargs['config_data'], expected_config_data) + + @patch('MultipleFiles.GUI_CA_exp_creator.warning') + def test_write_to_file_CA(self, mock_warning, mock_config_writer): + """Test write_to_file_CA function.""" + mock_ui = MagicMock() + mock_CA = MagicMock() + mock_CA_window = MagicMock() + AP_parameters = [1.0, 2.0, 0.0, 0.1, 0.01, 10] # Mock advanced parameters + + # Mock the return values for get_parameters + mock_ui.experiment_queue.count.return_value = 0 + mock_ui.experiment_queue.item.return_value = None + + write_to_file_CA(mock_ui, mock_CA, mock_CA_window, AP_parameters) + + # Check if config_writer_CA was called + mock_config_writer.assert_called_once() + + @patch('MultipleFiles.GUI_CA_exp_creator.get_parameters') + @patch('MultipleFiles.GUI_CA_exp_creator.warning') + def test_CA_window_writer(self, mock_warning, mock_get_parameters): + """Test CA_window_writer function.""" + mock_exp = MagicMock() + config_data = { + "general_parameters": { + "data_output_filename": "test_experiment", + "data_output_path": "/mock/path", + "rest_time": 5, + "step_number": 10, + }, + "chronoamperometry": { + "voltage": 1.0, + "time": 10, + } + } + + CA_window_writer(mock_exp, config_data) + + # Check if the UI fields are set correctly + mock_exp.experiment_file_name.setText.assert_called_with("test_experiment") + mock_exp.experiment_file_path.setText.assert_called_with("/mock/path") + mock_exp.experiment_rest_time.setText.assert_called_with("5") + mock_exp.experiment_step_number.setText.assert_called_with("10") + mock_exp.experiment_voltage.setText.assert_called_with("1.0") + mock_exp.experiment_time.setText.assert_called_with("10") + + @patch('MultipleFiles.GUI_CA_exp_creator.Ui_Mainwindow') + @patch('MultipleFiles.GUI_CA_exp_creator.CA_main') + def test_CA_main(self, mock_CA_main, mock_Ui_Mainwindow): + """Test CA_main function.""" + mock_ui = MagicMock() + mock_config_data = None # No config data for this test + + CA_main(mock_ui, mock_config_data) + + # Check if CA_main was called with the correct parameters + mock_CA_main.assert_called_once_with(mock_ui, mock_config_data) + + +if __name__ == "__main__": + unittest.main() From f40706e0e902a8183bce4c1eef2dc888f188ae88 Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:49:47 +0100 Subject: [PATCH 16/23] commit --- src/GUI/gui_test/test_gui_cv_exp_creator.py | 184 ++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/GUI/gui_test/test_gui_cv_exp_creator.py diff --git a/src/GUI/gui_test/test_gui_cv_exp_creator.py b/src/GUI/gui_test/test_gui_cv_exp_creator.py new file mode 100644 index 00000000..4dc3c189 --- /dev/null +++ b/src/GUI/gui_test/test_gui_cv_exp_creator.py @@ -0,0 +1,184 @@ +import unittest +from unittest.mock import MagicMock, patch + +from src.GUI.code.GUI_CV_exp_creator import ( + CV_data, + CV_main, + CV_window_writer, + check, + config_writer_CV, + get_AP_parameters, + get_parameters, + load_APwindow, + load_folder, + plot, + preview_CV, + save_file, + time_converter, + write_to_file_CV, +) + + +class TestGUICVExpCreator(unittest.TestCase): + + def setUp(self): + # Create a mock CV object with necessary attributes + self.CV_mock = MagicMock() + self.exp_mock = MagicMock() + self.grid_mock = MagicMock() + + @patch("MultipleFiles.GUI_CV_exp_creator.QFileDialog.getExistingDirectory") + def test_load_folder(self, mock_get_existing_directory): + mock_get_existing_directory.return_value = "/mock/path" + load_folder(self.CV_mock) + self.CV_mock.experiment_file_path.setText.assert_called_with("/mock/path") + + def test_get_AP_parameters(self): + # Set mock values + self.exp_mock.experiment_conversion_factor.text.return_value = "1.0" + self.exp_mock.experiment_setpoint_gain.text.return_value = "2.0" + self.exp_mock.experiment_setpoint_offset.text.return_value = "0.0" + self.exp_mock.experiment_shunt_resistor.text.return_value = "1000" + self.exp_mock.experiment_time_step.text.return_value = "0.01" + self.exp_mock.experiment_averag_number.text.return_value = "5" + + expected_params = [1.0, 2.0, 0.0, 1000.0, 0.01, 5] + actual_params = get_AP_parameters(self.exp_mock) + self.assertEqual(actual_params, expected_params) + + @patch("MultipleFiles.GUI_CV_exp_creator.warning") + def test_load_APwindow(self, mock_warning): + AP_params = [1.0, 2.0, 0.0, 1000.0, 0.01, 5] + load_APwindow(self.CV_mock, AP_params) + self.CV_mock.AP_window().experiment_conversion_factor.setText.assert_called_with("1.0") + + def test_time_converter(self): + self.assertEqual(time_converter(3661), "1:01:01") + self.assertEqual(time_converter(-1), "0:00:00") + + def test_plot(self): + x = [0, 1, 2] + y = [0, 1, 0] + canvas = plot(x, y) + self.assertIsNotNone(canvas) + + def test_CV_data(self): + stp_num = 10 + cv_sv = 0.0 + cv_ftv = 1.0 + cv_stv = 0.5 + cv_sr = 0.1 + times_step_list, steps_list = CV_data(stp_num, cv_sv, cv_ftv, cv_stv, cv_sr) + self.assertEqual(len(times_step_list), stp_num) + self.assertEqual(len(steps_list), stp_num) + + @patch("MultipleFiles.GUI_CV_exp_creator.warning") + def test_preview_CV(self, mock_warning): + self.exp_mock.experiment_step_number.text.return_value = "10" + self.exp_mock.experiment_start_voltage.text.return_value = "0.0" + self.exp_mock.experiment_first_vertex_voltage.text.return_value = "1.0" + self.exp_mock.experiment_second_vertex_voltage.text.return_value = "0.5" + self.exp_mock.experiment_sweep_rate.text.return_value = "0.1" + self.exp_mock.experiment_cycle_number.text.return_value = "1" + + preview_CV(self.exp_mock, self.grid_mock) + self.assertTrue(self.grid_mock.count() > 0) + + @patch("builtins.open", new_callable=MagicMock) + def test_save_file(self, mock_open): + config_path_name = "test_config.yml" + config_data = {"key": "value"} + line_skipper = 0 + + save_file(config_path_name, config_data, line_skipper) + mock_open.assert_called_once_with(config_path_name, "w") + + @patch("MultipleFiles.GUI_CV_exp_creator.save_file") + def test_config_writer_CV(self, mock_save_file): + config_writer_CV( + "CV", + 10, + 5, + "output", + "/mock/path", + 1.0, + 2.0, + 0.0, + 1000.0, + 0.01, + 5, + 0.0, + 1.0, + 0.5, + 0.1, + 1 + ) + mock_save_file.assert_called() + + def test_check(self): + self.assertTrue(check("123")) + self.assertFalse(check("abc")) + self.assertFalse(check("-123.45")) + + @patch("MultipleFiles.GUI_CV_exp_creator.warning") + def test_get_parameters(self, mock_warning): + self.exp_mock.experiment_file_name.text.return_value = "output" + self.exp_mock.experiment_file_path.text.return_value = "/mock/path" + self.exp_mock.experiment_rest_time.text.return_value = "10" + self.exp_mock.experiment_step_number.text.return_value = "5" + self.exp_mock.experiment_start_voltage.text.return_value = "0.0" + self.exp_mock.experiment_first_vertex_voltage.text.return_value = "1.0" + self.exp_mock.experiment_second_vertex_voltage.text.return_value = "0.5" + self.exp_mock.experiment_sweep_rate.text.return_value = "0.1" + self.exp_mock.experiment_cycle_number.text.return_value = "1" + + AP_parameters = [1.0, 2.0, 0.0, 1000.0, 0.01, 5] + params = get_parameters(self.exp_mock, "CV", AP_parameters) + self.assertEqual(len(params), 12) + + @patch("MultipleFiles.GUI_CV_exp_creator.warning") + def test_write_to_file_CV(self, mock_warning): + self.exp_mock.experiment_file_name.text.return_value = "output" + self.exp_mock.experiment_file_path.text.return_value = "/mock/path" + AP_params = [1.0, 2.0, 0.0, 1000.0, 0.01, 5] + write_to_file_CV(self.CV_mock, self.CV_mock, self.CV_mock, AP_params) + self.assertTrue(mock_warning.called) + + @patch("MultipleFiles.GUI_CV_exp_creator.warning") + def test_CV_window_writer(self, mock_warning): + config_data = { + "general_parameters": { + "experiment_type": "CV", + "data_output_filename": "output", + "data_output_path": "/mock/path", + "rest_time": 10, + "step_number": 5, + }, + "cyclic_voltammetry": { + "start_voltage": 0.0, + "first_turnover_voltage": 1.0, + "second_turnover_voltage": 0.5, + "sweep_rate": 0.1, + "number_of_cycles": 1, + }, + "advanced_parameters": { + "conversion_factor": 1.0, + "setpoint_gain": 2.0, + "setpoint_offset": 0.0, + "shunt_resistor": 1000.0, + "time_step": 0.01, + "average_number": 5, + }, + } + CV_window_writer(self.exp_mock, config_data) + self.assertEqual(self.exp_mock.experiment_file_name.setText.call_count, 1) + + @patch("MultipleFiles.GUI_CV_exp_creator.QMainWindow") + def test_CV_main(self, mock_QMainWindow): + config_data = None + CV = CV_main(self.CV_mock, config_data) + self.assertIsNotNone(CV) + + +if __name__ == "__main__": + unittest.main() From 5fccdfdcb999c41605db5bb4e6fd8dacffd99789 Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:50:01 +0100 Subject: [PATCH 17/23] commit --- src/GUI/gui_test/test_gui_file_operation.py | 96 +++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/GUI/gui_test/test_gui_file_operation.py diff --git a/src/GUI/gui_test/test_gui_file_operation.py b/src/GUI/gui_test/test_gui_file_operation.py new file mode 100644 index 00000000..04c0efd8 --- /dev/null +++ b/src/GUI/gui_test/test_gui_file_operation.py @@ -0,0 +1,96 @@ +import unittest +from unittest.mock import MagicMock, patch + +from src.GUI.code.GUI_file_operation import ( + add_exp, + edit_file, + load_file, + remove_file, +) + + +class TestGUIFileOperation(unittest.TestCase): + + def setUp(self): + # Create a mock UI object + self.ui_mock = MagicMock() + self.ui_mock.show_exp.return_value.ca_button = MagicMock() + self.ui_mock.show_exp.return_value.cv_button = MagicMock() + self.ui_mock.show_exp.return_value.lsv_button = MagicMock() + self.ui_mock.load_config.return_value = "gui_test/config_CA.yml" + self.ui_mock.experiment_queue = MagicMock() + + @patch("MultipleFiles.GUI_file_operation.CA_main") + @patch("MultipleFiles.GUI_file_operation.CV_main") + @patch("MultipleFiles.GUI_file_operation.LSV_main") + def test_add_exp(self, mock_lsv_main, mock_cv_main, mock_ca_main): + add_exp(self.ui_mock) + self.ui_mock.show_exp.return_value.ca_button.clicked.emit() + mock_ca_main.assert_called_once_with(self.ui_mock) + + self.ui_mock.show_exp.return_value.cv_button.clicked.emit() + mock_cv_main.assert_called_once_with(self.ui_mock) + + self.ui_mock.show_exp.return_value.lsv_button.clicked.emit() + mock_lsv_main.assert_called_once_with(self.ui_mock) + + @patch("MultipleFiles.GUI_file_operation.QListWidgetItem") + @patch("MultipleFiles.GUI_file_operation.QIcon") + def test_load_file(self, mock_qicon, mock_qlistwidgetitem): + mock_item = MagicMock() + mock_qlistwidgetitem.return_value = mock_item + mock_qicon.return_value = MagicMock() + + load_file(self.ui_mock) + + # Check if the filename is parsed correctly + self.ui_mock.experiment_queue.addItem.assert_called_once_with(mock_item) + mock_item.setData.assert_any_call(1, mock_qicon.return_value) + mock_item.setData.assert_any_call(2, "config_CA.yml") + mock_item.setData.assert_any_call(3, "gui_test/config_CA.yml") + + @patch("MultipleFiles.GUI_file_operation.CA_main") + @patch("MultipleFiles.GUI_file_operation.CV_main") + @patch("MultipleFiles.GUI_file_operation.LSV_main") + @patch("MultipleFiles.GUI_file_operation.parse_config_file") + @patch("MultipleFiles.GUI_file_operation.warning") + def test_edit_file(self, mock_warning, mock_parse_config_file, mock_lsv_main, mock_cv_main, mock_ca_main): + # Mock the current item in the experiment queue + mock_item = MagicMock() + mock_item.data.return_value = "gui_test/config_CA.yml" + self.ui_mock.experiment_queue.currentItem.return_value = mock_item + mock_parse_config_file.return_value = {"key": "value"} + + edit_file(self.ui_mock) + + # Check if the parse_config_file was called with the correct filename + mock_parse_config_file.assert_called_once_with("gui_test/config_CA.yml") + + # Check if the correct main function was called + mock_ca_main.assert_called_once() # Assuming CA_main is called for CA config + + @patch("MultipleFiles.GUI_file_operation.warning") + def test_edit_file_no_item(self, mock_warning): + # Simulate no current item selected + self.ui_mock.experiment_queue.currentItem.return_value = None + edit_file(self.ui_mock) + + # Check if the warning was called + mock_warning.assert_called_once_with( + "Do you want to edit a existing config file? If so, please load it!" + ) + + def test_remove_file(self): + # Create a mock item and add it to the experiment queue + mock_item = MagicMock() + self.ui_mock.experiment_queue.selectedItems.return_value = [mock_item] + self.ui_mock.experiment_queue.row.return_value = 0 + + remove_file(self.ui_mock) + + # Check if the item was removed from the experiment queue + self.ui_mock.experiment_queue.takeItem.assert_called_once_with(0) + + +if __name__ == "__main__": + unittest.main() From 607c2dc06f3090df7ddfb088988434ead176c80b Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:50:19 +0100 Subject: [PATCH 18/23] commit --- src/GUI/gui_test/test_gui_load_config.py | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/GUI/gui_test/test_gui_load_config.py diff --git a/src/GUI/gui_test/test_gui_load_config.py b/src/GUI/gui_test/test_gui_load_config.py new file mode 100644 index 00000000..d3089850 --- /dev/null +++ b/src/GUI/gui_test/test_gui_load_config.py @@ -0,0 +1,63 @@ +import unittest +from unittest.mock import MagicMock, patch + +from PySide6.QtWidgets import QApplication + +from src.GUI.code.GUI_load_config import Ui_Load + + +class TestUiLoad(unittest.TestCase): + + def setUp(self): + self.app = QApplication([]) # Create a QApplication instance + self.ui = Ui_Load() # Create an instance of the Ui_Load class + + @patch("MultipleFiles.GUI_load_config.QFileDialog.getOpenFileName") + def test_openFileNameDialog(self, mock_getOpenFileName): + # Mock the return value of the file dialog + mock_getOpenFileName.return_value = ("path/to/config.yml", "") + + file = self.ui.openFileNameDialog() + + # Check if the returned file path is correct + self.assertEqual(file, "path/to/config.yml") + + @patch("MultipleFiles.GUI_load_config.QFileDialog.getExistingDirectory") + def test_saveFileDialog(self, mock_getExistingDirectory): + # Mock the return value of the directory dialog + mock_getExistingDirectory.return_value = "path/to/directory" + + folder = self.ui.saveFileDialog() + + # Check if the returned folder path is correct + self.assertEqual(folder, "path/to/directory/") + + @patch("MultipleFiles.GUI_load_config.QMainWindow") + def test_setupUi(self, mock_main_window): + mock_main_window.setObjectName = MagicMock() + mock_main_window.resize = MagicMock() + mock_main_window.setWindowIcon = MagicMock() + + file = self.ui.setupUi(mock_main_window) + + # Check if the main window is set up correctly + mock_main_window.setObjectName.assert_called_with("MainWindow") + mock_main_window.resize.assert_called_with(640, 480) + self.assertIsNotNone(file) # Ensure a file is returned + + @patch("MultipleFiles.GUI_load_config.QMainWindow") + def test_setupUi_save(self, mock_main_window): + mock_main_window.setObjectName = MagicMock() + mock_main_window.resize = MagicMock() + mock_main_window.setWindowIcon = MagicMock() + + folder = self.ui.setupUi_save(mock_main_window) + + # Check if the main window is set up correctly + mock_main_window.setObjectName.assert_called_with("MainWindow") + mock_main_window.resize.assert_called_with(640, 480) + self.assertIsNotNone(folder) # Ensure a folder is returned + + +if __name__ == "__main__": + unittest.main() From 4425accb330692711abcb12c4603960f90bf852e Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:50:35 +0100 Subject: [PATCH 19/23] commit --- src/GUI/gui_test/test_gui_lsv_exp_creator.py | 164 +++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 src/GUI/gui_test/test_gui_lsv_exp_creator.py diff --git a/src/GUI/gui_test/test_gui_lsv_exp_creator.py b/src/GUI/gui_test/test_gui_lsv_exp_creator.py new file mode 100644 index 00000000..70b2d116 --- /dev/null +++ b/src/GUI/gui_test/test_gui_lsv_exp_creator.py @@ -0,0 +1,164 @@ +import unittest +from unittest.mock import MagicMock, patch + +from src.GUI.code.GUI_LSV_exp_creator import ( + LSV_main, + LSV_window_writer, + check, + config_writer_LSV, + get_AP_parameters, + get_parameters, + load_APwindow, + load_folder, + plot, + preview_LSV, + save_file, + time_converter, + write_to_file_LSV, +) + + +class TestGUILSVExpCreator(unittest.TestCase): + + def setUp(self): + # Create mock objects for the UI components + self.LSV_mock = MagicMock() + self.exp_mock = MagicMock() + self.grid_mock = MagicMock() + + @patch("MultipleFiles.GUI_LSV_exp_creator.QFileDialog.getExistingDirectory") + def test_load_folder(self, mock_get_existing_directory): + mock_get_existing_directory.return_value = "/mock/path" + load_folder(self.LSV_mock) + self.LSV_mock.experiment_file_path.setText.assert_called_with("/mock/path") + + def test_get_AP_parameters(self): + # Set mock values + self.exp_mock.experiment_conversion_factor.text.return_value = "1.0" + self.exp_mock.experiment_setpoint_gain.text.return_value = "2.0" + self.exp_mock.experiment_setpoint_offset.text.return_value = "0.0" + self.exp_mock.experiment_shunt_resistor.text.return_value = "1000" + self.exp_mock.experiment_time_step.text.return_value = "0.01" + self.exp_mock.experiment_averag_number.text.return_value = "5" + + expected_params = [1.0, 2.0, 0.0, 1000.0, 0.01, 5] + actual_params = get_AP_parameters(self.exp_mock) + self.assertEqual(actual_params, expected_params) + + @patch("MultipleFiles.GUI_LSV_exp_creator.warning") + def test_load_APwindow(self, mock_warning): + AP_params = [1.0, 2.0, 0.0, 1000.0, 0.01, 5] + load_APwindow(self.LSV_mock, AP_params) + self.LSV_mock.AP_window().experiment_conversion_factor.setText.assert_called_with("1.0") + + def test_time_converter(self): + self.assertEqual(time_converter(3661), "1:01:01") + self.assertEqual(time_converter(-1), "0:00:00") + + def test_plot(self): + x = [0, 1, 2] + y = [0, 1, 0] + canvas = plot(x, y) + self.assertIsNotNone(canvas) + + def test_preview_LSV(self): + self.exp_mock.experiment_step_number.text.return_value = "10" + self.exp_mock.experiment_start_voltage.text.return_value = "0.0" + self.exp_mock.experiment_end_voltage.text.return_value = "1.0" + self.exp_mock.experiment_sweep_rate.text.return_value = "0.1" + + preview_LSV(self.exp_mock, self.grid_mock) + self.assertTrue(self.grid_mock.count() > 0) + + @patch("builtins.open", new_callable=MagicMock) + def test_save_file(self, mock_open): + config_path_name = "test_config.yml" + config_data = {"key": "value"} + line_skipper = 0 + + save_file(config_path_name, config_data, line_skipper) + mock_open.assert_called_once_with(config_path_name, "w") + + @patch("MultipleFiles.GUI_LSV_exp_creator.save_file") + def test_config_writer_LSV(self, mock_save_file): + config_writer_LSV( + "LSV", + 10, + 5, + "output", + "/mock/path", + 1.0, + 2.0, + 0.0, + 1000.0, + 0.01, + 5, + 0.0, + 1.0, + 0.1, + ) + mock_save_file.assert_called() + + def test_check(self): + self.assertTrue(check("123")) + self.assertFalse(check("abc")) + self.assertFalse(check("-123.45")) + + @patch("MultipleFiles.GUI_LSV_exp_creator.warning") + def test_get_parameters(self, mock_warning): + self.exp_mock.experiment_file_name.text.return_value = "output" + self.exp_mock.experiment_file_path.text.return_value = "/mock/path" + self.exp_mock.experiment_rest_time.text.return_value = "10" + self.exp_mock.experiment_step_number.text.return_value = "5" + self.exp_mock.experiment_start_voltage.text.return_value = "0.0" + self.exp_mock.experiment_end_voltage.text.return_value = "1.0" + self.exp_mock.experiment_sweep_rate.text.return_value = "0.1" + + AP_parameters = [1.0, 2.0, 0.0, 1000.0, 0.01, 5] + params = get_parameters(self.exp_mock, "LSV", AP_parameters) + self.assertEqual(len(params), 12) + + @patch("MultipleFiles.GUI_LSV_exp_creator.warning") + def test_write_to_file_LSV(self, mock_warning): + self.exp_mock.experiment_file_name.text.return_value = "output" + self.exp_mock.experiment_file_path.text.return_value = "/mock/path" + AP_params = [1.0, 2.0, 0.0, 1000.0, 0.01, 5] + write_to_file_LSV(self.LSV_mock, self.LSV_mock, self.LSV_mock, AP_params) + self.assertTrue(mock_warning.called) + + @patch("MultipleFiles.GUI_LSV_exp_creator.QMainWindow") + def test_LSV_window_writer(self, mock_main_window): + config_data = { + "general_parameters": { + "experiment_type": "LSV", + "data_output_filename": "output", + "data_output_path": "/mock/path", + "rest_time": 10, + "step_number": 5, + }, + "linear_sweep_voltammetry": { + "start_voltage": 0.0, + "end_voltage": 1.0, + "sweep_rate": 0.1, + }, + "advanced_parameters": { + "conversion_factor": 1.0, + "setpoint_gain": 2.0, + "setpoint_offset": 0.0, + "shunt_resistor": 1000.0, + "time_step": 0.01, + "average_number": 5, + }, + } + LSV_window_writer(self.exp_mock, config_data) + self.assertEqual(self.exp_mock.experiment_file_name.setText.call_count, 1) + + @patch("MultipleFiles.GUI_LSV_exp_creator.QMainWindow") + def test_LSV_main(self, mock_QMainWindow): + config_data = None + LSV = LSV_main(self.LSV_mock, config_data) + self.assertIsNotNone(LSV) + + +if __name__ == "__main__": + unittest.main() From 556c8d635dd2ca2763e06205f9b509cba77ef301 Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:51:00 +0100 Subject: [PATCH 20/23] commit --- src/GUI/gui_test/test_gui_operator.py | 124 +++++++++++++++++++++++++ src/GUI/gui_test/test_gui_plotter.py | 74 +++++++++++++++ src/GUI/gui_test/test_gui_routines.py | 121 +++++++++++++++++++++++++ src/GUI/gui_test/test_gui_run_exp.py | 126 ++++++++++++++++++++++++++ src/GUI/gui_test/test_lsv_gui.py | 81 +++++++++++++++++ 5 files changed, 526 insertions(+) create mode 100644 src/GUI/gui_test/test_gui_operator.py create mode 100644 src/GUI/gui_test/test_gui_plotter.py create mode 100644 src/GUI/gui_test/test_gui_routines.py create mode 100644 src/GUI/gui_test/test_gui_run_exp.py create mode 100644 src/GUI/gui_test/test_lsv_gui.py diff --git a/src/GUI/gui_test/test_gui_operator.py b/src/GUI/gui_test/test_gui_operator.py new file mode 100644 index 00000000..71c2377c --- /dev/null +++ b/src/GUI/gui_test/test_gui_operator.py @@ -0,0 +1,124 @@ +import time +import unittest +from unittest.mock import MagicMock, patch + +import numpy as np + +from src.GUI.code.GUI_operator import experiment, read_write, start_exp + + +class TestGUIOperator(unittest.TestCase): + + def setUp(self): + # Create mock objects for Arduino pins and other parameters + self.d9_mock = MagicMock() + self.a0_mock = MagicMock() + self.a2_mock = MagicMock() + self.ini_plot_mock = MagicMock() + self.times = [] + self.voltages = [] + self.currents = [] + self.pr_mock = MagicMock() + self.tr_mock = MagicMock() + + @patch("MultipleFiles.GUI_operator.cr.get_rest") + def test_start_exp(self, mock_get_rest): + mock_get_rest.return_value = 1 # Mock the rest time + normalized_start = 0.5 # Example normalized start value + start_time = start_exp(self.d9_mock, normalized_start, None) + + # Check if the pin was written correctly + self.d9_mock.write.assert_called_once_with(normalized_start) + time.sleep(1) # Simulate the sleep time + self.assertAlmostEqual(start_time, time.time(), delta=0.1) + + @patch("MultipleFiles.GUI_operator.time.sleep") + def test_read_write(self, mock_sleep): + # Set up mock parameters + start_time = time.time() + step_number = 5 + steps_list = np.linspace(0, 1, num=step_number + 1).tolist() # Convert to list + current_exp_time = 10 + average = 5 + time_step = 0.1 + cf = 1.0 + sr = 1000.0 + cycle_number = 1 + + # Mock the read values + self.a0_mock.read.side_effect = [0.5] * average + self.a2_mock.read.side_effect = [0.5] * average + + # Call the read_write function + times, voltages, currents = read_write( + start_time, + self.d9_mock, + self.a0_mock, + self.a2_mock, + step_number, + steps_list, + current_exp_time, + average, + time_step, + cf, + sr, + self.ini_plot_mock, + self.times, + self.voltages, + self.currents, + self.pr_mock, + self.tr_mock, + total_exp_time=30, + passed_exp_time=0, + cycle_number=cycle_number, + ) + + # Check if the voltages and currents were calculated correctly + self.assertEqual(len(times), (step_number + 1) * cycle_number) # Adjusted for the number of steps + self.assertEqual(len(voltages), (step_number + 1) * cycle_number) + self.assertEqual(len(currents), (step_number + 1) * cycle_number) + + # Check if the progress bar was updated + self.pr_mock.setValue.assert_called() + self.tr_mock.setText.assert_called() + + @patch("MultipleFiles.GUI_operator.start_exp") + @patch("MultipleFiles.GUI_operator.read_write") + @patch("MultipleFiles.GUI_operator.cr.get_adv_params") + @patch("MultipleFiles.GUI_operator.cr.get_steps") + @patch("MultipleFiles.GUI_operator.cr.get_exp_type") + def test_experiment(self, mock_get_exp_type, mock_get_steps, mock_get_adv_params, mock_read_write, mock_start_exp): + # Mock the return values + mock_get_exp_type.return_value = "LSV" + mock_get_steps.return_value = 5 + mock_get_adv_params.return_value = (1.0, 1.0, 0.0, 1000.0, 0.01, 5) + + # Mock the start_exp function + mock_start_exp.return_value = time.time() + + # Call the experiment function + times, voltages, currents = experiment( + {}, # config_data + None, # board + self.a0_mock, + self.a2_mock, + self.d9_mock, + self.ini_plot_mock, + self.times, + self.voltages, + self.currents, + self.pr_mock, + self.tr_mock, + total_exp_time=30, + passed_exp_time=0, + ) + + # Check if read_write was called + mock_read_write.assert_called() + self.assertEqual(len(times), (5 + 1)) # 5 steps + 1 for the end + self.assertEqual(len(voltages), (5 + 1)) + self.assertEqual(len(currents), (5 + 1)) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/GUI/gui_test/test_gui_plotter.py b/src/GUI/gui_test/test_gui_plotter.py new file mode 100644 index 00000000..145adb3c --- /dev/null +++ b/src/GUI/gui_test/test_gui_plotter.py @@ -0,0 +1,74 @@ +import unittest +from unittest.mock import MagicMock + +import src.GUI.code.GUI_config_reader as cr +from src.GUI.code.GUI_plotter import Ui_Plot + + +class TestUiPlot(unittest.TestCase): + + def setUp(self): + """Set up the test case.""" + self.plot = Ui_Plot() + self.plot.canvas = MagicMock() # Mock the canvas to avoid GUI rendering + self.plot.ax = MagicMock() # Mock the axes to avoid actual plotting + + def test_plot_initialize_CA(self): + """Test the plot_initialize method for CA experiment type.""" + # Mock the config data and the functions from GUI_config_reader + config_data = {'exp_type': 'CA', 'exp_time': 5} + cr.get_exp_type = MagicMock(return_value='CA') + cr.get_exp_time = MagicMock(return_value=5) + + self.plot.plot_initialize(config_data) + + # Check if the axes limits are set correctly + self.plot.ax.set_xlim.assert_called_with(0, 10) # 2 * exp_time + self.plot.ax.set_xlabel.assert_called_with("Time (s)") + self.plot.ax.set_ylabel.assert_called_with("Current (mA)") + + def test_plot_initialize_LSV(self): + """Test the plot_initialize method for LSV experiment type.""" + # Mock the config data and the functions from GUI_config_reader + config_data = {'exp_type': 'LSV'} + cr.get_exp_type = MagicMock(return_value='LSV') + + self.plot.plot_initialize(config_data) + + # Check if the axes labels are set correctly + self.plot.ax.set_xlabel.assert_called_with("Voltage (V)") + self.plot.ax.set_ylabel.assert_called_with("Current (mA)") + + def test_plot_updater_CA(self): + """Test the plot_updater method for CA experiment type.""" + self.plot.exp_type = 'CA' + self.plot.lines = MagicMock() # Mock the line object + + # Sample data for testing + data = ([0, 1, 2, 3, 4, 5], [], [0, 1, 0, -1, 0, 1]) + self.plot.plot_updater(data) + + # Check if the line data is updated correctly + self.plot.lines.set_xdata.assert_called_with([0, 1, 2, 3, 4, 5]) + self.plot.lines.set_ydata.assert_called_with([0, 1, 0, -1, 0, 1]) + self.plot.ax.set_xlim.assert_called() # Check if xlim was set + self.plot.ax.set_ylim.assert_called() # Check if ylim was set + + def test_plot_updater_LSV(self): + """Test the plot_updater method for LSV experiment type.""" + self.plot.exp_type = 'LSV' + self.plot.lines = MagicMock() # Mock the line object + + # Sample data for testing + data = ([], [0, 1, 2, 3, 4, 5], [0, 1, 0, -1, 0, 1]) + self.plot.plot_updater(data) + + # Check if the line data is updated correctly + self.plot.lines.set_xdata.assert_called_with([0, 1, 2, 3, 4, 5]) + self.plot.lines.set_ydata.assert_called_with([0, 1, 0, -1, 0, 1]) + self.plot.ax.set_xlim.assert_called() # Check if xlim was set + self.plot.ax.set_ylim.assert_called() # Check if ylim was set + + +if __name__ == '__main__': + unittest.main() diff --git a/src/GUI/gui_test/test_gui_routines.py b/src/GUI/gui_test/test_gui_routines.py new file mode 100644 index 00000000..2f8999f6 --- /dev/null +++ b/src/GUI/gui_test/test_gui_routines.py @@ -0,0 +1,121 @@ +import sys +import unittest +from unittest.mock import MagicMock, patch + +from src.GUI.code.GUI_routines import ( + _initialize_arduino, + _load_arduino, + closing_routine, + disconnect_port_main, + find_port_main, + startup_routine, +) + + +class TestGUIRoutines(unittest.TestCase): + + @patch('serial.tools.list_ports.comports') + def test_load_arduino(self, mock_comports): + """Test the _load_arduino function.""" + mock_comports.return_value = [MagicMock(device='COM3'), MagicMock(device='COM4')] + ui_mock = MagicMock() + ports = _load_arduino(ui_mock) + + # Check if the ports are loaded correctly + self.assertEqual(ports, ['COM3', 'COM4']) + ui_mock.arduino_connection_name.addItem.assert_any_call('COM3') + ui_mock.arduino_connection_name.addItem.assert_any_call('COM4') + + @patch('pyfirmata.Arduino') + def test_initialize_arduino_success(self, mock_arduino): + """Test the _initialize_arduino function for successful + initialization.""" + com_port = 'COM3' + board = _initialize_arduino(com_port) + self.assertEqual(board, mock_arduino.return_value) + + @patch('pyfirmata.Arduino') + def test_initialize_arduino_failure(self, mock_arduino): + """Test the _initialize_arduino function for failure.""" + mock_arduino.side_effect = Exception("Error") + with self.assertRaises(SystemExit): + _initialize_arduino('COM3') + + @patch('serial.tools.list_ports.comports') + @patch('GUI_routines.warning') + @patch('pyfirmata.Arduino') + def test_startup_routine_success(self, mock_arduino, mock_warning, mock_comports): + """Test the startup_routine function for successful startup.""" + mock_comports.return_value = [MagicMock(device='COM3')] + ui_mock = MagicMock() + ui_mock.arduino_connection_name.currentText.return_value = 'COM3' + + board_mock = MagicMock() + mock_arduino.return_value = board_mock + + com, board, a0, a2, d9 = startup_routine(ui_mock) + + self.assertEqual(com, 'COM3') + self.assertEqual(board, board_mock) + self.assertIsNotNone(a0) + self.assertIsNotNone(a2) + self.assertIsNotNone(d9) + + @patch('serial.tools.list_ports.comports') + @patch('GUI_routines.warning') + def test_startup_routine_failure(self, mock_warning, mock_comports): + """Test the startup_routine function for failure when port is not + connected.""" + mock_comports.return_value = [] + ui_mock = MagicMock() + ui_mock.arduino_connection_name.currentText.return_value = 'COM3' + + com, board, a0, a2, d9 = startup_routine(ui_mock) + + self.assertEqual(com, 'COM3') + self.assertIsNone(board) + self.assertIsNone(a0) + self.assertIsNone(a2) + self.assertIsNone(d9) + mock_warning.assert_called_with("Current port not connected!") + + @patch('pyfirmata.Arduino') + def test_closing_routine(self, mock_arduino): + """Test the closing_routine function.""" + board_mock = MagicMock() + d9_mock = MagicMock() + closing_routine(board_mock, d9_mock) + + d9_mock.write.assert_called_with(0.5) + board_mock.exit.assert_called() + + @patch('GUI_routines.startup_routine') + @patch('GUI_routines.warning') + @patch('PySide6.QtGui.QIcon') + def test_find_port_main_success(self, mock_qicon, mock_warning, mock_startup_routine): + """Test the find_port_main function for successful port finding.""" + ui_mock = MagicMock() + mock_startup_routine.return_value = ('COM3', MagicMock(), MagicMock(), MagicMock(), MagicMock()) + + com, board_objects = find_port_main(ui_mock) + + self.assertEqual(com, 'COM3') + self.assertIsNotNone(board_objects) + ui_mock.arduino_connection_indicator.setIcon.assert_called_with(mock_qicon.return_value) + + @patch('GUI_routines.closing_routine') + @patch('PySide6.QtGui.QIcon') + def test_disconnect_port_main(self, mock_qicon, mock_closing_routine): + """Test the disconnect_port_main function.""" + ui_mock = MagicMock() + board_mock = MagicMock() + d9_mock = MagicMock() + + disconnect_port_main(ui_mock, board_mock, d9_mock) + + mock_closing_routine.assert_called_with(board_mock, d9_mock) + ui_mock.arduino_connection_indicator.setIcon.assert_called_with(mock_qicon.return_value) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/GUI/gui_test/test_gui_run_exp.py b/src/GUI/gui_test/test_gui_run_exp.py new file mode 100644 index 00000000..b9ca6e30 --- /dev/null +++ b/src/GUI/gui_test/test_gui_run_exp.py @@ -0,0 +1,126 @@ +import unittest +from unittest.mock import MagicMock, patch + +from src.GUI.code.GUI_run_exp import ( + calculate_time, + run_exp, + run_exp_main, + set_icon, + total_time, +) + + +class TestGUIRunExp(unittest.TestCase): + + @patch('GUI_run_exp.parse_config_file') + def test_calculate_time_CA(self, mock_parse): + """Test the calculate_time function for CA experiment type.""" + mock_parse.return_value = { + "chronoamperometry": {"time": 10} + } + filename = "experiment_CA.yml" + result = calculate_time(filename) + self.assertEqual(result, 10) + + @patch('GUI_run_exp.parse_config_file') + def test_calculate_time_CV(self, mock_parse): + """Test the calculate_time function for CV experiment type.""" + mock_parse.return_value = { + "cyclic_voltammetry": { + "number_of_cycles": 2, + "start_voltage": 0, + "first_turnover_voltage": 1, + "second_turnover_voltage": 2, + "sweep_rate": 0.1 + } + } + filename = "experiment_CV.yml" + result = calculate_time(filename) + self.assertEqual(result, 60) # (2 * (1 + 1 + 1) / 0.1) * 1000 + + @patch('GUI_run_exp.parse_config_file') + def test_calculate_time_LSV(self, mock_parse): + """Test the calculate_time function for LSV experiment type.""" + mock_parse.return_value = { + "linear_sweep_voltammetry": { + "start_voltage": 0, + "end_voltage": 1, + "sweep_rate": 0.1 + } + } + filename = "experiment_LSV.yml" + result = calculate_time(filename) + self.assertEqual(result, 10000) # (1 - 0) / 0.1 * 1000 + + @patch('GUI_run_exp.calculate_time') + def test_total_time(self, mock_calculate): + """Test the total_time function.""" + ui_mock = MagicMock() + ui_mock.experiment_queue.count.return_value = 2 + ui_mock.experiment_queue.item.side_effect = [ + MagicMock(data=MagicMock(return_value="experiment_CA.yml")), + MagicMock(data=MagicMock(return_value="experiment_CV.yml")) + ] + mock_calculate.side_effect = [10, 60] # Mock return values for calculate_time + + result = total_time(ui_mock) + self.assertEqual(result, [10, 60]) + + @patch('GUI_run_exp.QIcon') + def test_set_icon(self, mock_qicon): + """Test the set_icon function.""" + ui_mock = MagicMock() + ui_mock.experiment_queue.count.return_value = 2 + ui_mock.experiment_queue.item.side_effect = [ + MagicMock(data=MagicMock(return_value="experiment_CA.yml")), + MagicMock(data=MagicMock(return_value="experiment_CV.yml")) + ] + + set_icon(ui_mock) + + # Check if the icons were set correctly + ui_mock.experiment_queue.item(0).setData.assert_called_with(1, mock_qicon.return_value) + ui_mock.experiment_queue.item(1).setData.assert_called_with(1, mock_qicon.return_value) + + @patch('GUI_run_exp.warning') + @patch('GUI_run_exp.parse_config_file') + @patch('GUI_run_exp.experiment') + @patch('GUI_run_exp.total_time') + @patch('GUI_run_exp.set_icon') + def test_run_exp(self, mock_set_icon, mock_total_time, mock_experiment, mock_parse, mock_warning): + """Test the run_exp function.""" + ui_mock = MagicMock() + ui_mock.experiment_queue.count.return_value = 1 + ui_mock.experiment_queue.item.return_value.data.return_value = "experiment_CA.yml" + ui_mock.start_abort_experiment_button.isChecked.return_value = True + mock_parse.return_value = { + "general_parameters": { + "data_output_filename": "output", + "data_output_path": "./" + } + } + mock_total_time.return_value = [10] + mock_experiment.return_value = ([], [], []) + + run_exp(ui_mock, "COM3", ("board", "a0", "a2", "d9"), MagicMock(), MagicMock(), MagicMock(), MagicMock()) + + # Check if the experiment was run + mock_experiment.assert_called() + ui_mock.experiment_status_indicator.setIcon.assert_called() + + @patch('GUI_run_exp.run_exp') + def test_run_exp_main(self, mock_run_exp): + """Test the run_exp_main function.""" + ui_mock = MagicMock() + com_mock = "COM3" + board_objects_mock = ("board", "a0", "a2", "d9") + grid_mock = MagicMock() + + run_exp_main(ui_mock, com_mock, board_objects_mock, grid_mock) + + # Check if run_exp was called with the correct parameters + mock_run_exp.assert_called_with(ui_mock, com_mock, board_objects_mock, MagicMock(), grid_mock, MagicMock(), MagicMock()) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/GUI/gui_test/test_lsv_gui.py b/src/GUI/gui_test/test_lsv_gui.py new file mode 100644 index 00000000..0d44cb49 --- /dev/null +++ b/src/GUI/gui_test/test_lsv_gui.py @@ -0,0 +1,81 @@ +import unittest +from unittest.mock import MagicMock, patch + +from PySide6 import QtWidgets + +from src.GUI.code.LSV_GUI import Ui_LSV + + +class TestUiLSV(unittest.TestCase): + + @patch('LSV_GUI.Ui_Load') + def test_load_folder_name(self, mock_ui_load): + """Test the load_folder_name method.""" + # Arrange + mock_window = MagicMock() + mock_ui_load_instance = mock_ui_load.return_value + mock_ui_load_instance.setupUi_save.return_value = "test_file.txt" + + ui_lsv = Ui_LSV() + ui_lsv.window = mock_window + + # Act + result = ui_lsv.load_folder_name() + + # Assert + self.assertEqual(result, "test_file.txt") + mock_ui_load_instance.setupUi_save.assert_called_once_with(mock_window) + + @patch('LSV_GUI.Ui_Adv_Params') + def test_AP_window(self, mock_ui_adv_params): + """Test the AP_window method.""" + # Arrange + mock_window = MagicMock() + mock_ui_adv_params_instance = mock_ui_adv_params.return_value + ui_lsv = Ui_LSV() + ui_lsv.window = mock_window + + # Act + result_ap, result_window = ui_lsv.AP_window() + + # Assert + self.assertEqual(result_ap, mock_ui_adv_params_instance) + self.assertEqual(result_window, mock_window) + mock_ui_adv_params_instance.setupUi.assert_called_once_with(mock_window) + + @patch('PySide6.QtWidgets.QWidget') + @patch('PySide6.QtWidgets.QMainWindow') + @patch('PySide6.QtGui.QPalette') + @patch('PySide6.QtGui.QBrush') + @patch('PySide6.QtGui.QColor') + @patch('PySide6.QtGui.QIcon') + @patch('PySide6.QtGui.QPixmap') + def test_setupUi(self, mock_pixmap, mock_icon, mock_color, mock_brush, mock_palette, mock_main_window, mock_widget): + """Test the setupUi method.""" + # Arrange + mock_lsv = MagicMock() + mock_lsv.setObjectName = MagicMock() + mock_lsv.resize = MagicMock() + mock_lsv.setPalette = MagicMock() + mock_lsv.setWindowIcon = MagicMock() + mock_lsv.setCentralWidget = MagicMock() + mock_lsv.setMenuBar = MagicMock() + mock_lsv.setStatusBar = MagicMock() + + ui_lsv = Ui_LSV() + + # Act + ui_lsv.setupUi(mock_lsv) + + # Assert + mock_lsv.setObjectName.assert_called_once_with("LSV") + mock_lsv.resize.assert_called_once_with(800, 519) + mock_lsv.setPalette.assert_called_once() + mock_lsv.setWindowIcon.assert_called_once() + mock_lsv.setCentralWidget.assert_called_once() + mock_lsv.setMenuBar.assert_called_once() + mock_lsv.setStatusBar.assert_called_once() + + +if __name__ == '__main__': + unittest.main() From 57110c0ed0237b2f47f4da6fd4f5756767063d4e Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:51:23 +0100 Subject: [PATCH 21/23] commit --- src/GUI/gui_test/test_main.py | 102 ++++++++++++++++++ src/GUI/gui_test/test_mainwindow_gui.py | 132 ++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 src/GUI/gui_test/test_main.py create mode 100644 src/GUI/gui_test/test_mainwindow_gui.py diff --git a/src/GUI/gui_test/test_main.py b/src/GUI/gui_test/test_main.py new file mode 100644 index 00000000..98660e3d --- /dev/null +++ b/src/GUI/gui_test/test_main.py @@ -0,0 +1,102 @@ +import unittest +from unittest.mock import MagicMock, patch + +from src.GUI.code.main import disconnect_port, find_port, start_exp + + +class TestMain(unittest.TestCase): + + @patch('GUI_routines.find_port_main') + def test_find_port(self, mock_find_port_main): + """Test the find_port function.""" + # Arrange + mock_ui = MagicMock() + mock_find_port_main.return_value = ('COM3', (MagicMock(), None, None, None)) + + # Act + find_port() + + # Assert + mock_find_port_main.assert_called_once_with(mock_ui) + self.assertEqual(mock_ui.arduino_connection_name.currentText(), 'COM3') + + @patch('GUI_routines.disconnect_port_main') + def test_disconnect_port(self, mock_disconnect_port_main): + """Test the disconnect_port function.""" + # Arrange + mock_ui = MagicMock() + mock_board = MagicMock() + mock_d9 = MagicMock() + + # Act + disconnect_port() + + # Assert + mock_disconnect_port_main.assert_called_once_with(mock_ui, mock_board, mock_d9) + + @patch('GUI_run_exp.run_exp_main') + def test_start_exp(self, mock_run_exp_main): + """Test the start_exp function.""" + # Arrange + mock_grid = MagicMock() + mock_ui = MagicMock() + mock_com = 'COM3' + mock_board_objects = (MagicMock(), None, None, MagicMock()) + + # Act + start_exp(mock_grid) + + # Assert + mock_run_exp_main.assert_called_once_with(mock_ui, mock_com, mock_board_objects, mock_grid) + + @patch('GUI_file_operation.add_exp') + def test_add_experiment(self, mock_add_exp): + """Test the add experiment button connection.""" + mock_ui = MagicMock() + mock_add_exp.return_value = None + + # Act + mock_ui.add_experiment_button.clicked.emit() + + # Assert + mock_add_exp.assert_called_once_with(mock_ui) + + @patch('GUI_file_operation.load_file') + def test_load_experiment(self, mock_load_file): + """Test the load experiment button connection.""" + mock_ui = MagicMock() + mock_load_file.return_value = None + + # Act + mock_ui.load_experiment_button.clicked.emit() + + # Assert + mock_load_file.assert_called_once_with(mock_ui) + + @patch('GUI_file_operation.edit_file') + def test_edit_experiment(self, mock_edit_file): + """Test the edit experiment button connection.""" + mock_ui = MagicMock() + mock_edit_file.return_value = None + + # Act + mock_ui.edit_experiment_button.clicked.emit() + + # Assert + mock_edit_file.assert_called_once_with(mock_ui) + + @patch('GUI_file_operation.remove_file') + def test_remove_experiment(self, mock_remove_file): + """Test the remove experiment button connection.""" + mock_ui = MagicMock() + mock_remove_file.return_value = None + + # Act + mock_ui.remove_experiment_button.clicked.emit() + + # Assert + mock_remove_file.assert_called_once_with(mock_ui) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/GUI/gui_test/test_mainwindow_gui.py b/src/GUI/gui_test/test_mainwindow_gui.py new file mode 100644 index 00000000..21eef422 --- /dev/null +++ b/src/GUI/gui_test/test_mainwindow_gui.py @@ -0,0 +1,132 @@ +import unittest +from unittest.mock import MagicMock, patch + +from src.GUI.code.mainwindow_GUI import Ui_MainWindow + + +class TestUiMainWindow(unittest.TestCase): + + @patch('PySide6.QtWidgets.QDialog') + @patch('Exp_type_GUI.Ui_Experiment') + def test_show_exp(self, mock_ui_experiment, mock_qdialog): + """Test the show_exp method.""" + # Arrange + mock_window = MagicMock() + mock_ui_experiment_instance = mock_ui_experiment.return_value + mock_ui_experiment_instance.setupUi = MagicMock() + + ui_main_window = Ui_MainWindow() + ui_main_window.window = mock_window + + # Act + exp = ui_main_window.show_exp() + + # Assert + mock_ui_experiment_instance.setupUi.assert_called_once_with(mock_window) + self.assertEqual(exp, mock_ui_experiment_instance) + + @patch('PySide6.QtWidgets.QMainWindow') + @patch('LSV_GUI.Ui_LSV') + def test_show_LSVwindow(self, mock_ui_lsv, mock_qmainwindow): + """Test the show_LSVwindow method.""" + # Arrange + mock_window = MagicMock() + mock_ui_lsv_instance = mock_ui_lsv.return_value + mock_ui_lsv_instance.setupUi = MagicMock() + + ui_main_window = Ui_MainWindow() + ui_main_window.window = mock_window + + # Act + lsv, window = ui_main_window.show_LSVwindow() + + # Assert + mock_ui_lsv_instance.setupUi.assert_called_once_with(mock_window) + self.assertEqual(lsv, mock_ui_lsv_instance) + self.assertEqual(window, mock_window) + + @patch('PySide6.QtWidgets.QMainWindow') + @patch('CV_GUI.Ui_CV') + def test_show_CVwindow(self, mock_ui_cv, mock_qmainwindow): + """Test the show_CVwindow method.""" + # Arrange + mock_window = MagicMock() + mock_ui_cv_instance = mock_ui_cv.return_value + mock_ui_cv_instance.setupUi = MagicMock() + + ui_main_window = Ui_MainWindow() + ui_main_window.window = mock_window + + # Act + cv, window = ui_main_window.show_CVwindow() + + # Assert + mock_ui_cv_instance.setupUi.assert_called_once_with(mock_window) + self.assertEqual(cv, mock_ui_cv_instance) + self.assertEqual(window, mock_window) + + @patch('PySide6.QtWidgets.QMainWindow') + @patch('CA_GUI.Ui_CA') + def test_show_CAwindow(self, mock_ui_ca, mock_qmainwindow): + """Test the show_CAwindow method.""" + # Arrange + mock_window = MagicMock() + mock_ui_ca_instance = mock_ui_ca.return_value + mock_ui_ca_instance.setupUi = MagicMock() + + ui_main_window = Ui_MainWindow() + ui_main_window.window = mock_window + + # Act + ca, window = ui_main_window.show_CAwindow() + + # Assert + mock_ui_ca_instance.setupUi.assert_called_once_with(mock_window) + self.assertEqual(ca, mock_ui_ca_instance) + self.assertEqual(window, mock_window) + + @patch('PySide6.QtWidgets.QWidget') + @patch('GUI_load_config.Ui_Load') + def test_load_config(self, mock_ui_load, mock_qwidget): + """Test the load_config method.""" + # Arrange + mock_ui_load_instance = mock_ui_load.return_value + mock_ui_load_instance.setupUi = MagicMock() + + ui_main_window = Ui_MainWindow() + mock_window = MagicMock() + + # Act + result = ui_main_window.load_config() + + # Assert + mock_ui_load_instance.setupUi.assert_called_once_with(mock_window) + self.assertIsNone(result) # load_config does not return anything + + @patch('PySide6.QtWidgets.QMainWindow') + @patch('PySide6.QtGui.QPalette') + @patch('PySide6.QtGui.QBrush') + @patch('PySide6.QtGui.QColor') + @patch('PySide6.QtGui.QIcon') + @patch('PySide6.QtGui.QPixmap') + def test_setupUi(self, mock_pixmap, mock_icon, mock_color, mock_brush, mock_palette, mock_main_window): + """Test the setupUi method.""" + # Arrange + mock_main_window_instance = MagicMock() + ui_main_window = Ui_MainWindow() + + # Act + ui_main_window.setupUi(mock_main_window_instance) + + # Assert + mock_main_window_instance.setObjectName.assert_called_once_with("MainWindow") + mock_main_window_instance.resize.assert_called_once_with(1272, 953) + mock_main_window_instance.setPalette.assert_called_once() + mock_main_window_instance.setWindowIcon.assert_called_once() + mock_main_window_instance.setCentralWidget.assert_called_once() + mock_main_window_instance.setMenuBar.assert_called_once() + mock_main_window_instance.setStatusBar.assert_called_once() + + +if __name__ == '__main__': + unittest.main() From 90714c5eb988f9499df80ea0f99c919a604cd50e Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:56:01 +0100 Subject: [PATCH 22/23] commit --- src/GUI/gui_test/test_gui_config_reader.py | 121 +++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/GUI/gui_test/test_gui_config_reader.py diff --git a/src/GUI/gui_test/test_gui_config_reader.py b/src/GUI/gui_test/test_gui_config_reader.py new file mode 100644 index 00000000..b8b41edb --- /dev/null +++ b/src/GUI/gui_test/test_gui_config_reader.py @@ -0,0 +1,121 @@ +import unittest +from unittest.mock import mock_open, patch + +import yaml + +from src.GUI.code.GUI_config_reader import ( + check_config_inputs, + get_adv_params, + get_ca_params, + get_cv_params, + get_exp_time, + get_exp_type, + get_lsv_params, + get_output_params, + get_rest, + get_steps, + parse_config_file, +) + + +class TestGUIConfigReader(unittest.TestCase): + + def setUp(self): + # Sample configuration data + self.config_data = { + "general_parameters": { + "data_output_filename": "output", + "data_output_path": "desktop", + "experiment_type": "cyclic_voltammetry", + "rest_time": 10, + "step_number": 5, + }, + "linear_sweep_voltammetry": { + "start_voltage": 0, + "end_voltage": 1, + "sweep_rate": 0.1, + }, + "chronoamperometry": { + "voltage": 0.5, + "time": 60, + }, + "cyclic_voltammetry": { + "start_voltage": -0.5, + "first_turnover_voltage": 0.0, + "second_turnover_voltage": 0.5, + "sweep_rate": 0.1, + "number_of_cycles": 3, + }, + "advanced_parameters": { + "conversion_factor": 1.0, + "setpoint_gain": 10, + "setpoint_offset": 0, + "shunt_resistor": 1000, + "time_step": 0.01, + "average_number": 5, + } + } + + @patch("builtins.open", new_callable=mock_open, read_data=yaml.dump({})) + def test_parse_config_file(self, mock_file): + mock_file.return_value.read.return_value = yaml.dump(self.config_data) + config = parse_config_file("dummy_path") + self.assertEqual(config, self.config_data) + + def test_get_output_params(self): + out_name_ts, out_path = get_output_params(self.config_data) + self.assertTrue("output_", out_name_ts) + self.assertTrue(out_path.endswith("Desktop")) + + def test_get_lsv_params(self): + start_voltage, end_voltage, sweep_rate = get_lsv_params(self.config_data) + self.assertEqual(start_voltage, 0) + self.assertEqual(end_voltage, 1) + self.assertEqual(sweep_rate, 0.1) + + def test_get_ca_params(self): + voltage, time = get_ca_params(self.config_data) + self.assertEqual(voltage, 0.5) + self.assertEqual(time, 60) + + def test_get_cv_params(self): + params = get_cv_params(self.config_data) + self.assertEqual(params[0], -0.5) + self.assertEqual(params[1], 0.0) + self.assertEqual(params[2], 0.5) + self.assertEqual(params[3], 0.1) + self.assertEqual(params[4], 3) + + def test_get_exp_type(self): + exp_type = get_exp_type(self.config_data) + self.assertEqual(exp_type, "cyclic_voltammetry") + + def test_get_exp_time(self): + exp_time = get_exp_time(self.config_data) + self.assertEqual(exp_time, 60) + + def test_get_rest(self): + rest_time = get_rest(self.config_data) + self.assertEqual(rest_time, 10) + + def test_get_steps(self): + step_number = get_steps(self.config_data) + self.assertEqual(step_number, 5) + + def test_get_adv_params(self): + adv_params = get_adv_params(self.config_data) + self.assertEqual(adv_params[0], 1.0) + self.assertEqual(adv_params[1], 10) + self.assertEqual(adv_params[2], 0) + self.assertEqual(adv_params[3], 1000) + self.assertEqual(adv_params[4], 0.01) + self.assertEqual(adv_params[5], 5) + + def test_check_config_inputs(self): + self.assertTrue(check_config_inputs(10)) + self.assertTrue(check_config_inputs("10")) + self.assertFalse(check_config_inputs("not_a_number")) + + +if __name__ == "__main__": + unittest.main() From 2c866dd385f68bdf1a3aa4cf3fd905eaa3deb2e0 Mon Sep 17 00:00:00 2001 From: Abangma Jessika <127256410+InongoMag@users.noreply.github.com> Date: Sat, 25 Jan 2025 03:59:27 +0100 Subject: [PATCH 23/23] commit --- src/GUI/gui_test/test_exp_type_gui.py | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/GUI/gui_test/test_exp_type_gui.py diff --git a/src/GUI/gui_test/test_exp_type_gui.py b/src/GUI/gui_test/test_exp_type_gui.py new file mode 100644 index 00000000..d3c89a3e --- /dev/null +++ b/src/GUI/gui_test/test_exp_type_gui.py @@ -0,0 +1,55 @@ +import unittest + +from PySide6 import QtWidgets + +from src.GUI.code.Exp_type_GUI import Ui_Experiment + + +class TestUiExperiment(unittest.TestCase): + def setUp(self): + """Set up the application and the UI before each test.""" + self.app = QtWidgets.QApplication([]) + self.window = QtWidgets.QDialog() + self.ui = Ui_Experiment() + self.ui.setupUi(self.window) + + def test_window_title(self): + """Test if the window title is set correctly.""" + self.assertEqual(self.window.windowTitle(), "Experiment Type") + + def test_labels_exist(self): + """Test if all labels are created and have the correct text.""" + self.assertIsNotNone(self.ui.ca_label) + self.assertIsNotNone(self.ui.cv_label) + self.assertIsNotNone(self.ui.lsv_label) + self.assertIsNotNone(self.ui.question_label) + + self.assertEqual(self.ui.ca_label.text(), "CA") + self.assertEqual(self.ui.cv_label.text(), "CV") + self.assertEqual(self.ui.lsv_label.text(), "LSV") + self.assertEqual(self.ui.question_label.text(), "Please select an experiment") + + def test_buttons_exist(self): + """Test if all buttons are created.""" + self.assertIsNotNone(self.ui.ca_button) + self.assertIsNotNone(self.ui.cv_button) + self.assertIsNotNone(self.ui.lsv_button) + + # Check if buttons have icons set + self.assertTrue(self.ui.ca_button.icon().isNull() is False) + self.assertTrue(self.ui.cv_button.icon().isNull() is False) + self.assertTrue(self.ui.lsv_button.icon().isNull() is False) + + def test_button_box_exists(self): + """Test if the button box is created and has the correct buttons.""" + self.assertIsNotNone(self.ui.buttonBox) + self.assertTrue(QtWidgets.QDialogButtonBox.Ok in self.ui.buttonBox.standardButtons()) + self.assertTrue(QtWidgets.QDialogButtonBox.Cancel in self.ui.buttonBox.standardButtons()) + + def tearDown(self): + """Clean up after each test.""" + self.window.close() + + +if __name__ == "__main__": + unittest.main()