Skip to content
Merged

Spdx #1178

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ repos:
stages: [pre-push]

- repo: https://github.com/fsfe/reuse-tool
rev: v5.1.1
rev: v6.2.0
hooks:
- id: reuse-lint-file

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ def store_connection(connection_string: str) -> None:

settings = ProgramSettings._get_settings_as_dict()
history = settings.get("connection_history", [])
if not isinstance(history, list):
logging_error("The ProgramSetting connection_history was not a list")
history = []

if connection_string in history:
history.remove(connection_string)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def check_for_software_updates() -> bool:
return False


if __name__ == "__main__":
if __name__ == "__main__": # pragma: no cover
logging_basicConfig(level=logging_getLevelName("DEBUG"), format="%(asctime)s - %(levelname)s - %(message)s")
logging_warning(
_(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ def argument_parser() -> argparse.Namespace: # pragma: no cover
return add_common_arguments(parser).parse_args()


def setup_logging(loglevel: str) -> None:
def setup_logging(loglevel: str) -> None: # pragma: no cover
"""
Set up logging with the specified log level.

Expand Down
2 changes: 1 addition & 1 deletion tests/test__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from ardupilot_methodic_configurator.backend_flightcontroller import DEVICE_FC_PARAM_FROM_FILE
from ardupilot_methodic_configurator.frontend_tkinter_usage_popup_window import PopupWindow

# pylint: disable=,too-many-lines,redefined-outer-name,too-few-public-methods
# pylint: disable=too-many-lines,redefined-outer-name,too-few-public-methods

# ====== Fixtures following pytest_testing_instructions.md ======

Expand Down
2 changes: 1 addition & 1 deletion tests/test_backend_filesystem_program_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ def test_user_can_load_existing_settings_file(self, mock_user_config, sample_pro
expected_result["annotate_docs_into_param_files"] = False # Added by default
expected_result["gui_complexity"] = "simple" # Added by default
expected_result["motor_test"] = {"duration": 2, "throttle_pct": 10} # Added by default
expected_result["connection_history"] = [] # Added for port connection string to save
expected_result["connection_history"] = [] # Added by default
expected_result["display_usage_popup"]["component_editor_validation"] = True # Added by default
expected_result["display_usage_popup"]["workflow_explanation"] = True # Added by default
expected_result["display_usage_popup"]["bitmask_parameter_editor"] = True # Added by default
Expand Down
75 changes: 1 addition & 74 deletions tests/test_frontend_tkinter_motor_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
MotorTestView,
MotorTestWindow,
_create_motor_test_view,
argument_parser,
main,
register_motor_test_plugin,
)

Expand Down Expand Up @@ -796,78 +794,7 @@ def fake_base_init(self) -> None:


class TestCommandLineWorkflow:
"""Verifies CLI helpers, entry points, and plugin registration hooks."""

def test_argument_parser_wires_common_arguments(self, mocker) -> None:
"""
Argument parser delegates to backend helpers and parses args.

GIVEN: Patched backend adders returning the provided parser
WHEN: argument_parser is invoked
THEN: Each backend helper receives the parser instance
"""
parser_mock = MagicMock()
fc_adder = mocker.patch(
"ardupilot_methodic_configurator.backend_flightcontroller.FlightController.add_argparse_arguments",
side_effect=lambda parser: parser,
)
fs_adder = mocker.patch(
"ardupilot_methodic_configurator.backend_filesystem.LocalFilesystem.add_argparse_arguments",
side_effect=lambda parser: parser,
)
mocker.patch(
"ardupilot_methodic_configurator.frontend_tkinter_motor_test.add_common_arguments",
return_value=parser_mock,
)
parser_mock.parse_args.return_value = SimpleNamespace()

argument_parser()
assert fc_adder.called
assert fs_adder.called
fc_parser = fc_adder.call_args.args[0]
fs_parser = fs_adder.call_args.args[0]
assert fc_parser is fs_parser
parser_mock.parse_args.assert_called_once_with()

def test_main_success_and_failure_paths(self, mocker) -> None:
"""
The standalone entry point starts the window and reports failures.

GIVEN: Patched dependencies for ApplicationState, window, and dialogs
WHEN: main succeeds once and fails the next time
THEN: The window mainloop executes and failures trigger showerror
"""
args = SimpleNamespace()
mocker.patch(
"ardupilot_methodic_configurator.frontend_tkinter_motor_test.argument_parser",
return_value=args,
)
state = SimpleNamespace(flight_controller=MagicMock(), local_filesystem=MagicMock())
mocker.patch(
"ardupilot_methodic_configurator.frontend_tkinter_motor_test.ApplicationState",
return_value=state,
)
mocker.patch("ardupilot_methodic_configurator.frontend_tkinter_motor_test.setup_logging")
mocker.patch("ardupilot_methodic_configurator.frontend_tkinter_motor_test.initialize_flight_controller_and_filesystem")
window_mock = MagicMock()
window_mock.root = MagicMock()
mocker.patch(
"ardupilot_methodic_configurator.frontend_tkinter_motor_test.MotorTestWindow",
return_value=window_mock,
)
mocker.patch(
"ardupilot_methodic_configurator.frontend_tkinter_motor_test.MotorTestDataModel", return_value=MagicMock()
)

main()
window_mock.root.mainloop.assert_called_once()

mocker.patch(
"ardupilot_methodic_configurator.frontend_tkinter_motor_test.MotorTestWindow",
side_effect=RuntimeError("boom"),
)
main()
state.flight_controller.disconnect.assert_called()
"""Verifies plugin registration hooks."""

def test_plugin_registration_and_factory_creation(
self,
Expand Down
129 changes: 1 addition & 128 deletions tests/test_frontend_tkinter_project_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
VehicleProjectCreationError,
)
from ardupilot_methodic_configurator.frontend_tkinter_base_window import BaseWindow
from ardupilot_methodic_configurator.frontend_tkinter_project_creator import VehicleProjectCreatorWindow, argument_parser, main
from ardupilot_methodic_configurator.frontend_tkinter_project_creator import VehicleProjectCreatorWindow

# pylint: disable=redefined-outer-name, unused-argument, duplicate-code

Expand Down Expand Up @@ -432,130 +432,3 @@ def test_user_workflow_with_flight_controller_connected(self, mock_project_manag
# Assert: Window was created with flight controller configuration
# The NewVehicleProjectSettings.get_all_settings_metadata should be called with fc_connected=True
assert mock_settings.get_all_settings_metadata.called


class TestArgumentParserAndMainFunction:
"""Test argument parser and main function for coverage."""

def test_user_can_parse_command_line_arguments_successfully(self) -> None:
"""
User can parse command line arguments for the application.

GIVEN: A user wants to run the application with command line arguments
WHEN: Arguments are parsed with argument_parser
THEN: Parser should return valid namespace with expected defaults
"""
# Arrange: Mock sys.argv to provide clean arguments
with patch("sys.argv", ["test_script"]):
# Act: Parse arguments using the argument_parser function
args = argument_parser()

# Assert: Arguments parsed correctly with proper attributes
assert hasattr(args, "loglevel")
assert hasattr(args, "vehicle_dir")
assert hasattr(args, "vehicle_type")
assert hasattr(args, "allow_editing_template_files")
assert hasattr(args, "save_component_to_system_templates")

def test_user_can_access_main_function_window_creation_path(self) -> None:
"""
User can access main function window creation path for coverage.

GIVEN: A user runs the main function with valid configuration
WHEN: Main function creates window
THEN: Window creation path should be covered
"""
# Arrange: Mock all dependencies for main function
with (
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.argument_parser") as mock_parser,
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.logging_basicConfig"),
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.logging_warning"),
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.LocalFilesystem"),
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.VehicleProjectManager") as mock_pm,
patch(
"ardupilot_methodic_configurator.frontend_tkinter_project_creator.VehicleProjectCreatorWindow"
) as mock_window_class,
):
# Set up mock arguments
mock_args = MagicMock()
mock_args.loglevel = "INFO"
mock_args.vehicle_dir = "/test/dir"
mock_args.vehicle_type = "ArduCopter"
mock_args.allow_editing_template_files = False
mock_args.save_component_to_system_templates = False
mock_parser.return_value = mock_args

# Set up project manager with files found
mock_project_manager = MagicMock()
mock_project_manager.get_file_parameters_list.return_value = ["file1.param", "file2.param"]
mock_pm.return_value = mock_project_manager

# Set up window mock
mock_window = MagicMock()
mock_window_class.return_value = mock_window

# Act: Call main function (covers lines 491, 493-494)
main()

# Assert: Window was created and mainloop called
mock_window_class.assert_called_once_with(mock_project_manager)
mock_window.root.mainloop.assert_called_once()


class TestMainFunctionErrorPaths:
"""Test main function error paths for coverage."""

def test_user_sees_error_when_no_parameter_files_found_in_main(self) -> None:
"""
User sees error when no parameter files found in main function.

GIVEN: A main function call with no parameter files
WHEN: Main function checks for parameter files but finds none
THEN: Error should be logged and logged appropriately
"""
# Arrange: Mock dependencies with empty file list
with (
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.argument_parser") as mock_parser,
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.logging_basicConfig"),
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.logging_warning"),
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.logging_error") as mock_error,
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.LocalFilesystem"),
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.VehicleProjectManager") as mock_pm,
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.VehicleProjectCreatorWindow"),
):
# Set up mock arguments
mock_args = MagicMock()
mock_args.loglevel = "INFO"
mock_args.vehicle_dir = "/test/dir"
mock_args.vehicle_type = "ArduCopter"
mock_args.allow_editing_template_files = False
mock_args.save_component_to_system_templates = False
mock_parser.return_value = mock_args

# Set up project manager with NO files found (covers line 491)
mock_project_manager = MagicMock()
mock_project_manager.get_file_parameters_list.return_value = [] # Empty list
mock_pm.return_value = mock_project_manager

# Act: Call main function - should hit error path
main()

# Assert: Error logging called for missing files (line 491)
mock_error.assert_called()

def test_if_name_main_calls_main_function(self) -> None:
"""
Test that if __name__ == '__main__' calls main function.

GIVEN: A module run as main script
WHEN: The if __name__ == '__main__' block executes
THEN: The main function should be called
"""
# This test covers line 498: if __name__ == "__main__": main()
# We can't easily test this directly as it's module-level code
# But we can test the main function exists and is callable

# Assert: main function exists and is callable
assert callable(main)
# This implicitly tests that the main function is importable and available
# which covers the import structure that supports line 498
Loading
Loading