-
Notifications
You must be signed in to change notification settings - Fork 308
/
test.py
257 lines (203 loc) · 9.33 KB
/
test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#!/usr/bin/env python3
import logging
import unittest
from unittest.mock import Mock, patch
from shared_utils import (
InstrDict,
check_arg_lut,
check_overlapping_bits,
extract_isa_type,
find_extension_file,
handle_arg_lut_mapping,
initialize_encoding,
is_rv_variant,
overlaps,
pad_to_equal_length,
parse_instruction_line,
process_enc_line,
process_fixed_ranges,
process_standard_instructions,
same_base_isa,
update_encoding_for_fixed_range,
validate_bit_range,
)
class EncodingUtilsTest(unittest.TestCase):
"""Tests for basic encoding utilities"""
def setUp(self):
self.logger = logging.getLogger()
self.logger.disabled = True
def test_initialize_encoding(self):
"""Test encoding initialization with different bit lengths"""
self.assertEqual(initialize_encoding(32), ["-"] * 32)
self.assertEqual(initialize_encoding(16), ["-"] * 16)
self.assertEqual(initialize_encoding(), ["-"] * 32) # default case
def test_validate_bit_range(self):
"""Test bit range validation"""
# Valid cases
validate_bit_range(7, 3, 15, "test_instr") # 15 fits in 5 bits
validate_bit_range(31, 0, 0xFFFFFFFF, "test_instr") # max 32-bit value
# Invalid cases
with self.assertRaises(SystemExit):
validate_bit_range(3, 7, 1, "test_instr") # msb < lsb
with self.assertRaises(SystemExit):
validate_bit_range(3, 0, 16, "test_instr") # value too large for range
def test_parse_instruction_line(self):
"""Test instruction line parsing"""
name, remaining = parse_instruction_line("add.w r1, r2, r3")
self.assertEqual(name, "add_w")
self.assertEqual(remaining, "r1, r2, r3")
name, remaining = parse_instruction_line("lui rd imm20 6..2=0x0D")
self.assertEqual(name, "lui")
self.assertEqual(remaining, "rd imm20 6..2=0x0D")
class BitManipulationTest(unittest.TestCase):
"""Tests for bit manipulation and checking functions"""
def setUp(self):
self.logger = logging.getLogger()
self.logger.disabled = True
self.test_encoding = initialize_encoding()
def test_check_overlapping_bits(self):
"""Test overlapping bits detection"""
# Valid case - no overlap
self.test_encoding[31 - 5] = "-"
check_overlapping_bits(self.test_encoding, 5, "test_instr")
# Invalid case - overlap
self.test_encoding[31 - 5] = "1"
with self.assertRaises(SystemExit):
check_overlapping_bits(self.test_encoding, 5, "test_instr")
def test_update_encoding_for_fixed_range(self):
"""Test encoding updates for fixed ranges"""
encoding = initialize_encoding()
update_encoding_for_fixed_range(encoding, 6, 2, 0x0D, "test_instr")
# Check specific bits are set correctly
self.assertEqual(encoding[31 - 6 : 31 - 1], ["0", "1", "1", "0", "1"])
def test_process_fixed_ranges(self):
"""Test processing of fixed bit ranges"""
encoding = initialize_encoding()
remaining = "rd imm20 6..2=0x0D 1..0=3"
result = process_fixed_ranges(remaining, encoding, "test_instr")
self.assertNotIn("6..2=0x0D", result)
self.assertNotIn("1..0=3", result)
class EncodingArgsTest(unittest.TestCase):
"""Tests for encoding arguments handling"""
def setUp(self):
self.logger = logging.getLogger()
self.logger.disabled = True
@patch.dict("shared_utils.arg_lut", {"rd": (11, 7), "rs1": (19, 15)})
def test_check_arg_lut(self):
"""Test argument lookup table checking"""
encoding_args = initialize_encoding()
args = ["rd", "rs1"]
check_arg_lut(args, encoding_args, "test_instr")
# Verify encoding_args has been updated correctly
self.assertEqual(encoding_args[31 - 11 : 31 - 6], ["rd"] * 5)
self.assertEqual(encoding_args[31 - 19 : 31 - 14], ["rs1"] * 5)
@patch.dict("shared_utils.arg_lut", {"rs1": (19, 15)})
def test_handle_arg_lut_mapping(self):
"""Test handling of argument mappings"""
# Valid mapping
result = handle_arg_lut_mapping("rs1=new_arg", "test_instr")
self.assertEqual(result, "rs1=new_arg")
# Invalid mapping
with self.assertRaises(SystemExit):
handle_arg_lut_mapping("invalid_arg=new_arg", "test_instr")
class ISAHandlingTest(unittest.TestCase):
"""Tests for ISA type handling and validation"""
def test_extract_isa_type(self):
"""Test ISA type extraction"""
self.assertEqual(extract_isa_type("rv32_i"), "rv32")
self.assertEqual(extract_isa_type("rv64_m"), "rv64")
self.assertEqual(extract_isa_type("rv_c"), "rv")
def test_is_rv_variant(self):
"""Test RV variant checking"""
self.assertTrue(is_rv_variant("rv32", "rv"))
self.assertTrue(is_rv_variant("rv", "rv64"))
self.assertFalse(is_rv_variant("rv32", "rv64"))
def test_same_base_isa(self):
"""Test base ISA comparison"""
self.assertTrue(same_base_isa("rv32_i", ["rv32_m", "rv32_a"]))
self.assertTrue(same_base_isa("rv_i", ["rv32_i", "rv64_i"]))
self.assertFalse(same_base_isa("rv32_i", ["rv64_m"]))
class StringManipulationTest(unittest.TestCase):
"""Tests for string manipulation utilities"""
def test_pad_to_equal_length(self):
"""Test string padding"""
str1, str2 = pad_to_equal_length("101", "1101")
self.assertEqual(len(str1), len(str2))
self.assertEqual(str1, "-101")
self.assertEqual(str2, "1101")
def test_overlaps(self):
"""Test string overlap checking"""
self.assertTrue(overlaps("1-1", "101"))
self.assertTrue(overlaps("---", "101"))
self.assertFalse(overlaps("111", "101"))
class InstructionProcessingTest(unittest.TestCase):
"""Tests for instruction processing and validation"""
def setUp(self):
self.logger = logging.getLogger()
self.logger.disabled = True
# Create a patch for arg_lut
self.arg_lut_patcher = patch.dict(
"shared_utils.arg_lut", {"rd": (11, 7), "imm20": (31, 12)}
)
self.arg_lut_patcher.start()
def tearDown(self):
self.arg_lut_patcher.stop()
@patch("shared_utils.fixed_ranges")
@patch("shared_utils.single_fixed")
def test_process_enc_line(self, mock_single_fixed: Mock, mock_fixed_ranges: Mock):
"""Test processing of encoding lines"""
# Setup mock return values
mock_fixed_ranges.findall.return_value = [(6, 2, "0x0D")]
mock_fixed_ranges.sub.return_value = "rd imm20"
mock_single_fixed.findall.return_value = []
mock_single_fixed.sub.return_value = "rd imm20"
# Create a mock for split() that returns the expected list
mock_split = Mock(return_value=["rd", "imm20"])
mock_single_fixed.sub.return_value = Mock(split=mock_split)
name, data = process_enc_line("lui rd imm20 6..2=0x0D", "rv_i")
self.assertEqual(name, "lui")
self.assertEqual(data["extension"], ["rv_i"])
self.assertIn("rd", data["variable_fields"])
self.assertIn("imm20", data["variable_fields"])
@patch("os.path.exists")
@patch("shared_utils.logging.error")
def test_find_extension_file(self, mock_logging: Mock, mock_exists: Mock):
"""Test extension file finding"""
# Test successful case - file exists in main directory
mock_exists.side_effect = [True, False]
result = find_extension_file("rv32i", "/path/to/opcodes")
self.assertEqual(result, "/path/to/opcodes/rv32i")
# Test successful case - file exists in unratified directory
mock_exists.side_effect = [False, True]
result = find_extension_file("rv32i", "/path/to/opcodes")
self.assertEqual(result, "/path/to/opcodes/unratified/rv32i")
# Test failure case - file doesn't exist anywhere
mock_exists.side_effect = [False, False]
with self.assertRaises(SystemExit):
find_extension_file("rv32i", "/path/to/opcodes")
mock_logging.assert_called_with("Extension rv32i not found.")
def test_process_standard_instructions(self):
"""Test processing of standard instructions"""
lines = [
"add rd rs1 rs2 31..25=0 14..12=0 6..2=0x0C 1..0=3",
"sub rd rs1 rs2 31..25=0x20 14..12=0 6..2=0x0C 1..0=3",
"$pseudo add_pseudo rd rs1 rs2", # Should be skipped
"$import rv32i::mul", # Should be skipped
]
instr_dict: InstrDict = {}
file_name = "rv32i"
with patch("shared_utils.process_enc_line") as mock_process_enc:
# Setup mock return values
mock_process_enc.side_effect = [
("add", {"extension": ["rv32i"], "encoding": "encoding1"}),
("sub", {"extension": ["rv32i"], "encoding": "encoding2"}),
]
process_standard_instructions(lines, instr_dict, file_name)
# Verify process_enc_line was called twice (skipping pseudo and import)
self.assertEqual(mock_process_enc.call_count, 2)
# Verify the instruction dictionary was updated correctly
self.assertEqual(len(instr_dict), 2)
self.assertIn("add", instr_dict)
self.assertIn("sub", instr_dict)
if __name__ == "__main__":
unittest.main()