from __future__ import annotations import ast from pathlib import Path NOTEBOOK = Path(__file__).resolve().parents[1] / "notebooks" / "verification.py" def _find_calls(tree: ast.AST, func_name: str) -> list[ast.Call]: """Find all Call nodes for a named function (simple name, not attribute).""" return [ node for node in ast.walk(tree) if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == func_name ] def test_verification_notebook_uses_scenegraph_query_api(): source = NOTEBOOK.read_text() assert "query_image_against_scene_graph" in source assert "text_labels=text_labels" in source def test_verification_notebook_uses_hash_codec_for_object_hashes(): source = NOTEBOOK.read_text() # Notebook should no longer do hash conversion directly; # the builder handles it internally. assert "bits_tensor_to_hash_bytes" not in source assert "np.packbits" not in source def test_verification_notebook_uses_scene_graph_builder(): source = NOTEBOOK.read_text() assert "SceneGraphBuilder" in source assert "SceneGraphBuildConfig" in source assert "flatten_room_views" in source assert "scene_graph, build_artifacts = builder.build_from_room_views" in source # Verify no direct ObjectNode construction (builder handles it internally) tree = ast.parse(source) object_node_calls = _find_calls(tree, "ObjectNode") assert len(object_node_calls) == 0, ( "Notebook should not construct ObjectNode() directly; " "the builder handles it" ) def test_verification_notebook_uses_bbox_depth_center_positioning(): source = NOTEBOOK.read_text() tree = ast.parse(source) # --- SceneGraphBuildConfig must use position_strategy='bbox_depth_center' --- sg_config_calls = _find_calls(tree, "SceneGraphBuildConfig") assert len(sg_config_calls) >= 1, ( "Notebook must instantiate SceneGraphBuildConfig" ) config_kwargs = { kw.arg: kw.value for kw in sg_config_calls[0].keywords if kw.arg is not None } assert "position_strategy" in config_kwargs pos_val = config_kwargs["position_strategy"] assert isinstance(pos_val, ast.Constant) and pos_val.value == "bbox_depth_center", ( f"Expected position_strategy='bbox_depth_center', got {pos_val!r}" ) assert "camera_hfov_degrees" in config_kwargs, ( "SceneGraphBuildConfig should include camera_hfov_degrees" ) # --- object_rows dict literals must include bbox_xyxy and position_confidence --- found_bbox = False found_pos_conf = False for node in ast.walk(tree): if isinstance(node, ast.Dict): keys = {k.value for k in node.keys if isinstance(k, ast.Constant)} if "obj_id" in keys: if "bbox_xyxy" in keys: found_bbox = True if "position_confidence" in keys: found_pos_conf = True assert found_bbox, "object_rows must include bbox_xyxy column" assert found_pos_conf, "object_rows must include position_confidence column" # --- No direct ObjectNode( construction --- object_node_calls = _find_calls(tree, "ObjectNode") assert len(object_node_calls) == 0, ( "Notebook should not construct ObjectNode() directly" )