Implementation layer and conditions (4/6) - #7
Merged
tobiaskaestner merged 4 commits intoOct 2, 2026
Merged
tobiaskaestner merged 4 commits into
tobiaskaestner merged 4 commits into
Conversation
This was referenced Oct 1, 2026
Merged
tobiaskaestner
added this pull request to stack #10
October 1, 2026 18:47
tobiaskaestner
force-pushed
the
tkaestner/4-implementation-layer
branch
2 times, most recently
from
October 2, 2026 15:05
47a63bd to
93c08dd
Compare
The API Doxygen project carries Doxygen 1.16 `\satisfies` on its symbols
(191 of them in the safety docset, 300 requirement refs), but the parser
read only `<verifies>`, and the Sphinx API document emitted no needs. A
requirement page could show what verifies it, never what implements it.
A `kind: sphinx` document with a registry block
symbol_needs:
doxygen_source: <kind: doxygen id>
now loads a new `symbol_needs` extension. Its `.. symbolneeds::` directive
(optional argument: one Doxygen group; without it the whole project,
sectioned by group or file) emits one need per symbol with `<satisfies>`:
titled with the symbol, id `<TYPE>-<symbol>`, the kind, brief, declaring
file and a link to its Doxygen page in the body, and a link to each
requirement. Type and link are engine roles, `implementation` and
`satisfies`, named by the consumer (defaults `impl`, `satisfies`;
`symbolneeds_need_types` / `symbolneeds_need_links`), as ADR-0009 has it
for the test directives. With the document also publishing
`needs: {source: json}`, a requirement's page lists the symbols under the
link's incoming name next to "verified by". Without the block nothing is
loaded, so existing consumers are unchanged.
A `\satisfies` naming an unknown UID is caught the way a `verifies` is:
Doxygen writes the same `requirement_<UID>` refid for it, so the XML cannot
tell, but sphinx-needs reports the unknown outgoing link, and Doxygen's own
warning trips the stage-2 gate. The fixture is real Doxygen 1.16.1 output
with one such UID, and the test asserts the warning.
The registry validates the block at configure time, docrefs resolves its
XML dir and HTML URL, and add_docs_from_registry adds the same stage-2
edge to the Doxygen document as testmodule's. The input tracking that
testmodule/testreport use moves to its own `input_tracking` extension, so
the new directive re-reads when the XML changes without loading
test_module. doxygen_parser's `\verifies` reading becomes
`requirement_uids(memberdef, relation)`, shared with `\satisfies`. (Also
wraps one over-long line in the parameterized-test tests.)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
Safety sources are getting `@kconfig_depends{<condition>}`, an alias for
`\xrefitem kconfig_depends "Depends on" "Kconfig dependencies" \1`, which
a Doxygen input filter adds to code built under a CONFIG condition. A test
case or API function that exists only with CONFIG_ASSERT should say so on
its need, and the parser ignored the xrefsect.
doxygen_parser.kconfig_depends() reads it into `depends_on` on test cases
(parse_memberdef) and API symbols (parse_symbol). The Doxygen 1.16.1 shape,
checked with real runs (fixture fixtures/doxygen-kconfig): adjacent
commands share one xrefsect with a <para> per condition, commands apart get
one xrefsect each, and like @Testid they can sit inside the last list item,
so the whole description is searched by id prefix `kconfig_depends_`. Each
condition is kept verbatim, once, in order, without Doxygen's trailing
space. `&&`, `!` and parentheses survive, and `\,` arrives as `,`. The
@reqref/@Testid loop now skips these xrefsects explicitly, rather than
relying on the id not containing "reqrefs".
Both need builders render the conditions in the body, labelled with the
alias's own title ("Depends on: CONFIG_ASSERT"), because the consumer's
layout decides which fields show. They set the `depends_on` field, the
conditions joined with "; ", only when the consumer's sphinx-needs schema
declares it; otherwise every need would warn "Unknown option". A string
field is the declaration to use. An array field is used only while no
condition contains ; | or , (sphinx-needs splits an array value there, so
`A || B` would become pieces); a need with such a condition gets a
warning instead of a wrong value. That check is in the new needs_fields
module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
A test case need's `suite` was its inner suite group's Doxygen name, and testreport finds the test case for a twister result by (suite, function) (SpecLookup). Two test modules can declare the same ZTEST_SUITE: Zephyr's tests/kernel/workq/user_work and work_queue both declare workqueue_api. They cannot share one group, because both module pages would then render every test in it and the need ids would collide. With a group per module, though, the suite field no longer named the ztest suite, and no result could find its case. A new config value `testmodule_suite_qualifier` (default "", today's behaviour) fixes this. When it is set, e.g. to "__", the suite is the part of the group name after the qualifier's last occurrence: kernel_workq_user_work_module__workqueue_api gives workqueue_api. A name without the qualifier, or with nothing after it, is used whole. A consumer sets it in the conf.py of the document holding the testmodule directives, after configure(), like testmodule_need_types. The suite heading's fallback (a group without a title) uses the derived suite too. The fallback id of a case without @Testid, testspec-<group>-<function>, keeps the whole group name, not the suite. Two modules can have a function of the same name in the same suite (the fixture's test_shared). Scoped by the suite, both would get testspec-workqueue_api-test_shared, and sphinx-needs would fail the build with a duplicate id. The group name is unique by construction. A twister result of such a function is still ambiguous by (suite, function), and testreport skips it with the existing warning. That is the same as for any pair two modules document. The qualifier splits only the group name the suite is derived from. The (suite, function) key is unchanged, and it now sees the real suite name. The reference page says so. Tests: fixtures/doxygen-qualifier (hand-written in Doxygen's shape, two modules with groups m1__workqueue_api and m2__workqueue_api) and a Sphinx root that renders both modules on one page. Every need carries suite workqueue_api, the four ids are distinct, and load_spec_lookup on the built needs.json maps each module's result to its own case. The unset and no-occurrence cases are unchanged. On the previous engine the new tests fail (9 of 11: the helper does not exist, and the suite stays m1__workqueue_api), and the two unset-qualifier guards pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
…hy it skipped A test case's depends_on (@kconfig_depends) says under which Kconfig condition it is meaningful; a result only linked to its case, so whether the condition held in the build that produced the result was not recorded. And a skipped result carried twister's reason only, which does not tell a skip the specification explains from one it does not. testreport now sets two result fields: - depends_met: yes / no / n/a. The case's conditions (all must hold; the "; " of depends_on is an and) are evaluated against the .config twister keeps for that build, <out>/<platform>/<toolchain>/<test path>/ <scenario>/zephyr/.config (or the --detailed-test-id layout), looked up exactly, never in another scenario's directory. CONFIG_X and defined(CONFIG_X) are true when the symbol has any value, not "is not set"; !, &&, || and parentheses combine them. n/a for no condition, no .config, or a condition outside that grammar (another macro, IS_ENABLED(), a comparison): nothing is guessed, and the build warns once per case and condition. Each .config read is a tracked input. - skip_class, on skipped results: build-only (built only), platform (a memory region overflowed, or filtered by platform), config (ztest skip and depends_met no), unexplained (anything else). A parameterized test's class comes from its values' common reason. Both are field roles with names the consumer may choose (testreport_need_fields, defaults depends_met / skip_class), merged into the role->name mapping, and set only where the consumer's sphinx-needs schema declares the field; ADR-0009 notes the two result fields as the exception to literal field names. The spec lookup now reads a case's depends_on (string or array). On the safety docset (twister-out-b4b, 5386 results): depends_met yes 595, no 424, n/a 4367, no unparseable condition; skipped 610 = config 416, platform 156, build-only 6, unexplained 32; no result ran although its condition was false. Tests: fixtures/twister-depends (one module on two boards, .config files with the feature set on one), the grammar and its rejections, .config reading and lookup, every class, the assessment of each fixture result, the field names by role, and a Sphinx root that renames both fields and checks needs.json and the single warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tobias Kaestner <tobias.kaestner@inovex.de>
tobiaskaestner
force-pushed
the
tkaestner/4-implementation-layer
branch
from
October 2, 2026 15:07
93c08dd to
27eca98
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 4 of 6 of a stacked series. It is based on #6 (
tkaestner/3-test-report-correctness); review the commits below only.symbolneeds: oneimplneed per API symbol with a\satisfies, linkedsatisfiesto its requirement. The registry key issymbol_needs: {doxygen_source: <id>}. The need type and link names belong to the consumer(ADR-0009).
@kconfig_dependsbecomes the string fielddepends_onon test cases andimpl needs.
testmodule_suite_qualifier: a suite group can have a qualified name(
<module>__<suite>). Two modules can then have tests of the same suite, anda suite can have the name of a C function.
depends_on(
depends_met, from the.configthat twister kept), and why it skipped(
skip_class: config, platform, build-only, unexplained).Commits
Testing
python3 -m pytest sphinx/_extensions/_tests -q) at the head of this PR: 327 passed.The stack
🤖 Generated with Claude Code