From 80c1c928ca156c9e35a06a122351c77487809cbf Mon Sep 17 00:00:00 2001 From: do Carmo Lucas Date: Tue, 18 Jun 2024 19:20:29 +0200 Subject: [PATCH] IMPROVEMENT: increased test coverage --- MethodicConfigurator/annotate_params.py | 7 +- unittests/annotate_params_test.py | 102 +++++++++++++++++++++++- unittests/annotate_params_test.sh | 21 ++++- 3 files changed, 124 insertions(+), 6 deletions(-) diff --git a/MethodicConfigurator/annotate_params.py b/MethodicConfigurator/annotate_params.py index 26dcffb7..42a0cac6 100755 --- a/MethodicConfigurator/annotate_params.py +++ b/MethodicConfigurator/annotate_params.py @@ -673,8 +673,13 @@ def get_xml_url(vehicle_type: str, firmware_version: str) -> str: 'Heli': 'versioned/Heli/stable-', 'SITL': 'versioned/SITL/stable-' } + try: + vehicle_subdir = vehicle_parm_subdir[vehicle_type] + firmware_version + except KeyError as e: + raise ValueError(f"Vehicle type '{vehicle_type}' is not supported.") from e + if firmware_version: - xml_url = BASE_URL + vehicle_parm_subdir[vehicle_type] + firmware_version + "/" + xml_url = BASE_URL + vehicle_subdir + "/" else: xml_url = BASE_URL + vehicle_type + "/" return xml_url diff --git a/unittests/annotate_params_test.py b/unittests/annotate_params_test.py index 4affa84f..95c263f5 100755 --- a/unittests/annotate_params_test.py +++ b/unittests/annotate_params_test.py @@ -11,16 +11,20 @@ # pylint: skip-file import tempfile -from unittest.mock import patch, mock_open +from unittest.mock import patch, mock_open, Mock import os import unittest import requests import mock +import argparse from xml.etree import ElementTree as ET # no parsing, just data-structure manipulation from defusedxml import ElementTree as DET # just parsing, no data-structure manipulation +from MethodicConfigurator.annotate_params import arg_parser +from MethodicConfigurator.annotate_params import main from MethodicConfigurator.annotate_params import get_xml_data +from MethodicConfigurator.annotate_params import get_xml_url from MethodicConfigurator.annotate_params import remove_prefix from MethodicConfigurator.annotate_params import split_into_lines from MethodicConfigurator.annotate_params import create_doc_dict @@ -79,7 +83,7 @@ def test_get_xml_data_local_file(self, mock_load_param, mock_isfile, mock_open): self.assertIsInstance(result, ET.Element) # Assert that the file was opened correctly - mock_open.assert_called_once_with('./test.xml', 'r', encoding='utf-8') + mock_open.assert_called_once_with(os.path.join(".", "test.xml"), 'r', encoding='utf-8') @patch('requests.get') def test_get_xml_data_remote_file(self, mock_get): @@ -552,5 +556,99 @@ def test_empty_parameter_file(self): self.assertEqual(updated_content, "") +class AnnotateParamsTest(unittest.TestCase): + + @patch('argparse.ArgumentParser.parse_args') + def test_arg_parser_valid_arguments(self, mock_parse_args): + test_args = ['--vehicle-type', 'ArduCopter', '--sort', 'none', '--target', 'parameters'] + mock_parse_args.return_value = argparse.Namespace( + vehicle_type='ArduCopter', + sort='none', + target='parameters', + verbose=False, + max_line_length=100, + ) + with patch('sys.argv', test_args): + args = arg_parser() + self.assertEqual(args.vehicle_type, 'ArduCopter') + self.assertEqual(args.sort, 'none') + self.assertEqual(args.target, 'parameters') + self.assertEqual(args.verbose, False) + self.assertEqual(args.max_line_length, 100) + + def test_arg_parser_invalid_vehicle_type(self): + test_args = ['--vehicle-type', 'InvalidType', '--sort', 'none', '--target', 'parameters'] + with patch('sys.argv', test_args): + with self.assertRaises(SystemExit): + arg_parser() + + def test_arg_parser_invalid_sort_option(self): + test_args = ['--vehicle-type', 'ArduCopter', '--sort', 'invalid', '--target', 'parameters'] + with patch('sys.argv', test_args): + with self.assertRaises(SystemExit): + arg_parser() + + def test_arg_parser_invalid_target_option(self): + test_args = ['--vehicle-type', 'ArduCopter', '--sort', 'none', '--target', 'invalid'] + with patch('sys.argv', test_args): + with self.assertRaises(SystemExit): + arg_parser() + + +class TestAnnotateParamsExceptionHandling(unittest.TestCase): + + @patch('annotate_params.arg_parser') + @patch('annotate_params.get_xml_url') + @patch('annotate_params.get_xml_dir') + @patch('annotate_params.parse_parameter_metadata') + @patch('annotate_params.update_parameter_documentation') + @patch('builtins.open', new_callable=mock_open) + def test_main_ioerror( + self, mock_file, mock_update, mock_parse_metadata, mock_get_xml_dir, mock_get_xml_url, mock_arg_parser + ): + mock_arg_parser.return_value = Mock( + vehicle_type='ArduCopter', + target='.', + sort='none', + delete_documentation_annotations=False, + verbose=False + ) + mock_file.side_effect = IOError("Mocked IO Error") + + with self.assertRaises(SystemExit) as cm: + main() + + self.assertEqual(cm.exception.code, 1) + + @patch('annotate_params.arg_parser') + @patch('annotate_params.get_xml_url') + @patch('annotate_params.get_xml_dir') + @patch('annotate_params.parse_parameter_metadata') + @patch('annotate_params.update_parameter_documentation') + @patch('builtins.open', new_callable=mock_open) + def test_main_oserror( + self, mock_file, mock_update, mock_parse_metadata, mock_get_xml_dir, mock_get_xml_url, mock_arg_parser + ): + mock_arg_parser.return_value = Mock( + vehicle_type='ArduCopter', + target='.', + sort='none', + delete_documentation_annotations=False, + verbose=False + ) + mock_file.side_effect = OSError("Mocked OS Error") + + with self.assertRaises(SystemExit) as cm: + main() + + self.assertEqual(cm.exception.code, 1) + + @patch('annotate_params.get_xml_url') + def test_get_xml_url_exception(self, mock_get_xml_url): + mock_get_xml_url.side_effect = ValueError("Mocked Value Error") + with self.assertRaises(ValueError): + get_xml_url('NonExistingVehicle', '4.0') + + if __name__ == '__main__': unittest.main() diff --git a/unittests/annotate_params_test.sh b/unittests/annotate_params_test.sh index b742b695..f265ee7b 100755 --- a/unittests/annotate_params_test.sh +++ b/unittests/annotate_params_test.sh @@ -2,8 +2,23 @@ # # SPDX-FileCopyrightText: 2024 Amilcar do Carmo Lucas # -#SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-License-Identifier: GPL-3.0-or-later -PYTHONPATH=../MethodicConfigurator python3 -m coverage run -m unittest annotate_params_test.py -python3 -m coverage html +REQUIRED_PKGS=("coverage" "mock" "defusedxml") + +is_installed() { + pip show "$1" > /dev/null 2>&1 +} + +for pkg in "${REQUIRED_PKGS[@]}"; do + if ! is_installed "$pkg"; then + echo "Installing $pkg..." + pip install "$pkg" + else + echo "$pkg is already installed." + fi +done + +PYTHONPATH=../MethodicConfigurator python -m coverage run -m unittest annotate_params_test.py +python -m coverage html firefox htmlcov/annotate_params_py.html \ No newline at end of file