Skip to content

Commit c6e71c6

Browse files
committed
[rules_score]: bugfix dependable element sphinx build
1 parent 0a1baf6 commit c6e71c6

3 files changed

Lines changed: 192 additions & 5 deletions

File tree

bazel/rules/rules_score/private/sphinx_module.bzl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,12 @@ def _score_html_impl(ctx):
181181
# Sphinx only accepts a single directory to read its doc sources from.
182182
# Because plain files and generated files are in different directories,
183183
# we need to merge the two into a single directory.
184-
for orig_file in ctx.files.srcs:
185-
_relocate(orig_file)
184+
index_source_file = ctx.attr.index.files.to_list()[0]
186185
relocated_index_file = ""
187-
for input_file in sphinx_source_files:
188-
if input_file.path.endswith("/index.rst"):
189-
relocated_index_file = input_file.path
186+
for orig_file in ctx.files.srcs:
187+
dest = _relocate(orig_file)
188+
if orig_file.path == index_source_file.path:
189+
relocated_index_file = dest.path
190190

191191
# Build HTML with external needs
192192
html_inputs = sphinx_source_files + ctx.files.needs + filtered_files + [config_file, needs_external_needs_json]

bazel/rules/rules_score/test/BUILD

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,12 @@ load(
6868
)
6969
load(
7070
":seooc_test.bzl",
71+
"seooc_all_sources_relocated_test",
7172
"seooc_artifacts_copied_test",
7273
"seooc_index_generation_test",
74+
"seooc_multi_index_files_exist_test",
7375
"seooc_needs_provider_test",
76+
"seooc_sphinx_entry_point_is_root_index_test",
7477
"seooc_sphinx_module_generated_test",
7578
)
7679
load(
@@ -496,6 +499,22 @@ seooc_index_generation_test(
496499
target_under_test = ":seooc_test_lib_index",
497500
)
498501

502+
# Regression tests: correct index.rst resolution when multiple index.rst files exist
503+
seooc_multi_index_files_exist_test(
504+
name = "seooc_tests_multi_index_files_exist",
505+
target_under_test = ":test_dependable_element_index",
506+
)
507+
508+
seooc_sphinx_entry_point_is_root_index_test(
509+
name = "seooc_tests_sphinx_entry_point_is_root_index",
510+
target_under_test = ":test_dependable_element_doc",
511+
)
512+
513+
seooc_all_sources_relocated_test(
514+
name = "seooc_tests_all_sources_relocated",
515+
target_under_test = ":test_dependable_element_doc",
516+
)
517+
499518
# ============================================================================
500519
# Test Suites
501520
# ============================================================================
@@ -560,9 +579,12 @@ sphinx_module_providers_test_suite(name = "sphinx_module_providers_tests")
560579
test_suite(
561580
name = "seooc_tests",
562581
tests = [
582+
":seooc_tests_all_sources_relocated",
563583
":seooc_tests_artifacts_copied",
564584
":seooc_tests_index_generation",
585+
":seooc_tests_multi_index_files_exist",
565586
":seooc_tests_needs_provider",
587+
":seooc_tests_sphinx_entry_point_is_root_index",
566588
":seooc_tests_sphinx_module_generated",
567589
],
568590
)

bazel/rules/rules_score/test/seooc_test.bzl

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,168 @@ def _seooc_needs_provider_test_impl(ctx):
115115
seooc_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+
)

0 commit comments

Comments
 (0)