Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions doc/manual/howto/render-test-specifications.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ vocabulary rather than ``test_case``/``verifies``/etc., is a matching pair of
: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 ``testcase.yaml``
for the scenario table.
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)
---------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion doc/manual/reference/directives-and-roles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ The chain from annotated ztest C source to a rendered, traceable test report

``testmodule``'s argument is a Doxygen ``@defgroup`` name (the *module* group,
never a suite or a path); ``:module:`` is a project-relative path used only to
locate that module's ``testcase.yaml`` for the rendered scenario table. Every
locate that module's scenario file for the rendered scenario table: the first
of ``testcase.yaml``, ``tests.yaml`` and ``sample.yaml`` that exists. Every
``ZTEST``/``ZTEST_SUITE``/... in the named group and its inner suite/procedure
groups becomes one need each — nothing is written by hand per test case.

Expand Down
27 changes: 27 additions & 0 deletions sphinx/_extensions/_tests/test_rst_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,33 @@ def test_build_scenario_table_missing_yaml():
assert lines == []


def test_find_scenario_yaml_takes_tests_yaml(tmp_path):
# Newer Zephyr trees name the file tests.yaml.
(tmp_path / "tests.yaml").write_text("tests: {}\n")
assert rb.find_scenario_yaml(tmp_path) == tmp_path / "tests.yaml"


def test_find_scenario_yaml_takes_sample_yaml(tmp_path):
(tmp_path / "sample.yaml").write_text("tests: {}\n")
assert rb.find_scenario_yaml(tmp_path) == tmp_path / "sample.yaml"


def test_find_scenario_yaml_follows_twister_order(tmp_path):
# Twister reads testcase.yaml first, so it wins over tests.yaml.
for name in ("testcase.yaml", "tests.yaml", "sample.yaml"):
(tmp_path / name).write_text("tests: {}\n")
assert rb.find_scenario_yaml(tmp_path) == tmp_path / "testcase.yaml"


def test_find_scenario_yaml_warns_with_the_names_it_tried(tmp_path, caplog):
with caplog.at_level("WARNING", logger=rb.logger.name):
assert rb.find_scenario_yaml(tmp_path) is None
message = caplog.text
assert str(tmp_path) in message
for name in ("testcase.yaml", "tests.yaml", "sample.yaml"):
assert name in message


def test_build_procedure_need_rst_links_prose_but_keeps_title_plain():
# A need's title is not parsed as RST, so link markup there would render
# verbatim; the details are parsed and link like the see-also line does.
Expand Down
5 changes: 3 additions & 2 deletions sphinx/_extensions/_tests/test_test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,15 +406,16 @@ def test_testmodule_directive_suite_heading(app):


@pytest.mark.sphinx("html", srcdir=str(_ROOTS / "test-testmodule"))
def test_testmodule_directive_notes_its_xml_and_testcase_yaml_as_inputs(app):
def test_testmodule_directive_notes_its_xml_and_scenario_files_as_inputs(app):
# Without these, an incremental build after the test sources change (a
# retagged status, a renamed test) keeps the old test cases.
app.build()
inputs = set(app.env.zdocs_report_inputs["index"])
xml_dir = Path(app.config.testmodule_xml_dir)
assert str(xml_dir / "index.xml") in inputs
assert any(p.endswith(".xml") and "group__" in p for p in inputs)
assert any(p.endswith("testcase.yaml") for p in inputs)
for name in ("testcase.yaml", "tests.yaml", "sample.yaml"):
assert any(p.endswith(name) for p in inputs)


@pytest.mark.sphinx("html", srcdir=str(_ROOTS / "test-testmodule"))
Expand Down
28 changes: 27 additions & 1 deletion sphinx/_extensions/rst_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"build_result_rst",
"build_symbol_need_rst",
"build_scenario_table",
"SCENARIO_YAML_NAMES",
"find_scenario_yaml",
]

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -392,8 +394,32 @@ def build_symbol_need_rst(info, need_names=None, depends_field=False):
return "\n".join(lines)


# The names that twister reads for the scenarios of a test directory, in
# twister's order (scripts/pylib/twister/twisterlib/testplan.py). Newer Zephyr
# trees name the file tests.yaml, older ones testcase.yaml.
SCENARIO_YAML_NAMES = ("testcase.yaml", "tests.yaml", "sample.yaml")


def find_scenario_yaml(module_dir):
"""Return the first scenario file in ``module_dir`` that exists, or None.

The names come from ``SCENARIO_YAML_NAMES``. When no file exists, log a
warning that names each file it tried.
"""
module_dir = Path(module_dir)
for name in SCENARIO_YAML_NAMES:
path = module_dir / name
if path.is_file():
return path
logger.warning(
f"testmodule: no scenario file in {module_dir} "
f"(tried {', '.join(SCENARIO_YAML_NAMES)})"
)
return None


def build_scenario_table(testcase_yaml_path):
"""Return RST lines for a list-table of scenarios from testcase.yaml."""
"""Return RST lines for a list-table of scenarios from a scenario file."""
try:
with open(testcase_yaml_path) as f:
data = yaml.safe_load(f)
Expand Down
12 changes: 9 additions & 3 deletions sphinx/_extensions/test_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
from needs_fields import depends_field, field_type
from rst_builders import (
RESULT_FIELD_ROLES,
SCENARIO_YAML_NAMES,
_need_name,
build_need_rst,
build_procedure_need_rst,
build_result_rst,
build_scenario_table,
find_scenario_yaml,
)
from sphinx.util import logging
from twister_reader import (
Expand Down Expand Up @@ -629,9 +631,13 @@ def run(self):
suite_refids, proc_refids = _classify_inner_groups(module_cdef, xml_dir)

need_names = _need_names_from_config(app)
testcase_yaml = Path(module_root) / module_path / "testcase.yaml"
_note_input(env, testcase_yaml)
scenario_lines = build_scenario_table(testcase_yaml)
# Each name twister accepts is an input, so a scenario file that is
# added or renamed later also re-reads the document.
module_dir = Path(module_root) / module_path
for name in SCENARIO_YAML_NAMES:
_note_input(env, module_dir / name)
scenario_yaml = find_scenario_yaml(module_dir)
scenario_lines = build_scenario_table(scenario_yaml) if scenario_yaml else []
all_rst = list(scenario_lines)
for suite_refid in suite_refids:
all_rst += _build_suite_rst(
Expand Down