blob: dd803430e180bd9a9a7efc528105cd15d5690674 [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.
"""Utilities for test code."""
from compiler.util import ir_pb2
def proto_is_superset(proto, expected_values, path=""):
"""Returns true if every value in expected_values is set in proto.
This is intended to be used in assertTrue in a unit test, like so:
self.assertTrue(*proto_is_superset(proto, expected))
Arguments:
proto: The proto to check.
expected_values: The reference proto.
path: The path to the elements being compared. Clients can generally leave
this at default.
Returns:
A tuple; the first element is True if the fields set in proto are a strict
superset of the fields set in expected_values. The second element is an
informational string specifying the path of a value found in expected_values
but not in proto.
Every atomic field that is set in expected_values must be set to the same
value in proto; every message field set in expected_values must have a
matching field in proto, such that proto_is_superset(proto.field,
expected_values.field) is true.
For repeated fields in expected_values, each element in the expected_values
proto must have a corresponding element at the same index in proto; proto
may have additional elements.
"""
if path:
path += "."
for name, expected_value in expected_values.raw_fields.items():
field_path = "{}{}".format(path, name)
value = getattr(proto, name)
if issubclass(proto.field_specs[name].type, ir_pb2.Message):
if isinstance(proto.field_specs[name], ir_pb2.Repeated):
if len(expected_value) > len(value):
return False, "{}[{}] missing".format(field_path,
len(getattr(proto, name)))
for i in range(len(expected_value)):
result = proto_is_superset(value[i], expected_value[i],
"{}[{}]".format(field_path, i))
if not result[0]:
return result
else:
if (expected_values.HasField(name) and
not proto.HasField(name)):
return False, "{} missing".format(field_path)
result = proto_is_superset(value, expected_value, field_path)
if not result[0]:
return result
else:
# Zero-length repeated fields and not-there repeated fields are "the
# same."
if (expected_value != value and
(isinstance(proto.field_specs[name], ir_pb2.Optional) or
len(expected_value))):
if isinstance(proto.field_specs[name], ir_pb2.Repeated):
return False, "{} differs: found {}, expected {}".format(
field_path, list(value), list(expected_value))
else:
return False, "{} differs: found {}, expected {}".format(
field_path, value, expected_value)
return True, ""
def dict_file_reader(file_dict):
"""Returns a callable that retrieves entries from file_dict as files.
This can be used to call glue.parse_emboss_file with file text declared
inline.
Arguments:
file_dict: A dictionary from "file names" to "contents."
Returns:
A callable that can be passed to glue.parse_emboss_file in place of the
"read" builtin.
"""
def read(file_name):
try:
return file_dict[file_name], None
except KeyError:
return None, ["File '{}' not found.".format(file_name)]
return read