コード例 #1
0
    def _compare(self, message_original, message_update):
        # 1. If original message is None, then a new message is added.
        if self.message_original is None:
            msg = 'A new message {} is added.'.format(self.message_update.name)
            FindingContainer.addFinding(FindingCategory.MESSAGE_ADDITION, "",
                                        msg, False)
            return
        # 2. If updated message is None, then the original message is removed.
        if self.message_update is None:
            msg = 'A message {} is removed'.format(self.message_original.name)
            FindingContainer.addFinding(FindingCategory.MESSAGE_REMOVAL, "",
                                        msg, True)
            return

        # 3. Check breaking changes in each fields. Note: Fields are identified by number, not by name.
        # Descriptor.fields_by_number (dict int -> FieldDescriptor) indexed by number.
        if message_original.fields_by_number or message_update.fields_by_number:
            self._compareNestedFields(message_original.fields_by_number,
                                      message_update.fields_by_number)

        # 4. Check breaking changes in nested message.
        # Descriptor.nested_types_by_name (dict str -> Descriptor) indexed by name.
        # Recursively call _compare for nested message type comparison.
        if (message_original.nested_types_by_name
                or message_update.nested_types_by_name):
            self._compareNestedMessages(message_original.nested_types_by_name,
                                        message_update.nested_types_by_name)
コード例 #2
0
class Detector:
    """Detect the breaking changes in the two versions of FileDescriptorSet"""
    def __init__(
        self,
        descriptor_set_original: desc.FileDescriptorSet,
        descriptor_set_update: desc.FileDescriptorSet,
        opts: Optional[Options] = None,
    ):
        self.descriptor_set_original = descriptor_set_original
        self.descriptor_set_update = descriptor_set_update
        self.opts = opts
        self.finding_container = FindingContainer()

    def detect_breaking_changes(self):
        # Init FileSetComparator and compare the two FileDescriptorSet.
        FileSetComparator(
            FileSet(self.descriptor_set_original),
            FileSet(self.descriptor_set_update),
            self.finding_container,
        ).compare()

        if not self.opts:
            return self.finding_container.getActionableFindings()
        # Output json file of findings and human-readable messages if the
        # command line option is enabled.
        with open(self.opts.output_json_path, "w") as write_json_file:
            json.dump(self.finding_container.toDictArr(), write_json_file)

        if self.opts.human_readable_message:
            sys.stdout.write(self.finding_container.toHumanReadableMessage())
コード例 #3
0
 def compare(self):
     # 1. If original service is None, then a new service is added.
     if self.service_original is None:
         FindingContainer.addFinding(
             category=FindingCategory.SERVICE_ADDITION,
             location=f"{self.service_update.proto_file_name} Line: {self.service_update.source_code_line}",
             message=f"A new service {self.service_update.name} is added.",
             actionable=False,
         )
         return
     # 2. If updated service is None, then the original service is removed.
     if self.service_update is None:
         FindingContainer.addFinding(
             category=FindingCategory.SERVICE_REMOVAL,
             location=f"{self.service_original.proto_file_name} Line: {self.service_original.source_code_line}",
             message=f"A service {self.service_original.name} is removed",
             actionable=True,
         )
         return
     self.messages_map_original = self.service_original.messages_map
     self.messages_map_update = self.service_update.messages_map
     # 3. Check the methods list
     self._compareRpcMethods(
         self.service_original,
         self.service_update,
         self.messages_map_original,
         self.messages_map_update,
     )
コード例 #4
0
class EnumValueComparatorTest(unittest.TestCase):
    def setUp(self):
        L = descriptor_pb2.SourceCodeInfo.Location
        locations = [L(path=(2, 1), span=(1, 2))]
        self.enum_foo = make_enum_value(
            name="FOO",
            number=1,
            proto_file_name="test.proto",
            locations=locations,
            path=(2, 1),
        )
        self.enum_bar = make_enum_value(
            name="BAR",
            number=1,
            proto_file_name="test_update.proto",
            locations=locations,
            path=(2, 1),
        )
        self.finding_container = FindingContainer()

    def test_enum_value_removal(self):
        EnumValueComparator(
            self.enum_foo,
            None,
            self.finding_container,
        ).compare()
        finding = self.finding_container.getAllFindings()[0]
        self.assertEqual(finding.message,
                         "An existing EnumValue `FOO` is removed.")
        self.assertEqual(finding.category.name, "ENUM_VALUE_REMOVAL")
        self.assertEqual(finding.change_type.name, "MAJOR")
        self.assertEqual(finding.location.proto_file_name, "test.proto")
        self.assertEqual(finding.location.source_code_line, 2)

    def test_enum_value_addition(self):
        EnumValueComparator(None, self.enum_foo,
                            self.finding_container).compare()
        finding = self.finding_container.getAllFindings()[0]
        self.assertEqual(finding.message, "A new EnumValue `FOO` is added.")
        self.assertEqual(finding.category.name, "ENUM_VALUE_ADDITION")
        self.assertEqual(finding.change_type.name, "MINOR")
        self.assertEqual(finding.location.proto_file_name, "test.proto")
        self.assertEqual(finding.location.source_code_line, 2)

    def test_name_change(self):
        EnumValueComparator(self.enum_foo, self.enum_bar,
                            self.finding_container).compare()
        finding = self.finding_container.getAllFindings()[0]
        self.assertEqual(
            finding.message,
            "Name of the EnumValue is changed from `FOO` to `BAR`.")
        self.assertEqual(finding.category.name, "ENUM_VALUE_NAME_CHANGE")
        self.assertEqual(finding.change_type.name, "MAJOR")
        self.assertEqual(finding.location.proto_file_name, "test_update.proto")
        self.assertEqual(finding.location.source_code_line, 2)

    def test_no_api_change(self):
        EnumValueComparator(self.enum_foo, self.enum_foo,
                            self.finding_container).compare()
        self.assertEqual(len(self.finding_container.getAllFindings()), 0)
 def setUp(self):
     L = descriptor_pb2.SourceCodeInfo.Location
     # fmt: off
     locations = [
         L(path=(
             4,
             0,
         ), span=(1, 2, 3, 4)),
         # Enum will add (2, 1) for each EnumValue in the path.
         L(path=(4, 0, 2, 1), span=(2, 3, 4, 5)),
     ]
     self.enum_foo = make_enum(
         name="Foo",
         values=(("VALUE1", 1), ),
         proto_file_name="test.proto",
         locations=locations,
         path=(
             4,
             0,
         ),
     )
     self.enum_bar = make_enum(
         name="Bar",
         values=(
             ("VALUE1", 1),
             ("VALUE2", 2),
         ),
         proto_file_name="test_update.proto",
         locations=locations,
         path=(
             4,
             0,
         ),
     )
     self.finding_container = FindingContainer()
コード例 #6
0
 def nestedMessageChange(self):
     # Field `type` in nested message `PhoneNumber` is re-numbered. So it is taken as one field removed and one field added.
     DescriptorComparator(self.person_msg, self.person_msg_update).compare()
     findingLength = len(FindingContainer.getAllFindings())
     self.assertEqual(
         FindingContainer.getAllFindings()[findingLength - 1].category.name,
         'FIELD_ADDITION')
     self.assertEqual(
         FindingContainer.getAllFindings()[findingLength - 2].category.name,
         'FIELD_REMOVAL')
コード例 #7
0
 def __init__(
     self,
     descriptor_set_original: desc.FileDescriptorSet,
     descriptor_set_update: desc.FileDescriptorSet,
     opts: Optional[Options] = None,
 ):
     self.descriptor_set_original = descriptor_set_original
     self.descriptor_set_update = descriptor_set_update
     self.opts = opts
     self.finding_container = FindingContainer()
コード例 #8
0
    def _compare(self, message_original, message_update):
        # 1. If original message is None, then a new message is added.
        if message_original is None:
            FindingContainer.addFinding(
                category=FindingCategory.MESSAGE_ADDITION,
                location=
                f"{message_update.proto_file_name} Line: {message_update.source_code_line}",
                message=f"A new message {message_update.name} is added.",
                actionable=False,
            )
            return
        # 2. If updated message is None, then the original message is removed.
        if message_update is None:
            FindingContainer.addFinding(
                category=FindingCategory.MESSAGE_REMOVAL,
                location=
                f"{message_original.proto_file_name} Line: {message_original.source_code_line}",
                message=f"A message {message_original.name} is removed",
                actionable=True,
            )
            return

        self.global_resources_original = self.message_original.file_resources
        self.global_resources_update = self.message_update.file_resources
        # 3. Check breaking changes in each fields. Note: Fields are
        # identified by number, not by name. Descriptor.fields_by_number
        # (dict int -> FieldDescriptor) indexed by number.
        if message_original.fields or message_update.fields:
            self._compare_nested_fields(
                message_original.fields,
                message_update.fields,
            )

        # 4. Check breaking changes in nested message.
        # Descriptor.nested_types_by_name (dict str -> Descriptor)
        # indexed by name. Recursively call _compare for nested
        # message type comparison.
        if message_original.nested_messages or message_update.nested_messages:
            self._compare_nested_messages(
                message_original.nested_messages,
                message_update.nested_messages,
            )
        # 5. Check breaking changes in nested enum.
        if message_original.nested_enums or message_update.nested_enums:
            self._compare_nested_enums(
                message_original.nested_enums,
                message_update.nested_enums,
            )

        # 6. Check `google.api.resource` annotation.
        self._compare_resources(message_original.resource,
                                message_update.resource)
コード例 #9
0
 def fieldChange(self):
     DescriptorComparator(self.addressBook_msg,
                          self.addressBook_msg_update).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message,
                      'The Field deprecated is moved out of one-of')
     self.assertEqual(finding.category.name, 'FIELD_ONEOF_REMOVAL')
コード例 #10
0
    def test_http_annotation_change(self):
        ServiceComparator(
            self.service_annotation_original,
            self.service_annotation_update,
        ).compare()
        findings_map = {f.message: f for f in FindingContainer.getAllFindings()}
        # TODO(xiaozhenliu): This should be removed once we have version updates
        # support. The URI update from `v1/example:foo` to `v1beta1/example:foo`
        # is allowed.
        uri_change_finding = findings_map["An existing http method URI is changed."]
        method_change_finding = findings_map["An existing http method is changed."]
        body_change_finding = findings_map["An existing http method body is changed."]

        self.assertEqual(uri_change_finding.category.name, "HTTP_ANNOTATION_CHANGE")
        self.assertEqual(
            uri_change_finding.location.path,
            "service_annotation_v1beta1.proto Line: 14",
        )
        self.assertEqual(method_change_finding.category.name, "HTTP_ANNOTATION_CHANGE")
        self.assertEqual(
            method_change_finding.location.path,
            "service_annotation_v1beta1.proto Line: 14",
        )
        self.assertEqual(body_change_finding.category.name, "HTTP_ANNOTATION_CHANGE")
        self.assertEqual(
            body_change_finding.location.path,
            "service_annotation_v1beta1.proto Line: 22",
        )
 def test_enum_addition(self):
     EnumComparator(None, self.enum_update).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message, "A new Enum PhoneType is added.")
     self.assertEqual(finding.category.name, "ENUM_ADDITION")
     self.assertEqual(finding.location.path,
                      "message_v1beta1.proto Line: 10")
 def test_enum_value_change(self):
     EnumComparator(self.enum_original, self.enum_update).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message, "A new EnumValue SCHOOL is added.")
     self.assertEqual(finding.category.name, "ENUM_VALUE_ADDITION")
     self.assertEqual(finding.location.path,
                      "message_v1beta1.proto Line: 14")
コード例 #13
0
 def fieldAddition(self):
     field_married = update_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields_by_name['married']
     FieldComparator(None, field_married).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message, 'A new Field married is added.')
     self.assertEqual(finding.category.name, 'FIELD_ADDITION')
コード例 #14
0
 def fieldRemoval(self):
     field_company_address = update_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields_by_name['company_address']
     FieldComparator(field_company_address, None).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message, 'A Field company_address is removed')
     self.assertEqual(finding.category.name, 'FIELD_REMOVAL')
コード例 #15
0
 def test_resource_reference_change(self):
     _INVOKER_ORIGNAL = UnittestInvoker(
         ["resource_reference_v1.proto"],
         "resource_reference_v1_descriptor_set.pb",
         True,
     )
     _INVOKER_UPDATE = UnittestInvoker(
         ["resource_reference_v1beta1.proto"],
         "resource_reference_v1beta1_descriptor_set.pb",
         True,
     )
     FileSetComparator(FileSet(_INVOKER_ORIGNAL.run()),
                       FileSet(_INVOKER_UPDATE.run())).compare()
     findings_map = {
         f.message: f
         for f in FindingContainer.getAllFindings()
     }
     self.assertEqual(
         findings_map[
             "The child_type 'example.googleapis.com/t1' and type 'example.googleapis.com/t1' of resource reference option in field 'topic' cannot be resolved to the identical resource."]
         .category.name,
         "RESOURCE_REFERENCE_CHANGE",
     )
     _INVOKER_ORIGNAL.cleanup()
     _INVOKER_UPDATE.cleanup()
コード例 #16
0
 def enumNameChange(self):
     EnumComparator(self.enum_original, self.enum_update).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(
         finding.message,
         'Name of the Enum is changed, the original is PhoneType, but the updated is PhoneTypeUpdate'
     )
     self.assertEqual(finding.category.name, 'ENUM_NAME_CHANGE')
コード例 #17
0
class FindingContainerTest(unittest.TestCase):
    # The unit tests are executed in alphabetical order by test name
    # automatically, so test_reset() will be run in the middle which will break us.
    # This is a monolithic test, so we give numbers to indicate
    # the steps and also ensure the execution orders.
    finding_container = FindingContainer()

    def test1_add_findings(self):
        self.finding_container.add_finding(
            category=FindingCategory.METHOD_REMOVAL,
            proto_file_name="my_proto.proto",
            source_code_line=12,
            change_type=ChangeType.MAJOR,
            subject="subject",
            context="context",
        )
        self.assertEqual(len(self.finding_container.get_all_findings()), 1)

    def test2_get_actionable_findings(self):
        self.finding_container.add_finding(
            category=FindingCategory.FIELD_ADDITION,
            proto_file_name="my_proto.proto",
            source_code_line=15,
            change_type=ChangeType.MINOR,
        )
        self.assertEqual(len(self.finding_container.get_actionable_findings()),
                         1)

    def test3_to_dict_arr(self):
        dict_arr_output = self.finding_container.to_dict_arr()
        self.assertEqual(dict_arr_output[0]["category"], "METHOD_REMOVAL")
        self.assertEqual(dict_arr_output[1]["category"], "FIELD_ADDITION")

    def test4_to_human_readable_message(self):
        self.finding_container.add_finding(
            category=FindingCategory.RESOURCE_DEFINITION_REMOVAL,
            proto_file_name="my_proto.proto",
            source_code_line=5,
            change_type=ChangeType.MAJOR,
            subject="subject",
        )
        self.finding_container.add_finding(
            category=FindingCategory.METHOD_SIGNATURE_REMOVAL,
            proto_file_name="my_other_proto.proto",
            source_code_line=-1,
            change_type=ChangeType.MAJOR,
            type="type",
            subject="subject",
            context="context",
        )
        self.assertEqual(
            self.finding_container.to_human_readable_message(),
            "my_other_proto.proto: An existing method_signature `type` is removed from method `subject` in service `context`.\n"
            +
            "my_proto.proto L5: An existing resource_definition `subject` is removed.\n"
            +
            "my_proto.proto L12: An existing method `subject` is removed from service `context`.\n",
        )
コード例 #18
0
 def test_service_removal(self):
     ServiceComparator(
         self.service_original,
         None,
     ).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message, "A service Example is removed")
     self.assertEqual(finding.category.name, "SERVICE_REMOVAL")
     self.assertEqual(finding.location.path, "service_v1.proto Line: 5")
コード例 #19
0
 def setUp(self):
     L = descriptor_pb2.SourceCodeInfo.Location
     locations = [L(path=(2, 1), span=(1, 2))]
     self.enum_foo = make_enum_value(
         name="FOO",
         number=1,
         proto_file_name="test.proto",
         locations=locations,
         path=(2, 1),
     )
     self.enum_bar = make_enum_value(
         name="BAR",
         number=1,
         proto_file_name="test_update.proto",
         locations=locations,
         path=(2, 1),
     )
     self.finding_container = FindingContainer()
 def enumValueNameChange(self):
     EnumValueComparator(self.enumValue_mobile,
                         self.enumValue_home).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(
         finding.message,
         'Name of the EnumValue is changed, the original is MOBILE, but the updated is HOME'
     )
     self.assertEqual(finding.category.name, 'ENUM_VALUE_NAME_CHANGE')
コード例 #21
0
 def _compare_method_signatures(self, method_original, method_update):
     signatures_original = method_original.method_signatures.value
     signatures_update = method_update.method_signatures.value
     if len(signatures_original) > len(signatures_update):
         FindingContainer.addFinding(
             category=FindingCategory.METHOD_SIGNATURE_CHANGE,
             location=f"{method_original.proto_file_name} Line: {method_original.method_signatures.source_code_line}",
             message="An existing method_signature is removed.",
             actionable=True,
         )
     for old_sig, new_sig in zip(signatures_original, signatures_update):
         if old_sig != new_sig:
             FindingContainer.addFinding(
                 category=FindingCategory.METHOD_SIGNATURE_CHANGE,
                 location=f"{method_update.proto_file_name} Line: {method_update.method_signatures.source_code_line}",
                 message=f"An existing method_signature is changed from '{old_sig}' to '{new_sig}'.",
                 actionable=True,
             )
コード例 #22
0
 def test_service_addition(self):
     ServiceComparator(
         None,
         self.service_original,
     ).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message, "A new service Example is added.")
     self.assertEqual(finding.category.name, "SERVICE_ADDITION")
     self.assertEqual(finding.location.path, "service_v1.proto Line: 5")
コード例 #23
0
    def compare(self):
        # 1. If original EnumDescriptor is None, then a new
        # EnumDescriptor is added.
        if self.enum_original is None:
            FindingContainer.addFinding(
                category=FindingCategory.ENUM_ADDITION,
                location=
                f"{self.enum_update.proto_file_name} Line: {self.enum_update.source_code_line}",
                message=f"A new Enum {self.enum_update.name} is added.",
                actionable=False,
            )

        # 2. If updated EnumDescriptor is None, then the original
        # EnumDescriptor is removed.
        elif self.enum_update is None:
            FindingContainer.addFinding(
                category=FindingCategory.ENUM_REMOVAL,
                location=
                f"{self.enum_original.proto_file_name} Line: {self.enum_original.source_code_line}",
                message=f"An Enum {self.enum_original.name} is removed",
                actionable=True,
            )

        # 3. If the EnumDescriptors have the same name, check the values
        # of them stay the same. Enum values are identified by number,
        # not by name.
        else:
            enum_values_dict_original = self.enum_original.values
            enum_values_dict_update = self.enum_update.values
            enum_values_keys_set_original = set(
                enum_values_dict_original.keys())
            enum_values_keys_set_update = set(enum_values_dict_update.keys())
            # Compare Enum values that only exist in original version
            for number in enum_values_keys_set_original - enum_values_keys_set_update:
                EnumValueComparator(enum_values_dict_original[number],
                                    None).compare()
            # Compare Enum values that only exist in update version
            for number in enum_values_keys_set_update - enum_values_keys_set_original:
                EnumValueComparator(None,
                                    enum_values_dict_update[number]).compare()
            # Compare Enum values that exist both in original and update versions
            for number in enum_values_keys_set_original & enum_values_keys_set_update:
                EnumValueComparator(enum_values_dict_original[number],
                                    enum_values_dict_update[number]).compare()
コード例 #24
0
    def compare(self):
        # 1. If original service is None, then a new service is added.
        if self.service_original is None:
            msg = 'A new service {} is added.'.format(self.service_update.name)
            FindingContainer.addFinding(FindingCategory.SERVICE_ADDITION, "",
                                        msg, False)
            return
        # 2. If updated service is None, then the original service is removed.
        if self.service_update is None:
            msg = 'A service {} is removed'.format(self.service_original.name)
            FindingContainer.addFinding(FindingCategory.SERVICE_REMOVAL, "",
                                        msg, True)
            return

        # 3. TODO(xiaozhenliu): method_signature annotation
        # 4. TODO(xiaozhenliu): LRO operation_info annotation
        # 5. TODO(xiaozhenliu): google.api.http annotation
        # 6. Check the methods list
        self._compareRpcMethods(self.service_original, self.service_update)
コード例 #25
0
 def moveExistingFieldOutofOneof(self):
     field_email_original = original_version.DESCRIPTOR.message_types_by_name[
         "AddressBook"].fields_by_name['deprecated']
     field_email_update = update_version.DESCRIPTOR.message_types_by_name[
         "AddressBook"].fields_by_name['deprecated']
     FieldComparator(field_email_original, field_email_update).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message,
                      'The Field deprecated is moved out of one-of')
     self.assertEqual(finding.category.name, 'FIELD_ONEOF_REMOVAL')
コード例 #26
0
 def moveExistingFieldIntoOneof(self):
     field_email_original = original_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields_by_name['home_address']
     field_email_update = update_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields_by_name['home_address']
     FieldComparator(field_email_original, field_email_update).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(finding.message,
                      'The Field home_address is moved into one-of')
     self.assertEqual(finding.category.name, 'FIELD_ONEOF_ADDITION')
コード例 #27
0
class FindingContainerTest(unittest.TestCase):
    # The unit tests are executed in alphabetical order by test name
    # automatically, so test_reset() will be run in the middle which will break us.
    # This is a monolithic test, so we give numbers to indicate
    # the steps and also ensure the execution orders.
    finding_container = FindingContainer()

    def test1_add_findings(self):
        self.finding_container.addFinding(
            category=FindingCategory.METHOD_REMOVAL,
            proto_file_name="my_proto.proto",
            source_code_line=12,
            message="An rpc method bar is removed.",
            change_type=ChangeType.MAJOR,
        )
        self.assertEqual(len(self.finding_container.getAllFindings()), 1)

    def test2_get_actionable_findings(self):
        self.finding_container.addFinding(
            category=FindingCategory.FIELD_ADDITION,
            proto_file_name="my_proto.proto",
            source_code_line=15,
            message="Not breaking change.",
            change_type=ChangeType.MINOR,
        )
        self.assertEqual(len(self.finding_container.getActionableFindings()),
                         1)

    def test3_toDictArr(self):
        dict_arr_output = self.finding_container.toDictArr()
        self.assertEqual(dict_arr_output[0]["category"], "METHOD_REMOVAL")
        self.assertEqual(dict_arr_output[1]["category"], "FIELD_ADDITION")

    def test4_toHumanReadableMessage(self):
        self.finding_container.addFinding(
            category=FindingCategory.RESOURCE_DEFINITION_CHANGE,
            proto_file_name="my_proto.proto",
            source_code_line=5,
            message="An existing file-level resource definition has changed.",
            change_type=ChangeType.MAJOR,
        )
        self.finding_container.addFinding(
            category=FindingCategory.METHOD_SIGNATURE_CHANGE,
            proto_file_name="my_other_proto.proto",
            source_code_line=-1,
            message=
            "An existing method_signature is changed from 'sig1' to 'sig2'.",
            change_type=ChangeType.MAJOR,
        )
        self.assertEqual(
            self.finding_container.toHumanReadableMessage(),
            "my_proto.proto L5: An existing file-level resource definition has changed.\n"
            + "my_proto.proto L12: An rpc method bar is removed.\n" +
            "my_other_proto.proto: An existing method_signature is changed from 'sig1' to 'sig2'.\n",
        )
 def test_name_change(self):
     EnumValueComparator(self.enumValue_mobile,
                         self.enumValue_home).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(
         finding.message,
         "Name of the EnumValue is changed, the original "
         "is MOBILE, but the updated is HOME",
     )
     self.assertEqual(finding.category.name, "ENUM_VALUE_NAME_CHANGE")
     self.assertEqual(finding.location.path, "message_v1.proto Line: 12")
 def test_type_change(self):
     # Field `id` is `int32` type in `message_v1.proto`,
     # but updated to `string` in `message_v1beta1.proto`.
     FieldComparator(self.person_fields_v1[2],
                     self.person_fields_v1beta1[2]).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(
         finding.message,
         "Type of the field is changed, the original is TYPE_INT32,"
         " but the updated is TYPE_STRING",
     )
     self.assertEqual(finding.category.name, "FIELD_TYPE_CHANGE")
 def test_repeated_label_change(self):
     # Field `phones` in `message_v1.proto` has `repeated` label,
     # but it's removed in the `message_v1beta1.proto`.
     FieldComparator(self.person_fields_v1[4],
                     self.person_fields_v1beta1[4]).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(
         finding.message,
         "Repeated state of the Field is changed, the original is LABEL_REPEATED,"
         " but the updated is LABEL_OPTIONAL",
     )
     self.assertEqual(finding.category.name, "FIELD_REPEATED_CHANGE")