diff --git a/camera/blur_pipeline.py b/camera/blur_pipeline.py index 540b411..2907d77 100644 --- a/camera/blur_pipeline.py +++ b/camera/blur_pipeline.py @@ -317,7 +317,7 @@ def _run_and_verify(opts): b_t = iron.zeros(16 * 16, dtype=np.int32, device="npu") out_t = iron.zeros(tensor_size, dtype=np.int8, device="npu") - edge_detect(in_t, b_t, out_t, **_compile_kwargs(opts)) + blur(in_t, b_t, out_t, **_compile_kwargs(opts)) in_uint8 = in_np.view(np.uint8) expected_uint8 = _edge_detect_ref(in_uint8, opts.height, opts.width) @@ -339,7 +339,7 @@ def _run_and_verify(opts): def main(): opts = _make_argparser().parse_args() run_design_cli( - edge_detect, + blur, opts, compile_kwargs=_compile_kwargs, run_and_verify=_run_and_verify, diff --git a/camera/test_blur_pipeline_cli_symbol.py b/camera/test_blur_pipeline_cli_symbol.py new file mode 100644 index 0000000..1b9d653 --- /dev/null +++ b/camera/test_blur_pipeline_cli_symbol.py @@ -0,0 +1,68 @@ +# Regression guard for the blur_pipeline CLI / self-verify design symbol. +# +# blur_pipeline.py was adapted from AMD's `edge_detect` IRON example. The design +# it actually defines is `blur` (see `def blur(...)` and +# `from blur_pipeline import blur` in npu_camera_daemon.py / test_blur.py), but +# `_run_and_verify()` and `main()` still called the old name `edge_detect`, +# which is never defined or imported in this module. Because the module imports +# `aie.iron` at the top, a host without the NPU toolchain fails at that import +# first (so CI's import-boundary check stays green) -- but on a real NPU box +# `python blur_pipeline.py` reaches main() and dies with +# `NameError: name 'edge_detect' is not defined` instead of running the design. +# +# This test is pure-AST (no aie/NPU toolchain, no numpy) so it runs anywhere, +# and it asserts the design symbol the CLI hands to the NPU is one the module +# actually defines. +import ast +import builtins +import os + +HERE = os.path.dirname(os.path.abspath(__file__)) +SRC = os.path.join(HERE, "blur_pipeline.py") + + +def _module_bound_names(tree): + names = set() + for node in tree.body: + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + names.add(node.name) + elif isinstance(node, (ast.Import, ast.ImportFrom)): + for alias in node.names: + names.add(alias.asname or alias.name.split(".")[0]) + elif isinstance(node, ast.Assign): + for tgt in node.targets: + if isinstance(tgt, ast.Name): + names.add(tgt.id) + return names + + +def _cli_referenced_names(tree): + """Bare names called (or handed to run_design_cli) inside the CLI paths.""" + refs = set() + for node in ast.walk(tree): + if isinstance(node, ast.FunctionDef) and node.name in ("_run_and_verify", "main"): + for sub in ast.walk(node): + if isinstance(sub, ast.Call) and isinstance(sub.func, ast.Name): + refs.add(sub.func.id) + # run_design_cli(, ...) -> first positional arg is the design fn + if sub.func.id == "run_design_cli" and sub.args and isinstance(sub.args[0], ast.Name): + refs.add(sub.args[0].id) + return refs + + +def test_cli_design_symbol_is_defined(): + tree = ast.parse(open(SRC, encoding="utf-8").read()) + bound = _module_bound_names(tree) + assert "blur" in bound, "expected the design function `blur` to be defined" + refs = _cli_referenced_names(tree) + assert "edge_detect" not in refs, ( + "blur_pipeline.py references undefined `edge_detect` (leftover from the " + "AMD edge_detect example) in its CLI/self-verify path; it should call `blur`" + ) + undefined = {r for r in refs if r not in bound and not hasattr(builtins, r)} + assert not undefined, f"CLI/self-verify references undefined names: {sorted(undefined)}" + + +if __name__ == "__main__": + test_cli_design_symbol_is_defined() + print("OK: blur_pipeline CLI design symbol is defined")