xref: /aosp_15_r20/external/emboss/compiler/front_end/glue_test.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1# Copyright 2019 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Tests for glue."""
16
17import pkgutil
18import unittest
19
20from compiler.front_end import glue
21from compiler.util import error
22from compiler.util import ir_data
23from compiler.util import ir_data_utils
24from compiler.util import parser_types
25from compiler.util import test_util
26
27_location = parser_types.make_location
28
29_ROOT_PACKAGE = "testdata.golden"
30_GOLDEN_PATH = ""
31
32_SPAN_SE_LOG_FILE_PATH = _GOLDEN_PATH + "span_se_log_file_status.emb"
33_SPAN_SE_LOG_FILE_EMB = pkgutil.get_data(
34    _ROOT_PACKAGE, _SPAN_SE_LOG_FILE_PATH).decode(encoding="UTF-8")
35_SPAN_SE_LOG_FILE_READER = test_util.dict_file_reader(
36    {_SPAN_SE_LOG_FILE_PATH: _SPAN_SE_LOG_FILE_EMB})
37_SPAN_SE_LOG_FILE_IR = ir_data_utils.IrDataSerializer.from_json(ir_data.Module,
38    pkgutil.get_data(
39        _ROOT_PACKAGE,
40        _GOLDEN_PATH + "span_se_log_file_status.ir.txt"
41    ).decode(encoding="UTF-8"))
42_SPAN_SE_LOG_FILE_PARSE_TREE_TEXT = pkgutil.get_data(
43    _ROOT_PACKAGE,
44    _GOLDEN_PATH + "span_se_log_file_status.parse_tree.txt"
45).decode(encoding="UTF-8")
46_SPAN_SE_LOG_FILE_TOKENIZATION_TEXT = pkgutil.get_data(
47    _ROOT_PACKAGE,
48    _GOLDEN_PATH + "span_se_log_file_status.tokens.txt"
49).decode(encoding="UTF-8")
50
51
52class FrontEndGlueTest(unittest.TestCase):
53  """Tests for front_end.glue."""
54
55  def test_parse_module(self):
56    # parse_module(file) should return the same thing as
57    # parse_module_text(text), assuming file can be read.
58    main_module, debug_info, errors = glue.parse_module(
59        _SPAN_SE_LOG_FILE_PATH, _SPAN_SE_LOG_FILE_READER)
60    main_module2, debug_info2, errors2 = glue.parse_module_text(
61        _SPAN_SE_LOG_FILE_EMB, _SPAN_SE_LOG_FILE_PATH)
62    self.assertEqual([], errors)
63    self.assertEqual([], errors2)
64    self.assertEqual(main_module, main_module2)
65    self.assertEqual(debug_info, debug_info2)
66
67  def test_parse_module_no_such_file(self):
68    file_name = "nonexistent.emb"
69    ir, debug_info, errors = glue.parse_emboss_file(
70        file_name, test_util.dict_file_reader({}))
71    self.assertEqual([[
72        error.error("nonexistent.emb", _location((1, 1), (1, 1)),
73                    "Unable to read file."),
74        error.note("nonexistent.emb", _location((1, 1), (1, 1)),
75                   "File 'nonexistent.emb' not found."),
76    ]], errors)
77    self.assertFalse(file_name in debug_info.modules)
78    self.assertFalse(ir)
79
80  def test_parse_module_tokenization_error(self):
81    file_name = "tokens.emb"
82    ir, debug_info, errors = glue.parse_emboss_file(
83        file_name, test_util.dict_file_reader({file_name: "@"}))
84    self.assertTrue(debug_info.modules[file_name].source_code)
85    self.assertTrue(errors)
86    self.assertEqual("Unrecognized token", errors[0][0].message)
87    self.assertFalse(ir)
88
89  def test_parse_module_indentation_error(self):
90    file_name = "indent.emb"
91    ir, debug_info, errors = glue.parse_emboss_file(
92        file_name, test_util.dict_file_reader(
93            {file_name: "struct Foo:\n"
94                        "  1 [+1] Int x\n"
95                        " 2 [+1] Int y\n"}))
96    self.assertTrue(debug_info.modules[file_name].source_code)
97    self.assertTrue(errors)
98    self.assertEqual("Bad indentation", errors[0][0].message)
99    self.assertFalse(ir)
100
101  def test_parse_module_parse_error(self):
102    file_name = "parse.emb"
103    ir, debug_info, errors = glue.parse_emboss_file(
104        file_name, test_util.dict_file_reader(
105            {file_name: "struct foo:\n"
106                        "  1 [+1] Int x\n"
107                        "  3 [+1] Int y\n"}))
108    self.assertTrue(debug_info.modules[file_name].source_code)
109    self.assertEqual([[
110        error.error(file_name, _location((1, 8), (1, 11)),
111                    "A type name must be CamelCase.\n"
112                    "Found 'foo' (SnakeWord), expected CamelWord.")
113    ]], errors)
114    self.assertFalse(ir)
115
116  def test_parse_error(self):
117    file_name = "parse.emb"
118    ir, debug_info, errors = glue.parse_emboss_file(
119        file_name, test_util.dict_file_reader(
120            {file_name: "struct foo:\n"
121                        "  1 [+1]  Int  x\n"
122                        "  2 [+1]  Int  y\n"}))
123    self.assertTrue(debug_info.modules[file_name].source_code)
124    self.assertEqual([[
125        error.error(file_name, _location((1, 8), (1, 11)),
126                    "A type name must be CamelCase.\n"
127                    "Found 'foo' (SnakeWord), expected CamelWord.")
128    ]], errors)
129    self.assertFalse(ir)
130
131  def test_circular_dependency_error(self):
132    file_name = "cycle.emb"
133    ir, debug_info, errors = glue.parse_emboss_file(
134        file_name, test_util.dict_file_reader({
135            file_name: "struct Foo:\n"
136                       "  0 [+field1]  UInt  field1\n"
137        }))
138    self.assertTrue(debug_info.modules[file_name].source_code)
139    self.assertTrue(errors)
140    self.assertEqual("Dependency cycle\nfield1", errors[0][0].message)
141    self.assertFalse(ir)
142
143  def test_ir_from_parse_module(self):
144    log_file_path_ir = ir_data_utils.copy(_SPAN_SE_LOG_FILE_IR)
145    log_file_path_ir.source_file_name = _SPAN_SE_LOG_FILE_PATH
146    self.assertEqual(log_file_path_ir, glue.parse_module(
147        _SPAN_SE_LOG_FILE_PATH, _SPAN_SE_LOG_FILE_READER).ir)
148
149  def test_debug_info_from_parse_module(self):
150    debug_info = glue.parse_module(_SPAN_SE_LOG_FILE_PATH,
151                                   _SPAN_SE_LOG_FILE_READER).debug_info
152    self.maxDiff = 200000  # pylint:disable=invalid-name
153    self.assertEqual(_SPAN_SE_LOG_FILE_TOKENIZATION_TEXT.strip(),
154                     debug_info.format_tokenization().strip())
155    self.assertEqual(_SPAN_SE_LOG_FILE_PARSE_TREE_TEXT.strip(),
156                     debug_info.format_parse_tree().strip())
157    self.assertEqual(_SPAN_SE_LOG_FILE_IR, debug_info.ir)
158    self.assertEqual(ir_data_utils.IrDataSerializer(_SPAN_SE_LOG_FILE_IR).to_json(indent=2),
159                     debug_info.format_module_ir())
160
161  def test_parse_emboss_file(self):
162    # parse_emboss_file calls parse_module, wraps its results, and calls
163    # symbol_resolver.resolve_symbols() on the resulting IR.
164    ir, debug_info, errors = glue.parse_emboss_file(_SPAN_SE_LOG_FILE_PATH,
165                                                    _SPAN_SE_LOG_FILE_READER)
166    module_ir, module_debug_info, module_errors = glue.parse_module(
167        _SPAN_SE_LOG_FILE_PATH, _SPAN_SE_LOG_FILE_READER)
168    self.assertEqual([], errors)
169    self.assertEqual([], module_errors)
170    self.assertTrue(test_util.proto_is_superset(ir.module[0], module_ir))
171    self.assertEqual(module_debug_info,
172                     debug_info.modules[_SPAN_SE_LOG_FILE_PATH])
173    self.assertEqual(2, len(debug_info.modules))
174    self.assertEqual(2, len(ir.module))
175    self.assertEqual(_SPAN_SE_LOG_FILE_PATH, ir.module[0].source_file_name)
176    self.assertEqual("", ir.module[1].source_file_name)
177
178  def test_synthetic_error(self):
179    file_name = "missing_byte_order_attribute.emb"
180    ir, unused_debug_info, errors = glue.only_parse_emboss_file(
181        file_name, test_util.dict_file_reader({
182            file_name: "struct Foo:\n"
183                       "  0 [+8]  UInt  field\n"
184        }))
185    self.assertFalse(errors)
186    # Artificially mark the first field as is_synthetic.
187    first_field = ir.module[0].type[0].structure.field[0]
188    first_field.source_location.is_synthetic = True
189    ir, errors = glue.process_ir(ir, None)
190    self.assertTrue(errors)
191    self.assertEqual("Attribute 'byte_order' required on field which is byte "
192                     "order dependent.", errors[0][0].message)
193    self.assertTrue(errors[0][0].location.is_synthetic)
194    self.assertFalse(ir)
195
196  def test_suppressed_synthetic_error(self):
197    file_name = "triplicate_symbol.emb"
198    ir, unused_debug_info, errors = glue.only_parse_emboss_file(
199        file_name, test_util.dict_file_reader({
200            file_name: "struct Foo:\n"
201                       "  0 [+1]  UInt  field\n"
202                       "  1 [+1]  UInt  field\n"
203                       "  2 [+1]  UInt  field\n"
204        }))
205    self.assertFalse(errors)
206    # Artificially mark the name of the second field as is_synthetic.
207    second_field = ir.module[0].type[0].structure.field[1]
208    second_field.name.source_location.is_synthetic = True
209    second_field.name.name.source_location.is_synthetic = True
210    ir, errors = glue.process_ir(ir, None)
211    self.assertEqual(1, len(errors))
212    self.assertEqual("Duplicate name 'field'", errors[0][0].message)
213    self.assertFalse(errors[0][0].location.is_synthetic)
214    self.assertFalse(errors[0][1].location.is_synthetic)
215    self.assertFalse(ir)
216
217
218class DebugInfoTest(unittest.TestCase):
219  """Tests for DebugInfo and ModuleDebugInfo classes."""
220
221  def test_debug_info_initialization(self):
222    debug_info = glue.DebugInfo()
223    self.assertEqual({}, debug_info.modules)
224
225  def test_debug_info_invalid_attribute_set(self):
226    debug_info = glue.DebugInfo()
227    with self.assertRaises(AttributeError):
228      debug_info.foo = "foo"
229
230  def test_debug_info_equality(self):
231    debug_info = glue.DebugInfo()
232    debug_info2 = glue.DebugInfo()
233    self.assertEqual(debug_info, debug_info2)
234    debug_info.modules["foo"] = glue.ModuleDebugInfo("foo")
235    self.assertNotEqual(debug_info, debug_info2)
236    debug_info2.modules["foo"] = glue.ModuleDebugInfo("foo")
237    self.assertEqual(debug_info, debug_info2)
238
239  def test_module_debug_info_initialization(self):
240    module_info = glue.ModuleDebugInfo("bar.emb")
241    self.assertEqual("bar.emb", module_info.file_name)
242    self.assertEqual(None, module_info.tokens)
243    self.assertEqual(None, module_info.parse_tree)
244    self.assertEqual(None, module_info.ir)
245    self.assertEqual(None, module_info.used_productions)
246
247  def test_module_debug_info_attribute_set(self):
248    module_info = glue.ModuleDebugInfo("bar.emb")
249    module_info.tokens = "a"
250    module_info.parse_tree = "b"
251    module_info.ir = "c"
252    module_info.used_productions = "d"
253    module_info.source_code = "e"
254    self.assertEqual("a", module_info.tokens)
255    self.assertEqual("b", module_info.parse_tree)
256    self.assertEqual("c", module_info.ir)
257    self.assertEqual("d", module_info.used_productions)
258    self.assertEqual("e", module_info.source_code)
259
260  def test_module_debug_info_bad_attribute_set(self):
261    module_info = glue.ModuleDebugInfo("bar.emb")
262    with self.assertRaises(AttributeError):
263      module_info.foo = "foo"
264
265  def test_module_debug_info_equality(self):
266    module_info = glue.ModuleDebugInfo("foo")
267    module_info2 = glue.ModuleDebugInfo("foo")
268    module_info_bar = glue.ModuleDebugInfo("bar")
269    self.assertEqual(module_info, module_info2)
270    module_info_bar = glue.ModuleDebugInfo("bar")
271    self.assertNotEqual(module_info, module_info_bar)
272    module_info.tokens = []
273    self.assertNotEqual(module_info, module_info2)
274    module_info2.tokens = []
275    self.assertEqual(module_info, module_info2)
276    module_info.parse_tree = []
277    self.assertNotEqual(module_info, module_info2)
278    module_info2.parse_tree = []
279    self.assertEqual(module_info, module_info2)
280    module_info.ir = []
281    self.assertNotEqual(module_info, module_info2)
282    module_info2.ir = []
283    self.assertEqual(module_info, module_info2)
284    module_info.used_productions = []
285    self.assertNotEqual(module_info, module_info2)
286    module_info2.used_productions = []
287    self.assertEqual(module_info, module_info2)
288
289
290class TestFormatProductionSet(unittest.TestCase):
291  """Tests for format_production_set."""
292
293  def test_format_production_set(self):
294    production_texts = ["A -> B", "B -> C", "A -> C", "C -> A"]
295    productions = [parser_types.Production.parse(p) for p in production_texts]
296    self.assertEqual("\n".join(sorted(production_texts)),
297                     glue.format_production_set(set(productions)))
298
299
300if __name__ == "__main__":
301  unittest.main()
302