@@ -156,3 +156,133 @@ def test_single_map_parameter_names():
156156 # Clean up temporary files
157157 os .unlink (yaml_file .name )
158158 os .unlink (output_file .name )
159+
160+
161+ def test_control_modes_nested_structures ():
162+ """Test nested structures within mapped parameters.
163+
164+ This tests the specific issue where parameters nested within mapped structures
165+ (like control_modes.__map_control_mode_ids.fixed_heading.enabled) were not
166+ generating loop iteration code correctly.
167+ """
168+
169+ control_modes_yaml_content = """autopilot_params:
170+ active_control_mode:
171+ type: string
172+ default_value: "dynamic_heading"
173+ description: "The current active control mode"
174+
175+ control_mode_ids:
176+ type: string_array
177+ default_value: ["dynamic_heading", "fixed_heading"]
178+ description: "List of available control modes"
179+
180+ control_modes:
181+ __map_control_mode_ids:
182+ use_trajectory:
183+ type: bool
184+ default_value: true
185+ description: "Use generated trajectory as control reference"
186+
187+ fixed_heading:
188+ enabled:
189+ type: bool
190+ default_value: false
191+ description: "Enable fixed heading control"
192+ angle:
193+ type: double
194+ default_value: 0.0
195+ description: "Fixed heading angle in degrees"
196+ validation:
197+ bounds<>: [0.0, 360.0]
198+ """
199+
200+ with tempfile .NamedTemporaryFile (
201+ mode = 'w' , suffix = '.yaml' , delete = False
202+ ) as yaml_file :
203+ yaml_file .write (control_modes_yaml_content )
204+ yaml_file .flush ()
205+
206+ with tempfile .NamedTemporaryFile (
207+ mode = 'w' , suffix = '.py' , delete = False
208+ ) as output_file :
209+ try :
210+ run_python (output_file .name , yaml_file .name , 'test_validate.hpp' )
211+
212+ with open (output_file .name , 'r' ) as f :
213+ generated_code = f .read ()
214+
215+ # Check for proper loop generation
216+ assert (
217+ 'for value_1 in updated_params.control_mode_ids:' in generated_code
218+ ), 'Should generate loop over control_mode_ids'
219+
220+ # Check that we're using get_entry correctly
221+ assert (
222+ 'updated_params.control_modes.get_entry(value_1)' in generated_code
223+ or 'entry = updated_params.control_modes.get_entry(value_1)'
224+ in generated_code
225+ ), 'Should use get_entry to access mapped parameters'
226+
227+ # Get all parameter name lines
228+ lines = generated_code .split ('\n ' )
229+ param_name_lines = [
230+ line
231+ for line in lines
232+ if 'param_name' in line and 'control_modes' in line
233+ ]
234+
235+ # Assert there are parameter name lines
236+ assert (
237+ len (param_name_lines ) > 0
238+ ), 'Should have found parameter name lines for control_modes'
239+
240+ # Check for expected parameter patterns (using {value_1} for dynamic substitution)
241+ expected_patterns = [
242+ 'control_modes.{value_1}.use_trajectory' ,
243+ 'control_modes.{value_1}.fixed_heading.enabled' ,
244+ 'control_modes.{value_1}.fixed_heading.angle' ,
245+ ]
246+
247+ for pattern in expected_patterns :
248+ pattern_found = any (pattern in line for line in param_name_lines )
249+ assert pattern_found , (
250+ f"Expected pattern '{ pattern } ' not found in generated code.\n Param lines:\n "
251+ + '\n ' .join (param_name_lines [:5 ])
252+ )
253+
254+ # Ensure no double dots in parameter names
255+ for line in param_name_lines :
256+ assert (
257+ '..' not in line
258+ ), f'Found double dots in parameter name: { line .strip ()} '
259+
260+ # Check that we're accessing nested parameters via entry, not via __map_
261+ entry_access_lines = [
262+ line
263+ for line in lines
264+ if 'entry.' in line and ('fixed_heading' in line )
265+ ]
266+ assert (
267+ len (entry_access_lines ) > 0
268+ ), 'Should access nested parameters via entry variable'
269+
270+ # Verify no direct access to __map_control_mode_ids in actual code
271+ # (error messages can reference the YAML path, but code should not access __map_ attributes)
272+ wrong_access_lines = [
273+ line
274+ for line in lines
275+ if 'control_modes.__map_control_mode_ids' in line
276+ and not line .strip ().startswith ('#' ) # Ignore comments
277+ and 'InvalidParameterValueException'
278+ not in line # Ignore error messages
279+ ]
280+ assert len (wrong_access_lines ) == 0 , (
281+ f'Should not directly access __map_control_mode_ids in code. Found:\n '
282+ + '\n ' .join (wrong_access_lines )
283+ )
284+
285+ finally :
286+ # Clean up temporary files
287+ os .unlink (yaml_file .name )
288+ os .unlink (output_file .name )
0 commit comments