chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
# Tensorflow protobuf utility package
load("@rules_python//python:proto.bzl", "py_proto_library")
load("@xla//third_party/rules_python/python:py_library.bzl", "py_library")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable", "tf_py_strict_test")
load("//tensorflow/core/platform:build_config.bzl", "tf_proto_library") # @unused
visibility = [
"//third_party/cloud_tpu/convergence_tools:__subpackages__",
"//tensorflow:internal",
"//tensorflow/lite/toco/python:__pkg__",
"//tensorflow_models:__subpackages__",
"//tensorflow_model_optimization:__subpackages__",
"//third_party/py/cleverhans:__subpackages__",
"//third_party/py/launchpad:__subpackages__",
"//third_party/py/reverb:__subpackages__",
"//third_party/py/neural_structured_learning:__subpackages__",
"//third_party/py/tensorflow_examples:__subpackages__",
"//third_party/py/tf_agents:__subpackages__", # For benchmarks.
"//third_party/py/tf_slim:__subpackages__",
"//third_party/py/tensorflow_docs:__subpackages__",
]
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:license"],
default_visibility = visibility,
licenses = ["notice"],
)
tf_proto_library(
name = "compare_test_proto",
testonly = 1,
srcs = ["compare_test.proto"],
)
tf_py_strict_test(
name = "protobuf_compare_test",
size = "small",
srcs = ["compare_test.py"],
main = "compare_test.py",
tags = ["no_pip"], # compare_test_pb2 proto is not available in pip.
deps = [
":compare_test_proto_py",
":protobuf",
"//tensorflow/python/platform:test",
],
)
filegroup(
name = "compare_test_proto_src",
srcs = ["compare_test.proto"],
)
# copybara:uncomment_begin(google-only)
# py_proto_library(
# name = "compare_test_py_pb2",
# testonly = 1,
# has_services = 0,
# deps = [":compare_test_proto"],
# )
# copybara:uncomment_end
py_library(
name = "protobuf",
srcs = glob(
["*.py"],
exclude = ["*_test.py"],
),
compatible_with = get_compatible_with_portable(),
strict_deps = True,
visibility = visibility + [
"//tensorflow:__pkg__",
"//third_party/py/tensorflow_core:__subpackages__",
"//third_party/py/tensorflow_data_validation:__subpackages__",
"//third_party/py/tf_agents:__subpackages__",
"//third_party/py/tfx:__subpackages__",
],
deps = [
# global_test_configuration is added here because all major tests depend on this
# library. It isn't possible to add these test dependencies via tensorflow.bzl's
# py_test because not all tensorflow tests use tensorflow.bzl's py_test.
"//tensorflow/python:global_test_configuration",
"@com_google_protobuf//:protobuf_python",
"//tensorflow/python/util:compat",
],
)
+377
View File
@@ -0,0 +1,377 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# 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
#
# http://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.
# ==============================================================================
"""Utility functions for comparing proto2 messages in Python.
ProtoEq() compares two proto2 messages for equality.
ClearDefaultValuedFields() recursively clears the fields that are set to their
default values. This is useful for comparing protocol buffers where the
semantics of unset fields and default valued fields are the same.
assertProtoEqual() is useful for unit tests. It produces much more helpful
output than assertEqual() for proto2 messages, e.g. this:
outer {
inner {
- strings: "x"
? ^
+ strings: "y"
? ^
}
}
...compared to the default output from assertEqual() that looks like this:
AssertionError: <my.Msg object at 0x9fb353c> != <my.Msg object at 0x9fb35cc>
Call it inside your unit test's googletest.TestCase subclasses like this:
from tensorflow.python.util.protobuf import compare
class MyTest(googletest.TestCase):
...
def testXXX(self):
...
compare.assertProtoEqual(self, a, b)
Alternatively:
from tensorflow.python.util.protobuf import compare
class MyTest(compare.ProtoAssertions, googletest.TestCase):
...
def testXXX(self):
...
self.assertProtoEqual(a, b)
"""
import collections.abc as collections_abc
import difflib
import math
from google.protobuf import descriptor
from google.protobuf import descriptor_pool
from google.protobuf import message
from google.protobuf import text_format
# TODO(alankelly): Distinguish between signalling and quiet NaNs.
def isClose(x, y, relative_tolerance): # pylint: disable=invalid-name
"""Returns True if x is close to y given the relative tolerance or if x and y are both inf, both -inf, or both NaNs.
This function does not distinguish between signalling and non-signalling NaN.
Args:
x: float value to be compared
y: float value to be compared
relative_tolerance: float. The allowable difference between the two values
being compared is determined by multiplying the relative tolerance by the
maximum of the two values. If this is not provided, then all floats are
compared using string comparison.
"""
# NaNs are considered equal.
if math.isnan(x) or math.isnan(y):
return math.isnan(x) == math.isnan(y)
if math.isinf(x) or math.isinf(y):
return x == y
return abs(x - y) <= relative_tolerance * max(abs(x), abs(y))
def checkFloatEqAndReplace(self, expected, actual, relative_tolerance): # pylint: disable=invalid-name
"""Recursively replaces the floats in actual with those in expected iff they are approximately equal.
This is done because string equality will consider values such as 5.0999999999
and 5.1 as not being equal, despite being extremely close.
Args:
self: googletest.TestCase
expected: expected values
actual: actual values
relative_tolerance: float, relative tolerance.
"""
for expected_fields, actual_fields in zip(
expected.ListFields(), actual.ListFields()
):
is_repeated = True
expected_desc, expected_values = expected_fields
actual_values = actual_fields[1]
if expected_desc.label != descriptor.FieldDescriptor.LABEL_REPEATED:
is_repeated = False
expected_values = [expected_values]
actual_values = [actual_values]
if (
expected_desc.type == descriptor.FieldDescriptor.TYPE_FLOAT
or expected_desc.type == descriptor.FieldDescriptor.TYPE_DOUBLE
):
for i, (x, y) in enumerate(zip(expected_values, actual_values)):
# Replace the actual value with the expected value if the test passes,
# otherwise leave it and let it fail in the next test so that the error
# message is nicely formatted
if isClose(x, y, relative_tolerance):
if is_repeated:
getattr(actual, actual_fields[0].name)[i] = x
else:
setattr(actual, actual_fields[0].name, x)
if (
expected_desc.type == descriptor.FieldDescriptor.TYPE_MESSAGE
or expected_desc.type == descriptor.FieldDescriptor.TYPE_GROUP
):
if (
expected_desc.type == descriptor.FieldDescriptor.TYPE_MESSAGE
and expected_desc.message_type.has_options
and expected_desc.message_type.GetOptions().map_entry
):
# This is a map, only recurse if it has type message type.
if (
expected_desc.message_type.fields_by_number[2].type
== descriptor.FieldDescriptor.TYPE_MESSAGE
):
for e_v, a_v in zip(
iter(expected_values.values()), iter(actual_values.values())
):
checkFloatEqAndReplace(
self,
expected=e_v,
actual=a_v,
relative_tolerance=relative_tolerance,
)
else:
for v, a in zip(expected_values, actual_values):
# recursive step
checkFloatEqAndReplace(
self, expected=v, actual=a, relative_tolerance=relative_tolerance
)
def assertProtoEqual(
self,
a,
b,
check_initialized=True,
normalize_numbers=False,
msg=None,
relative_tolerance=None,
): # pylint: disable=invalid-name(
"""Fails with a useful error if a and b aren't equal.
Comparison of repeated fields matches the semantics of
unittest.TestCase.assertEqual(), ie order and extra duplicates fields matter.
Args:
self: googletest.TestCase
a: proto2 PB instance, or text string representing one.
b: proto2 PB instance -- message.Message or subclass thereof.
check_initialized: boolean, whether to fail if either a or b isn't
initialized.
normalize_numbers: boolean, whether to normalize types and precision of
numbers before comparison.
msg: if specified, is used as the error message on failure.
relative_tolerance: float, relative tolerance. If this is not provided, then
all floats are compared using string comparison otherwise, floating point
comparisons are done using the relative tolerance provided.
"""
pool = descriptor_pool.Default()
if isinstance(a, str):
a = text_format.Parse(a, b.__class__(), descriptor_pool=pool)
for pb in a, b:
if check_initialized:
errors = pb.FindInitializationErrors()
if errors:
self.fail('Initialization errors: %s\n%s' % (errors, pb))
if normalize_numbers:
NormalizeNumberFields(pb)
if relative_tolerance is not None:
checkFloatEqAndReplace(
self, expected=b, actual=a, relative_tolerance=relative_tolerance
)
a_str = text_format.MessageToString(a, descriptor_pool=pool)
b_str = text_format.MessageToString(b, descriptor_pool=pool)
# Some Python versions would perform regular diff instead of multi-line
# diff if string is longer than 2**16. We substitute this behavior
# with a call to unified_diff instead to have easier-to-read diffs.
# For context, see: https://bugs.python.org/issue11763.
if len(a_str) < 2**16 and len(b_str) < 2**16:
self.assertMultiLineEqual(a_str, b_str, msg=msg)
else:
diff = ''.join(
difflib.unified_diff(a_str.splitlines(True), b_str.splitlines(True)))
if diff:
self.fail('%s :\n%s' % (msg, diff))
def NormalizeNumberFields(pb):
"""Normalizes types and precisions of number fields in a protocol buffer.
Due to subtleties in the python protocol buffer implementation, it is possible
for values to have different types and precision depending on whether they
were set and retrieved directly or deserialized from a protobuf. This function
normalizes integer values to ints and longs based on width, 32-bit floats to
five digits of precision to account for python always storing them as 64-bit,
and ensures doubles are floating point for when they're set to integers.
Modifies pb in place. Recurses into nested objects.
Args:
pb: proto2 message.
Returns:
the given pb, modified in place.
"""
for desc, values in pb.ListFields():
is_repeated = True
if desc.label != descriptor.FieldDescriptor.LABEL_REPEATED:
is_repeated = False
values = [values]
normalized_values = None
# We force 32-bit values to int and 64-bit values to long to make
# alternate implementations where the distinction is more significant
# (e.g. the C++ implementation) simpler.
if desc.type in (descriptor.FieldDescriptor.TYPE_INT64,
descriptor.FieldDescriptor.TYPE_UINT64,
descriptor.FieldDescriptor.TYPE_SINT64):
normalized_values = [int(x) for x in values]
elif desc.type in (descriptor.FieldDescriptor.TYPE_INT32,
descriptor.FieldDescriptor.TYPE_UINT32,
descriptor.FieldDescriptor.TYPE_SINT32,
descriptor.FieldDescriptor.TYPE_ENUM):
normalized_values = [int(x) for x in values]
elif desc.type == descriptor.FieldDescriptor.TYPE_FLOAT:
normalized_values = [round(x, 6) for x in values]
elif desc.type == descriptor.FieldDescriptor.TYPE_DOUBLE:
normalized_values = [round(float(x), 7) for x in values]
if normalized_values is not None:
if is_repeated:
pb.ClearField(desc.name)
getattr(pb, desc.name).extend(normalized_values)
else:
setattr(pb, desc.name, normalized_values[0])
if (desc.type == descriptor.FieldDescriptor.TYPE_MESSAGE or
desc.type == descriptor.FieldDescriptor.TYPE_GROUP):
if (desc.type == descriptor.FieldDescriptor.TYPE_MESSAGE and
desc.message_type.has_options and
desc.message_type.GetOptions().map_entry):
# This is a map, only recurse if the values have a message type.
if (desc.message_type.fields_by_number[2].type ==
descriptor.FieldDescriptor.TYPE_MESSAGE):
for v in iter(values.values()):
NormalizeNumberFields(v)
else:
for v in values:
# recursive step
NormalizeNumberFields(v)
return pb
def _IsMap(value):
return isinstance(value, collections_abc.Mapping)
def _IsRepeatedContainer(value):
if isinstance(value, str):
return False
try:
iter(value)
return True
except TypeError:
return False
def ProtoEq(a, b):
"""Compares two proto2 objects for equality.
Recurses into nested messages. Uses list (not set) semantics for comparing
repeated fields, ie duplicates and order matter.
Args:
a: A proto2 message or a primitive.
b: A proto2 message or a primitive.
Returns:
`True` if the messages are equal.
"""
def Format(pb):
"""Returns a dictionary or unchanged pb bases on its type.
Specifically, this function returns a dictionary that maps tag
number (for messages) or element index (for repeated fields) to
value, or just pb unchanged if it's neither.
Args:
pb: A proto2 message or a primitive.
Returns:
A dict or unchanged pb.
"""
if isinstance(pb, message.Message):
return dict((desc.number, value) for desc, value in pb.ListFields())
elif _IsMap(pb):
return dict(pb.items())
elif _IsRepeatedContainer(pb):
return dict(enumerate(list(pb)))
else:
return pb
a, b = Format(a), Format(b)
# Base case
if not isinstance(a, dict) or not isinstance(b, dict):
return a == b
# This list performs double duty: it compares two messages by tag value *or*
# two repeated fields by element, in order. the magic is in the format()
# function, which converts them both to the same easily comparable format.
for tag in sorted(set(a.keys()) | set(b.keys())):
if tag not in a or tag not in b:
return False
else:
# Recursive step
if not ProtoEq(a[tag], b[tag]):
return False
# Didn't find any values that differed, so they're equal!
return True
class ProtoAssertions(object):
"""Mix this into a googletest.TestCase class to get proto2 assertions.
Usage:
class SomeTestCase(compare.ProtoAssertions, googletest.TestCase):
...
def testSomething(self):
...
self.assertProtoEqual(a, b)
See module-level definitions for method documentation.
"""
# pylint: disable=invalid-name
def assertProtoEqual(self, *args, **kwargs):
return assertProtoEqual(self, *args, **kwargs)
@@ -0,0 +1,83 @@
// Copyright 2026 The TensorFlow Authors. All Rights Reserved.
//
// 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
//
// http://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.
// ==============================================================================
// Test messages used in compare_test.py.
syntax = "proto2";
package compare_test;
option cc_enable_arenas = true;
enum Enum {
A = 0;
B = 1;
C = 2;
}
message Small {
repeated string strings = 1;
}
message Medium {
repeated int32 int32s = 1;
repeated Small smalls = 2;
repeated group GroupA = 3 {
repeated group GroupB = 4 {
required string strings = 5;
}
}
repeated float floats = 6;
}
message Large {
optional string string_ = 1;
optional int64 int64_ = 2;
optional float float_ = 3;
optional bool bool_ = 4;
optional Enum enum_ = 5;
repeated int64 int64s = 6;
optional Medium medium = 7;
optional Small small = 8;
optional double double_ = 9;
optional WithMap with_map = 10;
}
message Labeled {
required int32 required = 1;
optional int32 optional = 2;
}
message WithMap {
map<int32, Small> value_message = 1;
map<string, string> value_string = 2;
}
message Floats {
optional float float_ = 1;
optional double double_ = 2;
}
message RepeatedFloats {
repeated float float_ = 1 [packed = true];
repeated double double_ = 2 [packed = true];
}
message NestedFloats {
optional Floats floats = 1;
}
message MapFloats {
map<int64, Floats> int_to_floats = 1;
}
@@ -0,0 +1,533 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# 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
#
# http://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 python.util.protobuf.compare."""
import copy
import re
import sys
import textwrap
from google.protobuf import text_format
from tensorflow.python.platform import googletest
from tensorflow.python.util.protobuf import compare
from tensorflow.python.util.protobuf import compare_test_pb2
def LargePbs(*args):
"""Converts ASCII string Large PBs to messages."""
return [text_format.Merge(arg, compare_test_pb2.Large()) for arg in args]
class ProtoEqTest(googletest.TestCase):
def assertNotEquals(self, a, b):
"""Asserts that ProtoEq says a != b."""
a, b = LargePbs(a, b)
googletest.TestCase.assertEqual(self, compare.ProtoEq(a, b), False)
def assertEqual(self, a, b):
"""Asserts that ProtoEq says a == b."""
a, b = LargePbs(a, b)
googletest.TestCase.assertEqual(self, compare.ProtoEq(a, b), True)
def testPrimitives(self):
googletest.TestCase.assertEqual(self, True, compare.ProtoEq('a', 'a'))
googletest.TestCase.assertEqual(self, False, compare.ProtoEq('b', 'a'))
def testEmpty(self):
self.assertEqual('', '')
def testPrimitiveFields(self):
self.assertNotEqual('string_: "a"', '')
self.assertEqual('string_: "a"', 'string_: "a"')
self.assertNotEqual('string_: "b"', 'string_: "a"')
self.assertNotEqual('string_: "ab"', 'string_: "aa"')
self.assertNotEqual('int64_: 0', '')
self.assertEqual('int64_: 0', 'int64_: 0')
self.assertNotEqual('int64_: -1', '')
self.assertNotEqual('int64_: 1', 'int64_: 0')
self.assertNotEqual('int64_: 0', 'int64_: -1')
self.assertNotEqual('float_: 0.0', '')
self.assertEqual('float_: 0.0', 'float_: 0.0')
self.assertNotEqual('float_: -0.1', '')
self.assertNotEqual('float_: 3.14', 'float_: 0')
self.assertNotEqual('float_: 0', 'float_: -0.1')
self.assertEqual('float_: -0.1', 'float_: -0.1')
self.assertNotEqual('bool_: true', '')
self.assertNotEqual('bool_: false', '')
self.assertNotEqual('bool_: true', 'bool_: false')
self.assertEqual('bool_: false', 'bool_: false')
self.assertEqual('bool_: true', 'bool_: true')
self.assertNotEqual('enum_: A', '')
self.assertNotEqual('enum_: B', 'enum_: A')
self.assertNotEqual('enum_: C', 'enum_: B')
self.assertEqual('enum_: C', 'enum_: C')
def testRepeatedPrimitives(self):
self.assertNotEqual('int64s: 0', '')
self.assertEqual('int64s: 0', 'int64s: 0')
self.assertNotEqual('int64s: 1', 'int64s: 0')
self.assertNotEqual('int64s: 0 int64s: 0', '')
self.assertNotEqual('int64s: 0 int64s: 0', 'int64s: 0')
self.assertNotEqual('int64s: 1 int64s: 0', 'int64s: 0')
self.assertNotEqual('int64s: 0 int64s: 1', 'int64s: 0')
self.assertNotEqual('int64s: 1', 'int64s: 0 int64s: 2')
self.assertNotEqual('int64s: 2 int64s: 0', 'int64s: 1')
self.assertEqual('int64s: 0 int64s: 0', 'int64s: 0 int64s: 0')
self.assertEqual('int64s: 0 int64s: 1', 'int64s: 0 int64s: 1')
self.assertNotEqual('int64s: 1 int64s: 0', 'int64s: 0 int64s: 0')
self.assertNotEqual('int64s: 1 int64s: 0', 'int64s: 0 int64s: 1')
self.assertNotEqual('int64s: 1 int64s: 0', 'int64s: 0 int64s: 2')
self.assertNotEqual('int64s: 1 int64s: 1', 'int64s: 1 int64s: 0')
self.assertNotEqual('int64s: 1 int64s: 1', 'int64s: 1 int64s: 0 int64s: 2')
def testMessage(self):
self.assertNotEqual('small <>', '')
self.assertEqual('small <>', 'small <>')
self.assertNotEqual('small < strings: "a" >', '')
self.assertNotEqual('small < strings: "a" >', 'small <>')
self.assertEqual('small < strings: "a" >', 'small < strings: "a" >')
self.assertNotEqual('small < strings: "b" >', 'small < strings: "a" >')
self.assertNotEqual(
'small < strings: "a" strings: "b" >', 'small < strings: "a" >'
)
self.assertNotEqual('string_: "a"', 'small <>')
self.assertNotEqual('string_: "a"', 'small < strings: "b" >')
self.assertNotEqual('string_: "a"', 'small < strings: "b" strings: "c" >')
self.assertNotEqual('string_: "a" small <>', 'small <>')
self.assertNotEqual('string_: "a" small <>', 'small < strings: "b" >')
self.assertEqual('string_: "a" small <>', 'string_: "a" small <>')
self.assertNotEqual(
'string_: "a" small < strings: "a" >', 'string_: "a" small <>'
)
self.assertEqual('string_: "a" small < strings: "a" >',
'string_: "a" small < strings: "a" >')
self.assertNotEqual(
'string_: "a" small < strings: "a" >',
'int64_: 1 small < strings: "a" >',
)
self.assertNotEqual('string_: "a" small < strings: "a" >', 'int64_: 1')
self.assertNotEqual('string_: "a"', 'int64_: 1 small < strings: "a" >')
self.assertNotEqual(
'string_: "a" int64_: 0 small < strings: "a" >',
'int64_: 1 small < strings: "a" >',
)
self.assertNotEqual(
'string_: "a" int64_: 1 small < strings: "a" >',
'string_: "a" int64_: 0 small < strings: "a" >',
)
self.assertEqual('string_: "a" int64_: 0 small < strings: "a" >',
'string_: "a" int64_: 0 small < strings: "a" >')
def testNestedMessage(self):
self.assertNotEqual('medium <>', '')
self.assertEqual('medium <>', 'medium <>')
self.assertNotEqual('medium < smalls <> >', 'medium <>')
self.assertEqual('medium < smalls <> >', 'medium < smalls <> >')
self.assertNotEqual(
'medium < smalls <> smalls <> >', 'medium < smalls <> >'
)
self.assertEqual('medium < smalls <> smalls <> >',
'medium < smalls <> smalls <> >')
self.assertNotEqual('medium < int32s: 0 >', 'medium < smalls <> >')
self.assertNotEqual(
'medium < smalls < strings: "a"> >', 'medium < smalls <> >'
)
def testTagOrder(self):
"""Tests that different fields are ordered by tag number.
For reference, here are the relevant tag numbers from compare_test.proto:
optional string string_ = 1;
optional int64 int64_ = 2;
optional float float_ = 3;
optional Small small = 8;
optional Medium medium = 7;
optional Small small = 8;
"""
self.assertNotEqual(
'string_: "a" ',
' int64_: 1 ',
)
self.assertNotEqual(
'string_: "a" int64_: 2 ',
' int64_: 1 ',
)
self.assertNotEqual(
'string_: "b" int64_: 1 ',
'string_: "a" int64_: 2 ',
)
self.assertEqual('string_: "a" int64_: 1 ',
'string_: "a" int64_: 1 ')
self.assertNotEqual(
'string_: "a" int64_: 1 float_: 0.0',
'string_: "a" int64_: 1 ',
)
self.assertEqual('string_: "a" int64_: 1 float_: 0.0',
'string_: "a" int64_: 1 float_: 0.0')
self.assertNotEqual(
'string_: "a" int64_: 1 float_: 0.1',
'string_: "a" int64_: 1 float_: 0.0',
)
self.assertNotEqual(
'string_: "a" int64_: 2 float_: 0.0',
'string_: "a" int64_: 1 float_: 0.1',
)
self.assertNotEqual(
'string_: "a" ',
' int64_: 1 float_: 0.1',
)
self.assertNotEqual(
'string_: "a" float_: 0.0',
' int64_: 1 ',
)
self.assertNotEqual(
'string_: "b" float_: 0.0',
'string_: "a" int64_: 1 ',
)
self.assertNotEqual('string_: "a"', 'small < strings: "a" >')
self.assertNotEqual(
'string_: "a" small < strings: "a" >', 'small < strings: "b" >'
)
self.assertNotEqual(
'string_: "a" small < strings: "b" >',
'string_: "a" small < strings: "a" >',
)
self.assertEqual('string_: "a" small < strings: "a" >',
'string_: "a" small < strings: "a" >')
self.assertNotEqual(
'string_: "a" medium <>', 'string_: "a" small < strings: "a" >'
)
self.assertNotEqual(
'string_: "a" medium < smalls <> >',
'string_: "a" small < strings: "a" >',
)
self.assertNotEqual('medium <>', 'small < strings: "a" >')
self.assertNotEqual('medium <> small <>', 'small < strings: "a" >')
self.assertNotEqual('medium < smalls <> >', 'small < strings: "a" >')
self.assertNotEqual(
'medium < smalls < strings: "a" > >', 'small < strings: "b" >'
)
def testIsClose(self):
self.assertTrue(compare.isClose(1, 1, 1e-10))
self.assertTrue(compare.isClose(65061.0420, 65061.0322, 1e-5))
self.assertFalse(compare.isClose(65061.0420, 65061.0322, 1e-7))
def testIsCloseNan(self):
self.assertTrue(compare.isClose(float('nan'), float('nan'), 1e-10))
self.assertFalse(compare.isClose(float('nan'), 1, 1e-10))
self.assertFalse(compare.isClose(1, float('nan'), 1e-10))
self.assertFalse(compare.isClose(float('nan'), float('inf'), 1e-10))
def testIsCloseInf(self):
self.assertTrue(compare.isClose(float('inf'), float('inf'), 1e-10))
self.assertTrue(compare.isClose(float('-inf'), float('-inf'), 1e-10))
self.assertFalse(compare.isClose(float('-inf'), float('inf'), 1e-10))
self.assertFalse(compare.isClose(float('inf'), 1, 1e-10))
self.assertFalse(compare.isClose(1, float('inf'), 1e-10))
def testIsCloseSubnormal(self):
x = sys.float_info.min * sys.float_info.epsilon
self.assertTrue(compare.isClose(x, x, 1e-10))
self.assertFalse(compare.isClose(x, 1, 1e-10))
class NormalizeNumbersTest(googletest.TestCase):
"""Tests for NormalizeNumberFields()."""
def testNormalizesInts(self):
pb = compare_test_pb2.Large(int64_=4)
compare.NormalizeNumberFields(pb)
self.assertIsInstance(pb.int64_, int)
pb.int64_ = 4
compare.NormalizeNumberFields(pb)
self.assertIsInstance(pb.int64_, int)
pb.int64_ = 9999999999999999
compare.NormalizeNumberFields(pb)
self.assertIsInstance(pb.int64_, int)
def testNormalizesRepeatedInts(self):
pb = compare_test_pb2.Large(int64s=[1, 400, 999999999999999])
compare.NormalizeNumberFields(pb)
self.assertIsInstance(pb.int64s[0], int)
self.assertIsInstance(pb.int64s[1], int)
self.assertIsInstance(pb.int64s[2], int)
def testNormalizesFloats(self):
pb1 = compare_test_pb2.Large(float_=1.2314352351231)
pb2 = compare_test_pb2.Large(float_=1.231435)
self.assertNotEqual(pb1.float_, pb2.float_)
compare.NormalizeNumberFields(pb1)
compare.NormalizeNumberFields(pb2)
self.assertEqual(pb1.float_, pb2.float_)
def testNormalizesRepeatedFloats(self):
pb = compare_test_pb2.Large(
medium=compare_test_pb2.Medium(floats=[0.111111111, 0.111111])
)
compare.NormalizeNumberFields(pb)
for value in pb.medium.floats:
self.assertAlmostEqual(0.111111, value)
def testNormalizesDoubles(self):
pb1 = compare_test_pb2.Large(double_=1.2314352351231)
pb2 = compare_test_pb2.Large(double_=1.2314352)
self.assertNotEqual(pb1.double_, pb2.double_)
compare.NormalizeNumberFields(pb1)
compare.NormalizeNumberFields(pb2)
self.assertEqual(pb1.double_, pb2.double_)
def testNormalizesMaps(self):
pb = compare_test_pb2.WithMap()
pb.value_message[4].strings.extend(['a', 'b', 'c'])
pb.value_string['d'] = 'e'
compare.NormalizeNumberFields(pb)
class AssertTest(googletest.TestCase):
"""Tests assertProtoEqual()."""
def assertProtoEqual(self, a, b, **kwargs):
if isinstance(a, str) and isinstance(b, str):
a, b = LargePbs(a, b)
compare.assertProtoEqual(self, a, b, **kwargs)
def assertAll(self, a, **kwargs):
"""Checks that all possible asserts pass."""
self.assertProtoEqual(a, a, **kwargs)
def assertSameNotEqual(self, a, b):
"""Checks that assertProtoEqual() fails."""
self.assertRaises(AssertionError, self.assertProtoEqual, a, b)
def assertNone(self, a, b, message, **kwargs):
"""Checks that all possible asserts fail with the given message."""
message = re.escape(textwrap.dedent(message))
self.assertRaisesRegex(AssertionError, message, self.assertProtoEqual, a, b,
**kwargs)
def testCheckInitialized(self):
# neither is initialized
a = compare_test_pb2.Labeled(optional=1)
self.assertNone(a, a, 'Initialization errors: ', check_initialized=True)
self.assertAll(a, check_initialized=False)
# a is initialized, b isn't
b = copy.deepcopy(a)
a.required = 2
self.assertNone(a, b, 'Initialization errors: ', check_initialized=True)
self.assertNone(
a,
b,
"""
- required: 2
optional: 1
""",
check_initialized=False)
# both are initialized
a = compare_test_pb2.Labeled(required=2)
self.assertAll(a, check_initialized=True)
self.assertAll(a, check_initialized=False)
b = copy.deepcopy(a)
b.required = 3
message = """
- required: 2
? ^
+ required: 3
? ^
"""
self.assertNone(a, b, message, check_initialized=True)
self.assertNone(a, b, message, check_initialized=False)
def testAssertEqualWithStringArg(self):
pb = compare_test_pb2.Large(string_='abc', float_=1.234)
compare.assertProtoEqual(self, """
string_: 'abc'
float_: 1.234
""", pb)
def testNormalizesNumbers(self):
pb1 = compare_test_pb2.Large(int64_=4)
pb2 = compare_test_pb2.Large(int64_=4)
compare.assertProtoEqual(self, pb1, pb2)
def testNormalizesFloat(self):
pb1 = compare_test_pb2.Large(double_=4.0)
pb2 = compare_test_pb2.Large(double_=4)
compare.assertProtoEqual(self, pb1, pb2, normalize_numbers=True)
def testLargeProtoData(self):
# Proto size should be larger than 2**16.
number_of_entries = 2**13
string_value = 'dummystr' # Has length of 2**3.
pb1_txt = 'strings: "dummystr"\n' * number_of_entries
pb2 = compare_test_pb2.Small(strings=[string_value] * number_of_entries)
compare.assertProtoEqual(self, pb1_txt, pb2)
with self.assertRaises(AssertionError):
compare.assertProtoEqual(self, pb1_txt + 'strings: "Should fail."', pb2)
def testPrimitives(self):
self.assertAll('string_: "x"')
self.assertNone('string_: "x"', 'string_: "y"', """
- string_: "x"
? ^
+ string_: "y"
? ^
""")
def testRepeatedPrimitives(self):
self.assertAll('int64s: 0 int64s: 1')
self.assertSameNotEqual('int64s: 0 int64s: 1', 'int64s: 1 int64s: 0')
self.assertSameNotEqual('int64s: 0 int64s: 1 int64s: 2',
'int64s: 2 int64s: 1 int64s: 0')
self.assertSameNotEqual('int64s: 0', 'int64s: 0 int64s: 0')
self.assertSameNotEqual('int64s: 0 int64s: 1',
'int64s: 1 int64s: 0 int64s: 1')
self.assertNone('int64s: 0', 'int64s: 0 int64s: 2', """
int64s: 0
+ int64s: 2
""")
self.assertNone('int64s: 0 int64s: 1', 'int64s: 0 int64s: 2', """
int64s: 0
- int64s: 1
? ^
+ int64s: 2
? ^
""")
def testMessage(self):
self.assertAll('medium: {}')
self.assertAll('medium: { smalls: {} }')
self.assertAll('medium: { int32s: 1 smalls: {} }')
self.assertAll('medium: { smalls: { strings: "x" } }')
self.assertAll(
'medium: { smalls: { strings: "x" } } small: { strings: "y" }')
self.assertSameNotEqual('medium: { smalls: { strings: "x" strings: "y" } }',
'medium: { smalls: { strings: "y" strings: "x" } }')
self.assertSameNotEqual(
'medium: { smalls: { strings: "x" } smalls: { strings: "y" } }',
'medium: { smalls: { strings: "y" } smalls: { strings: "x" } }')
self.assertSameNotEqual(
'medium: { smalls: { strings: "x" strings: "y" strings: "x" } }',
'medium: { smalls: { strings: "y" strings: "x" } }')
self.assertSameNotEqual(
'medium: { smalls: { strings: "x" } int32s: 0 }',
'medium: { int32s: 0 smalls: { strings: "x" } int32s: 0 }')
self.assertNone('medium: {}', 'medium: { smalls: { strings: "x" } }', """
medium {
+ smalls {
+ strings: "x"
+ }
}
""")
self.assertNone('medium: { smalls: { strings: "x" } }',
'medium: { smalls: {} }', """
medium {
smalls {
- strings: "x"
}
}
""")
self.assertNone('medium: { int32s: 0 }', 'medium: { int32s: 1 }', """
medium {
- int32s: 0
? ^
+ int32s: 1
? ^
}
""")
def testMsgPassdown(self):
self.assertRaisesRegex(
AssertionError,
'test message passed down',
self.assertProtoEqual,
'medium: {}',
'medium: { smalls: { strings: "x" } }',
msg='test message passed down')
def testRepeatedMessage(self):
self.assertAll('medium: { smalls: {} smalls: {} }')
self.assertAll('medium: { smalls: { strings: "x" } } medium: {}')
self.assertAll('medium: { smalls: { strings: "x" } } medium: { int32s: 0 }')
self.assertAll('medium: { smalls: {} smalls: { strings: "x" } } small: {}')
self.assertSameNotEqual('medium: { smalls: { strings: "x" } smalls: {} }',
'medium: { smalls: {} smalls: { strings: "x" } }')
self.assertSameNotEqual('medium: { smalls: {} }',
'medium: { smalls: {} smalls: {} }')
self.assertSameNotEqual('medium: { smalls: {} smalls: {} } medium: {}',
'medium: {} medium: {} medium: { smalls: {} }')
self.assertSameNotEqual(
'medium: { smalls: { strings: "x" } smalls: {} }',
'medium: { smalls: {} smalls: { strings: "x" } smalls: {} }')
self.assertNone('medium: {}', 'medium: {} medium { smalls: {} }', """
medium {
+ smalls {
+ }
}
""")
self.assertNone('medium: { smalls: {} smalls: { strings: "x" } }',
'medium: { smalls: {} smalls: { strings: "y" } }', """
medium {
smalls {
}
smalls {
- strings: "x"
? ^
+ strings: "y"
? ^
}
}
""")
class MixinTests(compare.ProtoAssertions, googletest.TestCase):
def testAssertEqualWithStringArg(self):
pb = compare_test_pb2.Large(string_='abc', float_=1.234)
self.assertProtoEqual("""
string_: 'abc'
float_: 1.234
""", pb)
if __name__ == '__main__':
googletest.main()