blob: deb6dac353ed8cf61aa62f969556e4cf7a0579c6 [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 util.traverse_ir."""
import collections
import unittest
from public import ir_pb2
from util import traverse_ir
_EXAMPLE_IR = ir_pb2.EmbossIr.from_json("""{
"module": [
{
"type": [
{
"structure": {
"field": [
{
"location": {
"start": { "constant": { "value": "0" } },
"size": { "constant": { "value": "8" } }
},
"type": {
"atomic_type": {
"reference": {
"canonical_name": {
"module_file": "",
"object_path": ["UInt"]
}
}
}
},
"name": { "name": { "text": "field1" } }
},
{
"location": {
"start": { "constant": { "value": "8" } },
"size": { "constant": { "value": "16" } }
},
"type": {
"array_type": {
"base_type": {
"atomic_type": {
"reference": {
"canonical_name": {
"module_file": "",
"object_path": ["UInt"]
}
}
}
},
"element_count": { "constant": { "value": "8" } }
}
},
"name": { "name": { "text": "field2" } }
}
]
},
"name": { "name": { "text": "Foo" } },
"subtype": [
{
"structure": {
"field": [
{
"location": {
"start": { "constant": { "value": "24" } },
"size": { "constant": { "value": "32" } }
},
"type": {
"atomic_type": {
"reference": {
"canonical_name": {
"module_file": "",
"object_path": ["UInt"]
}
}
}
},
"name": { "name": { "text": "bar_field1" } }
},
{
"location": {
"start": { "constant": { "value": "32" } },
"size": { "constant": { "value": "320" } }
},
"type": {
"array_type": {
"base_type": {
"array_type": {
"base_type": {
"atomic_type": {
"reference": {
"canonical_name": {
"module_file": "",
"object_path": ["UInt"]
}
}
}
},
"element_count": { "constant": { "value": "16" } }
}
},
"automatic": { }
}
},
"name": { "name": { "text": "bar_field2" } }
}
]
},
"name": { "name": { "text": "Bar" } }
}
]
},
{
"enumeration": {
"value": [
{
"name": { "name": { "text": "ONE" } },
"value": { "constant": { "value": "1" } }
},
{
"name": { "name": { "text": "TWO" } },
"value": {
"function": {
"function": "ADDITION",
"args": [
{ "constant": { "value": "1" } },
{ "constant": { "value": "1" } }
],
"function_name": { "text": "+" }
}
}
}
]
},
"name": { "name": { "text": "Bar" } }
}
],
"source_file_name": "t.emb"
},
{
"type": [
{
"external": { },
"name": {
"name": { "text": "UInt" },
"canonical_name": { "module_file": "", "object_path": ["UInt"] }
},
"attribute": [
{
"name": { "text": "statically_sized" },
"value": { "expression": { "boolean_constant": { "value": true } } }
},
{
"name": { "text": "size_in_bits" },
"value": { "expression": { "constant": { "value": "64" } } }
}
]
}
],
"source_file_name": ""
}
]
}""")
def _count_entries(sequence):
counts = collections.Counter()
for entry in sequence:
counts[entry] += 1
return counts
def _record_constant(constant, constant_list):
constant_list.append(int(constant.value))
def _record_field_name_and_constant(constant, constant_list, field):
constant_list.append((field.name.name.text, int(constant.value)))
def _record_file_name_and_constant(constant, constant_list, source_file_name):
constant_list.append((source_file_name, int(constant.value)))
def _record_location_parameter_and_constant(constant, constant_list,
location=None):
constant_list.append((location, int(constant.value)))
def _record_kind_and_constant(constant, constant_list, type_definition):
if type_definition.HasField("enumeration"):
constant_list.append(("enumeration", int(constant.value)))
elif type_definition.HasField("structure"):
constant_list.append(("structure", int(constant.value)))
elif type_definition.HasField("external"):
constant_list.append(("external", int(constant.value)))
else:
assert False, "Shouldn't be here."
class TraverseIrTest(unittest.TestCase):
def test_filter_on_type(self):
constants = []
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.NumericConstant], _record_constant,
parameters={"constant_list": constants})
self.assertEqual(
_count_entries([0, 8, 8, 8, 16, 24, 32, 16, 32, 320, 1, 1, 1, 64]),
_count_entries(constants))
def test_filter_on_type_in_type(self):
constants = []
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR,
[ir_pb2.Function, ir_pb2.Expression, ir_pb2.NumericConstant],
_record_constant,
parameters={"constant_list": constants})
self.assertEqual([1, 1], constants)
def test_filter_on_type_star_type(self):
struct_constants = []
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.Structure, ir_pb2.NumericConstant],
_record_constant,
parameters={"constant_list": struct_constants})
self.assertEqual(_count_entries([0, 8, 8, 8, 16, 24, 32, 16, 32, 320]),
_count_entries(struct_constants))
enum_constants = []
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.Enum, ir_pb2.NumericConstant], _record_constant,
parameters={"constant_list": enum_constants})
self.assertEqual(_count_entries([1, 1, 1]), _count_entries(enum_constants))
def test_filter_on_not_type(self):
notstruct_constants = []
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.NumericConstant], _record_constant,
skip_descendants_of=(ir_pb2.Structure,),
parameters={"constant_list": notstruct_constants})
self.assertEqual(_count_entries([1, 1, 1, 64]),
_count_entries(notstruct_constants))
def test_field_is_populated(self):
constants = []
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.Field, ir_pb2.NumericConstant],
_record_field_name_and_constant,
parameters={"constant_list": constants})
self.assertEqual(_count_entries([
("field1", 0), ("field1", 8), ("field2", 8), ("field2", 8),
("field2", 16), ("bar_field1", 24), ("bar_field1", 32),
("bar_field2", 16), ("bar_field2", 32), ("bar_field2", 320)
]), _count_entries(constants))
def test_file_name_is_populated(self):
constants = []
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.NumericConstant], _record_file_name_and_constant,
parameters={"constant_list": constants})
self.assertEqual(_count_entries([
("t.emb", 0), ("t.emb", 8), ("t.emb", 8), ("t.emb", 8), ("t.emb", 16),
("t.emb", 24), ("t.emb", 32), ("t.emb", 16), ("t.emb", 32),
("t.emb", 320), ("t.emb", 1), ("t.emb", 1), ("t.emb", 1), ("", 64)
]), _count_entries(constants))
def test_type_definition_is_populated(self):
constants = []
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.NumericConstant], _record_kind_and_constant,
parameters={"constant_list": constants})
self.assertEqual(_count_entries([
("structure", 0), ("structure", 8), ("structure", 8), ("structure", 8),
("structure", 16), ("structure", 24), ("structure", 32),
("structure", 16), ("structure", 32), ("structure", 320),
("enumeration", 1), ("enumeration", 1), ("enumeration", 1),
("external", 64)
]), _count_entries(constants))
def test_keyword_args_dict_in_action(self):
call_counts = {"populated": 0, "not": 0}
def check_field_is_populated(node, **kwargs):
del node # Unused.
self.assertTrue(kwargs["field"])
call_counts["populated"] += 1
def check_field_is_not_populated(node, **kwargs):
del node # Unused.
self.assertFalse("field" in kwargs)
call_counts["not"] += 1
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.Field, ir_pb2.Type], check_field_is_populated)
self.assertEqual(7, call_counts["populated"])
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.Enum, ir_pb2.EnumValue],
check_field_is_not_populated)
self.assertEqual(2, call_counts["not"])
def test_pass_only_to_sub_nodes(self):
constants = []
def pass_location_down(field):
return {
"location": (int(field.location.start.constant.value),
int(field.location.size.constant.value))
}
traverse_ir.fast_traverse_ir_top_down(
_EXAMPLE_IR, [ir_pb2.NumericConstant],
_record_location_parameter_and_constant,
incidental_actions={ir_pb2.Field: pass_location_down},
parameters={"constant_list": constants, "location": None})
self.assertEqual(_count_entries([
((0, 8), 0), ((0, 8), 8), ((8, 16), 8), ((8, 16), 8), ((8, 16), 16),
((24, 32), 24), ((24, 32), 32), ((32, 320), 16), ((32, 320), 32),
((32, 320), 320), (None, 1), (None, 1), (None, 1), (None, 64)
]), _count_entries(constants))
if __name__ == "__main__":
unittest.main()