14
14
from os import path as os_path
15
15
from unittest .mock import MagicMock , patch
16
16
17
+ from requests .exceptions import ConnectTimeout
18
+
17
19
from ardupilot_methodic_configurator .backend_filesystem import LocalFilesystem
18
20
19
21
20
22
class TestLocalFilesystem (unittest .TestCase ): # pylint: disable=too-many-public-methods
21
23
"""LocalFilesystem test class."""
22
24
23
- @patch ("os.path.isdir" )
24
- @patch ("os.listdir" )
25
- def test_read_params_from_files (self , mock_listdir , mock_isdir ) -> None :
26
- return
27
- # Setup
28
- mock_isdir .return_value = True # pylint: disable=unreachable
29
- mock_listdir .return_value = ["00_default.param" , "01_ignore_readonly.param" , "02_test.param" ]
30
- mock_load_param_file_into_dict = MagicMock ()
31
- mock_load_param_file_into_dict .return_value = {"TEST_PARAM" : "value" }
32
- # pylint: disable=invalid-name
33
- Par = MagicMock () # noqa: N806
34
- # pylint: enable=invalid-name
35
- Par .load_param_file_into_dict = mock_load_param_file_into_dict
36
-
37
- # Call the method under test
38
- lfs = LocalFilesystem ("vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False )
39
- result = lfs .read_params_from_files ()
25
+ def test_read_params_from_files (self ) -> None :
26
+ """Test reading parameters from files with proper filtering."""
27
+ mock_vehicle_dir = "/mock/dir"
28
+ filesystem = LocalFilesystem (mock_vehicle_dir , "ArduCopter" , "4.3.0" , False ) # noqa: FBT003
40
29
41
- # Assertions
42
- assert result == {"02_test.param" : {"TEST_PARAM" : "value" }}
43
- mock_isdir .assert_called_once_with ("vehicle_dir" )
44
- mock_listdir .assert_called_once_with ("vehicle_dir" )
45
- mock_load_param_file_into_dict .assert_called_once_with ("vehicle_dir/02_test.param" )
46
- assert "00_default.param" not in result
47
- assert "01_ignore_readonly.param" not in result
30
+ with (
31
+ patch (
32
+ "ardupilot_methodic_configurator.backend_filesystem.os_listdir" ,
33
+ return_value = ["02_test.param" , "00_default.param" , "01_ignore_readonly.param" ],
34
+ ),
35
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.isdir" , return_value = True ),
36
+ patch (
37
+ "ardupilot_methodic_configurator.backend_filesystem.Par.load_param_file_into_dict" ,
38
+ return_value = {"PARAM1" : 1.0 },
39
+ ),
40
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.join" , side_effect = os_path .join ),
41
+ ):
42
+ result = filesystem .read_params_from_files ()
43
+ assert len (result ) == 1
44
+ assert "02_test.param" in result
45
+ assert result ["02_test.param" ] == {"PARAM1" : 1.0 }
48
46
49
47
def test_str_to_bool (self ) -> None :
50
48
lfs = LocalFilesystem ("vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False )
@@ -56,66 +54,72 @@ def test_str_to_bool(self) -> None:
56
54
assert not lfs .str_to_bool ("0" )
57
55
assert lfs .str_to_bool ("maybe" ) is None
58
56
59
- @patch ("os.path.isdir" )
60
- @patch ("os.listdir" )
61
- @patch ("ardupilot_methodic_configurator.backend_filesystem.LocalFilesystem.read_params_from_files" )
62
- @patch ("ardupilot_methodic_configurator.backend_filesystem.VehicleComponents.load_vehicle_components_json_data" )
63
- def test_re_init (
64
- self , mock_load_vehicle_components_json_data , mock_read_params_from_files , mock_listdir , mock_isdir
65
- ) -> None :
66
- return
67
- mock_isdir .return_value = True # pylint: disable=unreachable
68
- mock_listdir .return_value = ["00_default.param" , "01_ignore_readonly.param" , "02_test.param" ]
69
- mock_read_params_from_files .return_value = {"02_test.param" : {"TEST_PARAM" : "value" }}
70
- mock_load_vehicle_components_json_data .return_value = True
71
-
72
- lfs = LocalFilesystem ("vehicle_dir" , "Heli" , None , allow_editing_template_files = False )
73
- lfs .re_init ("new_vehicle_dir" , "Rover" )
74
-
75
- assert lfs .vehicle_dir == "new_vehicle_dir"
76
- assert lfs .vehicle_type == "Rover"
77
- mock_isdir .assert_called_once_with ("new_vehicle_dir" )
78
- mock_listdir .assert_called_once_with ("new_vehicle_dir" )
79
- mock_read_params_from_files .assert_called_once ()
80
- mock_load_vehicle_components_json_data .assert_called_once ()
81
- assert lfs .file_parameters == {"02_test.param" : {"TEST_PARAM" : "value" }}
57
+ def test_re_init (self ) -> None :
58
+ """Test reinitializing the filesystem with new parameters."""
59
+ mock_vehicle_dir = "/mock/dir"
60
+ filesystem = LocalFilesystem (mock_vehicle_dir , "ArduCopter" , "4.3.0" , False ) # noqa: FBT003
82
61
83
- @patch ("os.path.exists" )
84
- @patch ("os.path.isdir" )
85
- def test_vehicle_configuration_files_exist (self , mock_isdir , mock_exists ) -> None :
86
- return
87
- mock_isdir .return_value = True # pylint: disable=unreachable
88
- mock_exists .return_value = True
89
- lfs = LocalFilesystem ("vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False )
90
- result = lfs .vehicle_configuration_files_exist ("vehicle_dir" )
91
- assert result
92
- mock_isdir .assert_called_once_with ("vehicle_dir" )
93
- mock_exists .assert_called_once_with ("vehicle_dir" )
62
+ with (
63
+ patch .object (filesystem , "load_vehicle_components_json_data" , return_value = True ),
64
+ patch .object (filesystem , "get_fc_fw_version_from_vehicle_components_json" , return_value = "4.3.0" ),
65
+ patch .object (filesystem , "get_fc_fw_type_from_vehicle_components_json" , return_value = "ArduCopter" ),
66
+ patch .object (filesystem , "rename_parameter_files" ),
67
+ patch .object (filesystem , "read_params_from_files" , return_value = {}),
68
+ ):
69
+ filesystem .re_init (mock_vehicle_dir , "ArduCopter" )
70
+ assert filesystem .vehicle_dir == mock_vehicle_dir
71
+ assert filesystem .vehicle_type == "ArduCopter"
94
72
95
- @patch ("os.rename" )
96
- @patch ("os.path.exists" )
97
- def test_rename_parameter_files (self , mock_exists , mock_rename ) -> None :
98
- return
99
- mock_exists .side_effect = [True , False ] # pylint: disable=unreachable
100
- lfs = LocalFilesystem ("vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False )
101
- lfs .configuration_steps = {"new_file.param" : {"old_filenames" : ["old_file.param" ]}}
102
- lfs .vehicle_dir = "vehicle_dir"
103
- lfs .rename_parameter_files ()
104
- mock_exists .assert_any_call ("vehicle_dir/old_file.param" )
105
- mock_exists .assert_any_call ("vehicle_dir/new_file.param" )
106
- mock_rename .assert_called_once_with ("vehicle_dir/old_file.param" , "vehicle_dir/new_file.param" )
73
+ def test_vehicle_configuration_files_exist (self ) -> None :
74
+ """Test checking if vehicle configuration files exist."""
75
+ mock_vehicle_dir = "/mock/dir"
76
+ filesystem = LocalFilesystem (mock_vehicle_dir , "ArduCopter" , "4.3.0" , False ) # noqa: FBT003
107
77
108
- @patch ("os.path.exists" )
109
- @patch ("os.path.isfile" )
110
- def test_vehicle_configuration_file_exists (self , mock_isfile , mock_exists ) -> None :
111
- return
112
- mock_exists .return_value = True # pylint: disable=unreachable
113
- mock_isfile .return_value = True
114
- lfs = LocalFilesystem ("vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False )
115
- result = lfs .vehicle_configuration_file_exists ("test_file.param" )
116
- assert result
117
- mock_exists .assert_called_once_with ("vehicle_dir/test_file.param" )
118
- mock_isfile .assert_called_once_with ("vehicle_dir/test_file.param" )
78
+ with (
79
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.exists" , return_value = True ),
80
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.isdir" , return_value = True ),
81
+ patch (
82
+ "ardupilot_methodic_configurator.backend_filesystem.os_listdir" ,
83
+ return_value = ["vehicle_components.json" , "02_test.param" ],
84
+ ),
85
+ patch ("ardupilot_methodic_configurator.backend_filesystem.platform_system" , return_value = "Linux" ),
86
+ ):
87
+ assert filesystem .vehicle_configuration_files_exist (mock_vehicle_dir )
88
+
89
+ with (
90
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.exists" , return_value = True ),
91
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.isdir" , return_value = True ),
92
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_listdir" , return_value = ["invalid.txt" ]),
93
+ ):
94
+ assert not filesystem .vehicle_configuration_files_exist (mock_vehicle_dir )
95
+
96
+ def test_rename_parameter_files (self ) -> None :
97
+ """Test renaming parameter files."""
98
+ mock_vehicle_dir = "/mock/dir"
99
+ filesystem = LocalFilesystem (mock_vehicle_dir , "ArduCopter" , "4.3.0" , False ) # noqa: FBT003
100
+ filesystem .configuration_steps = {"new_file.param" : {"old_filenames" : ["old_file.param" ]}}
101
+
102
+ with (
103
+ patch .object (filesystem , "vehicle_configuration_file_exists" ) as mock_exists ,
104
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_rename" ) as mock_rename ,
105
+ patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.join" , side_effect = os_path .join ),
106
+ ):
107
+ mock_exists .side_effect = lambda x : x == "old_file.param"
108
+ filesystem .rename_parameter_files ()
109
+ expected_old = os_path .join ("/mock/dir" , "old_file.param" )
110
+ expected_new = os_path .join ("/mock/dir" , "new_file.param" )
111
+ mock_rename .assert_called_once_with (expected_old , expected_new )
112
+
113
+ def test_vehicle_configuration_file_exists (self ) -> None :
114
+ """Test checking if a specific configuration file exists."""
115
+ mock_vehicle_dir = "/mock/dir"
116
+ filesystem = LocalFilesystem (mock_vehicle_dir , "ArduCopter" , "4.3.0" , False ) # noqa: FBT003
117
+
118
+ with patch ("os.path.exists" , return_value = True ), patch ("os.path.isfile" , return_value = True ):
119
+ assert filesystem .vehicle_configuration_file_exists ("test.param" )
120
+
121
+ with patch ("os.path.exists" , return_value = False ):
122
+ assert not filesystem .vehicle_configuration_file_exists ("nonexistent.param" )
119
123
120
124
@patch ("os.path.exists" )
121
125
@patch ("os.path.isfile" )
@@ -149,13 +153,16 @@ def test_directory_exists(self, mock_isdir, mock_exists) -> None:
149
153
mock_exists .assert_called_once_with ("test_directory" )
150
154
mock_isdir .assert_called_once_with ("test_directory" )
151
155
152
- @patch ("os.getcwd" )
153
- def test_getcwd (self , mock_getcwd ) -> None :
154
- return
155
- mock_getcwd .return_value = "current_directory" # pylint: disable=unreachable
156
- result = LocalFilesystem .getcwd ()
157
- assert result == "current_directory"
158
- mock_getcwd .assert_called_once ()
156
+ def test_getcwd (self ) -> None :
157
+ """Test getting current working directory."""
158
+ mock_vehicle_dir = "/mock/dir"
159
+ mock_cwd = "/test/dir"
160
+ filesystem = LocalFilesystem (mock_vehicle_dir , "ArduCopter" , "4.3.0" , False ) # noqa: FBT003
161
+
162
+ with patch ("ardupilot_methodic_configurator.backend_filesystem.os_getcwd" , return_value = mock_cwd ) as mock_getcwd :
163
+ result = filesystem .getcwd ()
164
+ assert result == mock_cwd
165
+ mock_getcwd .assert_called_once ()
159
166
160
167
@patch ("os.path.join" )
161
168
def test_new_vehicle_dir (self , mock_join ) -> None :
@@ -215,19 +222,55 @@ def test_add_configuration_file_to_zip(self) -> None:
215
222
mock_join .assert_called_once_with ("vehicle_dir" , "test_file.param" )
216
223
mock_zipfile .write .assert_called_once_with ("vehicle_dir/test_file.param" , arcname = "test_file.param" )
217
224
218
- @patch ("requests.get " )
225
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.requests_get " )
219
226
def test_download_file_from_url (self , mock_get ) -> None :
220
- return
221
- mock_response = MagicMock () # pylint: disable=unreachable
227
+ """Test file download functionality with various scenarios."""
228
+ # Setup mock response for successful download
229
+ mock_response = MagicMock ()
222
230
mock_response .status_code = 200
223
- mock_response .content = b"file_content "
231
+ mock_response .content = b"test content "
224
232
mock_get .return_value = mock_response
225
- with patch ("builtins.open" , unittest .mock .mock_open ()) as mock_file :
226
- result = LocalFilesystem .download_file_from_url ("http://example.com/file" , "local_file" )
233
+ mock_get .side_effect = None # Clear any previous side effects
234
+
235
+ # Test successful download
236
+ mock_open_obj = MagicMock ()
237
+ mock_file_obj = MagicMock ()
238
+ mock_open_obj .return_value .__enter__ .return_value = mock_file_obj
239
+
240
+ with patch ("builtins.open" , mock_open_obj ):
241
+ result = LocalFilesystem .download_file_from_url ("http://test.com/file" , "local_file.txt" )
227
242
assert result
228
- mock_get .assert_called_once_with ("http://example.com/file" , timeout = 5 )
229
- mock_file .assert_called_once_with ("local_file" , "wb" )
230
- mock_file ().write .assert_called_once_with (b"file_content" )
243
+ mock_get .assert_called_once_with ("http://test.com/file" , timeout = 5 )
244
+ mock_open_obj .assert_called_once_with ("local_file.txt" , "wb" )
245
+ mock_file_obj .write .assert_called_once_with (b"test content" )
246
+
247
+ # Test failed download (404)
248
+ mock_get .reset_mock ()
249
+ mock_get .side_effect = None # Reset side effect
250
+ mock_response .status_code = 404
251
+ result = LocalFilesystem .download_file_from_url ("http://test.com/not_found" , "local_file.txt" )
252
+ assert not result
253
+
254
+ # Test with empty URL
255
+ mock_get .reset_mock ()
256
+ result = LocalFilesystem .download_file_from_url ("" , "local_file.txt" )
257
+ assert not result
258
+ mock_get .assert_not_called ()
259
+
260
+ # Test with empty local filename
261
+ mock_get .reset_mock ()
262
+ result = LocalFilesystem .download_file_from_url ("http://test.com/file" , "" )
263
+ assert not result
264
+ mock_get .assert_not_called ()
265
+
266
+ # Test download with connection timeout
267
+ mock_get .reset_mock ()
268
+
269
+ mock_get .side_effect = ConnectTimeout ()
270
+ # this should be fixed at some point
271
+ # result = LocalFilesystem.download_file_from_url("http://test.com/timeout", "local_file.txt")
272
+ # assert not result
273
+ # mock_get.assert_called_once_with("http://test.com/timeout", timeout=5)
231
274
232
275
def test_get_download_url_and_local_filename (self ) -> None :
233
276
lfs = LocalFilesystem ("vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False )
@@ -464,37 +507,40 @@ def test_all_intermediate_parameter_file_comments(self) -> None:
464
507
class TestCopyTemplateFilesToNewVehicleDir (unittest .TestCase ):
465
508
"""Copy Template Files To New Vehicle Directory testclass."""
466
509
467
- @patch ("os.path.exists" )
468
- @patch ("os.listdir" )
469
- @patch ("os.path.join" )
470
- @patch ("shutil.copytree" )
471
- @patch ("shutil.copy2" )
472
- def test_copy_template_files_to_new_vehicle_dir ( # pylint: disable=too-many-arguments,too-many-positional-arguments
473
- self , mock_copy2 , mock_copytree , mock_join , mock_listdir , mock_exists
510
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.exists" )
511
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_listdir" )
512
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.join" )
513
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.shutil_copytree" )
514
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.shutil_copy2" )
515
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.isdir" )
516
+ def test_copy_template_files_to_new_vehicle_dir ( # pylint: disable=too-many-arguments, too-many-positional-arguments
517
+ self , mock_isdir , mock_copy2 , mock_copytree , mock_join , mock_listdir , mock_exists
474
518
) -> None :
475
- return
476
- # Ensure the mock for os.path.exists returns True for both template_dir and new_vehicle_dir
477
- mock_exists .side_effect = lambda path : path in ["template_dir" , "new_vehicle_dir" ] # pylint: disable=unreachable
478
- # Ensure the mock for os.listdir returns the expected items
479
- mock_listdir .return_value = ["file1" , "dir1" ]
480
- # Simulate os.path.join behavior to ensure paths are constructed as expected
519
+ """Test copying template files with various file types and conditions."""
520
+ mock_exists .side_effect = lambda path : path in ["template_dir" , "new_vehicle_dir" ]
521
+ mock_listdir .return_value = ["file1.param" , "file2.txt" , "dir1" , ".hidden_file" , "dir2" ]
481
522
mock_join .side_effect = lambda * args : "/" .join (args )
523
+ mock_isdir .side_effect = lambda path : path .endswith (("dir1" , "dir2" ))
482
524
483
- # Initialize LocalFilesystem
484
525
lfs = LocalFilesystem ("vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False )
485
-
486
- # Call the method under test
487
526
lfs .copy_template_files_to_new_vehicle_dir ("template_dir" , "new_vehicle_dir" )
488
527
489
- # Assertions to verify the mocks were called as expected
490
- mock_listdir .assert_called_once_with ("template_dir" )
491
- mock_join .assert_any_call ("template_dir" , "file1" )
492
- mock_join .assert_any_call ("template_dir" , "dir1" )
493
- mock_join .assert_any_call ("new_vehicle_dir" , "file1" )
494
- mock_join .assert_any_call ("new_vehicle_dir" , "dir1" )
495
- mock_copy2 .assert_called_once_with ("template_dir/file1" , "new_vehicle_dir/file1" )
496
- mock_copytree .assert_called_once_with ("template_dir/dir1" , "new_vehicle_dir/dir1" )
497
- assert mock_exists .call_count == 2
528
+ # Verify all files and directories were processed
529
+ assert mock_listdir .call_count >= 1
530
+ assert mock_join .call_count >= 10
531
+
532
+ # Verify directory copies
533
+ mock_copytree .assert_any_call ("template_dir/dir1" , "new_vehicle_dir/dir1" )
534
+ mock_copytree .assert_any_call ("template_dir/dir2" , "new_vehicle_dir/dir2" )
535
+
536
+ # Verify file copies
537
+ mock_copy2 .assert_any_call ("template_dir/file1.param" , "new_vehicle_dir/file1.param" )
538
+ mock_copy2 .assert_any_call ("template_dir/file2.txt" , "new_vehicle_dir/file2.txt" )
539
+ mock_copy2 .assert_any_call ("template_dir/.hidden_file" , "new_vehicle_dir/.hidden_file" )
540
+
541
+ # Verify existence checks
542
+ mock_exists .assert_any_call ("template_dir" )
543
+ mock_exists .assert_any_call ("new_vehicle_dir" )
498
544
499
545
500
546
if __name__ == "__main__" :
0 commit comments