Ejemplo n.º 1
0
 def test_resource_reference_change(self):
     _INVOKER_ORIGNAL = Loader(
         proto_definition_dirs=[self.PROTO_DIR, self.COMMON_PROTOS_DIR],
         proto_files=[os.path.join(self.PROTO_DIR, "resource_reference_v1.proto")],
         descriptor_set=None,
     )
     _INVOKER_UPDATE = Loader(
         proto_definition_dirs=[self.PROTO_DIR, self.COMMON_PROTOS_DIR],
         proto_files=[
             os.path.join(self.PROTO_DIR, "resource_reference_v1beta1.proto")
         ],
         descriptor_set=None,
     )
     FileSetComparator(
         FileSet(_INVOKER_ORIGNAL.get_descriptor_set()),
         FileSet(_INVOKER_UPDATE.get_descriptor_set()),
         self.finding_container,
     ).compare()
     finding = next(
         f
         for f in self.finding_container.get_all_findings()
         if f.category.name == "RESOURCE_REFERENCE_CHANGE_CHILD_TYPE"
     )
     self.assertEqual(
         finding.location.proto_file_name, "resource_reference_v1beta1.proto"
     )
     self.assertEqual(finding.location.source_code_line, 25)
     # Find more details in comments of `resource_reference_v1beta1.proto`
     # 1. Resource_reference annotation is removed for `string name=1`,
     # but it is added in message-level. Non-breaking change.
     # 2. File-level resource definition `t2` is removed, but is added
     # to message-level resource. Non-breaking change.
     breaking_changes = self.finding_container.get_actionable_findings()
     self.assertEqual(len(breaking_changes), 1)
 def setUp(self):
     # We take the enumDescriptorProto `phoneType` and `phoneType` from the original
     # and updated `_descriptor_set.pb` files for comparison.
     self.enum_original = (FileSet(
         self._PB_ORIGNAL).messages_map["Person"].nested_enums["PhoneType"])
     self.enum_update = (FileSet(
         self._PB_UPDATE).messages_map["Person"].nested_enums["PhoneType"])
Ejemplo n.º 3
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()
 def test_get_root_package(self):
     common = descriptor_pb2.FileDescriptorProto(
         name="google/cloud/common_resources.proto",
         package="google.cloud",
     )
     speech_resource = descriptor_pb2.FileDescriptorProto(
         name="google/cloud/speech/v1/resource.proto",
         package="google.cloud.speech.v1",
     )
     speech_with_dep = descriptor_pb2.FileDescriptorProto(
         name="google/cloud/speech/v1/cloud_speech.proto",
         package="google.cloud.speech.v1",
         dependency=["google/cloud/speech/v1/resource.proto"],
     )
     speech_no_dep = descriptor_pb2.FileDescriptorProto(
         name="google/cloud/speech/v1/cloud_speech.proto",
         package="google.cloud.speech.v1",
     )
     self.assertEqual(
         FileSet.get_root_package(
             descriptor_pb2.FileDescriptorSet(
                 file=[common, speech_with_dep, speech_resource])),
         "google.cloud.speech.v1",
     )
     self.assertEqual(
         FileSet.get_root_package(
             descriptor_pb2.FileDescriptorSet(
                 file=[common, speech_no_dep])),
         "google.cloud.speech.v1",
     )
     self.assertEqual(
         FileSet.get_root_package(
             descriptor_pb2.FileDescriptorSet(file=[common])),
         "",
     )
Ejemplo n.º 5
0
    def test_resources_change(self):
        _INVOKER_ORIGNAL = Loader(
            proto_definition_dirs=[self.PROTO_DIR, self.COMMON_PROTOS_DIR],
            proto_files=[os.path.join(self.PROTO_DIR, "resource_database_v1.proto")],
            descriptor_set=None,
        )
        _INVOKER_UPDATE = Loader(
            proto_definition_dirs=[self.PROTO_DIR, self.COMMON_PROTOS_DIR],
            proto_files=[
                os.path.join(self.PROTO_DIR, "resource_database_v1beta1.proto")
            ],
            descriptor_set=None,
        )
        FileSetComparator(
            FileSet(_INVOKER_ORIGNAL.get_descriptor_set()),
            FileSet(_INVOKER_UPDATE.get_descriptor_set()),
            self.finding_container,
        ).compare()

        addition_finding = next(
            f
            for f in self.finding_container.get_all_findings()
            if f.category.name == "RESOURCE_PATTERN_ADDITION"
        )
        removal_finding = next(
            f
            for f in self.finding_container.get_all_findings()
            if f.category.name == "RESOURCE_PATTERN_REMOVAL"
        )
        resource_definition_removal_finding = next(
            f
            for f in self.finding_container.get_all_findings()
            if f.category.name == "RESOURCE_DEFINITION_REMOVAL"
        )
        self.assertEqual(
            addition_finding.location.proto_file_name,
            "resource_database_v1.proto",
        )
        self.assertEqual(addition_finding.location.source_code_line, 13)

        self.assertEqual(
            removal_finding.location.proto_file_name,
            "resource_database_v1.proto",
        )
        self.assertEqual(
            removal_finding.location.source_code_line,
            13,
        )
        self.assertEqual(
            resource_definition_removal_finding.location.proto_file_name,
            "resource_database_v1.proto",
        )
        self.assertEqual(
            resource_definition_removal_finding.location.source_code_line, 34
        )
        self.assertEqual(resource_definition_removal_finding.change_type.value, 1)
Ejemplo n.º 6
0
 def setUp(self):
     # Get `Example` service from the original and updated `service_*.proto` files.
     self.service_original = FileSet(
         self._INVOKER_SERVICE_ORIGNAL.run()
     ).services_map["Example"]
     self.service_update = FileSet(self._INVOKER_SERVICE_UPDATE.run()).services_map[
         "Example"
     ]
     # Get `Example` service from the original and updated `service_annotation_*.proto` files.
     self.service_annotation_original = FileSet(
         self._INVOKER_ANNOTATION_ORIGNAL.run()
     ).services_map["Example"]
     self.service_annotation_update = FileSet(
         self._INVOKER_ANNOTATION_UPDATE.run()
     ).services_map["Example"]
Ejemplo n.º 7
0
 def test_enum_change(self):
     _INVOKER_ORIGNAL = UnittestInvoker(["enum_v1.proto"],
                                        "enum_v1_descriptor_set.pb")
     _INVOKER_UPDATE = UnittestInvoker(["enum_v1beta1.proto"],
                                       "enum_v1beta1_descriptor_set.pb")
     FileSetComparator(FileSet(_INVOKER_ORIGNAL.run()),
                       FileSet(_INVOKER_UPDATE.run())).compare()
     findings_map = {
         f.message: f
         for f in FindingContainer.getAllFindings()
     }
     finding = findings_map["An Enum BookType is removed"]
     self.assertEqual(finding.category.name, "ENUM_REMOVAL")
     self.assertEqual(finding.location.path, "enum_v1.proto Line: 5")
     _INVOKER_ORIGNAL.cleanup()
     _INVOKER_UPDATE.cleanup()
Ejemplo n.º 8
0
    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())
Ejemplo n.º 9
0
 def test_message_change(self):
     _INVOKER_ORIGNAL = UnittestInvoker(["message_v1.proto"],
                                        "message_v1_descriptor_set.pb")
     _INVOKER_UPDATE = UnittestInvoker(["message_v1beta1.proto"],
                                       "message_v1beta1_descriptor_set.pb")
     FileSetComparator(FileSet(_INVOKER_ORIGNAL.run()),
                       FileSet(_INVOKER_UPDATE.run())).compare()
     findings_map = {
         f.message: f
         for f in FindingContainer.getAllFindings()
     }
     self.assertEqual(
         findings_map[
             "Type of the field is changed, the original is TYPE_INT32, but the updated is TYPE_STRING"]
         .category.name,
         "FIELD_TYPE_CHANGE",
     )
     _INVOKER_ORIGNAL.cleanup()
     _INVOKER_UPDATE.cleanup()
Ejemplo n.º 10
0
 def test_resources_change(self):
     _INVOKER_ORIGNAL = UnittestInvoker(
         ["resource_database_v1.proto"],
         "resource_database_v1_descriptor_set.pb",
         True,
     )
     _INVOKER_UPDATE = UnittestInvoker(
         ["resource_database_v1beta1.proto"],
         "resource_database_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[
             "Pattern value of the resource definition 'example.googleapis.com/t2' is updated from 'foo/{foo}/bar/{bar}/t2' to 'foo/{foo}/bar/{bar}/t2_update'."]
         .category.name,
         "RESOURCE_DEFINITION_CHANGE",
     )
     self.assertEqual(
         findings_map[
             "A file-level resource definition 'example.googleapis.com/t3' has been added."]
         .category.name,
         "RESOURCE_DEFINITION_ADDITION",
     )
     self.assertEqual(
         findings_map[
             "The pattern of message-level resource definition has changed from ['foo/{foo}/bar/{bar}'] to ['foo/{foo}/bar']."]
         .category.name,
         "RESOURCE_DEFINITION_CHANGE",
     )
     self.assertEqual(
         findings_map[
             "A message-level resource definition example.googleapis.com/Test has been removed."]
         .category.name,
         "RESOURCE_DEFINITION_REMOVAL",
     )
     _INVOKER_ORIGNAL.cleanup()
     _INVOKER_UPDATE.cleanup()
Ejemplo n.º 11
0
    def test_service_change(self):
        _INVOKER_ORIGNAL = UnittestInvoker(["service_v1.proto"],
                                           "service_v1_descriptor_set.pb")
        _INVOKER_UPDATE = UnittestInvoker(["service_v1beta1.proto"],
                                          "service_v1beta1_descriptor_set.pb")
        FileSetComparator(FileSet(_INVOKER_ORIGNAL.run()),
                          FileSet(_INVOKER_UPDATE.run())).compare()
        findings_map = {
            f.message: f
            for f in FindingContainer.getAllFindings()
        }
        finding = findings_map[
            "The paginated response of method paginatedMethod is changed"]
        self.assertEqual(finding.category.name,
                         "METHOD_PAGINATED_RESPONSE_CHANGE")
        self.assertEqual(finding.location.path,
                         "service_v1beta1.proto Line: 11")

        _INVOKER_ORIGNAL.cleanup()
        _INVOKER_UPDATE.cleanup()
 def test_resource_reference_change(self):
     _INVOKER_ORIGNAL = Loader(
         proto_definition_dirs=[self.PROTO_DIR, self.COMMON_PROTOS_DIR],
         proto_files=[os.path.join(self.PROTO_DIR, "resource_reference_v1.proto")],
         descriptor_set=None,
     )
     _INVOKER_UPDATE = Loader(
         proto_definition_dirs=[self.PROTO_DIR, self.COMMON_PROTOS_DIR],
         proto_files=[
             os.path.join(self.PROTO_DIR, "resource_reference_v1beta1.proto")
         ],
         descriptor_set=None,
     )
     FileSetComparator(
         FileSet(_INVOKER_ORIGNAL.get_descriptor_set()),
         FileSet(_INVOKER_UPDATE.get_descriptor_set()),
         self.finding_container,
     ).compare()
     findings_map = {f.message: f for f in self.finding_container.getAllFindings()}
     # Type of the resource_reference is changed from type to child_type, but
     # they can not be resoved to the identical resource. Breaking change.
     finding = 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."
     ]
     self.assertEqual(finding.category.name, "RESOURCE_REFERENCE_CHANGE")
     self.assertEqual(
         finding.location.proto_file_name, "resource_reference_v1beta1.proto"
     )
     self.assertEqual(finding.location.source_code_line, 25)
     # Find more details in comments of `resource_reference_v1beta1.proto`
     # 1. Resource_reference annotation is removed for `string name=1`,
     # but it is added in message-level. Non-breaking change.
     # 2. File-level resource definition `t2` is removed, but is added
     # to message-level resource. Non-breaking change.
     breaking_changes = self.finding_container.getActionableFindings()
     self.assertEqual(len(breaking_changes), 1)
Ejemplo n.º 13
0
    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 self.opts and self.opts.output_json_path:
            # 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.to_dict_arr(), write_json_file, indent=2
                )

        if self.opts and self.opts.human_readable_message:
            sys.stdout.write(
                self.finding_container.to_human_readable_message(
                    line_numbers=self.opts.line_numbers
                )
            )

        return self.finding_container.get_actionable_findings()
Ejemplo n.º 14
0
class WrappersTest(unittest.TestCase):
    # This is for tesing the behavior of classes in src.comparator.wrapper.
    # UnittestInvoker helps us to execute the protoc command to compile the proto file,
    # get a *_descriptor_set.pb file (by -o option) which contains the serialized data in protos, and
    # create a FileDescriptorSet out of it.
    _CURRENT_DIR = os.getcwd()
    COMMON_PROTOS_DIR = os.path.join(_CURRENT_DIR, "api-common-protos")

    _INVOKER = Loader(
        proto_definition_dirs=[
            os.path.join(_CURRENT_DIR, "test/testdata/protos/example/"),
            COMMON_PROTOS_DIR,
        ],
        proto_files=[
            os.path.join(_CURRENT_DIR,
                         "test/testdata/protos/example/wrappers.proto")
        ],
        descriptor_set=None,
    )
    _FILE_SET = FileSet(_INVOKER.get_descriptor_set())

    def test_file_set_wrapper(self):
        self.assertTrue(self._FILE_SET.messages_map)
        self.assertTrue(self._FILE_SET.enums_map)
        self.assertTrue(self._FILE_SET.services_map)
        resources_database = self._FILE_SET.resources_database
        self.assertEqual(
            resources_database.types["example.googleapis.com/t1"].value.
            pattern,
            ["foo/{foo}", "foo/{foo}/bar/{bar}/t1"],
        )
        # The service from imported dependency `google/longrunning/operations`
        # is included in the file set.
        self.assertEqual(list(self._FILE_SET.services_map.keys()), ["Example"])
        self.assertEqual(self._FILE_SET.root_package, "example.v1alpha")
        self.assertEqual(len(self._FILE_SET.definition_files), 1)
        self.assertEqual(self._FILE_SET.definition_files[0].name,
                         "wrappers.proto")
        self.assertEqual(self._FILE_SET.api_version, "v1alpha")
        # Check the global messages/enums map
        self.assertEqual(
            [
                message_name
                for message_name in self._FILE_SET.global_messages_map.keys()
                if message_name.startswith(".example.v1alpha")
            ],
            [
                ".example.v1alpha.MapMessage",
                ".example.v1alpha.FooMetadata",
                ".example.v1alpha.FooResponse",
                ".example.v1alpha.FooRequest",
                ".example.v1alpha.FooRequest.NestedMessage",
            ],
        )
        self.assertEqual(
            [
                message_name
                for message_name in self._FILE_SET.global_messages_map.keys()
                if message_name.startswith(".google.api")
            ],
            [
                ".google.api.ResourceReference",
                ".google.api.ResourceDescriptor",
                ".google.api.CustomHttpPattern",
                ".google.api.HttpRule",
                ".google.api.Http",
            ],
        )
        self.assertEqual(
            [
                enum_name
                for enum_name in self._FILE_SET.global_enums_map.keys()
                if enum_name.startswith(".example.v1alpha")
            ],
            [
                ".example.v1alpha.Enum1",
                ".example.v1alpha.FooRequest.NestedEnum",
            ],
        )
        # Check the used messages map.
        used_messages_name = [
            ".example.v1alpha.FooRequest",
            ".example.v1alpha.FooResponse",
            ".google.longrunning.Operation",
            ".example.v1alpha.FooMetadata",
            ".example.v1alpha.MapMessage",
            ".google.rpc.Status",
        ]
        self.assertTrue(
            set(self._FILE_SET.messages_map.keys()) <= set(
                self._FILE_SET.global_messages_map.keys()))
        self.assertEqual(list(self._FILE_SET.messages_map.keys()),
                         used_messages_name)
        self.assertEqual(list(self._FILE_SET.enums_map.keys()),
                         [".example.v1alpha.Enum1"])

    def test_service_wrapper(self):
        service = self._FILE_SET.services_map["Example"]
        # Service `Example` is defined at Line20 in .proto file.
        self.assertEqual(service.source_code_line, 20)
        self.assertEqual(service.api_version, "v1alpha")
        self.assertEqual(service.proto_file_name, "wrappers.proto")
        self.assertEqual(len(service.oauth_scopes), 1)
        self.assertEqual(
            service.oauth_scopes[0].value,
            "https://www.googleapis.com/auth/cloud-platform",
        )
        self.assertEqual(service.oauth_scopes[0].source_code_line, 21)
        foo_method = service.methods["Foo"]
        bar_method = service.methods["Bar"]
        self.assertEqual(foo_method.input.value, ".example.v1alpha.FooRequest")
        self.assertEqual(foo_method.output.value,
                         ".example.v1alpha.FooResponse")
        self.assertEqual(foo_method.paged_result_field, None)
        self.assertEqual(foo_method.method_signatures.value,
                         ["content", "error"])
        self.assertEqual(foo_method.http_annotation.value["http_uri"],
                         "/v1/example:foo")

        # Method `Foo` is defined at Line23 in .proto file.
        self.assertEqual(foo_method.source_code_line, 23)
        self.assertEqual(foo_method.proto_file_name, "wrappers.proto")

        self.assertEqual(bar_method.input.value, ".example.v1alpha.FooRequest")
        self.assertEqual(bar_method.output.value,
                         ".google.longrunning.Operation")
        self.assertEqual(bar_method.paged_result_field, None)
        self.assertTrue(bar_method.longrunning)
        self.assertEqual(bar_method.lro_annotation.value["response_type"],
                         "FooResponse")
        self.assertEqual(bar_method.lro_annotation.value["metadata_type"],
                         "FooMetadata")
        self.assertEqual(bar_method.http_annotation.value["http_uri"],
                         "/v1/example:bar")
        # Method `Bar` is defined at Line31 in .proto file.
        self.assertEqual(bar_method.source_code_line, 31)

    def test_message_wrapper(self):
        messages_map = self._FILE_SET.messages_map
        foo_request_message = messages_map[".example.v1alpha.FooRequest"]
        # Message `FooRequest` is defined at Line43 in .proto file.
        self.assertEqual(foo_request_message.source_code_line, 43)
        self.assertEqual(foo_request_message.api_version, "v1alpha")
        self.assertTrue(foo_request_message.nested_messages["NestedMessage"])
        self.assertEqual(foo_request_message.proto_file_name, "wrappers.proto")
        self.assertEqual(list(foo_request_message.oneofs.keys()), ["response"])
        # Nested message `NestedMessage` is defined at Line52 in .proto file.
        self.assertEqual(
            foo_request_message.nested_messages["NestedMessage"].
            source_code_line, 52)
        self.assertTrue(foo_request_message.nested_enums["NestedEnum"])
        # Nested enum `NestedEnum` is defined at Line53 in .proto file.
        self.assertEqual(
            foo_request_message.nested_enums["NestedEnum"].source_code_line,
            53)
        resource = foo_request_message.resource
        self.assertEqual(resource.value.pattern, ["foo/{foo}/bar/{bar}"])
        self.assertEqual(resource.value.type, "example.googleapis.com/Foo")

        # The message has auto-generated map entries (nested type) for fields that is map type.
        map_message = messages_map[".example.v1alpha.MapMessage"]
        self.assertEqual(len(map_message.fields.keys()), 1)
        self.assertEqual(map_message.fields[1].name, "first_field")
        self.assertEqual(list(map_message.map_entries.keys()),
                         ["FirstFieldEntry"])
        self.assertTrue(
            isinstance(map_message.map_entries["FirstFieldEntry"]["key"],
                       Field))
        self.assertTrue(
            isinstance(map_message.map_entries["FirstFieldEntry"]["value"],
                       Field))
        # It should not put into the nested message, so the nested message map is empty.
        self.assertFalse(map_message.nested_messages)

    def test_field_wrapper(self):
        foo_request_message = self._FILE_SET.messages_map[
            ".example.v1alpha.FooRequest"]
        # Oneof field `content` and `error` are defined at Line49,50 in .proto file.
        content_field_oneof = foo_request_message.fields[1]
        error_field_oneof = foo_request_message.fields[2]
        self.assertEqual(content_field_oneof.oneof_name, "response")
        self.assertEqual(error_field_oneof.oneof_name, "response")
        self.assertFalse(content_field_oneof.map_entry_type)
        self.assertEqual(content_field_oneof.source_code_line, 49)
        self.assertEqual(error_field_oneof.source_code_line, 50)
        foo_response_message = self._FILE_SET.messages_map[
            ".example.v1alpha.FooResponse"]
        enum_field = foo_response_message.fields[1]
        self.assertEqual(enum_field.api_version, "v1alpha")
        self.assertFalse(enum_field.repeated.value)
        self.assertFalse(enum_field.required.value)
        self.assertEqual(enum_field.proto_type.value, "enum")
        self.assertEqual(enum_field.type_name.value, ".example.v1alpha.Enum1")
        self.assertEqual(enum_field.is_primitive_type, False)
        self.assertEqual(enum_field.oneof, False)
        self.assertEqual(enum_field.child_type, True)
        self.assertEqual(enum_field.resource_reference.value.child_type,
                         "example.googleapis.com/t1")
        # Enum `enum_field` is defined at Line58 in .proto file.
        self.assertEqual(enum_field.source_code_line, 59)
        self.assertEqual(enum_field.proto_file_name, "wrappers.proto")

        foo_metadata_message = self._FILE_SET.messages_map[
            ".example.v1alpha.FooMetadata"]
        # Field `name` has `google.api.field_behavior` option as `required`.
        name_field = foo_metadata_message.fields[1]
        self.assertEqual(name_field.name, "name")
        self.assertFalse(name_field.repeated.value)
        self.assertTrue(name_field.required.value)

        map_field = self._FILE_SET.messages_map[
            ".example.v1alpha.MapMessage"].fields[1]
        self.assertTrue(map_field.map_entry)
        self.assertTrue(map_field.is_map_type)
        self.assertEqual(map_field.map_entry_type["key"], "string")
        self.assertEqual(map_field.map_entry_type["value"],
                         ".example.v1alpha.FooMetadata")

    def test_enum_wrapper(self):
        enum = self._FILE_SET.enums_map[".example.v1alpha.Enum1"]
        self.assertEqual(enum.values[0].name, "a")
        self.assertEqual(enum.values[1].name, "b")
        # EnumValue `a` and `b` are defined at Line71 in .proto file.
        self.assertEqual(enum.values[0].source_code_line, 71)
        self.assertEqual(enum.values[1].source_code_line, 72)
        self.assertEqual(enum.values[0].proto_file_name, "wrappers.proto")
 def setUp(self):
     # Get `MOBILE` and `HOME` enumValueDescriptorProto from `message_v1_descriptor_set.pb`.
     enum_type_values = (FileSet(self._PB_ORIGNAL).messages_map["Person"].
                         nested_enums["PhoneType"].values)
     self.enumValue_mobile = enum_type_values[0]
     self.enumValue_home = enum_type_values[1]
 def setUp(self):
     self.person_fields_v1 = FileSet(
         self._PB_ORIGNAL).messages_map["Person"].fields
     self.person_fields_v1beta1 = (FileSet(
         self._PB_UPDATE).messages_map["Person"].fields)
 def test_resources_change(self):
     _INVOKER_ORIGNAL = Loader(
         proto_definition_dirs=[self.PROTO_DIR, self.COMMON_PROTOS_DIR],
         proto_files=[os.path.join(self.PROTO_DIR, "resource_database_v1.proto")],
         descriptor_set=None,
     )
     _INVOKER_UPDATE = Loader(
         proto_definition_dirs=[self.PROTO_DIR, self.COMMON_PROTOS_DIR],
         proto_files=[
             os.path.join(self.PROTO_DIR, "resource_database_v1beta1.proto")
         ],
         descriptor_set=None,
     )
     FileSetComparator(
         FileSet(_INVOKER_ORIGNAL.get_descriptor_set()),
         FileSet(_INVOKER_UPDATE.get_descriptor_set()),
         self.finding_container,
     ).compare()
     findings_map = {f.message: f for f in self.finding_container.getAllFindings()}
     # An existing pattern of a file-level resource definition is changed.
     file_resource_pattern_change = findings_map[
         "An existing pattern value of the resource definition `example.googleapis.com/t2` is updated from `foo/{foo}/bar/{bar}/t2` to `foo/{foo}/bar/{bar}/t2_update`."
     ]
     self.assertEqual(
         file_resource_pattern_change.category.name, "RESOURCE_PATTERN_CHANGE"
     )
     self.assertEqual(
         file_resource_pattern_change.location.proto_file_name,
         "resource_database_v1beta1.proto",
     )
     self.assertEqual(file_resource_pattern_change.location.source_code_line, 13)
     # An existing pattern of a message-level resource annotation is changed.
     message_resource_pattern_change = findings_map[
         "An existing pattern value of the resource definition `example.googleapis.com/Foo` is updated from `foo/{foo}/bar/{bar}` to `foo/{foo}/bar`."
     ]
     self.assertEqual(
         message_resource_pattern_change.category.name,
         "RESOURCE_PATTERN_CHANGE",
     )
     self.assertEqual(
         message_resource_pattern_change.location.proto_file_name,
         "resource_database_v1beta1.proto",
     )
     self.assertEqual(
         message_resource_pattern_change.location.source_code_line,
         26,
     )
     # An existing message-level resource annotation is removed, and it is not moved to
     # file-level resource definition. So it is a breaking change.
     message_resource_removal = findings_map[
         "An existing resource definition `example.googleapis.com/Test` has been removed."
     ]
     self.assertEqual(
         message_resource_removal.category.name,
         "RESOURCE_DEFINITION_REMOVAL",
     )
     self.assertEqual(
         message_resource_removal.location.proto_file_name,
         "resource_database_v1.proto",
     )
     self.assertEqual(message_resource_removal.location.source_code_line, 34)
     self.assertEqual(message_resource_removal.change_type.value, 1)
class WrappersTest(unittest.TestCase):
    # This is for tesing the behavior of classes in src.comparator.wrapper.
    # UnittestInvoker helps us to execute the protoc command to compile the proto file,
    # get a *_descriptor_set.pb file (by -o option) which contains the serialized data in protos, and
    # create a FileDescriptorSet out of it.

    _INVOKER = UnittestInvoker(["wrappers.proto"],
                               "wrappers_descriptor_set.pb", True)
    _FILE_SET = FileSet(_INVOKER.run())

    def test_file_set_wrapper(self):
        self.assertTrue(self._FILE_SET.messages_map)
        self.assertTrue(self._FILE_SET.enums_map)
        self.assertTrue(self._FILE_SET.services_map)
        resources_database = self._FILE_SET.resources_database
        self.assertEqual(
            resources_database.types["example.googleapis.com/t1"].pattern,
            ["foo/{foo}", "foo/{foo}/bar/{bar}/t1"],
        )

    def test_service_wrapper(self):
        service = self._FILE_SET.services_map["Example"]
        # Service `Example` is defined at Line19 in .proto file.
        self.assertEqual(service.source_code_line, 19)
        self.assertEqual(service.proto_file_name, "wrappers.proto")
        foo_method = service.methods["Foo"]
        bar_method = service.methods["Bar"]
        self.assertEqual(foo_method.input.value, "FooRequest")
        self.assertEqual(foo_method.output.value, "FooResponse")
        self.assertEqual(foo_method.paged_result_field, None)
        self.assertEqual(foo_method.method_signatures.value,
                         ["content", "error"])
        self.assertEqual(foo_method.http_annotation.value["http_uri"],
                         "/v1/example:foo")

        # Method `Foo` is defined at Line21 in .proto file.
        self.assertEqual(foo_method.source_code_line, 21)
        self.assertEqual(foo_method.proto_file_name, "wrappers.proto")

        self.assertEqual(bar_method.input.value, "FooRequest")
        self.assertEqual(bar_method.output.value,
                         ".google.longrunning.Operation")
        self.assertEqual(bar_method.paged_result_field, None)
        self.assertTrue(bar_method.longrunning)
        self.assertEqual(bar_method.lro_annotation.value["response_type"],
                         "FooResponse")
        self.assertEqual(bar_method.lro_annotation.value["metadata_type"],
                         "FooMetadata")
        self.assertEqual(bar_method.http_annotation.value["http_uri"],
                         "/v1/example:bar")
        # Method `Bar` is defined at Line29 in .proto file.
        self.assertEqual(bar_method.source_code_line, 29)

    def test_message_wrapper(self):
        messages_map = self._FILE_SET.messages_map
        foo_request_message = messages_map["FooRequest"]
        # Message `FooRequest` is defined at Line41 in .proto file.
        self.assertEqual(foo_request_message.source_code_line, 41)
        self.assertTrue(foo_request_message.nested_messages["NestedMessage"])
        self.assertEqual(foo_request_message.proto_file_name, "wrappers.proto")
        # Nested message `NestedMessage` is defined at Line50 in .proto file.
        self.assertEqual(
            foo_request_message.nested_messages["NestedMessage"].
            source_code_line, 50)
        self.assertTrue(foo_request_message.nested_enums["NestedEnum"])
        # Nested enum `NestedEnum` is defined at Line51 in .proto file.
        self.assertEqual(
            foo_request_message.nested_enums["NestedEnum"].source_code_line,
            51)
        self.assertEqual(foo_request_message.oneof_fields[0].name, "content")
        self.assertEqual(foo_request_message.oneof_fields[1].name, "error")
        # Oneof field `content` and `error` are defined at Line47,48 in .proto file.
        self.assertEqual(foo_request_message.oneof_fields[0].source_code_line,
                         47)
        self.assertEqual(foo_request_message.oneof_fields[1].source_code_line,
                         48)
        resource = foo_request_message.resource
        self.assertEqual(resource.pattern, ["foo/{foo}/bar/{bar}"])
        self.assertEqual(resource.type, "example.googleapis.com/Foo")

    def test_field_wrapper(self):
        foo_response_message = self._FILE_SET.messages_map["FooResponse"]
        enum_field = foo_response_message.fields[1]
        self.assertEqual(enum_field.label, "LABEL_OPTIONAL")
        self.assertEqual(enum_field.required, False)
        self.assertEqual(enum_field.proto_type, "TYPE_ENUM")
        self.assertEqual(enum_field.oneof, False)
        self.assertEqual(enum_field.child_type, True)
        self.assertEqual(enum_field.resource_reference.child_type,
                         "example.googleapis.com/t1")
        # Enum `enum_field` is defined at Line57 in .proto file.
        self.assertEqual(enum_field.source_code_line, 57)
        self.assertEqual(enum_field.proto_file_name, "wrappers.proto")

    def test_enum_wrapper(self):
        enum = self._FILE_SET.enums_map["Enum1"]
        self.assertEqual(enum.values[0].name, "a")
        self.assertEqual(enum.values[1].name, "b")
        # EnumValue `a` and `b` are defined at Line57 in .proto file.
        self.assertEqual(enum.values[0].source_code_line, 65)
        self.assertEqual(enum.values[1].source_code_line, 66)
        self.assertEqual(enum.values[0].proto_file_name, "wrappers.proto")

    @classmethod
    def tearDownClass(cls):
        cls._INVOKER.cleanup()