16
16
from subprocess import SubprocessError
17
17
from unittest .mock import MagicMock , mock_open , patch
18
18
19
+ import pytest
20
+
19
21
from ardupilot_methodic_configurator .annotate_params import Par
20
22
from ardupilot_methodic_configurator .backend_filesystem import LocalFilesystem , is_within_tolerance
21
23
@@ -915,7 +917,9 @@ def test_copy_template_files_to_new_vehicle_dir( # pylint: disable=too-many-arg
915
917
lfs = LocalFilesystem (
916
918
"vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False , save_component_to_system_templates = False
917
919
)
918
- lfs .copy_template_files_to_new_vehicle_dir ("template_dir" , "new_vehicle_dir" , blank_change_reason = False )
920
+ lfs .copy_template_files_to_new_vehicle_dir (
921
+ "template_dir" , "new_vehicle_dir" , blank_change_reason = False , copy_vehicle_image = False
922
+ )
919
923
920
924
# Verify all files and directories were processed
921
925
assert mock_listdir .call_count >= 1
@@ -959,7 +963,9 @@ def test_copy_template_files_with_blank_change_reason( # pylint: disable=too-ma
959
963
)
960
964
961
965
# Test with blank_change_reason=True
962
- lfs .copy_template_files_to_new_vehicle_dir ("template_dir" , "new_vehicle_dir" , blank_change_reason = True )
966
+ lfs .copy_template_files_to_new_vehicle_dir (
967
+ "template_dir" , "new_vehicle_dir" , blank_change_reason = True , copy_vehicle_image = False
968
+ )
963
969
964
970
# Verify file handling when blank_change_reason=True
965
971
# First check if writelines was called at all
@@ -986,11 +992,154 @@ def test_copy_template_files_with_blank_change_reason( # pylint: disable=too-ma
986
992
mock_copytree .reset_mock ()
987
993
988
994
# Test with blank_change_reason=False
989
- lfs .copy_template_files_to_new_vehicle_dir ("template_dir" , "new_vehicle_dir" , blank_change_reason = False )
995
+ lfs .copy_template_files_to_new_vehicle_dir (
996
+ "template_dir" , "new_vehicle_dir" , blank_change_reason = False , copy_vehicle_image = False
997
+ )
990
998
991
999
# Verify param file was copied normally
992
1000
mock_copy2 .assert_any_call ("template_dir/file1.param" , "new_vehicle_dir/file1.param" )
993
1001
1002
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.exists" )
1003
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_listdir" )
1004
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.join" )
1005
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.shutil_copy2" )
1006
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.isdir" )
1007
+ def test_user_can_copy_vehicle_image_from_template (
1008
+ self , mock_isdir , mock_copy2 , mock_join , mock_listdir , mock_exists
1009
+ ) -> None :
1010
+ """
1011
+ User can copy vehicle image file when creating a new vehicle from template.
1012
+
1013
+ GIVEN: A template directory containing a vehicle.jpg file
1014
+ WHEN: The user creates a new vehicle with copy_vehicle_image=True
1015
+ THEN: The vehicle.jpg file should be copied to the new vehicle directory
1016
+ """
1017
+ # Arrange: Set up template directory with vehicle.jpg
1018
+ mock_exists .side_effect = lambda path : path in ["template_dir" , "new_vehicle_dir" ]
1019
+ mock_listdir .return_value = ["vehicle.jpg" , "config.param" ]
1020
+ mock_join .side_effect = lambda * args : "/" .join (args )
1021
+ mock_isdir .return_value = False
1022
+
1023
+ lfs = LocalFilesystem (
1024
+ "vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False , save_component_to_system_templates = False
1025
+ )
1026
+
1027
+ # Act: Copy template files with copy_vehicle_image=True
1028
+ result = lfs .copy_template_files_to_new_vehicle_dir (
1029
+ "template_dir" , "new_vehicle_dir" , blank_change_reason = False , copy_vehicle_image = True
1030
+ )
1031
+
1032
+ # Assert: Vehicle image should be copied
1033
+ assert result == "" # No error
1034
+ mock_copy2 .assert_any_call ("template_dir/vehicle.jpg" , "new_vehicle_dir/vehicle.jpg" )
1035
+ mock_copy2 .assert_any_call ("template_dir/config.param" , "new_vehicle_dir/config.param" )
1036
+
1037
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.exists" )
1038
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_listdir" )
1039
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.join" )
1040
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.shutil_copy2" )
1041
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.isdir" )
1042
+ def test_user_can_skip_copying_vehicle_image_from_template (
1043
+ self , mock_isdir , mock_copy2 , mock_join , mock_listdir , mock_exists
1044
+ ) -> None :
1045
+ """
1046
+ User can choose not to copy vehicle image file when creating a new vehicle from template.
1047
+
1048
+ GIVEN: A template directory containing a vehicle.jpg file
1049
+ WHEN: The user creates a new vehicle with copy_vehicle_image=False
1050
+ THEN: The vehicle.jpg file should not be copied to the new vehicle directory
1051
+ AND: Other files should still be copied normally
1052
+ """
1053
+ # Arrange: Set up template directory with vehicle.jpg and other files
1054
+ mock_exists .side_effect = lambda path : path in ["template_dir" , "new_vehicle_dir" ]
1055
+ mock_listdir .return_value = ["vehicle.jpg" , "config.param" , "readme.txt" ]
1056
+ mock_join .side_effect = lambda * args : "/" .join (args )
1057
+ mock_isdir .return_value = False
1058
+
1059
+ lfs = LocalFilesystem (
1060
+ "vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False , save_component_to_system_templates = False
1061
+ )
1062
+
1063
+ # Act: Copy template files with copy_vehicle_image=False
1064
+ result = lfs .copy_template_files_to_new_vehicle_dir (
1065
+ "template_dir" , "new_vehicle_dir" , blank_change_reason = False , copy_vehicle_image = False
1066
+ )
1067
+
1068
+ # Assert: Vehicle image should not be copied, but other files should be
1069
+ assert result == "" # No error
1070
+
1071
+ # Verify vehicle.jpg was NOT copied
1072
+ copy_calls = [call .args for call in mock_copy2 .call_args_list ]
1073
+ vehicle_jpg_calls = [call for call in copy_calls if "vehicle.jpg" in str (call )]
1074
+ assert len (vehicle_jpg_calls ) == 0 , "vehicle.jpg should not be copied when copy_vehicle_image=False"
1075
+
1076
+ # Verify other files were copied
1077
+ mock_copy2 .assert_any_call ("template_dir/config.param" , "new_vehicle_dir/config.param" )
1078
+ mock_copy2 .assert_any_call ("template_dir/readme.txt" , "new_vehicle_dir/readme.txt" )
1079
+
1080
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.exists" )
1081
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_listdir" )
1082
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.join" )
1083
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.shutil_copy2" )
1084
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.isdir" )
1085
+ def test_copy_vehicle_image_defaults_to_false (self , mock_isdir , mock_copy2 , mock_join , mock_listdir , mock_exists ) -> None :
1086
+ """
1087
+ Vehicle image copying defaults to disabled.
1088
+
1089
+ GIVEN: A template directory containing a vehicle.jpg file
1090
+ WHEN: The user creates a new vehicle without specifying copy_vehicle_image parameter
1091
+ THEN: The vehicle.jpg file should not be copied by default
1092
+ """
1093
+ # Arrange: Set up template directory with vehicle.jpg
1094
+ mock_exists .side_effect = lambda path : path in ["template_dir" , "new_vehicle_dir" ]
1095
+ mock_listdir .return_value = ["vehicle.jpg" , "config.param" ]
1096
+ mock_join .side_effect = lambda * args : "/" .join (args )
1097
+ mock_isdir .return_value = False
1098
+
1099
+ lfs = LocalFilesystem (
1100
+ "vehicle_dir" , "vehicle_type" , "" , allow_editing_template_files = False , save_component_to_system_templates = False
1101
+ )
1102
+
1103
+ # Act: Copy template files without specifying copy_vehicle_image
1104
+ # (this should cause an error since parameter is required)
1105
+ with pytest .raises (TypeError ):
1106
+ lfs .copy_template_files_to_new_vehicle_dir ("template_dir" , "new_vehicle_dir" , blank_change_reason = False )
1107
+
1108
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.exists" )
1109
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_listdir" )
1110
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.join" )
1111
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.shutil_copy2" )
1112
+ @patch ("ardupilot_methodic_configurator.backend_filesystem.os_path.isdir" )
1113
+ def test_copy_vehicle_image_with_no_image_file_present (
1114
+ self , mock_isdir , mock_copy2 , mock_join , mock_listdir , mock_exists
1115
+ ) -> None :
1116
+ """
1117
+ Vehicle image copying gracefully handles missing vehicle.jpg file.
1118
+
1119
+ GIVEN: A template directory without a vehicle.jpg file
1120
+ WHEN: The user creates a new vehicle with copy_vehicle_image=True
1121
+ THEN: No error should occur and other files should be copied normally
1122
+ """
1123
+ # Arrange: Set up template directory without vehicle.jpg
1124
+ mock_exists .side_effect = lambda path : path in ["template_dir" , "new_vehicle_dir" ]
1125
+ mock_listdir .return_value = ["config.param" , "readme.txt" ] # No vehicle.jpg
1126
+ mock_join .side_effect = lambda * args : "/" .join (args )
1127
+ mock_isdir .return_value = False
1128
+
1129
+ lfs = LocalFilesystem (
1130
+ "vehicle_dir" , "vehicle_type" , None , allow_editing_template_files = False , save_component_to_system_templates = False
1131
+ )
1132
+
1133
+ # Act: Copy template files with copy_vehicle_image=True (but no vehicle.jpg exists)
1134
+ result = lfs .copy_template_files_to_new_vehicle_dir (
1135
+ "template_dir" , "new_vehicle_dir" , blank_change_reason = False , copy_vehicle_image = True
1136
+ )
1137
+
1138
+ # Assert: No error and other files copied normally
1139
+ assert result == "" # No error
1140
+ mock_copy2 .assert_any_call ("template_dir/config.param" , "new_vehicle_dir/config.param" )
1141
+ mock_copy2 .assert_any_call ("template_dir/readme.txt" , "new_vehicle_dir/readme.txt" )
1142
+
994
1143
995
1144
def test_merge_forced_or_derived_parameters_comprehensive () -> None :
996
1145
"""Test merge_forced_or_derived_parameters with various scenarios."""
0 commit comments