Rendering test specifications from annotated source
You have a Sphinx document that should render sphinx-needs test cases
straight from Doxygen-annotated ZTEST sources, and (optionally) a second
document correlating those cases against a real Twister run. This is the
recipe; the mechanism is From annotated C to a test report and the
directive/role reference is Directives and roles. The
testmodule sample tree (zdocs-tests/samples/testmodule, with its own
README.md) is a complete, working instance of every step below.
1. Enable Doxygen XML for the project
doxygen_xml: true
Top-level, project-scoped — every kind: doxygen document generates XML,
not just the one you care about. Required: without it, testmodule finds an
empty XML directory, which looks like a parser fault and is not one.
2. Opt in on the specification document
documents:
spec:
kind: sphinx
builders: [html]
testmodule:
doxygen_source: dox-checks # kind: doxygen — its XML gets parsed
api_reference: dox-api # optional — where @see resolves to
The block’s presence is the entire opt-in: a document without it never loads
the extension at all. Both ids are validated at configure time — a typo or a
non-doxygen target is a hard error naming this document, not a silently
empty page. dox-checks and dox-api are this project’s own choice of
id, not an engine requirement — an id is taken verbatim, so checks and
api would work exactly as well; the dox- here is written into the id
because that project wants it in the folder name, the target name and the
published URL.
3. Annotate the Doxyfile
Two additions to the kind: doxygen document’s Doxyfile.in are easy to
miss:
ALIASES += "testid{1}=\xrefitem testids \"Test ID\" \"Test IDs\" \1"
ALIASES += "reqref{1}=\xrefitem reqrefs \"Requirement\" \"Requirements\" \1"
PREDEFINED = "ZTEST(suite, fn)=/** \ingroup suite */ void fn(void)" \
"ZTEST_SUITE(suite, predicate, setup, before, after, teardown)="
The alias names are yours; the \xrefitem keys (testids, reqrefs)
are matched by the parser and must be spelled exactly. Without
PREDEFINED macro expansion, every ZTEST is documented as a function
literally named ZTEST — expanding it also injects \ingroup suite from
the macro’s own argument, which is what attaches a test case to its suite
without a hand-written (and driftable) @ingroup.
4. Declare the needs vocabulary
Everything the directives emit — three need types (case/procedure/result),
three link types, and up to nine custom fields — must be declared in
needs_config.toml (ZDOCS_NEEDS_CONFIG), or sphinx-needs rejects the
need with an Unknown option/Unknown need type warning per occurrence.
The depends_on field (Kconfig conditions from @kconfig_depends) is the
exception: the directives set it only when you declare it.
Renaming the three roles away from the engine defaults, if you want project
vocabulary rather than test_case/verifies/etc., is a matching pair of
conf.py dicts — see Directives and roles.
5. Write the directive
.. testmodule:: widget_probe_module
:module: checks/widget/probe
The argument is the Doxygen module group’s name, never a suite; :module:
is a project-relative path used only to find that module’s scenario file
for the scenario table. That file is the first of testcase.yaml,
tests.yaml and sample.yaml that exists, in twister’s order.
6. Add the report half (optional)
report:
kind: sphinx
builders: [html]
testmodule:
spec: spec # whose needs.json to correlate against
doxygen_source: dox-checks
api_reference: dox-api
.. testreport:: twister_report.xml
:module: widget.probe
.. twisterinfo:: twister.json
:module: selects the runs by scenario-name prefix. Where one module’s
scenario name is a prefix of another’s (upstream Zephyr has many), select by
test directory instead: :path: takes the testsuite path as twister.json
records it, relative to ZEPHYR_BASE — for a test root outside the Zephyr
tree that starts with ../ (see Directives and roles).
Point ZDOCS_TWISTER_OUT at a real west twister output directory
(-DZDOCS_TWISTER_OUT=$(west topdir)/twister-out). Leaving it unset is
supported: both directives render a “not found” note and the build still
succeeds, since a docs build legitimately outrunning its test run is a normal
pipeline state. spec: is what lets the two ordering edges between the
report and its specification (a stage-1 needs-export race, and a stage-2
Doxygen-XML race) resolve automatically — nothing about them is hand-wired.