| # Copyright 2019 Google LLC |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Tests for dependency_checker.py.""" |
| |
| import unittest |
| from compiler.front_end import dependency_checker |
| from compiler.front_end import glue |
| from compiler.util import error |
| from compiler.util import test_util |
| |
| |
| def _parse_snippet(emb_file): |
| ir, unused_debug_info, errors = glue.parse_emboss_file( |
| "m.emb", |
| test_util.dict_file_reader({"m.emb": emb_file}), |
| stop_before_step="find_dependency_cycles") |
| assert not errors |
| return ir |
| |
| |
| def _find_dependencies_for_snippet(emb_file): |
| ir, unused_debug_info, errors = glue.parse_emboss_file( |
| "m.emb", |
| test_util.dict_file_reader({ |
| "m.emb": emb_file |
| }), |
| stop_before_step="set_dependency_order") |
| assert not errors, errors |
| return ir |
| |
| |
| class DependencyCheckerTest(unittest.TestCase): |
| |
| def test_error_on_simple_field_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " 0 [+field2] UInt field1\n" |
| " 0 [+field1] UInt field2\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nfield1"), |
| error.note("m.emb", struct.field[1].source_location, "field2") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_self_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " 0 [+field1] UInt field1\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nfield1") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_triple_field_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " 0 [+field2] UInt field1\n" |
| " 0 [+field3] UInt field2\n" |
| " 0 [+field1] UInt field3\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nfield1"), |
| error.note("m.emb", struct.field[1].source_location, "field2"), |
| error.note("m.emb", struct.field[2].source_location, "field3"), |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_complex_field_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " 0 [+field2] UInt field1\n" |
| " 0 [+field3+field4] UInt field2\n" |
| " 0 [+field1] UInt field3\n" |
| " 0 [+field2] UInt field4\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nfield1"), |
| error.note("m.emb", struct.field[1].source_location, "field2"), |
| error.note("m.emb", struct.field[2].source_location, "field3"), |
| error.note("m.emb", struct.field[3].source_location, "field4"), |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_simple_enum_value_cycle(self): |
| ir = _parse_snippet("enum Foo:\n" |
| " XX = YY\n" |
| " YY = XX\n") |
| enum = ir.module[0].type[0].enumeration |
| self.assertEqual([[ |
| error.error("m.emb", enum.value[0].source_location, |
| "Dependency cycle\nXX"), |
| error.note("m.emb", enum.value[1].source_location, "YY") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_no_error_on_no_cycle(self): |
| ir = _parse_snippet("enum Foo:\n" |
| " XX = 0\n" |
| " YY = XX\n") |
| self.assertEqual([], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_cycle_nested(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " struct Bar:\n" |
| " 0 [+field2] UInt field1\n" |
| " 0 [+field1] UInt field2\n" |
| " 0 [+1] UInt field\n") |
| struct = ir.module[0].type[0].subtype[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nfield1"), |
| error.note("m.emb", struct.field[1].source_location, "field2") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_import_cycle(self): |
| ir, unused_debug_info, errors = glue.parse_emboss_file( |
| "m.emb", |
| test_util.dict_file_reader({"m.emb": 'import "n.emb" as n\n', |
| "n.emb": 'import "m.emb" as m\n'}), |
| stop_before_step="find_dependency_cycles") |
| assert not errors |
| self.assertEqual([[ |
| error.error("m.emb", ir.module[0].source_location, |
| "Import dependency cycle\nm.emb"), |
| error.note("n.emb", ir.module[2].source_location, "n.emb") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_import_cycle_and_field_cycle(self): |
| ir, unused_debug_info, errors = glue.parse_emboss_file( |
| "m.emb", |
| test_util.dict_file_reader({"m.emb": 'import "n.emb" as n\n' |
| "struct Foo:\n" |
| " 0 [+field1] UInt field1\n", |
| "n.emb": 'import "m.emb" as m\n'}), |
| stop_before_step="find_dependency_cycles") |
| assert not errors |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", ir.module[0].source_location, |
| "Import dependency cycle\nm.emb"), |
| error.note("n.emb", ir.module[2].source_location, "n.emb") |
| ], [ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nfield1") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_field_existence_self_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " if x == 1:\n" |
| " 0 [+1] UInt x\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nx") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_field_existence_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " if y == 1:\n" |
| " 0 [+1] UInt x\n" |
| " if x == 0:\n" |
| " 1 [+1] UInt y\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nx"), |
| error.note("m.emb", struct.field[1].source_location, "y") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_virtual_field_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " let x = y\n" |
| " let y = x\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nx"), |
| error.note("m.emb", struct.field[1].source_location, "y") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_virtual_non_virtual_field_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " let x = y\n" |
| " x [+4] UInt y\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nx"), |
| error.note("m.emb", struct.field[1].source_location, "y") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_non_virtual_virtual_field_cycle(self): |
| ir = _parse_snippet("struct Foo:\n" |
| " y [+4] UInt x\n" |
| " let y = x\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nx"), |
| error.note("m.emb", struct.field[1].source_location, "y") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_error_on_cycle_involving_subfield(self): |
| ir = _parse_snippet("struct Bar:\n" |
| " foo_b.x [+4] Foo foo_a\n" |
| " foo_a.x [+4] Foo foo_b\n" |
| "struct Foo:\n" |
| " 0 [+4] UInt x\n") |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([[ |
| error.error("m.emb", struct.field[0].source_location, |
| "Dependency cycle\nfoo_a"), |
| error.note("m.emb", struct.field[1].source_location, "foo_b") |
| ]], dependency_checker.find_dependency_cycles(ir)) |
| |
| def test_dependency_ordering_with_no_dependencies(self): |
| ir = _find_dependencies_for_snippet("struct Foo:\n" |
| " 0 [+4] UInt a\n" |
| " 4 [+4] UInt b\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([0, 1], struct.fields_in_dependency_order[:2]) |
| |
| def test_dependency_ordering_with_dependency_in_order(self): |
| ir = _find_dependencies_for_snippet("struct Foo:\n" |
| " 0 [+4] UInt a\n" |
| " a [+4] UInt b\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([0, 1], struct.fields_in_dependency_order[:2]) |
| |
| def test_dependency_ordering_with_dependency_in_reverse_order(self): |
| ir = _find_dependencies_for_snippet("struct Foo:\n" |
| " b [+4] UInt a\n" |
| " 0 [+4] UInt b\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([1, 0], struct.fields_in_dependency_order[:2]) |
| |
| def test_dependency_ordering_with_extra_fields(self): |
| ir = _find_dependencies_for_snippet("struct Foo:\n" |
| " d [+4] UInt a\n" |
| " 4 [+4] UInt b\n" |
| " 8 [+4] UInt c\n" |
| " 12 [+4] UInt d\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([1, 2, 3, 0], struct.fields_in_dependency_order[:4]) |
| |
| def test_dependency_ordering_scrambled(self): |
| ir = _find_dependencies_for_snippet("struct Foo:\n" |
| " d [+4] UInt a\n" |
| " c [+4] UInt b\n" |
| " a [+4] UInt c\n" |
| " 12 [+4] UInt d\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([3, 0, 2, 1], struct.fields_in_dependency_order[:4]) |
| |
| def test_dependency_ordering_multiple_dependents(self): |
| ir = _find_dependencies_for_snippet("struct Foo:\n" |
| " d [+4] UInt a\n" |
| " d [+4] UInt b\n" |
| " d [+4] UInt c\n" |
| " 12 [+4] UInt d\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([3, 0, 1, 2], struct.fields_in_dependency_order[:4]) |
| |
| def test_dependency_ordering_multiple_dependencies(self): |
| ir = _find_dependencies_for_snippet("struct Foo:\n" |
| " b+c [+4] UInt a\n" |
| " 4 [+4] UInt b\n" |
| " 8 [+4] UInt c\n" |
| " a [+4] UInt d\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([1, 2, 0, 3], struct.fields_in_dependency_order[:4]) |
| |
| def test_dependency_ordering_with_parameter(self): |
| ir = _find_dependencies_for_snippet("struct Foo:\n" |
| " 0 [+1] Bar(x) b\n" |
| " 1 [+1] UInt x\n" |
| "struct Bar(x: UInt:8):\n" |
| " x [+1] UInt y\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([1, 0], struct.fields_in_dependency_order[:2]) |
| |
| def test_dependency_ordering_with_local_parameter(self): |
| ir = _find_dependencies_for_snippet("struct Foo(x: Int:13):\n" |
| " 0 [+x] Int b\n") |
| self.assertEqual([], dependency_checker.set_dependency_order(ir)) |
| struct = ir.module[0].type[0].structure |
| self.assertEqual([0], struct.fields_in_dependency_order[:1]) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |