OR-1 dataflow CPU sketch
at ba08ffded3d3b2badb2a7e22816feafaacea5ded 168 lines 5.6 kB view raw
1"""Tests for dfgraph/categories.py — opcode-to-category mapping. 2 3Tests verify: 4- Every opcode in MNEMONIC_TO_OP has a valid category via categorise() 5- ArithOp members map to ARITHMETIC 6- LogicOp logic ops map to LOGIC 7- LogicOp comparison ops map to COMPARISON 8- RoutingOp members (except CONST/FREE_CTX) map to ROUTING 9- RoutingOp.CONST and FREE_CTX map to CONFIG 10- MemOp members map to MEMORY 11- Every OpcodeCategory has a colour in CATEGORY_COLOURS 12""" 13 14import pytest 15 16from cm_inst import ArithOp, LogicOp, MemOp, RoutingOp 17from asm.opcodes import MNEMONIC_TO_OP 18from dfgraph.categories import categorise, OpcodeCategory, CATEGORY_COLOURS 19 20 21class TestCategoriseArithOp: 22 """Tests for ArithOp -> ARITHMETIC category mapping.""" 23 24 @pytest.mark.parametrize("op", [ 25 ArithOp.ADD, 26 ArithOp.SUB, 27 ArithOp.INC, 28 ArithOp.DEC, 29 ArithOp.SHIFT_L, 30 ArithOp.SHIFT_R, 31 ArithOp.ASHFT_R, 32 ]) 33 def test_arith_ops_map_to_arithmetic(self, op): 34 """All ArithOp members map to ARITHMETIC category.""" 35 assert categorise(op) == OpcodeCategory.ARITHMETIC 36 37 38class TestCategoriseLogicOp: 39 """Tests for LogicOp -> LOGIC or COMPARISON category mapping.""" 40 41 @pytest.mark.parametrize("op", [ 42 LogicOp.AND, 43 LogicOp.OR, 44 LogicOp.XOR, 45 LogicOp.NOT, 46 ]) 47 def test_logic_ops_map_to_logic(self, op): 48 """Pure logic opcodes (AND, OR, XOR, NOT) map to LOGIC category.""" 49 assert categorise(op) == OpcodeCategory.LOGIC 50 51 @pytest.mark.parametrize("op", [ 52 LogicOp.EQ, 53 LogicOp.LT, 54 LogicOp.LTE, 55 LogicOp.GT, 56 LogicOp.GTE, 57 ]) 58 def test_comparison_ops_map_to_comparison(self, op): 59 """Comparison opcodes (EQ, LT, LTE, GT, GTE) map to COMPARISON category.""" 60 assert categorise(op) == OpcodeCategory.COMPARISON 61 62 63class TestCategoriseRoutingOp: 64 """Tests for RoutingOp -> ROUTING or CONFIG category mapping.""" 65 66 @pytest.mark.parametrize("op", [ 67 RoutingOp.BREQ, 68 RoutingOp.BRGT, 69 RoutingOp.BRGE, 70 RoutingOp.BROF, 71 RoutingOp.SWEQ, 72 RoutingOp.SWGT, 73 RoutingOp.SWGE, 74 RoutingOp.SWOF, 75 RoutingOp.GATE, 76 RoutingOp.PASS, 77 RoutingOp.SEL, 78 RoutingOp.MRGE, 79 ]) 80 def test_routing_ops_map_to_routing(self, op): 81 """Routing/branch/switch/control opcodes map to ROUTING category.""" 82 assert categorise(op) == OpcodeCategory.ROUTING 83 84 @pytest.mark.parametrize("op", [ 85 RoutingOp.CONST, 86 RoutingOp.FREE_CTX, 87 ]) 88 def test_config_routing_ops_map_to_config(self, op): 89 """RoutingOp.CONST and RoutingOp.FREE_CTX map to CONFIG category.""" 90 assert categorise(op) == OpcodeCategory.CONFIG 91 92 93class TestCategoriseMemOp: 94 """Tests for MemOp -> MEMORY category mapping.""" 95 96 @pytest.mark.parametrize("op", [ 97 MemOp.READ, 98 MemOp.WRITE, 99 MemOp.CLEAR, 100 MemOp.ALLOC, 101 MemOp.FREE, 102 MemOp.RD_INC, 103 MemOp.RD_DEC, 104 MemOp.CMP_SW, 105 MemOp.EXEC, 106 MemOp.RAW_READ, 107 MemOp.SET_PAGE, 108 MemOp.WRITE_IMM, 109 MemOp.EXT, 110 ]) 111 def test_memory_ops_map_to_memory(self, op): 112 """All MemOp members map to MEMORY category.""" 113 assert categorise(op) == OpcodeCategory.MEMORY 114 115 116class TestCategoriseMnemonicToOp: 117 """Tests for all opcodes in MNEMONIC_TO_OP.""" 118 119 @pytest.mark.parametrize("mnemonic, op", MNEMONIC_TO_OP.items()) 120 def test_all_mnemonics_have_category(self, mnemonic, op): 121 """Every opcode in MNEMONIC_TO_OP has a valid category via categorise().""" 122 # Should not raise ValueError 123 category = categorise(op) 124 assert isinstance(category, OpcodeCategory) 125 126 127class TestCategoryColours: 128 """Tests for CATEGORY_COLOURS mapping.""" 129 130 def test_all_categories_have_colour(self): 131 """Every OpcodeCategory has a colour in CATEGORY_COLOURS.""" 132 for category in OpcodeCategory: 133 assert category in CATEGORY_COLOURS 134 colour = CATEGORY_COLOURS[category] 135 assert isinstance(colour, str) 136 assert colour.startswith("#") 137 138 def test_colours_are_valid_hex(self): 139 """All colours are valid 6-digit hex codes.""" 140 for category, colour in CATEGORY_COLOURS.items(): 141 assert len(colour) == 7, f"Colour for {category} is not 7 chars: {colour}" 142 assert colour[0] == "#", f"Colour for {category} doesn't start with #: {colour}" 143 try: 144 int(colour[1:], 16) 145 except ValueError: 146 pytest.fail(f"Colour for {category} is not valid hex: {colour}") 147 148 def test_expected_colour_values(self): 149 """Verify the design-specified colours are present.""" 150 assert CATEGORY_COLOURS[OpcodeCategory.ARITHMETIC] == "#4a90d9" 151 assert CATEGORY_COLOURS[OpcodeCategory.LOGIC] == "#4caf50" 152 assert CATEGORY_COLOURS[OpcodeCategory.COMPARISON] == "#ff9800" 153 assert CATEGORY_COLOURS[OpcodeCategory.ROUTING] == "#9c27b0" 154 assert CATEGORY_COLOURS[OpcodeCategory.MEMORY] == "#ff5722" 155 assert CATEGORY_COLOURS[OpcodeCategory.IO] == "#009688" 156 assert CATEGORY_COLOURS[OpcodeCategory.CONFIG] == "#9e9e9e" 157 158 159class TestCategoriseErrors: 160 """Tests for error handling in categorise().""" 161 162 def test_unknown_type_raises_valueerror(self): 163 """Passing an unknown type to categorise() raises ValueError.""" 164 class UnknownOp: 165 pass 166 167 with pytest.raises(ValueError, match="Unknown opcode type"): 168 categorise(UnknownOp()) # type: ignore