blob: a9110dacc22b5bf42b49d6061182aafa8be746a2 [file] [log] [blame]
# 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()