def test_resources_addition(self): file_set_original = make_file_set( files=[make_file_pb2(name="foo.proto", package=".example.v1")]) options_update = make_file_options_resource_definition( resource_type=".example.v1.Bar", resource_patterns=["foo/{foo}/bar/{bar}"]) file_pb2 = make_file_pb2(name="foo.proto", package=".example.v1", options=options_update) file_set_update = make_file_set(files=[file_pb2]) FileSetComparator(file_set_original, file_set_update, self.finding_container).compare() finding = self.finding_container.getAllFindings()[0] self.assertEqual( finding.message, "A new resource definition `.example.v1.Bar` has been added.", ) self.assertEqual(finding.change_type.name, "MINOR") self.assertEqual( finding.category.name, "RESOURCE_DEFINITION_ADDITION", ) self.assertEqual( finding.location.proto_file_name, "foo.proto", )
def test_resources_existing_pattern_removal(self): options_original = make_file_options_resource_definition( resource_type=".example.v1.Bar", resource_patterns=["bar/{bar}", "foo/{foo}/bar"], ) file_pb2 = make_file_pb2(name="foo.proto", package=".example.v1", options=options_original) file_set_original = make_file_set(files=[file_pb2]) options_update = make_file_options_resource_definition( resource_type=".example.v1.Bar", resource_patterns=["bar/{bar}"]) file_pb2 = make_file_pb2(name="foo.proto", package=".example.v1", options=options_update) file_set_update = make_file_set(files=[file_pb2]) FileSetComparator(file_set_original, file_set_update, self.finding_container).compare() finding = self.finding_container.getAllFindings()[0] self.assertEqual( finding.message, "An existing pattern value of the resource definition `.example.v1.Bar` is removed.", ) self.assertEqual(finding.change_type.name, "MAJOR") self.assertEqual(finding.category.name, "RESOURCE_PATTERN_REMOVEL") self.assertEqual( finding.location.proto_file_name, "foo.proto", )
def test_resources_removal(self): # Create message with resource options. message_options = make_message_options_resource_definition( resource_type="example.v1/Bar", resource_patterns=["user/{user}", "user/{user}/bar/"], ) message = make_message("Test", options=message_options) # Original file set with one resource defined at message level. file_set_original = make_file_set(files=[ make_file_pb2( name="bar.proto", package=".example.v1", messages=[message]) ]) # Update file set without any resources. file_set_update = make_file_set( files=[make_file_pb2(name="foo.proto", package=".example.v1")]) FileSetComparator(file_set_original, file_set_update, self.finding_container).compare() findings_map = { f.message: f for f in self.finding_container.getAllFindings() } file_resource_removal = findings_map[ "An existing resource definition `example.v1/Bar` has been removed."] self.assertEqual( file_resource_removal.category.name, "RESOURCE_DEFINITION_REMOVAL", ) self.assertEqual( file_resource_removal.location.proto_file_name, "bar.proto", )
def test_packaging_options_version_update(self): file_options_original = descriptor_pb2.FileOptions() file_options_original.java_outer_classname = "ServiceProto" file_options_original.java_package = "com.google.cloud.service.v1" file_options_original.csharp_namespace = "Google.Cloud.Service.V1" file_options_original.php_namespace = "Google\\Cloud\\Service\\V1" file_options_original.ruby_package = "Google::Cloud::Service::V1" file_original = make_file_pb2( name="original.proto", package="google.cloud.service.v1", options=file_options_original, ) file_options_update = descriptor_pb2.FileOptions() file_options_update.java_outer_classname = "ServiceProto" file_options_update.java_package = "com.google.cloud.service.v1alpha" file_options_update.csharp_namespace = "Google.Cloud.Service.V1alpha" file_options_update.php_namespace = "Google\\Cloud\\Service\\V1alpha" file_options_update.ruby_package = "Google::Cloud::Service::V1alpha" file_update = make_file_pb2( name="update.proto", package="google.cloud.service.v1alpha", options=file_options_update, ) FileSetComparator( make_file_set(files=[file_original]), make_file_set(files=[file_update]), self.finding_container, ).compare() self.assertEqual(self.finding_container.get_all_findings(), [])
def test_java_outer_classname_removal(self): option1 = descriptor_pb2.FileOptions() option1.java_outer_classname = "Foo" file1 = make_file_pb2( name="fil1.proto", package="example.v1", options=option1, ) option2 = descriptor_pb2.FileOptions() option2.java_outer_classname = "Bar" file2 = make_file_pb2( name="fil2.proto", package="example.v1", options=option2, ) file_set_original = make_file_set(files=[file1, file2]) option3 = descriptor_pb2.FileOptions() option3.java_outer_classname = "Bar" file3 = make_file_pb2(name="file3.proto", package="example.v1beta", options=option3) file_set_update = make_file_set(files=[file3]) FileSetComparator(file_set_original, file_set_update, self.finding_container).compare() finding = self.finding_container.getAllFindings()[0] self.assertEqual(finding.category.name, "PACKAGING_OPTION_REMOVAL") self.assertEqual( finding.message, "An existing packaging option `Foo` for `java_outer_classname` is removed.", ) self.assertEqual(finding.change_type.name, "MAJOR")
def test_packaging_options_version_update(self): file_options_original = descriptor_pb2.FileOptions() file_options_original.java_outer_classname = "ServiceProto" file_options_original.java_package = "com.google.cloud.service.v1" file_options_original.csharp_namespace = "Google.Cloud.Service.V1" file_options_original.php_namespace = "Google\\Cloud\\Service\\V1" file_options_original.ruby_package = "Google::Cloud::Service::V1" file_original = make_file_pb2( name="original.proto", package="google.cloud.service.v1", options=file_options_original, ) file_options_update = descriptor_pb2.FileOptions() file_options_update.java_outer_classname = "ServiceProto" file_options_update.java_package = "com.google.cloud.service.v1alpha" file_options_update.csharp_namespace = "Google.Cloud.Service.V1alpha" file_options_update.php_namespace = "Google\\Cloud\\Service\\V1alpha" file_options_update.ruby_package = "Google::Cloud::Service::V1alpha" file_update = make_file_pb2( name="update.proto", package="google.cloud.service.v1alpha", options=file_options_update, ) FileSetComparator( make_file_set(files=[file_original]), make_file_set(files=[file_update]), self.finding_container, ).compare() findings_map = self.finding_container.getAllFindings() # No breaking changes since there are only minor version updates. self.assertFalse(findings_map)
def test_resources_existing_pattern_change(self): options_original = make_file_options_resource_definition( resource_type=".example.v1.Bar", resource_patterns=["foo/{foo}/bar/{bar}"]) file_pb2 = make_file_pb2(name="foo.proto", package=".example.v1", options=options_original) file_set_original = make_file_set(files=[file_pb2]) options_update = make_file_options_resource_definition( resource_type=".example.v1.Bar", resource_patterns=["foo/{foo}/bar/"]) file_pb2 = make_file_pb2(name="foo.proto", package=".example.v1", options=options_update) file_set_update = make_file_set(files=[file_pb2]) FileSetComparator(file_set_original, file_set_update, self.finding_container).compare() finding = next(f for f in self.finding_container.get_all_findings() if f.change_type.name == "MAJOR") self.assertEqual(finding.category.name, "RESOURCE_PATTERN_REMOVAL") self.assertEqual( finding.location.proto_file_name, "foo.proto", )
def test_enum_change(self): enum_original = make_enum( name="Irrelevant", values=( ("RED", 1), ("GREEN", 2), ("BLUE", 3), ), ) enum_update = make_enum( name="Irrelevant", values=( ("RED", 1), ("GREEN", 2), ), ) FileSetComparator( make_file_set(files=[make_file_pb2(enums=[enum_original])]), make_file_set(files=[make_file_pb2(enums=[enum_update])]), self.finding_container, ).compare() finding = self.finding_container.getAllFindings()[0] self.assertEqual(finding.message, "An existing EnumValue `BLUE` is removed.") self.assertEqual(finding.category.name, "ENUM_VALUE_REMOVAL") self.assertEqual(finding.change_type.name, "MAJOR") self.assertEqual(finding.location.proto_file_name, "my_proto.proto")
def test_service_change(self): input_message = make_message(name="request", full_name=".example.v1.request") output_message = make_message(name="response", full_name=".example.v1.response") service_original = make_service(methods=[ make_method( name="DoThing", input_message=input_message, output_message=output_message, ) ]) service_update = make_service() FileSetComparator( make_file_set(files=[ make_file_pb2( services=[service_original], messages=[input_message, output_message], ) ]), make_file_set(files=[make_file_pb2(services=[service_update])]), self.finding_container, ).compare() finding = self.finding_container.getAllFindings()[0] self.assertEqual(finding.message, "An existing rpc method `DoThing` is removed.") self.assertEqual(finding.category.name, "METHOD_REMOVAL") self.assertEqual(finding.change_type.name, "MAJOR") self.assertEqual(finding.location.proto_file_name, "my_proto.proto")
def test_detector_basic(self): # Mock original and updated FileDescriptorSet. L = desc.SourceCodeInfo.Location # fmt: off locations = [ L(path=(6, 0, 2, 0), span=(1, 2, 3, 4)), L( path=( 4, 0, ), span=(5, 6, 7, 8), ), L(path=(4, 1), span=(11, 12, 13, 14)), ] # fmt: on input_msg = make_message(name="input", full_name=".example.v1.input") output_msg = make_message(name="output", full_name=".example.v1.output") service_original = make_service(methods=(make_method( name="DoThing", input_message=input_msg, output_message=output_msg), )) service_update = make_service() file_set_original = desc.FileDescriptorSet(file=[ make_file_pb2( services=[service_original], locations=locations, messages=[input_msg, output_msg], ) ]) file_set_update = desc.FileDescriptorSet( file=[make_file_pb2(services=[service_update])]) with mock.patch("os.path.isdir") as mocked_isdir: with mock.patch("os.path.isfile") as mocked_isfile: mocked_isdir.return_value = True mocked_isfile.return_value = True opts = Options( original_api_definition_dirs="c,d", update_api_definition_dirs="a,b", original_proto_files="pf1, pf2", update_proto_files="pf3, pf4", original_descriptor_set_file_path=None, update_descriptor_set_file_path=None, human_readable_message=True, ) with mock.patch("sys.stdout", new=StringIO()) as fake_output: result = Detector(file_set_original, file_set_update, opts).detect_breaking_changes() self.assertEqual( fake_output.getvalue(), "my_proto.proto L2: An existing method `DoThing` is removed from service `Placeholder`.\n" + "my_proto.proto L6: An existing message `input` is removed.\n" + "my_proto.proto L12: An existing message `output` is removed.\n", ) self.assertEqual(len(result), 3)
def test_file_set_resources(self): options = descriptor_pb2.FileOptions() options.Extensions[resource_pb2.resource_definition].append( resource_pb2.ResourceDescriptor( type="example.v1/Foo", pattern=["foo/{foo}"], )) # File1 with file-level resource definition `example.v1/Foo`. file1 = make_file_pb2( name="foo.proto", package=".example.v1", dependency=["bar.proto"], options=options, ) # File2 with resource definition in message and nested message. message_options = descriptor_pb2.MessageOptions() resource = message_options.Extensions[resource_pb2.resource] resource.pattern.append("user/{user}") resource.pattern.append("user/{user}/bar/") resource.type = "example.v1/Bar" nested_message_options = descriptor_pb2.MessageOptions() resource = nested_message_options.Extensions[resource_pb2.resource] resource.pattern.append("tests/{test}/") resource.type = "example.v1/Test" nesed_message = make_message("nested_message", options=nested_message_options) message = make_message( name="outer_message", nested_messages=[nesed_message], options=message_options, ) file2 = make_file_pb2(name="bar.proto", package=".example.bar", messages=[message]) file_set = make_file_set(files=[file1, file2]) # All resources should be registered in the database. resource_types = file_set.resources_database.types resource_patterns = file_set.resources_database.patterns self.assertEqual( list(resource_types.keys()), ["example.v1/Foo", "example.v1/Bar", "example.v1/Test"], ) self.assertEqual( list(resource_patterns.keys()), ["foo/{foo}", "user/{user}", "user/{user}/bar/", "tests/{test}/"], ) # Check used resources database. # File1 depends on file2, but they are not in the same package, only file1 # is API definition file. So only resources in file1 are in used_resource_database. self.assertEqual(list(file_set.used_resources_database.types.keys()), ["example.v1/Foo"]) self.assertEqual( list(file_set.used_resources_database.patterns.keys()), ["foo/{foo}"])
def test_file_set_api_version(self): dep1 = make_file_pb2(name="dep1", package=".example.external") dep2 = make_file_pb2(name="dep2", package=".example.external") file1 = make_file_pb2(name="proto1", dependency=["dep1", "dep2"], package=".example.common") file2 = make_file_pb2(name="proto2", dependency=["proto1"], package=".example.v1beta") file_set = make_file_set(files=[file1, file2, dep1, dep2]) self.assertEqual(file_set.api_version, "v1beta")
def test_detector_without_opts(self): # Mock original and updated FileDescriptorSet. enum_foo = make_enum(name="foo") enum_bar = make_enum(name="bar") file_set_original = desc.FileDescriptorSet( file=[make_file_pb2(name="original.proto", enums=[enum_foo])]) file_set_update = desc.FileDescriptorSet( file=[make_file_pb2(name="update.proto", enums=[enum_bar])]) breaking_changes = Detector(file_set_original, file_set_update).detect_breaking_changes() # Without options, the detector returns an array of actionable Findings. self.assertEqual(breaking_changes[0].message, "An existing Enum `foo` is removed.")
def test_message_change_breaking(self): message_original = make_message( fields=(make_field(name="field_one", number=1), )) message_update = make_message( fields=(make_field(name="field_two", number=1), )) FileSetComparator( make_file_set(files=[make_file_pb2(messages=[message_original])]), make_file_set(files=[make_file_pb2(messages=[message_update])]), self.finding_container, ).compare() finding = self.finding_container.get_all_findings()[0] self.assertEqual(finding.change_type.name, "MAJOR") self.assertEqual(finding.category.name, "FIELD_NAME_CHANGE") self.assertEqual(finding.location.proto_file_name, "my_proto.proto")
def test_enum_in_dependency_change_breaking(self): # Enum "dep_message" is imported from dep.proto and referenced as a field type. field_type_original = make_enum( name="dep_enum", proto_file_name="dep.proto", ) message_original = make_message( fields=[make_field(type_name=".test.import.dep_enum")], ) # Message "test_enum" is defined in update.proto referenced as a field type. field_type_update = make_enum(name="test_enum", ) message_update = make_message( fields=[make_field(type_name="test_enum")]) FileSetComparator( make_file_set(files=[ make_file_pb2( name="orignal.proto", messages=[message_original], dependency="test/import/dep.proto", package="example.v1", ), make_file_pb2( name="dep.proto", enums=[field_type_original], package="test.import", ), ]), make_file_set(files=[ make_file_pb2( name="update.proto", messages=[message_update], enums=[field_type_update], package="example.v1beta1", ) ]), self.finding_container, ).compare() # The breaking change should be in field level, instead of message removal, # since the message is imported from dependency file. finding = self.finding_container.getAllFindings()[0] self.assertEqual( finding.message, "Type of an existing field `my_field` is changed from `.test.import.dep_enum` to `test_enum`.", ) self.assertEqual(finding.change_type.name, "MAJOR") self.assertEqual(finding.category.name, "FIELD_TYPE_CHANGE") self.assertEqual(finding.location.proto_file_name, "update.proto")
def test_packaging_options_change(self): file_options_original = descriptor_pb2.FileOptions() file_options_original.php_namespace = "Google\\Cloud\\Service\\V1" file_options_original.csharp_namespace = "Google.Cloud.Service.V1" file_options_original.java_outer_classname = "ServiceProto" file_original = make_file_pb2( name="original.proto", package="google.cloud.service.v1", options=file_options_original, ) file_options_update = descriptor_pb2.FileOptions() # Breaking since version should be updated to `v1alpha` file_options_update.php_namespace = "Google\\Cloud\\Service\\V1beta" # No breaking change file_options_update.csharp_namespace = "Google.Cloud.Service.V1alpha" file_options_update.java_outer_classname = "ServiceUpdateProto" file_update = make_file_pb2( name="update.proto", package="google.cloud.service.v1alpha", options=file_options_update, ) FileSetComparator( make_file_set(files=[file_original]), make_file_set(files=[file_update]), self.finding_container, ).compare() findings_map = { f.message: f for f in self.finding_container.getAllFindings() } java_classname_option_change = findings_map[ "An existing packaging option `ServiceProto` for `java_outer_classname` is removed."] self.assertEqual(java_classname_option_change.category.name, "PACKAGING_OPTION_REMOVAL") php_namespace_option_removal = findings_map[ "An existing packaging option `Google\\Cloud\\Service\\V1` for `php_namespace` is removed."] self.assertEqual(php_namespace_option_removal.category.name, "PACKAGING_OPTION_REMOVAL") php_namespace_option_addition = findings_map[ "A new packaging option `Google\\Cloud\\Service\\V1beta` for `php_namespace` is added."] self.assertEqual(php_namespace_option_addition.category.name, "PACKAGING_OPTION_ADDITION")
def test_service_removal(self): file_set = make_file_set( files=[make_file_pb2(services=[make_service()], )]) FileSetComparator( file_set, make_file_set(), self.finding_container, ).compare() finding = self.finding_container.get_all_findings()[0] self.assertEqual(finding.change_type.name, "MAJOR")
def test_service_addition(self): file_set = make_file_set( files=[make_file_pb2(services=[make_service()], )]) FileSetComparator( make_file_set(), file_set, self.finding_container, ).compare() finding = self.finding_container.get_all_findings()[0] self.assertEqual(finding.category.name, "SERVICE_ADDITION") self.assertEqual(finding.change_type.name, "MINOR")
def test_service_addition(self): file_set = make_file_set( files=[make_file_pb2(services=[make_service()], )]) FileSetComparator( make_file_set(), file_set, self.finding_container, ).compare() finding = self.finding_container.getAllFindings()[0] self.assertEqual(finding.message, "A new service `Placeholder` is added.") self.assertEqual(finding.change_type.name, "MINOR")
def test_service_removal(self): file_set = make_file_set( files=[make_file_pb2(services=[make_service()], )]) FileSetComparator( file_set, make_file_set(), self.finding_container, ).compare() finding = self.finding_container.getAllFindings()[0] self.assertEqual(finding.message, "An existing service `Placeholder` is removed.") self.assertEqual(finding.change_type.name, "MAJOR")
def test_packaging_options_change(self): file_options_original = descriptor_pb2.FileOptions() file_options_original.php_namespace = "Google\\Cloud\\Service\\V1" file_options_original.csharp_namespace = "Google.Cloud.Service.V1" file_options_original.java_outer_classname = "ServiceProto" file_original = make_file_pb2( name="original.proto", package="google.cloud.service.v1", options=file_options_original, ) file_options_update = descriptor_pb2.FileOptions() # Breaking since version should be updated to `v1alpha` file_options_update.php_namespace = "Google\\Cloud\\Service\\V1beta" # No breaking change file_options_update.csharp_namespace = "Google.Cloud.Service.V1alpha" file_options_update.java_outer_classname = "ServiceUpdateProto" file_update = make_file_pb2( name="update.proto", package="google.cloud.service.v1alpha", options=file_options_update, ) FileSetComparator( make_file_set(files=[file_original]), make_file_set(files=[file_update]), self.finding_container, ).compare() java_classname_option_removal = next( f for f in self.finding_container.get_all_findings() if f.category.name == "PACKAGING_OPTION_REMOVAL" and f.subject == "java_outer_classname") php_namespace_option_removal = next( f for f in self.finding_container.get_all_findings() if f.category.name == "PACKAGING_OPTION_REMOVAL" and f.subject == "php_namespace") self.assertTrue(java_classname_option_removal) self.assertTrue(php_namespace_option_removal)
def test_file_set_root_package(self): dep1 = make_file_pb2(name="dep1", package="example.external") dep2 = make_file_pb2(name="dep2", package="example.external") file1 = make_file_pb2(name="proto1", dependency=["dep1", "dep2"], package="example.common") file2 = make_file_pb2(name="proto2", dependency=["proto1"], package="example.tutorial") file3 = make_file_pb2(name="proto3", dependency=["proto2", "dep1"], package="example.tutorial") file_set = make_file_set(files=[file1, file2, file3, dep1, dep2]) self.assertEqual(file_set.root_package, "example.tutorial") # The package name `example.tutorial` does not have any match for a version. self.assertFalse(file_set.api_version) # The `proto2` and `proto3` are API definition files, others are dependencies. self.assertEqual([f.name for f in file_set.definition_files], ["proto2", "proto3"]) # If the files are totally not related, the root_package will be the first file package. file_set = make_file_set(files=[dep1, dep2]) self.assertEqual(file_set.root_package, "example.external") self.assertFalse(file_set.api_version)
def test_file_set_source_code_location(self): L = descriptor_pb2.SourceCodeInfo.Location locations = [ L(path=(4, 0, 2, 0, 5), span=(5, 2, 3, 4)), L(path=(4, 0, 3, 0), span=(6, 1, 2, 4)), L(path=(4, 0, 4, 0), span=(7, 1, 2, 4)), L(path=(4, 0, 4, 0, 2, 1), span=(9, 1, 2, 4)), ] messages = [ make_message( "OuterMessage", fields=(make_field( name="hidden_message", number=1, ), ), nested_messages=(make_message(name="InterMessage"), ), nested_enums=(make_enum( name="InterEnum", values=( ("RED", 1), ("GREEN", 2), ("BLUE", 3), ), ), ), ) ] file_pb2 = make_file_pb2(messages=messages, locations=locations) file_set = make_file_set(files=[file_pb2]) self.assertEqual( file_set.messages_map[".example.v1.OuterMessage"].fields[1]. proto_type.source_code_line, 6, ) self.assertEqual( file_set.messages_map[".example.v1.OuterMessage"]. nested_messages["InterMessage"].source_code_line, 7, ) self.assertEqual( file_set.messages_map[".example.v1.OuterMessage"]. nested_enums["InterEnum"].source_code_line, 8, ) self.assertEqual( file_set.messages_map[".example.v1.OuterMessage"]. nested_enums["InterEnum"].values[2].source_code_line, 10, )
def test_java_multiple_files_addition(self): option1 = descriptor_pb2.FileOptions() option1.java_multiple_files = False file1 = make_file_pb2( name="file.proto", package="example.v1", options=option1, ) file_set_original = make_file_set(files=[file1]) option2 = descriptor_pb2.FileOptions() option2.java_multiple_files = True file2 = make_file_pb2( name="file.proto", package="example.v1", options=option2, ) file_set_update = make_file_set(files=[file2]) FileSetComparator(file_set_original, file_set_update, self.finding_container).compare() self.assertTrue(len(self.finding_container.get_all_findings())) finding = self.finding_container.get_all_findings()[0] self.assertEqual(finding.category.name, "PACKAGING_OPTION_ADDITION") self.assertEqual(finding.change_type.name, "MAJOR")
def test_file_set_properties(self): input_message = make_message(name="request", full_name=".example.v1.request") output_message = make_message(name="response", full_name=".example.v1.response") messages = [ make_message( "InnerMessage", fields=(make_field(name="hidden_message", number=1, type_name=".example.v1.request"), ), full_name=".example.v1.InnerMessage", ), input_message, output_message, ] services = [ make_service( name="ThingDoer", methods=(make_method( name="DoThing", input_message=input_message, output_message=output_message, ), ), ) ] enums = [ make_enum( name="Irrelevant", values=( ("RED", 1), ("GREEN", 2), ("BLUE", 3), ), ) ] file_foo = make_file_pb2( name="foo.proto", package="example.v1", services=services, enums=enums, dependency=["bar.proto"], ) file_bar = make_file_pb2(name="bar.proto", package="example.v1", messages=messages) file_set = make_file_set(files=[file_bar, file_foo]) # Default to be empty. self.assertFalse(file_set.packaging_options_map) self.assertEqual( list(file_set.messages_map.keys()), [ ".example.v1.InnerMessage", ".example.v1.request", ".example.v1.response" ], ) self.assertEqual(list(file_set.enums_map.keys()), [".example.v1.Irrelevant"]) self.assertEqual(list(file_set.services_map.keys()), ["ThingDoer"]) self.assertEqual( file_set.messages_map[".example.v1.InnerMessage"].fields[1].name, "hidden_message", ) self.assertEqual( file_set.enums_map[".example.v1.Irrelevant"].values[2].name, "GREEN") self.assertEqual( list(file_set.services_map["ThingDoer"].methods.keys()), ["DoThing"], ) self.assertEqual(list(file_set.global_enums_map.keys()), [".example.v1.Irrelevant"]) self.assertEqual( list(file_set.global_messages_map.keys()), [ ".example.v1.response", ".example.v1.request", ".example.v1.InnerMessage" ], )
def test_file_set_packaging_options(self): option1 = descriptor_pb2.FileOptions() option1.java_package = "com.google.example.v1" option1.php_namespace = "Google\\Cloud\\Example\\V1" option1.php_metadata_namespace = "php_metadata_namespace" option1.php_class_prefix = "php_class_prefix" option1.ruby_package = "ruby_package" option1.java_outer_classname = "Foo" option1.go_package = "go_package" option1.csharp_namespace = "csharp_namespace" option1.swift_prefix = "swift_prefix" option2 = descriptor_pb2.FileOptions() option2.java_outer_classname = "Bar" # Two proto files have the same packging options. file1 = make_file_pb2(name="proto1", options=option1) file2 = make_file_pb2(name="proto2", options=option2) file_set = make_file_set(files=[file1, file2]) self.assertTrue(file_set.packaging_options_map) # fmt: off self.assertEqual( list(file_set.packaging_options_map["java_package"].keys()), ["com.google.example.v1"], ) self.assertEqual( file_set.packaging_options_map["java_package"] ["com.google.example.v1"].path, ( 8, 1, ), ) self.assertEqual( list(file_set.packaging_options_map["php_namespace"].keys()), ["Google\\Cloud\\Example\\V1"], ) self.assertEqual( file_set.packaging_options_map["php_namespace"] ["Google\\Cloud\\Example\\V1"].path, ( 8, 41, ), ) self.assertEqual( list( file_set.packaging_options_map["java_outer_classname"].keys()), ["Foo", "Bar"], ) self.assertEqual( file_set.packaging_options_map["java_outer_classname"]["Foo"].path, (8, 8), ) self.assertEqual( file_set.packaging_options_map["java_outer_classname"]["Bar"].path, (8, 8), ) self.assertEqual( list(file_set.packaging_options_map["php_metadata_namespace"].keys( )), ["php_metadata_namespace"], ) self.assertEqual( file_set.packaging_options_map["php_metadata_namespace"] ["php_metadata_namespace"].path, (8, 44), ) self.assertEqual( list(file_set.packaging_options_map["php_class_prefix"].keys()), ["php_class_prefix"], ) self.assertEqual( file_set.packaging_options_map["php_class_prefix"] ["php_class_prefix"].path, (8, 40), ) self.assertEqual( list(file_set.packaging_options_map["ruby_package"].keys()), ["ruby_package"], ) self.assertEqual( file_set.packaging_options_map["ruby_package"] ["ruby_package"].path, (8, 45), ) self.assertEqual( list(file_set.packaging_options_map["go_package"].keys()), ["go_package"], ) self.assertEqual( file_set.packaging_options_map["go_package"]["go_package"].path, (8, 11), ) self.assertEqual( list(file_set.packaging_options_map["csharp_namespace"].keys()), ["csharp_namespace"], ) self.assertEqual( file_set.packaging_options_map["csharp_namespace"] ["csharp_namespace"].path, (8, 37), ) self.assertEqual( list(file_set.packaging_options_map["swift_prefix"].keys()), ["swift_prefix"], ) self.assertEqual( file_set.packaging_options_map["swift_prefix"] ["swift_prefix"].path, (8, 39), )