def test_nested_enum_change(self): nested_enum1 = make_enum( name="Foo", values=( ("RED", 1), ("GREEN", 2), ), ) nested_enum2 = make_enum( name="Foo", values=( ("RED", 1), ("GREEN", 2), ("BLUE", 3), ), ) message1 = make_message(nested_enums=[nested_enum1]) message2 = make_message(nested_enums=[nested_enum2]) DescriptorComparator(message1, message2, self.finding_container).compare() findings_map = { f.message: f for f in self.finding_container.getAllFindings() } finding = findings_map["A new EnumValue `BLUE` is added."] self.assertEqual(finding.category.name, "ENUM_VALUE_ADDITION") self.assertEqual(finding.change_type.name, "MINOR") self.assertEqual(finding.location.proto_file_name, "foo")
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_nested_enum_change(self): nested_enum1 = make_enum( name="Foo", values=( ("RED", 1), ("GREEN", 2), ), ) nested_enum2 = make_enum( name="Foo", values=( ("RED", 1), ("GREEN", 2), ("BLUE", 3), ), ) message1 = make_message(nested_enums=[nested_enum1]) message2 = make_message(nested_enums=[nested_enum2]) DescriptorComparator(message1, message2, self.finding_container, context="ctx").compare() finding = next(f for f in self.finding_container.get_all_findings() if f.category.name == "ENUM_VALUE_ADDITION") self.assertEqual(finding.change_type.name, "MINOR") self.assertEqual(finding.location.proto_file_name, "foo")
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()
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_source_code_properties(self): L = descriptor_pb2.SourceCodeInfo.Location # fmt: off locations = [L(path=( 4, 0, ), span=(1, 2, 3, 4))] enum = make_enum( name="Foo", proto_file_name="test.proto", locations=locations, path=( 4, 0, ), ) # fmt: on self.assertEqual(enum.name, "Foo") self.assertEqual(enum.values, {}) self.assertEqual(enum.proto_file_name, "test.proto") self.assertEqual( enum.source_code_line, 2, ) self.assertEqual(enum.path, (4, 0))
def test_nested_types(self): # Create the inner message. inner_msg = [ make_message( "InnerMessage", fields=(make_field( name="hidden_message", number=1, ), ), ) ] inner_enums = [make_enum(name="InnerEnum")] inner_fields = [make_field("not_interesting")] # Create the outer message, which contains inner fields, enums, messages. outer_msg = make_message( "Outer", fields=inner_fields, nested_enums=inner_enums, nested_messages=inner_msg, ) self.assertEqual(len(outer_msg.fields.keys()), 1) self.assertEqual(outer_msg.fields[1].name, "not_interesting") self.assertEqual(len(outer_msg.nested_enums.keys()), 1) self.assertEqual( outer_msg.nested_messages["InnerMessage"].fields[1].name, "hidden_message")
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_basic_properties(self): enum = make_enum("Foo") self.assertEqual(enum.name, "Foo") self.assertEqual(enum.proto_file_name, "foo") self.assertEqual(enum.path, ()) self.assertEqual( enum.source_code_line, -1, ) self.assertEqual(enum.full_name, ".example.foo.enum")
def test_nested_enum_addition(self): DescriptorComparator( make_message(), make_message(nested_enums=[make_enum(name="nested_message")]), self.finding_container, ).compare() finding = self.finding_container.getAllFindings()[0] self.assertEqual(finding.category.name, "ENUM_ADDITION") self.assertEqual(finding.change_type.name, "MINOR") self.assertEqual(finding.location.proto_file_name, "foo")
def test_nested_enum_removal(self): DescriptorComparator( make_message(nested_enums=[make_enum(name="nested_message")]), make_message(), self.finding_container, context="ctx", ).compare() finding = self.finding_container.get_all_findings()[0] self.assertEqual(finding.category.name, "ENUM_REMOVAL") self.assertEqual(finding.change_type.name, "MAJOR") self.assertEqual(finding.location.proto_file_name, "foo")
def test_enum_value_properties(self): enum_type = make_enum( name="Irrelevant", values=( ("RED", 1), ("GREEN", 2), ("BLUE", 3), ), ) self.assertEqual(len(enum_type.values), 3) for ev, expected in zip(enum_type.values.values(), ("RED", "GREEN", "BLUE")): self.assertEqual(ev.name, expected)
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_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" ], )