This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nuttx-ntfc.git


The following commit(s) were added to refs/heads/main by this push:
     new b2ad84e  mypytest: support alternatives and wildcards in ntfc.yaml 
requirements
b2ad84e is described below

commit b2ad84e7395e1f14481f62c1c81c91f75f98a3d9
Author: raiden00pl <[email protected]>
AuthorDate: Tue Aug 4 17:34:27 2026 +0200

    mypytest: support alternatives and wildcards in ntfc.yaml requirements
    
    Requirements only supported exact [key, value] matching, which cannot
    express init configuration that differs between build modes: flat
    builds use CONFIG_INIT_ENTRYPOINT, kernel builds CONFIG_INIT_FILEPATH.
    Add, backward compatibly:
    
    - [key, "*"]: any truthy value satisfies
    - [[key, value], ...]: alternatives, any one satisfies
    
    Also name the product, core and unmet requirement in the abort message.
    
    Signed-off-by: raiden00pl <[email protected]>
    Assisted-by: Claude Code
---
 Documentation/ntfc.yaml              |  4 ++-
 Documentation/writing-test-cases.rst | 14 ++++++++
 src/ntfc/pytest/mypytest.py          | 30 +++++++++++++---
 tests/pytest/test_mypytest.py        | 70 ++++++++++++++++++++++++++++++++++++
 4 files changed, 112 insertions(+), 6 deletions(-)

diff --git a/Documentation/ntfc.yaml b/Documentation/ntfc.yaml
index 55558a1..a338833 100644
--- a/Documentation/ntfc.yaml
+++ b/Documentation/ntfc.yaml
@@ -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", "*"]]
diff --git a/Documentation/writing-test-cases.rst 
b/Documentation/writing-test-cases.rst
index b13a59b..7637292 100644
--- a/Documentation/writing-test-cases.rst
+++ b/Documentation/writing-test-cases.rst
@@ -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
diff --git a/src/ntfc/pytest/mypytest.py b/src/ntfc/pytest/mypytest.py
index b85f695..5a1c15d 100644
--- a/src/ntfc/pytest/mypytest.py
+++ b/src/ntfc/pytest/mypytest.py
@@ -124,14 +124,31 @@ class MyPytest:
             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
 
@@ -143,9 +160,12 @@ class MyPytest:
 
             # 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)
 
diff --git a/tests/pytest/test_mypytest.py b/tests/pytest/test_mypytest.py
index c8affd5..6686b4f 100644
--- a/tests/pytest/test_mypytest.py
+++ b/tests/pytest/test_mypytest.py
@@ -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):

Reply via email to