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