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
