Skip to content
Open
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
4 changes: 3 additions & 1 deletion Documentation/ntfc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ dependencies: ["toml"] # python dependencies for test cases module
requirements: # nuttx config requirements
- ["CONFIG_DEBUG_SYMBOLS", True]
- ["CONFIG_SYSTEM_NSH", True]
- ["CONFIG_INIT_ENTRYPOINT", "nsh_main"]
# alternatives: any one entry satisfies the requirement;
# "*" accepts any set value
- [["CONFIG_INIT_ENTRYPOINT", "nsh_main"], ["CONFIG_INIT_FILEPATH", "*"]]
14 changes: 14 additions & 0 deletions Documentation/writing-test-cases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ String/Value Requirements:
- ["CONFIG_INIT_ENTRYPOINT", "nsh_main"] # CONFIG must equal value
- ["CONFIG_TASK_NAME_SIZE", "32"] # CONFIG must equal value

Wildcard Requirements:

.. code-block:: yaml

requirements:
- ["CONFIG_INIT_FILEPATH", "*"] # CONFIG must be set (any value)

Alternative Requirements (any one entry satisfies the requirement):

.. code-block:: yaml

requirements:
- [["CONFIG_INIT_ENTRYPOINT", "nsh_main"], ["CONFIG_INIT_FILEPATH", "*"]]

How Requirements Work:

1. NTFC reads NuttX ``.config`` file from configuration
Expand Down
30 changes: 25 additions & 5 deletions src/ntfc/pytest/mypytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,31 @@ def _write_session_config(self, result_dir: str) -> None:
json.dump(self._config.config, f, indent=2, sort_keys=True)
f.write("\n")

def _req_satisfied(self, product: Product, core: int, req: Any) -> bool:
"""Check a single ntfc.yaml requirement entry.

Supported entry forms:

- ``[key, value]``: config value must equal ``value``
- ``[key, "*"]``: any truthy config value satisfies
- ``[[key, value], ...]``: alternatives, any one satisfies
"""
if isinstance(req[0], list):
return any(self._req_satisfied(product, core, r) for r in req)

value = product.conf.kv_check(req[0], core)
if req[1] == "*":
return bool(value)
return bool(value == req[1])

def _kv_validate(
self, product: Product, core: int
) -> Tuple[bool, Optional[Any]]: # pragma: no cover
) -> Tuple[bool, Optional[Any]]:
"""Check if configuration can be used with this tool."""
requirements = pytest.ntfcyaml.get("requirements", {})

for req in requirements:
if product.conf.kv_check(req[0], core) != req[1]:
if not self._req_satisfied(product, core, req):
return False, req
return True, None

Expand All @@ -143,9 +160,12 @@ def _create_products(self, config: "EnvConfig") -> List[Product]:

# check config requirements only on cores that participate in tests
for core in p.conf.active_core_indices:
ret = self._kv_validate(p, core)
if ret[0] is False: # pragma: no cover
raise IOError(f"Missing kconfig dependency: {ret[1]}")
ok, req = self._kv_validate(p, core)
if not ok:
raise IOError(
f"product '{p.conf.name}' core {core}: "
f"missing kconfig requirement: {req}"
)

tmp.append(p)

Expand Down
70 changes: 70 additions & 0 deletions tests/pytest/test_mypytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,76 @@ def test_create_products_skips_requirements_for_flash_only_core(
assert products[0].cores == ["cpuapp"]


def test_create_products_requirement_forms(device_dummy, monkeypatch):
import pytest as pytest_module

config = {
"config": {},
"product": {
"name": "product",
"cores": {
"core0": {
"name": "cpuapp",
"device": "sim",
"conf_path": "./tests/resources/nuttx/sim/kv_config",
"elf_path": "./tests/resources/nuttx/sim/nuttx",
},
},
},
}

monkeypatch.setattr(
pytest_module,
"ntfcyaml",
{
"requirements": [
# plain form: exact match
["CONFIG_HOST_LINUX", True],
# alternatives: first unset, second matches
[
["CONFIG_INIT_FILEPATH", "*"],
["CONFIG_INIT_ENTRYPOINT", "nsh_main"],
],
# wildcard: any truthy value
["CONFIG_NSH_PROMPT_STRING", "*"],
]
},
raising=False,
)
with patch("ntfc.cores.get_device", return_value=device_dummy):
products = MyPytest(config)._create_products(EnvConfig(config))
assert len(products) == 1


def test_create_products_requirement_unmet(device_dummy, monkeypatch):
import pytest as pytest_module

config = {
"config": {},
"product": {
"name": "product",
"cores": {
"core0": {
"name": "cpuapp",
"device": "sim",
"conf_path": "./tests/resources/nuttx/sim/kv_config",
"elf_path": "./tests/resources/nuttx/sim/nuttx",
},
},
},
}

monkeypatch.setattr(
pytest_module,
"ntfcyaml",
{"requirements": [["CONFIG_INIT_FILEPATH", "*"]]},
raising=False,
)
with patch("ntfc.cores.get_device", return_value=device_dummy):
with pytest.raises(IOError, match="product.*core 0.*INIT_FILEPATH"):
MyPytest(config)._create_products(EnvConfig(config))


def test_device_stop_calls_stop(config_dummy, device_dummy):
"""_device_stop calls device.stop() for each core."""
with patch("ntfc.cores.get_device", return_value=device_dummy):
Expand Down