| # 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() |