@@ -21,7 +21,7 @@ Tests the SEooC (Safety Element out of Context) functionality including:
2121"""
2222
2323load ("@bazel_skylib//lib:unittest.bzl" , "analysistest" , "asserts" )
24- load ("@score_tooling//bazel/rules/rules_score:providers.bzl" , "SphinxModuleInfo" , "SphinxNeedsInfo" )
24+ load ("@score_tooling//bazel/rules/rules_score:providers.bzl" , "SphinxIndexFileInfo" , " SphinxModuleInfo" , "SphinxNeedsInfo" )
2525
2626def _seooc_index_generation_test_impl (ctx ):
2727 """Test that dependable_element generates proper index.rst file."""
@@ -115,3 +115,204 @@ def _seooc_needs_provider_test_impl(ctx):
115115seooc_needs_provider_test = analysistest .make (
116116 impl = _seooc_needs_provider_test_impl ,
117117)
118+
119+ # ============================================================================
120+ # Regression tests: correct index.rst resolution (multi-index scenario)
121+ #
122+ # These tests guard against a bug where _score_html_impl scanned all relocated
123+ # source files for any path ending with "/index.rst" and used the last match.
124+ # A dependable_element with components generates both:
125+ # <name>/index.rst (root – correct Sphinx entry point)
126+ # <name>/components/index.rst (sub-page – must NOT be the entry point)
127+ # The old code would pick the sub-page as the Sphinx entry point.
128+ # ============================================================================
129+
130+ def _seooc_multi_index_files_exist_test_impl (ctx ):
131+ """
132+ Given a dependable_element with at least one component,
133+ When the _index rule generates its output file set,
134+ Then both a root index.rst and a components/index.rst must be present,
135+ confirming the fixture exercises the multi-index scenario.
136+ """
137+ env = analysistest .begin (ctx )
138+ target_under_test = analysistest .target_under_test (env )
139+
140+ # When: collect all output paths
141+ files = target_under_test [DefaultInfo ].files .to_list ()
142+ paths = [f .path for f in files ]
143+
144+ # Then: a components/index.rst sub-page exists
145+ components_index_paths = [p for p in paths if p .endswith ("components/index.rst" )]
146+ asserts .true (
147+ env ,
148+ len (components_index_paths ) >= 1 ,
149+ "Expected a components/index.rst to be generated (fixture must have at least one component)" ,
150+ )
151+
152+ # Then: a root index.rst (not inside components/) also exists
153+ root_index_paths = [
154+ p
155+ for p in paths
156+ if p .endswith ("index.rst" ) and "components/" not in p
157+ ]
158+ asserts .true (
159+ env ,
160+ len (root_index_paths ) >= 1 ,
161+ "Expected a root index.rst (outside components/) to be generated" ,
162+ )
163+
164+ return analysistest .end (env )
165+
166+ seooc_multi_index_files_exist_test = analysistest .make (
167+ impl = _seooc_multi_index_files_exist_test_impl ,
168+ )
169+
170+ def _seooc_sphinx_entry_point_is_root_index_test_impl (ctx ):
171+ """
172+ Given a dependable_element whose source tree contains both a root index.rst
173+ and a components/index.rst,
174+ When the Sphinx HTML build action is constructed,
175+ Then the --index_file argument passed to Sphinx must point to the root
176+ index.rst and must NOT point to the components/index.rst sub-page.
177+ """
178+ env = analysistest .begin (ctx )
179+
180+ # When: inspect all actions produced for this target
181+ actions = analysistest .target_actions (env )
182+
183+ # Find the Sphinx HTML action: it is a run() action whose outputs contain
184+ # the "_html" directory (declared as <name>/_html).
185+ sphinx_html_action = None
186+ for action in actions :
187+ for output in action .outputs .to_list ():
188+ if output .basename == "_html" :
189+ sphinx_html_action = action
190+ break
191+ if sphinx_html_action :
192+ break
193+
194+ asserts .true (
195+ env ,
196+ sphinx_html_action != None ,
197+ "Expected to find the Sphinx HTML build action (output ending in '_html')" ,
198+ )
199+
200+ # Then: extract the value that follows --index_file in the argument list
201+ argv = sphinx_html_action .argv
202+ index_path = None
203+ for i , arg in enumerate (argv ):
204+ if arg == "--index_file" and i + 1 < len (argv ):
205+ index_path = argv [i + 1 ]
206+ break
207+
208+ asserts .true (
209+ env ,
210+ index_path != None ,
211+ "Sphinx HTML action must contain a --index_file argument" ,
212+ )
213+
214+ asserts .true (
215+ env ,
216+ index_path .endswith ("index.rst" ),
217+ "The --index_file value must end with index.rst; got: " + str (index_path ),
218+ )
219+
220+ asserts .false (
221+ env ,
222+ "components/" in index_path ,
223+ "The --index_file value must NOT point to components/index.rst; got: " + str (index_path ),
224+ )
225+
226+ return analysistest .end (env )
227+
228+ seooc_sphinx_entry_point_is_root_index_test = analysistest .make (
229+ impl = _seooc_sphinx_entry_point_is_root_index_test_impl ,
230+ )
231+
232+ def _seooc_all_sources_relocated_test_impl (ctx ):
233+ """
234+ Given a dependable_element that provides source files via SphinxSourcesInfo,
235+ When the Sphinx HTML build action is constructed,
236+ Then all source files must appear as inputs to the action, confirming that
237+ every file is relocated (symlinked) and none are silently dropped.
238+ """
239+ env = analysistest .begin (ctx )
240+ target_under_test = analysistest .target_under_test (env )
241+
242+ # When: find the Sphinx HTML action (same selection as the entry-point test)
243+ actions = analysistest .target_actions (env )
244+ sphinx_html_action = None
245+ for action in actions :
246+ for output in action .outputs .to_list ():
247+ if output .basename == "_html" :
248+ sphinx_html_action = action
249+ break
250+ if sphinx_html_action :
251+ break
252+
253+ asserts .true (
254+ env ,
255+ sphinx_html_action != None ,
256+ "Expected to find the Sphinx HTML build action (output ending in '_html')" ,
257+ )
258+
259+ # Then: every .rst / .md / .puml / .json source from the index target's
260+ # output file set must appear as an input to the HTML action.
261+ index_files = target_under_test [DefaultInfo ].files .to_list ()
262+ source_exts = ("rst" , "md" , "puml" , "plantuml" , "json" )
263+ expected_sources = [
264+ f
265+ for f in index_files
266+ if f .extension in source_exts
267+ ]
268+
269+ action_input_paths = {f .path : True for f in sphinx_html_action .inputs .to_list ()}
270+
271+ for src in expected_sources :
272+ asserts .true (
273+ env ,
274+ src .path in action_input_paths ,
275+ "Source file '{}' was not passed as input to the Sphinx HTML action" .format (src .path ),
276+ )
277+
278+ return analysistest .end (env )
279+
280+ seooc_all_sources_relocated_test = analysistest .make (
281+ impl = _seooc_all_sources_relocated_test_impl ,
282+ )
283+
284+ def _seooc_index_file_provider_test_impl (ctx ):
285+ """
286+ Given a _dependable_element_index target,
287+ When its providers are inspected,
288+ Then SphinxIndexFileInfo must be present and its index_file must be the
289+ root index.rst (not a components/index.rst sub-page).
290+ """
291+ env = analysistest .begin (ctx )
292+ target_under_test = analysistest .target_under_test (env )
293+
294+ asserts .true (
295+ env ,
296+ SphinxIndexFileInfo in target_under_test ,
297+ "_dependable_element_index must provide SphinxIndexFileInfo" ,
298+ )
299+
300+ index_file = target_under_test [SphinxIndexFileInfo ].index_file
301+
302+ asserts .true (
303+ env ,
304+ index_file .basename == "index.rst" ,
305+ "SphinxIndexFileInfo.index_file must be named index.rst; got: " + index_file .basename ,
306+ )
307+
308+ asserts .false (
309+ env ,
310+ "components/" in index_file .path ,
311+ "SphinxIndexFileInfo.index_file must NOT point to components/index.rst; got: " + index_file .path ,
312+ )
313+
314+ return analysistest .end (env )
315+
316+ seooc_index_file_provider_test = analysistest .make (
317+ impl = _seooc_index_file_provider_test_impl ,
318+ )
0 commit comments