From 5dbedadf0ae6fef3e4aa0518dda9f999e6a38b93 Mon Sep 17 00:00:00 2001 From: "Dr.-Ing. Amilcar do Carmo Lucas" Date: Thu, 16 Jan 2025 04:03:48 +0100 Subject: [PATCH] IMPROVEMENT: Add more tests --- .github/workflows/unit-tests.yml | 7 +- tests/test__main__.py | 67 +++++++++++++++++ tests/test_ardupilot_methodic_configurator.py | 35 --------- tests/test_argparse_check_range.py | 71 ++++++++++++++++++ tests/test_internationalization.py | 74 +++++++++++++++++++ 5 files changed, 218 insertions(+), 36 deletions(-) create mode 100755 tests/test__main__.py delete mode 100755 tests/test_ardupilot_methodic_configurator.py create mode 100755 tests/test_argparse_check_range.py create mode 100755 tests/test_internationalization.py diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index b790b39..7d6c93b 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -55,8 +55,13 @@ jobs: run: | cd tests python -m coverage run -m unittest test_annotate_params.py - python -m coverage run -m unittest test_ardupilot_methodic_configurator.py + python -m coverage run -m unittest test_argparse_check_range.py python -m coverage run -m unittest test_backend_filesystem.py + python -m coverage run -m unittest test_battery_cell_voltages.py + python -m coverage run -m unittest test_common_arguments.py python -m coverage run -m unittest test_extract_param_defaults.py + python -m coverage run -m unittest test_internationalization.py + python -m coverage run -m unittest test_middleware_template_overview.py python -m coverage run -m unittest test_param_pid_adjustment_update.py + python -m coverage run -m unittest version_test.py python -m coverage html diff --git a/tests/test__main__.py b/tests/test__main__.py new file mode 100755 index 0000000..c226049 --- /dev/null +++ b/tests/test__main__.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +""" +Tests for the __main__.py file. + +This file is part of Ardupilot methodic configurator. https://github.com/ArduPilot/MethodicConfigurator + +SPDX-FileCopyrightText: 2024 Amilcar do Carmo Lucas + +SPDX-License-Identifier: GPL-3.0-or-later +""" + +import argparse +import unittest +from unittest.mock import MagicMock, patch + +from ardupilot_methodic_configurator.__main__ import argument_parser, component_editor, connect_to_fc_and_read_parameters + + +class TestArgumentParser(unittest.TestCase): # pylint: disable=missing-class-docstring + @patch( + "argparse.ArgumentParser.parse_args", return_value=argparse.Namespace(conn="tcp:127.0.0.1:5760", params="params_dir") + ) + def test_argument_parser(self, mock_args) -> None: + args = argument_parser() + assert args.conn == "tcp:127.0.0.1:5760" + assert args.params == "params_dir" + + +class TestMainFunctions(unittest.TestCase): + @patch("ardupilot_methodic_configurator.__main__.FlightController") + def test_connect_to_fc_and_read_parameters(self, mock_flight_controller) -> None: + mock_fc = mock_flight_controller.return_value + mock_fc.connect.return_value = "" + mock_fc.info.vehicle_type = "quad" + mock_fc.info.flight_sw_version_and_type = "v1.0" + mock_fc.info.vendor = "vendor" + mock_fc.info.firmware_type = "type" + + args = argparse.Namespace(device="test_device", vehicle_type="", reboot_time=5) + flight_controller, vehicle_type = connect_to_fc_and_read_parameters(args) + assert flight_controller == mock_fc + assert vehicle_type == "quad" + + @patch("ardupilot_methodic_configurator.__main__.ComponentEditorWindow") + @patch("ardupilot_methodic_configurator.__main__.LocalFilesystem") + @patch("ardupilot_methodic_configurator.__main__.ProgramSettings") + def test_component_editor(self, mock_program_settings, mock_local_filesystem, mock_component_editor_window) -> None: + mock_program_settings = mock_program_settings.return_value + mock_program_settings.get_setting.return_value = True + + mock_local_filesystem = mock_local_filesystem.return_value + mock_local_filesystem.doc_dict = {} + + mock_fc = MagicMock() + mock_fc.fc_parameters = {"param1": "value1"} + mock_fc.info.flight_sw_version_and_type = "v1.0" + mock_fc.info.vendor = "vendor" + mock_fc.info.firmware_type = "type" + + args = argparse.Namespace(skip_component_editor=False) + component_editor(args, mock_fc, "quad", mock_local_filesystem, None) + mock_component_editor_window.assert_called_once() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_ardupilot_methodic_configurator.py b/tests/test_ardupilot_methodic_configurator.py deleted file mode 100755 index a34530c..0000000 --- a/tests/test_ardupilot_methodic_configurator.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 - -""" -Tests for the __main__.py file. - -This file is part of Ardupilot methodic configurator. https://github.com/ArduPilot/MethodicConfigurator - -SPDX-FileCopyrightText: 2024 Amilcar do Carmo Lucas - -SPDX-License-Identifier: GPL-3.0-or-later -""" - -# pylint: skip-file - -import argparse -import unittest -from unittest.mock import patch - -# from unittest.mock import MagicMock -# from unittest.mock import mock_open -from ardupilot_methodic_configurator.__main__ import argument_parser - - -class TestArgumentParser(unittest.TestCase): # pylint: disable=missing-class-docstring - @patch( - "argparse.ArgumentParser.parse_args", return_value=argparse.Namespace(conn="tcp:127.0.0.1:5760", params="params_dir") - ) - def test_argument_parser(self, mock_args) -> None: - args = argument_parser() - assert args.conn == "tcp:127.0.0.1:5760" - assert args.params == "params_dir" - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_argparse_check_range.py b/tests/test_argparse_check_range.py new file mode 100755 index 0000000..acef8ac --- /dev/null +++ b/tests/test_argparse_check_range.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 + +""" +Tests for the argparse_check_range.py file. + +This file is part of Ardupilot methodic configurator. https://github.com/ArduPilot/MethodicConfigurator + +SPDX-FileCopyrightText: 2024-2025 Amilcar do Carmo Lucas + +SPDX-License-Identifier: GPL-3.0-or-later +""" + +import unittest +from argparse import ArgumentParser + +import pytest + +from ardupilot_methodic_configurator.argparse_check_range import CheckRange + + +class TestCheckRange(unittest.TestCase): + def setUp(self) -> None: + self.parser = ArgumentParser() + + def test_init_with_both_min_and_inf(self) -> None: + with pytest.raises(ValueError, match="either min or inf, but not both"): + self.parser.add_argument("--test", action=CheckRange, min=0, inf=0) + + def test_init_with_both_max_and_sup(self) -> None: + with pytest.raises(ValueError, match="either max or sup, but not both"): + self.parser.add_argument("--test", action=CheckRange, max=10, sup=10) + + def test_interval_with_min_and_max(self) -> None: + action = CheckRange(option_strings=["--test"], dest="test", min=0, max=10) + assert action.interval() == "valid range: [0, 10]" + + def test_interval_with_inf_and_sup(self) -> None: + action = CheckRange(option_strings=["--test"], dest="test", inf=0, sup=10) + assert action.interval() == "valid range: (0, 10)" + + def test_interval_with_no_bounds(self) -> None: + action = CheckRange(option_strings=["--test"], dest="test") + assert action.interval() == "valid range: (-infinity, +infinity)" + + def test_call_with_non_number_value(self) -> None: + self.parser.add_argument("--test", action=CheckRange, min=0, max=10) + with pytest.raises(SystemExit) as excinfo: + self.parser.parse_args(["--test", "non-number"]) + assert str(excinfo.value) == "2" + + def test_call_with_value_out_of_range(self) -> None: + self.parser.add_argument("--test", action=CheckRange, min=0, max=10) + with pytest.raises(SystemExit) as excinfo: + self.parser.parse_args(["--test", "11"]) + assert str(excinfo.value) == "2" + + def test_call_with_value_equal_to_inf(self) -> None: + self.parser.add_argument("--test", action=CheckRange, inf=0, sup=10) + with pytest.raises(SystemExit) as excinfo: + self.parser.parse_args(["--test", "0"]) + assert str(excinfo.value) == "2" + + def test_call_with_value_equal_to_sup(self) -> None: + self.parser.add_argument("--test", action=CheckRange, inf=0, sup=10) + with pytest.raises(SystemExit) as excinfo: + self.parser.parse_args(["--test", "10"]) + assert str(excinfo.value) == "2" + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_internationalization.py b/tests/test_internationalization.py new file mode 100755 index 0000000..eff6e6e --- /dev/null +++ b/tests/test_internationalization.py @@ -0,0 +1,74 @@ +#!/usr/bin/python3 + +""" +Tests for the internationalization.py file. + +This file is part of Ardupilot methodic configurator. https://github.com/ArduPilot/MethodicConfigurator + +SPDX-FileCopyrightText: 2024-2025 Amilcar do Carmo Lucas + +SPDX-License-Identifier: GPL-3.0-or-later +""" + +import argparse +import unittest + +import pytest + +from ardupilot_methodic_configurator.internationalization import LANGUAGE_CHOICES, identity_function, load_translation + + +class TestInternationalization(unittest.TestCase): + def test_default_language_is_english(self) -> None: + assert LANGUAGE_CHOICES[0] == "en" + + def test_load_translation_default(self) -> None: + translation_function = load_translation() + assert translation_function == identity_function + + def test_identity_function(self) -> None: + test_string = "test" + assert identity_function(test_string) == test_string + + @pytest.mark.usefixtures("mock_args_de") + def test_load_translation_with_language(self) -> None: + translation_function = load_translation() + assert translation_function != identity_function + + @pytest.mark.usefixtures("mock_args_invalid") + def test_load_translation_with_invalid_language(self) -> None: + translation_function = load_translation() + assert translation_function == identity_function + + @pytest.mark.usefixtures("mock_gettext", "mock_args_zh_cn") + def test_load_translation_fallback(self) -> None: + translation_function = load_translation() + assert translation_function == identity_function + + def test_language_choices(self) -> None: + expected_languages = ["en", "zh_CN", "pt", "de"] + assert expected_languages == LANGUAGE_CHOICES + + +@pytest.fixture +def mock_args_de(mocker) -> None: + mocker.patch("argparse.ArgumentParser.parse_known_args", return_value=(argparse.Namespace(language="de"), [])) + + +@pytest.fixture +def mock_args_invalid(mocker) -> None: + mocker.patch("argparse.ArgumentParser.parse_known_args", return_value=(argparse.Namespace(language="invalid"), [])) + + +@pytest.fixture +def mock_args_zh_cn(mocker) -> None: + mocker.patch("argparse.ArgumentParser.parse_known_args", return_value=(argparse.Namespace(language="zh_CN"), [])) + + +@pytest.fixture +def mock_gettext(mocker) -> None: + mocker.patch("gettext.translation", side_effect=FileNotFoundError) + + +if __name__ == "__main__": + unittest.main()