def _compare_nested_fields(self, fields_dict_original, fields_dict_update):
        fields_number_original = set(fields_dict_original.keys())
        fields_number_update = set(fields_dict_update.keys())

        for field_number in fields_number_original - fields_number_update:
            FieldComparator(
                fields_dict_original[field_number],
                None,
                self.finding_container,
                context=self.context,
            ).compare()
        for field_number in fields_number_update - fields_number_original:
            FieldComparator(
                None,
                fields_dict_update[field_number],
                self.finding_container,
                context=self.context,
            ).compare()
        for field_number in fields_number_original & fields_number_update:
            FieldComparator(
                fields_dict_original[field_number],
                fields_dict_update[field_number],
                self.finding_container,
                context=self.context,
            ).compare()
    def test_resource_reference_change_type_conversion_non_breaking(self):
        child_resource = make_resource_descriptor(
            resource_type="example.v1/Foo",
            resource_patterns=["bar/{bar}/foo/{foo}", "bar/{bar}/foo"],
        )
        parent_resource = make_resource_descriptor(
            resource_type="example.v1/Bar", resource_patterns=["bar/{bar}"]
        )
        # Register two resources in database.
        resource_database = make_resource_database(
            resources=[child_resource, parent_resource]
        )
        # The original field is defined by child type.
        field_options_child = make_field_annotation_resource_reference(
            resource_type="example.v1/Foo", is_child_type=True
        )
        field_with_reference_child = make_field(
            name="Test",
            options=field_options_child,
            resource_database=resource_database,
        )

        # The update field is defined by parent type.
        field_options_parent = make_field_annotation_resource_reference(
            resource_type="example.v1/Bar", is_child_type=False
        )
        field_with_reference_parent = make_field(
            name="Test",
            options=field_options_parent,
            resource_database=resource_database,
        )
        # The two resources can be resolved to the identical resource.
        FieldComparator(
            field_with_reference_child,
            field_with_reference_parent,
            self.finding_container,
            context="ctx",
        ).compare()
        finding = self.finding_container.get_all_findings()
        # No breaking change should be detected.
        self.assertFalse(finding)

        # Reverse should be same since the two resources can
        # be resolved to the identical resource.
        FieldComparator(
            field_with_reference_parent,
            field_with_reference_child,
            self.finding_container,
            context="ctx",
        ).compare()
        finding = self.finding_container.get_all_findings()
        # No breaking change should be detected.
        self.assertFalse(finding)
Beispiel #3
0
    def _compare_nested_fields(self, fields_dict_original, fields_dict_update):
        fields_number_original = set(fields_dict_original.keys())
        fields_number_update = set(fields_dict_update.keys())

        for fieldNumber in fields_number_original - fields_number_update:
            FieldComparator(fields_dict_original[fieldNumber], None).compare()
        for fieldNumber in fields_number_update - fields_number_original:
            FieldComparator(None, fields_dict_update[fieldNumber]).compare()
        for fieldNumber in fields_number_original & fields_number_update:
            FieldComparator(
                fields_dict_original[fieldNumber],
                fields_dict_update[fieldNumber],
            ).compare()
Beispiel #4
0
    def _compareNestedFields(self, fieldsDict_original, fieldsDict_update):
        fieldsUnique_original = list(
            set(fieldsDict_original.keys()) - set(fieldsDict_update.keys()))
        fieldsUnique_update = list(
            set(fieldsDict_update.keys()) - set(fieldsDict_original.keys()))
        fieldsIntersaction = list(
            set(fieldsDict_original.keys()) & set(fieldsDict_update.keys()))

        for fieldNumber in fieldsUnique_original:
            FieldComparator(fieldsDict_original[fieldNumber], None).compare()
        for fieldNumber in fieldsUnique_update:
            FieldComparator(None, fieldsDict_update[fieldNumber]).compare()
        for fieldNumber in fieldsIntersaction:
            FieldComparator(fieldsDict_original[fieldNumber],
                            fieldsDict_update[fieldNumber]).compare()
 def test_field_behavior_change(self):
     field_required = make_field(required=True)
     field_non_required = make_field(required=False)
     # Required to optional, non-breaking change.
     FieldComparator(
         field_required, field_non_required, self.finding_container, context="ctx"
     ).compare()
     findings = self.finding_container.get_all_findings()
     self.assertFalse(findings)
     # Required to optional, non-breaking change.
     FieldComparator(
         field_non_required, field_required, self.finding_container, context="ctx"
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "FIELD_BEHAVIOR_CHANGE")
    def test_type_change_map_entry4(self):
        # Both fields are map type. But the key, value types are not identical.
        # But only the versions pare are different. Non-breaking change.
        # [Constructing] map<string, .example.v1.value> field
        key_original = make_field(proto_type="TYPE_STRING", number=1)
        value_original = make_field(
            proto_type="TYPE_MESSAGE", type_name=".example.v1.value", number=2
        )
        field_original = make_field(
            proto_type="TYPE_MESSAGE",
            type_name=".exmaple.MapEntry",
            map_entry={"key": key_original, "value": value_original},
            api_version="v1",
        )
        # [Constructing] map<string, .example.v1beta1.value> field
        key_update = make_field(proto_type="TYPE_STRING", number=1)
        value_update = make_field(
            proto_type="TYPE_MESSAGE", number=2, type_name=".example.v1beta1.value"
        )
        field_update = make_field(
            proto_type="TYPE_MESSAGE",
            type_name=".exmaple.MapEntry",
            map_entry={"key": key_update, "value": value_update},
            api_version="v1beta1",
        )

        FieldComparator(
            field_original, field_update, self.finding_container, context="ctx"
        ).compare()
        finding = self.finding_container.get_all_findings()
        self.assertFalse(finding)
    def test_type_change_map_entry3(self):
        # Both fields are map type. But the key, value types are not identical. Breaking change.
        # [Constructing] map<string, string> field
        key_original = make_field(proto_type="TYPE_STRING", number=1)
        value_original = make_field(proto_type="TYPE_STRING", number=2)
        field_original = make_field(
            proto_type="TYPE_MESSAGE",
            type_name=".exmaple.MapEntry",
            map_entry={"key": key_original, "value": value_original},
        )
        # [Constructing] map<key, value> field
        key_update = make_field(
            proto_type="TYPE_MESSAGE", number=1, type_name=".example.key"
        )
        value_update = make_field(
            proto_type="TYPE_MESSAGE", number=2, type_name=".example.value"
        )
        field_update = make_field(
            proto_type="TYPE_MESSAGE",
            type_name=".exmaple.MapEntry",
            map_entry={"key": key_update, "value": value_update},
        )

        FieldComparator(
            field_original, field_update, self.finding_container, context="ctx"
        ).compare()
        finding = self.finding_container.get_all_findings()[0]
        self.assertEqual(finding.change_type.name, "MAJOR")
        self.assertEqual(finding.category.name, "FIELD_TYPE_CHANGE")
        self.assertEqual(finding.location.proto_file_name, "foo")
Beispiel #8
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')
 def test_resource_reference_removal_breaking2(self):
     # Removed resource reference is defined by type, which is not identical
     # with the message options.
     # Original field has resource reference `example.v1/Foo`.
     field_options = make_field_annotation_resource_reference(
         resource_type="example.v1/Foo", is_child_type=False
     )
     field_with_reference = make_field(name="Test", options=field_options)
     # Update field has no resource reference, and the resource type
     # is different from the message options type.
     message_resource = make_resource_descriptor(
         resource_type="NotInteresting", resource_patterns=["NotInteresting"]
     )
     field_without_reference = make_field(
         name="Test", message_resource=message_resource
     )
     FieldComparator(
         field_with_reference,
         field_without_reference,
         self.finding_container,
         context="ctx",
     ).compare()
     finding = self.finding_container.get_actionable_findings()[0]
     self.assertEqual(finding.category.name, "RESOURCE_REFERENCE_REMOVAL")
     self.assertEqual(finding.change_type.name, "MAJOR")
 def test_field_addition(self):
     field_foo = make_field("Foo")
     FieldComparator(
         None, field_foo, self.finding_container, context="ctx"
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "FIELD_ADDITION")
    def test_resource_reference_removal_non_breaking1(self):
        # Removed resource reference is defined by type, and it is
        # added back to the message options.
        # Original field has resource reference `example.v1/Foo`.
        field_options = make_field_annotation_resource_reference(
            resource_type="example.v1/Foo", is_child_type=False
        )
        field_with_reference = make_field(name="Test", options=field_options)
        # Update field has no resource reference. But the message has
        # resource options `example.v1/Foo`.
        message_resource = make_resource_descriptor(
            resource_type="example.v1/Foo", resource_patterns=["bar/{bar}"]
        )

        field_without_reference = make_field(
            name="Test", message_resource=message_resource
        )
        FieldComparator(
            field_with_reference,
            field_without_reference,
            self.finding_container,
            context="ctx",
        ).compare()
        finding = self.finding_container.get_all_findings()[0]
        self.assertEqual(finding.category.name, "RESOURCE_REFERENCE_MOVED")
        self.assertEqual(finding.change_type.name, "MINOR")
 def test_resource_reference_removal_non_breaking2(self):
     # Removed resource reference is defined by child type, and it
     # can be resolved to the same resource with the message options.
     # Original field has resource reference `example.v1/Foo`.
     field_options = make_field_annotation_resource_reference(
         resource_type="example.v1/Foo", is_child_type=False
     )
     field_with_reference = make_field(name="Test", options=field_options)
     # Update field has no resource reference. But the message has
     # resource options `example.v1/Foo`.
     message_resource = make_resource_descriptor(
         resource_type="example.v1/Foo", resource_patterns=["bar/{bar}"]
     )
     field_resource = make_resource_descriptor(
         resource_type="example.v1/Foo", resource_patterns=["bar/{bar}/foo/{foo}"]
     )
     # Register the two resources in the database.
     resource_database = make_resource_database(
         resources=[message_resource, field_resource]
     )
     field_without_reference = make_field(
         name="Test",
         message_resource=message_resource,
         resource_database=resource_database,
     )
     # `bar/{bar}` is the parent resource of `bar/{bar}/foo/{foo}`.
     FieldComparator(
         field_with_reference,
         field_without_reference,
         self.finding_container,
         context="ctx",
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "RESOURCE_REFERENCE_MOVED")
     self.assertEqual(finding.change_type.name, "MINOR")
Beispiel #13
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')
 def test_resource_reference_child_type_addition_non_breaking(self):
     # The added resource reference is in the database. Non-breaking change.
     # The original field is without resource reference.
     field_without_reference = make_field(name="Test")
     # Create a database with resource `example.v1/Foo` registered.
     resource = make_resource_descriptor(
         resource_type="example.v1/Foo", resource_patterns=["foo/{foo}"]
     )
     resource_child = make_resource_descriptor(
         resource_type="example.v1/Bar",
         resource_patterns=["foo/{foo}/bar/{bar}"],
     )
     resource_database = make_resource_database(resources=[resource, resource_child])
     # The update field has resource reference of child_type `example.v1/Bar`.
     field_options = desc.FieldOptions()
     field_options.Extensions[
         resource_pb2.resource_reference
     ].child_type = "example.v1/Bar"
     field_with_reference = make_field(
         name="Test", options=field_options, resource_database=resource_database
     )
     FieldComparator(
         field_without_reference,
         field_with_reference,
         self.finding_container,
         context="ctx",
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "RESOURCE_REFERENCE_ADDITION")
     self.assertEqual(finding.change_type.name, "MINOR")
 def test_message_type_change(self):
     field_message = make_field(type_name=".example.v1.Enum")
     field_message_update = make_field(type_name=".example.v1beta1.EnumUpdate")
     FieldComparator(
         field_message, field_message_update, self.finding_container, context="ctx"
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "FIELD_TYPE_CHANGE")
 def test_primitive_type_change(self):
     field_int = make_field(proto_type="TYPE_INT32")
     field_string = make_field(proto_type="TYPE_STRING")
     FieldComparator(
         field_int, field_string, self.finding_container, context="ctx"
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "FIELD_TYPE_CHANGE")
 def test_field_removal(self):
     field_foo = make_field("Foo")
     FieldComparator(
         field_foo, None, self.finding_container, context="ctx"
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "FIELD_REMOVAL")
     self.assertEqual(finding.location.proto_file_name, "foo")
 def test_repeated_label_change(self):
     field_repeated = make_field(repeated=True)
     field_non_repeated = make_field(repeated=False)
     FieldComparator(
         field_repeated, field_non_repeated, self.finding_container, context="ctx"
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "FIELD_REPEATED_CHANGE")
 def test_name_change(self):
     field_foo = make_field("Foo", nested_path=["foo"])
     field_bar = make_field("Bar", nested_path=["bar"])
     FieldComparator(
         field_foo, field_bar, self.finding_container, context="ctx"
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "FIELD_NAME_CHANGE")
     self.assertEqual(finding.extra_info[0], "foo")
Beispiel #20
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')
 def test_message_type_change_minor_version_update(self):
     field_message = make_field(type_name=".example.v1.Enum", api_version="v1")
     field_message_update = make_field(
         type_name=".example.v1beta1.Enum", api_version="v1beta1"
     )
     FieldComparator(
         field_message, field_message_update, self.finding_container, context="ctx"
     ).compare()
     findings = self.finding_container.get_all_findings()
     self.assertFalse(findings)
Beispiel #22
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')
Beispiel #23
0
 def typeChange(self):
     field_id_original = original_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields[1]
     field_id_update = update_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields[1]
     FieldComparator(field_id_original, field_id_update).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_oneof_change(self):
     # Field `single = 5` in `message_v1.proto` is moved out of One-of.
     FieldComparator(self.person_fields_v1[5],
                     self.person_fields_v1beta1[5]).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(
         finding.message,
         "The existing field single is moved out of One-of.",
     )
     self.assertEqual(finding.category.name, "FIELD_ONEOF_REMOVAL")
     self.assertEqual(finding.location.path,
                      "message_v1beta1.proto Line: 22")
 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")
Beispiel #27
0
 def nameChange(self):
     field_email_original = original_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields_by_name['email']
     field_email_update = update_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields_by_name['email_address']
     FieldComparator(field_email_original, field_email_update).compare()
     finding = FindingContainer.getAllFindings()[0]
     self.assertEqual(
         finding.message,
         'Name of the Field is changed, the original is email, but the updated is email_address'
     )
     self.assertEqual(finding.category.name, 'FIELD_NAME_CHANGE')
 def test_into_oneof(self):
     field_oneof = make_field(name="Foo", oneof_index=0, oneof_name="oneof_field")
     field_not_oneof = make_field(name="Foo")
     FieldComparator(
         field_not_oneof, field_oneof, self.finding_container, context="ctx"
     ).compare()
     finding = next(
         f
         for f in self.finding_container.get_all_findings()
         if f.category.name == "FIELD_ONEOF_MOVE_IN"
     )
     self.assertTrue(finding)
Beispiel #29
0
 def repeatedLabelChange(self):
     field_phones_original = original_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields_by_name['phones']
     field_phones_update = update_version.DESCRIPTOR.message_types_by_name[
         "Person"].fields_by_name['phones']
     FieldComparator(field_phones_original, field_phones_update).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')
 def test_proto3_required_to_optional(self):
     # Change required field to be proto3 optional. Non-breaking change.
     field_optional = make_field(
         name="Foo", oneof_index=0, oneof_name="oneof_field", proto3_optional=True
     )
     field_not_optional = make_field(
         name="Foo", oneof_index=0, oneof_name="oneof_field"
     )
     FieldComparator(
         field_not_optional, field_optional, self.finding_container, context="ctx"
     ).compare()
     finding = self.finding_container.get_all_findings()[0]
     self.assertEqual(finding.category.name, "FIELD_PROTO3_OPTIONAL_CHANGE")