"""Tests for macro IR types (MacroParam, ParamRef, MacroDef, IRMacroCall). Tests verify: - MacroParam can be constructed with a name - ParamRef can be constructed with param, prefix, and suffix - MacroDef can be constructed with name, params, and body IRGraph - IRMacroCall can be constructed with name, positional and named args - IRNode.const field accepts ParamRef values - IRGraph.macro_defs and IRGraph.macro_calls fields can be populated - RegionKind.MACRO enum value exists and has correct value """ import pytest from asm.ir import ( IRGraph, IRNode, IREdge, IRDataDef, MacroParam, ParamRef, MacroDef, IRMacroCall, RegionKind, SourceLoc, ) from cm_inst import ArithOp, Port class TestMacroParam: """Tests for MacroParam dataclass.""" def test_macro_param_construction(self): """MacroParam can be constructed with a name.""" param = MacroParam(name="x") assert param.name == "x" def test_macro_param_frozen(self): """MacroParam is frozen (immutable).""" param = MacroParam(name="x") with pytest.raises(AttributeError): param.name = "y" class TestParamRef: """Tests for ParamRef dataclass.""" def test_param_ref_basic_construction(self): """ParamRef can be constructed with param name.""" ref = ParamRef(param="x") assert ref.param == "x" assert ref.prefix == "" assert ref.suffix == "" def test_param_ref_with_prefix(self): """ParamRef can include an optional prefix.""" ref = ParamRef(param="x", prefix="pre_") assert ref.param == "x" assert ref.prefix == "pre_" assert ref.suffix == "" def test_param_ref_with_suffix(self): """ParamRef can include an optional suffix.""" ref = ParamRef(param="x", suffix="_suf") assert ref.param == "x" assert ref.prefix == "" assert ref.suffix == "_suf" def test_param_ref_with_prefix_and_suffix(self): """ParamRef can include both prefix and suffix for token pasting.""" ref = ParamRef(param="label", prefix="&", suffix="_out") assert ref.param == "label" assert ref.prefix == "&" assert ref.suffix == "_out" def test_param_ref_frozen(self): """ParamRef is frozen (immutable).""" ref = ParamRef(param="x") with pytest.raises(AttributeError): ref.param = "y" class TestMacroDef: """Tests for MacroDef dataclass.""" def test_macro_def_basic_construction(self): """MacroDef can be constructed with name, params, and body.""" body = IRGraph() macro = MacroDef(name="loop", params=(), body=body) assert macro.name == "loop" assert macro.params == () assert macro.body is body assert macro.loc == SourceLoc(0, 0) def test_macro_def_with_params(self): """MacroDef can include formal parameters.""" params = (MacroParam(name="init"), MacroParam(name="limit")) body = IRGraph() macro = MacroDef(name="loop_counted", params=params, body=body) assert macro.name == "loop_counted" assert len(macro.params) == 2 assert macro.params[0].name == "init" assert macro.params[1].name == "limit" def test_macro_def_with_location(self): """MacroDef can include a source location.""" loc = SourceLoc(line=5, column=10) body = IRGraph() macro = MacroDef(name="test", params=(), body=body, loc=loc) assert macro.loc == loc def test_macro_def_body_with_nodes(self): """MacroDef body IRGraph can contain nodes.""" node = IRNode(name="&add", opcode=ArithOp.ADD) body = IRGraph(nodes={"&add": node}) macro = MacroDef(name="simple", params=(), body=body) assert "&add" in macro.body.nodes assert macro.body.nodes["&add"].opcode == ArithOp.ADD def test_macro_def_frozen(self): """MacroDef is frozen (immutable).""" macro = MacroDef(name="test", params=(), body=IRGraph()) with pytest.raises(AttributeError): macro.name = "other" class TestIRMacroCall: """Tests for IRMacroCall dataclass.""" def test_macro_call_basic_construction(self): """IRMacroCall can be constructed with name.""" call = IRMacroCall(name="loop") assert call.name == "loop" assert call.positional_args == () assert call.named_args == () def test_macro_call_with_positional_args(self): """IRMacroCall can include positional arguments.""" args = ("&init", "&limit") call = IRMacroCall(name="loop_counted", positional_args=args) assert call.name == "loop_counted" assert call.positional_args == args assert len(call.positional_args) == 2 def test_macro_call_with_named_args(self): """IRMacroCall can include named arguments.""" named = (("gate", "&my_gate"), ("trigger", "&my_trigger")) call = IRMacroCall(name="inject", named_args=named) assert call.name == "inject" assert call.named_args == named assert len(call.named_args) == 2 def test_macro_call_with_location(self): """IRMacroCall can include a source location.""" loc = SourceLoc(line=12, column=5) call = IRMacroCall(name="loop", loc=loc) assert call.loc == loc def test_macro_call_frozen(self): """IRMacroCall is frozen (immutable).""" call = IRMacroCall(name="test") with pytest.raises(AttributeError): call.name = "other" class TestIRNodeWithParamRef: """Tests for IRNode accepting ParamRef in const field.""" def test_ir_node_const_with_int(self): """IRNode.const can hold an integer.""" node = IRNode(name="&test", opcode=ArithOp.ADD, const=42) assert node.const == 42 def test_ir_node_const_with_param_ref(self): """IRNode.const can hold a ParamRef.""" ref = ParamRef(param="x") node = IRNode(name="&test", opcode=ArithOp.ADD, const=ref) assert isinstance(node.const, ParamRef) assert node.const.param == "x" def test_ir_node_const_with_param_ref_and_prefix(self): """IRNode.const can hold a ParamRef with prefix/suffix.""" ref = ParamRef(param="addr", prefix="0x", suffix="00") node = IRNode(name="&test", opcode=ArithOp.ADD, const=ref) assert node.const.param == "addr" assert node.const.prefix == "0x" assert node.const.suffix == "00" def test_ir_node_const_none(self): """IRNode.const can be None.""" node = IRNode(name="&test", opcode=ArithOp.ADD, const=None) assert node.const is None class TestIRGraphMacroFields: """Tests for IRGraph.macro_defs and IRGraph.macro_calls fields.""" def test_ir_graph_empty_macro_defs(self): """IRGraph starts with empty macro_defs list.""" graph = IRGraph() assert graph.macro_defs == [] def test_ir_graph_empty_macro_calls(self): """IRGraph starts with empty macro_calls list.""" graph = IRGraph() assert graph.macro_calls == [] def test_ir_graph_with_macro_defs(self): """IRGraph can store macro definitions.""" body = IRGraph() macro = MacroDef(name="loop", params=(), body=body) graph = IRGraph(macro_defs=[macro]) assert len(graph.macro_defs) == 1 assert graph.macro_defs[0].name == "loop" def test_ir_graph_with_macro_calls(self): """IRGraph can store macro invocations.""" call = IRMacroCall(name="loop", positional_args=("&src", "&dest")) graph = IRGraph(macro_calls=[call]) assert len(graph.macro_calls) == 1 assert graph.macro_calls[0].name == "loop" def test_ir_graph_with_both_macro_defs_and_calls(self): """IRGraph can store both macro definitions and invocations.""" body = IRGraph() macro_def = MacroDef(name="loop", params=(), body=body) macro_call = IRMacroCall(name="loop", positional_args=("&a", "&b")) graph = IRGraph(macro_defs=[macro_def], macro_calls=[macro_call]) assert len(graph.macro_defs) == 1 assert len(graph.macro_calls) == 1 class TestRegionKindMacro: """Tests for RegionKind.MACRO enum value.""" def test_region_kind_macro_exists(self): """RegionKind.MACRO enum value exists.""" assert hasattr(RegionKind, "MACRO") def test_region_kind_macro_value(self): """RegionKind.MACRO has correct string value.""" assert RegionKind.MACRO.value == "macro" def test_region_kind_macro_type(self): """RegionKind.MACRO is an enum member.""" assert isinstance(RegionKind.MACRO, RegionKind) def test_all_region_kinds(self): """RegionKind contains expected members.""" kinds = {kind.name for kind in RegionKind} assert "FUNCTION" in kinds assert "LOCATION" in kinds assert "MACRO" in kinds