Skip to content

Commit 7dbfdea

Browse files
committed
test(program settings): Add backend_filesystem_program_settings tests
1 parent d5389f7 commit 7dbfdea

File tree

1 file changed

+220
-0
lines changed

1 file changed

+220
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Tests for the backend_filesystem_program_settings.py file.
5+
6+
This file is part of ArduPilot methodic configurator. https://github.com/ArduPilot/MethodicConfigurator
7+
8+
SPDX-FileCopyrightText: 2024-2025 Amilcar do Carmo Lucas <amilcar.lucas@iav.de>
9+
10+
SPDX-License-Identifier: GPL-3.0-or-later
11+
"""
12+
13+
import json
14+
from unittest.mock import mock_open, patch
15+
16+
import pytest
17+
18+
from ardupilot_methodic_configurator.backend_filesystem_program_settings import ProgramSettings
19+
20+
# pylint: disable=protected-access
21+
22+
23+
class TestProgramSettings:
24+
"""Tests for the ProgramSettings class."""
25+
26+
def test_application_icon_filepath(self) -> None:
27+
with (
28+
patch("os.path.dirname") as mock_dirname,
29+
patch("os.path.abspath") as mock_abspath,
30+
patch("os.path.join") as mock_join,
31+
):
32+
mock_dirname.return_value = "/mock/dir"
33+
mock_abspath.return_value = "/mock/dir/file"
34+
mock_join.return_value = "/mock/dir/ArduPilot_icon.png"
35+
36+
result = ProgramSettings.application_icon_filepath()
37+
38+
mock_dirname.assert_called_once()
39+
mock_abspath.assert_called_once()
40+
mock_join.assert_called_once_with("/mock/dir", "ArduPilot_icon.png")
41+
assert result == "/mock/dir/ArduPilot_icon.png"
42+
43+
def test_application_logo_filepath(self) -> None:
44+
with (
45+
patch("os.path.dirname") as mock_dirname,
46+
patch("os.path.abspath") as mock_abspath,
47+
patch("os.path.join") as mock_join,
48+
):
49+
mock_dirname.return_value = "/mock/dir"
50+
mock_abspath.return_value = "/mock/dir/file"
51+
mock_join.return_value = "/mock/dir/ArduPilot_logo.png"
52+
53+
result = ProgramSettings.application_logo_filepath()
54+
55+
mock_dirname.assert_called_once()
56+
mock_abspath.assert_called_once()
57+
mock_join.assert_called_once_with("/mock/dir", "ArduPilot_logo.png")
58+
assert result == "/mock/dir/ArduPilot_logo.png"
59+
60+
def test_create_new_vehicle_dir_already_exists(self) -> None:
61+
with patch("os.path.exists", return_value=True):
62+
result = ProgramSettings.create_new_vehicle_dir("/mock/dir")
63+
assert result != "" # Error message returned
64+
65+
def test_create_new_vehicle_dir_failure(self) -> None:
66+
with (
67+
patch("os.path.exists", return_value=False),
68+
patch("os.makedirs", side_effect=OSError("Test error")),
69+
patch("ardupilot_methodic_configurator.backend_filesystem_program_settings.logging_error") as mock_log_error,
70+
):
71+
result = ProgramSettings.create_new_vehicle_dir("/mock/dir")
72+
73+
mock_log_error.assert_called_once()
74+
assert "Test error" in result
75+
76+
def test_valid_directory_name(self) -> None:
77+
assert ProgramSettings.valid_directory_name("valid_dir_name-123") is True
78+
assert ProgramSettings.valid_directory_name("invalid/dir/name") is True # Because '/' is allowed
79+
assert ProgramSettings.valid_directory_name("invalid<dir>name") is False
80+
assert ProgramSettings.valid_directory_name("invalid*dir?name") is False
81+
82+
def test_user_config_dir(self) -> None:
83+
with (
84+
patch(
85+
"ardupilot_methodic_configurator.backend_filesystem_program_settings.user_config_dir",
86+
return_value="/mock/user/config",
87+
),
88+
patch("os.path.exists", return_value=True),
89+
patch("os.path.isdir", return_value=True),
90+
):
91+
result = ProgramSettings._ProgramSettings__user_config_dir()
92+
93+
assert result == "/mock/user/config"
94+
95+
def test_user_config_dir_not_exists(self) -> None:
96+
with (
97+
patch("platformdirs.user_config_dir", return_value="/mock/user/config"),
98+
patch("os.path.exists", return_value=False),
99+
pytest.raises(FileNotFoundError),
100+
):
101+
ProgramSettings._ProgramSettings__user_config_dir()
102+
103+
def test_user_config_dir_not_directory(self) -> None:
104+
with (
105+
patch("platformdirs.user_config_dir", return_value="/mock/user/config"),
106+
patch("os.path.exists", return_value=True),
107+
patch("os.path.isdir", return_value=False),
108+
pytest.raises(NotADirectoryError),
109+
):
110+
ProgramSettings._ProgramSettings__user_config_dir()
111+
112+
def test_get_settings_as_dict_file_exists(self) -> None:
113+
mock_settings = {
114+
"Format version": 1,
115+
"directory_selection": {"template_dir": "/mock/template"},
116+
"display_usage_popup": {"component_editor": False, "parameter_editor": True},
117+
"auto_open_doc_in_browser": False,
118+
}
119+
120+
expected_result = mock_settings.copy()
121+
# The method adds this default setting if not present in the file
122+
expected_result["annotate_docs_into_param_files"] = False
123+
124+
with (
125+
patch.object(ProgramSettings, "_ProgramSettings__user_config_dir", return_value="/mock/config"),
126+
patch("os.path.join", return_value="/mock/config/settings.json"),
127+
patch("builtins.open", mock_open(read_data=json.dumps(mock_settings))),
128+
):
129+
result = ProgramSettings._ProgramSettings__get_settings_as_dict()
130+
131+
assert result == expected_result
132+
133+
def test_get_settings_as_dict_file_not_exists(self) -> None:
134+
with (
135+
patch.object(ProgramSettings, "_ProgramSettings__user_config_dir", return_value="/mock/config"),
136+
patch("os.path.join", return_value="/mock/config/settings.json"),
137+
patch("builtins.open", side_effect=FileNotFoundError),
138+
):
139+
result = ProgramSettings._ProgramSettings__get_settings_as_dict()
140+
141+
assert "Format version" in result
142+
assert "directory_selection" in result
143+
assert "display_usage_popup" in result
144+
assert "component_editor" in result["display_usage_popup"]
145+
assert "parameter_editor" in result["display_usage_popup"]
146+
assert "auto_open_doc_in_browser" in result
147+
assert "annotate_docs_into_param_files" in result
148+
149+
def test_set_settings_from_dict(self) -> None:
150+
mock_settings = {"test": "value"}
151+
152+
with (
153+
patch.object(ProgramSettings, "_ProgramSettings__user_config_dir", return_value="/mock/config"),
154+
patch("os.path.join", return_value="/mock/config/settings.json"),
155+
patch("builtins.open", mock_open()) as mock_file,
156+
):
157+
ProgramSettings._ProgramSettings__set_settings_from_dict(mock_settings)
158+
159+
mock_file.assert_called_once_with("/mock/config/settings.json", "w", encoding="utf-8")
160+
mock_file().write.assert_called()
161+
162+
def test_display_usage_popup(self) -> None:
163+
with patch.object(ProgramSettings, "_ProgramSettings__get_settings_as_dict") as mock_get_settings:
164+
mock_get_settings.return_value = {"display_usage_popup": {"component_editor": False, "parameter_editor": True}}
165+
166+
assert ProgramSettings.display_usage_popup("component_editor") is False
167+
assert ProgramSettings.display_usage_popup("parameter_editor") is True
168+
assert ProgramSettings.display_usage_popup("nonexistent_type") is True # Default is True
169+
170+
def test_set_display_usage_popup(self) -> None:
171+
with (
172+
patch.object(ProgramSettings, "_ProgramSettings__get_settings_config") as mock_get_config,
173+
patch.object(ProgramSettings, "_ProgramSettings__set_settings_from_dict") as mock_set_settings,
174+
):
175+
mock_get_config.return_value = (
176+
{"display_usage_popup": {"component_editor": True, "parameter_editor": True}},
177+
"pattern",
178+
"replacement",
179+
)
180+
181+
# Test valid type
182+
ProgramSettings.set_display_usage_popup("component_editor", value=False)
183+
mock_set_settings.assert_called_with(
184+
{"display_usage_popup": {"component_editor": False, "parameter_editor": True}}
185+
)
186+
187+
# Test invalid type
188+
mock_set_settings.reset_mock()
189+
ProgramSettings.set_display_usage_popup("nonexistent_type", value=False)
190+
mock_set_settings.assert_not_called()
191+
192+
def test_get_setting(self) -> None:
193+
with patch.object(ProgramSettings, "_ProgramSettings__get_settings_as_dict") as mock_get_settings:
194+
mock_get_settings.return_value = {"Format version": 2, "auto_open_doc_in_browser": False}
195+
196+
# Test existing settings
197+
assert ProgramSettings.get_setting("Format version") == 2
198+
assert ProgramSettings.get_setting("auto_open_doc_in_browser") is False
199+
200+
# Test default values
201+
assert ProgramSettings.get_setting("annotate_docs_into_param_files") is False
202+
203+
# Test non-existent setting
204+
assert ProgramSettings.get_setting("nonexistent_setting") is False
205+
206+
def test_set_setting(self) -> None:
207+
with (
208+
patch.object(ProgramSettings, "_ProgramSettings__get_settings_config") as mock_get_config,
209+
patch.object(ProgramSettings, "_ProgramSettings__set_settings_from_dict") as mock_set_settings,
210+
):
211+
mock_get_config.return_value = ({"Format version": 1, "auto_open_doc_in_browser": True}, "pattern", "replacement")
212+
213+
# Test valid setting
214+
ProgramSettings.set_setting("auto_open_doc_in_browser", value=False)
215+
mock_set_settings.assert_called_with({"Format version": 1, "auto_open_doc_in_browser": False})
216+
217+
# Test invalid setting
218+
mock_set_settings.reset_mock()
219+
ProgramSettings.set_setting("nonexistent_setting", value=True)
220+
mock_set_settings.assert_not_called()

0 commit comments

Comments
 (0)