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

commit 8fe88a555979417e2e24621b1e41e6970ca9f018
Author: raiden00pl <[email protected]>
AuthorDate: Tue Aug 4 17:40:25 2026 +0200

    coreconfig.py: dispatch cmd_check to application binaries
    
    With app_bindir configured, cmd_check resolves commands against the
    application binary directory first and falls back to symbol lookups
    over bin_debug for names that are not applications (NSH builtins like
    cmd_df, cmocka entries). Flat-mode behavior is unchanged.
    
    Signed-off-by: raiden00pl <[email protected]>
    Assisted-by: Claude Code
---
 Documentation/writing-test-cases.rst |  6 ++++-
 src/ntfc/coreconfig.py               | 34 +++++++++++++++++++++++++---
 src/ntfc/testfilter.py               |  4 ++--
 tests/test_coreconfig.py             | 44 ++++++++++++++++++++++++++++++++++++
 tests/test_filtertest.py             |  2 +-
 5 files changed, 83 insertions(+), 7 deletions(-)

diff --git a/Documentation/writing-test-cases.rst 
b/Documentation/writing-test-cases.rst
index 7637292..1f3a30e 100644
--- a/Documentation/writing-test-cases.rst
+++ b/Documentation/writing-test-cases.rst
@@ -192,7 +192,11 @@ Execute NSH command and verify output:
 
 Decorators:
 
-- ``@pytest.mark.cmd_check("symbol_name")``: Verify ELF symbol exists
+- ``@pytest.mark.cmd_check("symbol_name")``: Verify ELF symbol exists.
+  On kernel-mode targets (``CONFIG_BUILD_KERNEL=y``) the marker is first
+  matched against application file names (a trailing ``_main`` maps to
+  the file name, so ``hello_main`` matches the ``hello`` binary) and
+  then against symbols in the unstripped application binaries
 - ``@pytest.mark.dep_config("CONFIG_X", "CONFIG_Y")``: Skip if configs not
   enabled
 
diff --git a/src/ntfc/coreconfig.py b/src/ntfc/coreconfig.py
index 7026170..bb88e50 100644
--- a/src/ntfc/coreconfig.py
+++ b/src/ntfc/coreconfig.py
@@ -21,8 +21,10 @@
 """Product core configuration handler."""
 
 import os
+from functools import cached_property
 from typing import Any, Dict, Optional, Union
 
+from ntfc.lib.elf.app_bindir import AppBinDir, symbol_patterns
 from ntfc.lib.elf.elf_parser import ElfParser
 
 
@@ -211,10 +213,36 @@ class CoreConfig:
 
         return self._kv_values.get(cfg, False)
 
+    @cached_property
+    def _appbin(self) -> Optional[AppBinDir]:
+        """Return the installed kernel-mode application binaries."""
+        bindir = self.app_bindir
+        if not bindir or not os.path.isdir(bindir):
+            return None
+
+        return AppBinDir(bindir)
+
+    @property
+    def has_app_bindir(self) -> bool:
+        """Return True when kernel-mode application binaries are found."""
+        return self._appbin is not None
+
     def cmd_check(self, cmd: str, core: int = 0) -> bool:
-        """Check if command is available in binary."""
+        """Check if command is available in binary.
+
+        Kernel-mode cores resolve commands against the application binary
+        directory; otherwise the symbol must exist in the core ELF.
+        """
+        if self._appbin:
+            if self._appbin.has_command(cmd):
+                return True
+            # not an application: NSH builtins and test entry points
+            # are symbols inside the application binaries
+            return self._appbin.has_symbol(cmd)
+
         if not self._elf:
             raise AttributeError("no elf data")
 
-        symbol_name = f"{cmd}_main" if "cmocka" in cmd else cmd
-        return self._elf.has_symbol(symbol_name)
+        return any(
+            self._elf.has_symbol(symbol) for symbol in symbol_patterns(cmd)
+        )
diff --git a/src/ntfc/testfilter.py b/src/ntfc/testfilter.py
index 155c882..00d5bab 100644
--- a/src/ntfc/testfilter.py
+++ b/src/ntfc/testfilter.py
@@ -85,12 +85,12 @@ class FilterTest:
                 reason = f"Required config '{d}' not enabled"
                 break
 
-        # command available in ELF
+        # command available on the target
         if skip is False:
             for c in cmd:
                 if self._config.cmd_check(c) is False:
                     skip = True
-                    reason = f"Required symbol '{c}' not found in ELF"
+                    reason = f"Required command '{c}' not available"
                     break
 
         # check extra parameters
diff --git a/tests/test_coreconfig.py b/tests/test_coreconfig.py
index 082d3f0..9eb666e 100644
--- a/tests/test_coreconfig.py
+++ b/tests/test_coreconfig.py
@@ -18,6 +18,8 @@
 #
 ############################################################################
 
+import shutil
+
 import pytest
 
 from ntfc.coreconfig import CoreConfig
@@ -164,6 +166,48 @@ def test_core_config_app_bindir(tmp_path):
     }
     assert CoreConfig(conf).app_bindir is None
 
+    # a derived directory that does not exist is not a command source
+    conf = {
+        "name": "t",
+        "conf_path": str(kernel_cfg),
+        "elf_path": "./tests/resources/nuttx/sim/nuttx",
+    }
+    core_conf = CoreConfig(conf)
+    assert core_conf.app_bindir == "./tests/resources/nuttx/sim/bin"
+    assert core_conf.has_app_bindir is False
+
+
+def test_core_config_cmd_check_kernel_mode(tmp_path):
+    kernel_cfg = tmp_path / "kv_config"
+    kernel_cfg.write_text("CONFIG_BUILD_KERNEL=y\n")
+    bindir = tmp_path / "bin"
+    bindir.mkdir()
+    (bindir / "hello").write_bytes(b"\x7fELF" + b"\x00" * 12)
+
+    conf = {
+        "name": "t",
+        "conf_path": str(kernel_cfg),
+        "app_bindir": str(bindir),
+    }
+
+    p = CoreConfig(conf)
+    assert p.has_app_bindir is True
+    # command resolution by application file name, no ELF configured
+    assert p.cmd_check("hello") is True
+    assert p.cmd_check("hello_main") is True
+    assert p.cmd_check("cmd_df") is False
+
+    # symbol fallback over unstripped binaries in bin_debug
+    debug = tmp_path / "bin_debug"
+    debug.mkdir()
+    shutil.copy("./tests/resources/nuttx/sim/nuttx", debug / "sh")
+
+    p = CoreConfig(conf)
+    assert p.cmd_check("cmd_df") is True
+    assert p.cmd_check("missing|cmd_df") is True
+    assert p.cmd_check("cmd_d.*") is True
+    assert p.cmd_check("no_such_symbol_xyz") is False
+
 
 def test_core_config_read_poll_interval() -> None:
     assert CoreConfig({"name": "test"}).read_poll_interval == 0.1
diff --git a/tests/test_filtertest.py b/tests/test_filtertest.py
index e7df74b..cf85ce6 100644
--- a/tests/test_filtertest.py
+++ b/tests/test_filtertest.py
@@ -57,7 +57,7 @@ def test_filterest_filter():
         f.extract_test_requirements = mock_extract_test_requirements2
         skip, reason = f.check_test_support(None)
         assert skip is True
-        assert reason == "Required symbol 'CMD1' not found in ELF"
+        assert reason == "Required command 'CMD1' not available"
 
         config.kv_check.return_value = True
         config.cmd_check.return_value = True

Reply via email to