@@ -116,11 +116,17 @@ def test_configured_exclusion_family_reproduced_with_selection_and_difference():
116116
117117
118118def test_configured_exclusion_family_reproduced_in_a_single_getitem_call ():
119- """The YAML `elements:` selector list (`[QD2*, QF1*, ~QF1E-C05, ~Q???-C06]`) works verbatim through `[]`.
119+ """The YAML `elements:` selector list (`[QD2*, QF1*, ~QF1E-C05, ~Q???-C06]`) selects the
120+ same *family* of elements through `[]` as declared for `QForTest`.
120121
121122 Selection goes through the top-level holder, not `.magnets`, so it resolves patterns
122123 against the same global element pool `find_elements()`/`_fill_array` use to build
123- `QForTest` in the YAML, and so lands in the same order.
124+ `QForTest` in the YAML. The two therefore agree on *which* elements are selected, but not
125+ necessarily on their order: `_fill_array` concatenates matches pattern by pattern (all
126+ `QD2*` matches, then all `QF1*` matches) to build the named array, while `[]` (like every
127+ other list-selection accessor, see `test_every_accessor_returns_a_list_selection_in_configuration_order`)
128+ normalizes its result to configuration order regardless of how many patterns were
129+ involved or in what order they matched.
124130 """
125131 sr = Accelerator .load ("tests/config/EBSTune-patterns.yaml" , ignore_external = True )
126132 sr .design .get_lattice ().disable_6d ()
@@ -129,7 +135,8 @@ def test_configured_exclusion_family_reproduced_in_a_single_getitem_call():
129135 reproduced = sr .design [["QD2*" , "QF1*" , "~QF1E-C05" , "~Q???-C06" ]]
130136
131137 assert type (reproduced ) is type (q_for_test )
132- assert reproduced == q_for_test
138+ assert set (reproduced .names ()) == set (q_for_test .names ())
139+ assert reproduced .names () == [n for n in sr .design .get ().names () if n in set (q_for_test .names ())]
133140
134141
135142def test_array_getitem_supports_a_negated_regex_pattern ():
@@ -193,7 +200,9 @@ def test_magnet_holder_getitem_wildcard_returns_a_magnet_array(holder):
193200def test_magnet_and_magnets_holder_getitem_agree_on_order (holder ):
194201 """.magnet (singular) and .magnets (plural) must return the same elements in the same
195202 order for the same name list, regardless of the order the names were requested in. The
196- reference order is the configuration order, not the order of the requested names."""
203+ reference order is the configuration order, not the order of the requested names. This
204+ must also hold for the top-level holder itself (`[]` and `find_elements`), not just its
205+ typed sub-holders."""
197206 forward = holder .magnet [["SH1A-C01-H" , "SH1A-C01-V" ]]
198207 backward = holder .magnet [["SH1A-C01-V" , "SH1A-C01-H" ]]
199208 assert forward .names () == backward .names () == ["SH1A-C01-H" , "SH1A-C01-V" ]
@@ -202,6 +211,33 @@ def test_magnet_and_magnets_holder_getitem_agree_on_order(holder):
202211 backward_plural = holder .magnets [["SH1A-C01-V" , "SH1A-C01-H" ]]
203212 assert forward_plural .names () == backward_plural .names () == forward .names ()
204213
214+ forward_top_level = holder [["SH1A-C01-H" , "SH1A-C01-V" ]]
215+ backward_top_level = holder [["SH1A-C01-V" , "SH1A-C01-H" ]]
216+ assert forward_top_level .names () == backward_top_level .names () == forward .names ()
217+
218+ assert holder .find_elements (["SH1A-C01-H" , "SH1A-C01-V" ]) == forward .names ()
219+ assert holder .find_elements (["SH1A-C01-V" , "SH1A-C01-H" ]) == forward .names ()
220+
221+
222+ def test_combined_function_magnet_and_bpm_holders_return_list_selections_in_configuration_order (holder ):
223+ """Every typed sub-holder must normalize list selections to configuration order, not just
224+ .magnet/.magnets: this must also hold for .combined_function_magnet(s) and .bpm(s)."""
225+ cfm_forward = holder .combined_function_magnet [["SH1A-C01" , "SH1A-C02" ]]
226+ cfm_backward = holder .combined_function_magnet [["SH1A-C02" , "SH1A-C01" ]]
227+ assert cfm_forward .names () == cfm_backward .names () == ["SH1A-C01" , "SH1A-C02" ]
228+
229+ cfm_forward_plural = holder .combined_function_magnets [["SH1A-C01" , "SH1A-C02" ]]
230+ cfm_backward_plural = holder .combined_function_magnets [["SH1A-C02" , "SH1A-C01" ]]
231+ assert cfm_forward_plural .names () == cfm_backward_plural .names () == cfm_forward .names ()
232+
233+ bpm_forward = holder .diagnostic .bpm [["BPM_C04-01" , "BPM_C04-02" ]]
234+ bpm_backward = holder .diagnostic .bpm [["BPM_C04-02" , "BPM_C04-01" ]]
235+ assert bpm_forward .names () == bpm_backward .names () == ["BPM_C04-01" , "BPM_C04-02" ]
236+
237+ bpm_forward_plural = holder .diagnostic .bpms [["BPM_C04-01" , "BPM_C04-02" ]]
238+ bpm_backward_plural = holder .diagnostic .bpms [["BPM_C04-02" , "BPM_C04-01" ]]
239+ assert bpm_forward_plural .names () == bpm_backward_plural .names () == bpm_forward .names ()
240+
205241
206242@pytest .mark .parametrize (
207243 "select" ,
@@ -214,12 +250,29 @@ def test_magnet_and_magnets_holder_getitem_agree_on_order(holder):
214250 ],
215251)
216252@pytest .mark .parametrize ("reverse" , [False , True ], ids = ["forward" , "reversed" ])
217- def test_every_accessor_returns_a_list_selection_in_configuration_order (holder , select , reverse ):
253+ @pytest .mark .parametrize (
254+ "key, expected_members" ,
255+ [
256+ pytest .param (
257+ ["SH1A-C01-H" , "SH1A-C01-V" , "SH1A-C02-H" ],
258+ {"SH1A-C01-H" , "SH1A-C01-V" , "SH1A-C02-H" },
259+ id = "literal-names" ,
260+ ),
261+ pytest .param (
262+ ["SH1A-C0?-H" , "SH1A-C01-V" ],
263+ {"SH1A-C01-H" , "SH1A-C02-H" , "SH1A-C01-V" },
264+ id = "wildcard-and-literal" ,
265+ ),
266+ ],
267+ )
268+ def test_every_accessor_returns_a_list_selection_in_configuration_order (holder , select , reverse , key , expected_members ):
218269 """Every name-based accessor must return a list-of-patterns selection in configuration
219- order, whatever the order of the requested names, so that positional values line up the
220- same way whichever accessor built the array."""
221- key = ["SH1A-C01-H" , "SH1A-C01-V" , "SH1A-C02-H" ]
222- expected = [n for n in holder .get ().names () if n in key ]
270+ order, whatever the order of the requested names/patterns, so that positional values line
271+ up the same way whichever accessor built the array. This must hold both for a list of
272+ literal names and for a list mixing a wildcard (matching several names) with a literal
273+ name, since a wildcard entry is where a naive pattern-order passthrough diverges from
274+ configuration order the most visibly."""
275+ expected = [n for n in holder .get ().names () if n in expected_members ]
223276 assert select (holder , key [::- 1 ] if reverse else key ) == expected
224277
225278
0 commit comments