diff options
author | 2014-11-10 17:34:54 -0800 | |
---|---|---|
committer | 2014-11-10 17:34:54 -0800 | |
commit | 6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e (patch) | |
tree | d17c61ff9f3ae28224fbddac6d26bfc59e2cf755 /java/src/test | |
parent | baca1a8a1aa180c42de6278d3b8286c4496c6a10 (diff) |
Down-integrate from internal code base.
Diffstat (limited to 'java/src/test')
33 files changed, 2784 insertions, 52 deletions
diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java index 3f656e25..18d8142c 100644 --- a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java +++ b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java @@ -455,19 +455,19 @@ public class CodedInputStreamTest extends TestCase { } public void testMaliciousRecursion() throws Exception { - ByteString data64 = makeRecursiveMessage(64).toByteString(); - ByteString data65 = makeRecursiveMessage(65).toByteString(); + ByteString data100 = makeRecursiveMessage(100).toByteString(); + ByteString data101 = makeRecursiveMessage(101).toByteString(); - assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64); + assertMessageDepth(TestRecursiveMessage.parseFrom(data100), 100); try { - TestRecursiveMessage.parseFrom(data65); + TestRecursiveMessage.parseFrom(data101); fail("Should have thrown an exception!"); } catch (InvalidProtocolBufferException e) { // success. } - CodedInputStream input = data64.newCodedInput(); + CodedInputStream input = data100.newCodedInput(); input.setRecursionLimit(8); try { TestRecursiveMessage.parseFrom(input); diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java index 0092771b..05832a12 100644 --- a/java/src/test/java/com/google/protobuf/DescriptorsTest.java +++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -706,6 +706,12 @@ public class DescriptorsTest extends TestCase { assertTrue(TestMultipleExtensionRanges.getDescriptor().isExtensionNumber(4143)); } + public void testToString() { + assertEquals("protobuf_unittest.TestAllTypes.optional_uint64", + UnittestProto.TestAllTypes.getDescriptor().findFieldByNumber( + UnittestProto.TestAllTypes.OPTIONAL_UINT64_FIELD_NUMBER).toString()); + } + public void testPackedEnumField() throws Exception { FileDescriptorProto fileDescriptorProto = FileDescriptorProto.newBuilder() .setName("foo.proto") diff --git a/java/src/test/java/com/google/protobuf/FieldPresenceTest.java b/java/src/test/java/com/google/protobuf/FieldPresenceTest.java new file mode 100644 index 00000000..acf2b023 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/FieldPresenceTest.java @@ -0,0 +1,363 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.FieldPresenceTestProto.TestAllTypes; +import com.google.protobuf.FieldPresenceTestProto.TestOptionalFieldsOnly; +import com.google.protobuf.FieldPresenceTestProto.TestRepeatedFieldsOnly; +import protobuf_unittest.UnittestProto; + +import junit.framework.TestCase; + +/** + * Unit tests for protos that doesn't support field presence test for optional + * non-message fields. + */ +public class FieldPresenceTest extends TestCase { + private static boolean hasMethod(Class clazz, String name) { + try { + if (clazz.getMethod(name, new Class[]{}) != null) { + return true; + } else { + return false; + } + } catch (NoSuchMethodException e) { + return false; + } + } + + private static boolean isHasMethodRemoved( + Class classWithFieldPresence, + Class classWithoutFieldPresence, + String camelName) { + return hasMethod(classWithFieldPresence, "get" + camelName) + && hasMethod(classWithFieldPresence, "has" + camelName) + && hasMethod(classWithoutFieldPresence, "get" + camelName) + && !hasMethod(classWithoutFieldPresence, "has" + camelName); + } + + public void testHasMethod() { + // Optional non-message fields don't have a hasFoo() method generated. + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.class, + TestAllTypes.class, + "OptionalInt32")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.class, + TestAllTypes.class, + "OptionalString")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.class, + TestAllTypes.class, + "OptionalBytes")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.class, + TestAllTypes.class, + "OptionalNestedEnum")); + + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.Builder.class, + TestAllTypes.Builder.class, + "OptionalInt32")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.Builder.class, + TestAllTypes.Builder.class, + "OptionalString")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.Builder.class, + TestAllTypes.Builder.class, + "OptionalBytes")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.Builder.class, + TestAllTypes.Builder.class, + "OptionalNestedEnum")); + + // message fields still have the hasFoo() method generated. + assertFalse(TestAllTypes.newBuilder().build().hasOptionalNestedMessage()); + assertFalse(TestAllTypes.newBuilder().hasOptionalNestedMessage()); + + // oneof fields don't have hasFoo() methods (even for message types). + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.class, + TestAllTypes.class, + "OneofUint32")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.class, + TestAllTypes.class, + "OneofString")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.class, + TestAllTypes.class, + "OneofBytes")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.class, + TestAllTypes.class, + "OneofNestedMessage")); + + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.Builder.class, + TestAllTypes.Builder.class, + "OneofUint32")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.Builder.class, + TestAllTypes.Builder.class, + "OneofString")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.Builder.class, + TestAllTypes.Builder.class, + "OneofBytes")); + assertTrue(isHasMethodRemoved( + UnittestProto.TestAllTypes.Builder.class, + TestAllTypes.Builder.class, + "OneofNestedMessage")); + } + + public void testFieldPresence() { + // Optional non-message fields set to their default value are treated the + // same way as not set. + + // Serialization will ignore such fields. + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.setOptionalInt32(0); + builder.setOptionalString(""); + builder.setOptionalBytes(ByteString.EMPTY); + builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO); + TestAllTypes message = builder.build(); + assertEquals(0, message.getSerializedSize()); + + // mergeFrom() will ignore such fields. + TestAllTypes.Builder a = TestAllTypes.newBuilder(); + a.setOptionalInt32(1); + a.setOptionalString("x"); + a.setOptionalBytes(ByteString.copyFromUtf8("y")); + a.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR); + TestAllTypes.Builder b = TestAllTypes.newBuilder(); + b.setOptionalInt32(0); + b.setOptionalString(""); + b.setOptionalBytes(ByteString.EMPTY); + b.setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO); + a.mergeFrom(b.build()); + message = a.build(); + assertEquals(1, message.getOptionalInt32()); + assertEquals("x", message.getOptionalString()); + assertEquals(ByteString.copyFromUtf8("y"), message.getOptionalBytes()); + assertEquals(TestAllTypes.NestedEnum.BAR, message.getOptionalNestedEnum()); + + // equals()/hashCode() should produce the same results. + TestAllTypes empty = TestAllTypes.newBuilder().build(); + message = builder.build(); + assertTrue(empty.equals(message)); + assertTrue(message.equals(empty)); + assertEquals(empty.hashCode(), message.hashCode()); + } + + public void testFieldPresenceByReflection() { + Descriptor descriptor = TestAllTypes.getDescriptor(); + FieldDescriptor optionalInt32Field = descriptor.findFieldByName("optional_int32"); + FieldDescriptor optionalStringField = descriptor.findFieldByName("optional_string"); + FieldDescriptor optionalBytesField = descriptor.findFieldByName("optional_bytes"); + FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum"); + + // Field not present. + TestAllTypes message = TestAllTypes.newBuilder().build(); + assertFalse(message.hasField(optionalInt32Field)); + assertFalse(message.hasField(optionalStringField)); + assertFalse(message.hasField(optionalBytesField)); + assertFalse(message.hasField(optionalNestedEnumField)); + assertEquals(0, message.getAllFields().size()); + + // Field set to default value is seen as not present. + message = TestAllTypes.newBuilder() + .setOptionalInt32(0) + .setOptionalString("") + .setOptionalBytes(ByteString.EMPTY) + .setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO) + .build(); + assertFalse(message.hasField(optionalInt32Field)); + assertFalse(message.hasField(optionalStringField)); + assertFalse(message.hasField(optionalBytesField)); + assertFalse(message.hasField(optionalNestedEnumField)); + assertEquals(0, message.getAllFields().size()); + + // Field set to non-default value is seen as present. + message = TestAllTypes.newBuilder() + .setOptionalInt32(1) + .setOptionalString("x") + .setOptionalBytes(ByteString.copyFromUtf8("y")) + .setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR) + .build(); + assertTrue(message.hasField(optionalInt32Field)); + assertTrue(message.hasField(optionalStringField)); + assertTrue(message.hasField(optionalBytesField)); + assertTrue(message.hasField(optionalNestedEnumField)); + assertEquals(4, message.getAllFields().size()); + } + + public void testMessageField() { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + assertFalse(builder.hasOptionalNestedMessage()); + assertFalse(builder.build().hasOptionalNestedMessage()); + + TestAllTypes.NestedMessage.Builder nestedBuilder = + builder.getOptionalNestedMessageBuilder(); + assertTrue(builder.hasOptionalNestedMessage()); + assertTrue(builder.build().hasOptionalNestedMessage()); + + nestedBuilder.setValue(1); + assertEquals(1, builder.build().getOptionalNestedMessage().getValue()); + + builder.clearOptionalNestedMessage(); + assertFalse(builder.hasOptionalNestedMessage()); + assertFalse(builder.build().hasOptionalNestedMessage()); + + // Unlike non-message fields, if we set a message field to its default value (i.e., + // default instance), the field should be seen as present. + builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); + assertTrue(builder.hasOptionalNestedMessage()); + assertTrue(builder.build().hasOptionalNestedMessage()); + } + + public void testSerializeAndParse() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.setOptionalInt32(1234); + builder.setOptionalString("hello"); + builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); + // Set an oneof field to its default value and expect it to be serialized (i.e., + // an oneof field set to the default value should be treated as present). + builder.setOneofInt32(0); + ByteString data = builder.build().toByteString(); + + TestAllTypes message = TestAllTypes.parseFrom(data); + assertEquals(1234, message.getOptionalInt32()); + assertEquals("hello", message.getOptionalString()); + // Fields not set will have the default value. + assertEquals(ByteString.EMPTY, message.getOptionalBytes()); + assertEquals(TestAllTypes.NestedEnum.FOO, message.getOptionalNestedEnum()); + // The message field is set despite that it's set with a default instance. + assertTrue(message.hasOptionalNestedMessage()); + assertEquals(0, message.getOptionalNestedMessage().getValue()); + // The oneof field set to its default value is also present. + assertEquals( + TestAllTypes.OneofFieldCase.ONEOF_INT32, message.getOneofFieldCase()); + } + + // Regression test for b/16173397 + // Make sure we haven't screwed up the code generation for repeated fields. + public void testRepeatedFields() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.setOptionalInt32(1234); + builder.setOptionalString("hello"); + builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); + builder.addRepeatedInt32(4321); + builder.addRepeatedString("world"); + builder.addRepeatedNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); + ByteString data = builder.build().toByteString(); + + TestOptionalFieldsOnly optionalOnlyMessage = TestOptionalFieldsOnly.parseFrom(data); + assertEquals(1234, optionalOnlyMessage.getOptionalInt32()); + assertEquals("hello", optionalOnlyMessage.getOptionalString()); + assertTrue(optionalOnlyMessage.hasOptionalNestedMessage()); + assertEquals(0, optionalOnlyMessage.getOptionalNestedMessage().getValue()); + + TestRepeatedFieldsOnly repeatedOnlyMessage = TestRepeatedFieldsOnly.parseFrom(data); + assertEquals(1, repeatedOnlyMessage.getRepeatedInt32Count()); + assertEquals(4321, repeatedOnlyMessage.getRepeatedInt32(0)); + assertEquals(1, repeatedOnlyMessage.getRepeatedStringCount()); + assertEquals("world", repeatedOnlyMessage.getRepeatedString(0)); + assertEquals(1, repeatedOnlyMessage.getRepeatedNestedMessageCount()); + assertEquals(0, repeatedOnlyMessage.getRepeatedNestedMessage(0).getValue()); + } + + public void testIsInitialized() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + + // Test optional proto2 message fields. + UnittestProto.TestRequired.Builder proto2Builder = + builder.getOptionalProto2MessageBuilder(); + assertFalse(builder.isInitialized()); + assertFalse(builder.buildPartial().isInitialized()); + + proto2Builder.setA(1).setB(2).setC(3); + assertTrue(builder.isInitialized()); + assertTrue(builder.buildPartial().isInitialized()); + + // Test oneof proto2 message fields. + proto2Builder = builder.getOneofProto2MessageBuilder(); + assertFalse(builder.isInitialized()); + assertFalse(builder.buildPartial().isInitialized()); + + proto2Builder.setA(1).setB(2).setC(3); + assertTrue(builder.isInitialized()); + assertTrue(builder.buildPartial().isInitialized()); + + // Test repeated proto2 message fields. + proto2Builder = builder.addRepeatedProto2MessageBuilder(); + assertFalse(builder.isInitialized()); + assertFalse(builder.buildPartial().isInitialized()); + + proto2Builder.setA(1).setB(2).setC(3); + assertTrue(builder.isInitialized()); + assertTrue(builder.buildPartial().isInitialized()); + } + + + // Test that unknown fields are dropped. + public void testUnknownFields() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.setOptionalInt32(1234); + builder.addRepeatedInt32(5678); + TestAllTypes message = builder.build(); + ByteString data = message.toByteString(); + + TestOptionalFieldsOnly optionalOnlyMessage = + TestOptionalFieldsOnly.parseFrom(data); + // UnknownFieldSet should be empty. + assertEquals( + 0, optionalOnlyMessage.getUnknownFields().toByteString().size()); + assertEquals(1234, optionalOnlyMessage.getOptionalInt32()); + message = TestAllTypes.parseFrom(optionalOnlyMessage.toByteString()); + assertEquals(1234, message.getOptionalInt32()); + // The repeated field is discarded because it's unknown to the optional-only + // message. + assertEquals(0, message.getRepeatedInt32Count()); + + DynamicMessage dynamicOptionalOnlyMessage = + DynamicMessage.getDefaultInstance( + TestOptionalFieldsOnly.getDescriptor()) + .getParserForType().parseFrom(data); + assertEquals( + 0, dynamicOptionalOnlyMessage.getUnknownFields().toByteString().size()); + assertEquals(optionalOnlyMessage.toByteString(), + dynamicOptionalOnlyMessage.toByteString()); + } +} diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java index 0b3482c3..41ed7bd0 100644 --- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java +++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -157,15 +157,12 @@ public class GeneratedMessageTest extends TestCase { public void testProtosShareRepeatedArraysIfDidntChange() throws Exception { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); builder.addRepeatedInt32(100); - builder.addRepeatedImportEnum(UnittestImport.ImportEnum.IMPORT_BAR); builder.addRepeatedForeignMessage(ForeignMessage.getDefaultInstance()); TestAllTypes value1 = builder.build(); TestAllTypes value2 = value1.toBuilder().build(); assertSame(value1.getRepeatedInt32List(), value2.getRepeatedInt32List()); - assertSame(value1.getRepeatedImportEnumList(), - value2.getRepeatedImportEnumList()); assertSame(value1.getRepeatedForeignMessageList(), value2.getRepeatedForeignMessageList()); } @@ -1512,4 +1509,142 @@ public class GeneratedMessageTest extends TestCase { assertEquals(message2.getFooMessage().getQuxInt(), 234); } } + + public void testGetRepeatedFieldBuilder() { + Descriptor descriptor = TestAllTypes.getDescriptor(); + + FieldDescriptor fieldDescriptor = + descriptor.findFieldByName("repeated_nested_message"); + FieldDescriptor foreignFieldDescriptor = + descriptor.findFieldByName("repeated_foreign_message"); + FieldDescriptor importFieldDescriptor = + descriptor.findFieldByName("repeated_import_message"); + + // Mutate the message with new field builder + // Mutate nested message + TestAllTypes.Builder builder1 = TestAllTypes.newBuilder(); + Message.Builder fieldBuilder1 = builder1.newBuilderForField( + fieldDescriptor); + FieldDescriptor subFieldDescriptor1 = + fieldBuilder1.getDescriptorForType().findFieldByName("bb"); + fieldBuilder1.setField(subFieldDescriptor1, 1); + builder1.addRepeatedField(fieldDescriptor, fieldBuilder1.build()); + + // Mutate foreign message + Message.Builder foreignFieldBuilder1 = builder1.newBuilderForField( + foreignFieldDescriptor); + FieldDescriptor subForeignFieldDescriptor1 = + foreignFieldBuilder1.getDescriptorForType().findFieldByName("c"); + foreignFieldBuilder1.setField(subForeignFieldDescriptor1, 2); + builder1.addRepeatedField(foreignFieldDescriptor, + foreignFieldBuilder1.build()); + + // Mutate import message + Message.Builder importFieldBuilder1 = builder1.newBuilderForField( + importFieldDescriptor); + FieldDescriptor subImportFieldDescriptor1 = + importFieldBuilder1.getDescriptorForType().findFieldByName("d"); + importFieldBuilder1.setField(subImportFieldDescriptor1, 3); + builder1.addRepeatedField(importFieldDescriptor, + importFieldBuilder1.build()); + + Message newMessage1 = builder1.build(); + + // Mutate the message with existing field builder + // Mutate nested message + TestAllTypes.Builder builder2 = TestAllTypes.newBuilder(); + builder2.addRepeatedNestedMessageBuilder(); + Message.Builder fieldBuilder2 = builder2.getRepeatedFieldBuilder( + fieldDescriptor, 0); + FieldDescriptor subFieldDescriptor2 = + fieldBuilder2.getDescriptorForType().findFieldByName("bb"); + fieldBuilder2.setField(subFieldDescriptor2, 1); + + // Mutate foreign message + Message.Builder foreignFieldBuilder2 = builder2.newBuilderForField( + foreignFieldDescriptor); + FieldDescriptor subForeignFieldDescriptor2 = + foreignFieldBuilder2.getDescriptorForType().findFieldByName("c"); + foreignFieldBuilder2.setField(subForeignFieldDescriptor2, 2); + builder2.addRepeatedField(foreignFieldDescriptor, + foreignFieldBuilder2.build()); + + // Mutate import message + Message.Builder importFieldBuilder2 = builder2.newBuilderForField( + importFieldDescriptor); + FieldDescriptor subImportFieldDescriptor2 = + importFieldBuilder2.getDescriptorForType().findFieldByName("d"); + importFieldBuilder2.setField(subImportFieldDescriptor2, 3); + builder2.addRepeatedField(importFieldDescriptor, + importFieldBuilder2.build()); + + Message newMessage2 = builder2.build(); + + // These two messages should be equal. + assertEquals(newMessage1, newMessage2); + } + + public void testGetRepeatedFieldBuilderWithInitializedValue() { + Descriptor descriptor = TestAllTypes.getDescriptor(); + FieldDescriptor fieldDescriptor = + descriptor.findFieldByName("repeated_nested_message"); + + // Before setting field, builder is initialized by default value. + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.addRepeatedNestedMessageBuilder(); + NestedMessage.Builder fieldBuilder = + (NestedMessage.Builder) builder.getRepeatedFieldBuilder(fieldDescriptor, 0); + assertEquals(0, fieldBuilder.getBb()); + + // Setting field value with new field builder instance. + builder = TestAllTypes.newBuilder(); + NestedMessage.Builder newFieldBuilder = + builder.addRepeatedNestedMessageBuilder(); + newFieldBuilder.setBb(2); + // Then get the field builder instance by getRepeatedFieldBuilder(). + fieldBuilder = + (NestedMessage.Builder) builder.getRepeatedFieldBuilder(fieldDescriptor, 0); + // It should contain new value. + assertEquals(2, fieldBuilder.getBb()); + // These two builder should be equal. + assertSame(fieldBuilder, newFieldBuilder); + } + + public void testGetRepeatedFieldBuilderNotSupportedException() { + Descriptor descriptor = TestAllTypes.getDescriptor(); + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + try { + builder.getRepeatedFieldBuilder(descriptor.findFieldByName("repeated_int32"), 0); + fail("Exception was not thrown"); + } catch (UnsupportedOperationException e) { + // We expect this exception. + } + try { + builder.getRepeatedFieldBuilder( + descriptor.findFieldByName("repeated_nested_enum"), 0); + fail("Exception was not thrown"); + } catch (UnsupportedOperationException e) { + // We expect this exception. + } + try { + builder.getRepeatedFieldBuilder(descriptor.findFieldByName("optional_int32"), 0); + fail("Exception was not thrown"); + } catch (UnsupportedOperationException e) { + // We expect this exception. + } + try { + builder.getRepeatedFieldBuilder( + descriptor.findFieldByName("optional_nested_enum"), 0); + fail("Exception was not thrown"); + } catch (UnsupportedOperationException e) { + // We expect this exception. + } + try { + builder.getRepeatedFieldBuilder( + descriptor.findFieldByName("optional_nested_message"), 0); + fail("Exception was not thrown"); + } catch (UnsupportedOperationException e) { + // We expect this exception. + } + } } diff --git a/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java b/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java index 00e3a843..9de794fe 100644 --- a/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java +++ b/java/src/test/java/com/google/protobuf/LazyMessageLiteTest.java @@ -36,8 +36,6 @@ import protobuf_unittest.LazyFieldsLite.LazyNestedInnerMessageLite; import junit.framework.TestCase; -import org.easymock.classextension.EasyMock; - import java.util.ArrayList; /** @@ -52,14 +50,10 @@ public class LazyMessageLiteTest extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - - originalLazyInnerMessageLiteParser = LazyInnerMessageLite.PARSER; } @Override protected void tearDown() throws Exception { - LazyInnerMessageLite.PARSER = originalLazyInnerMessageLiteParser; - super.tearDown(); } @@ -291,29 +285,4 @@ public class LazyMessageLiteTest extends TestCase { assertEquals(bytes, deserialized.toByteString()); } - - public void testLaziness() throws InvalidProtocolBufferException { - LazyInnerMessageLite inner = LazyInnerMessageLite.newBuilder() - .setNum(2) - .build(); - LazyMessageLite outer = LazyMessageLite.newBuilder() - .setNum(1) - .setInner(inner) - .setOneofInner(inner) - .build(); - ByteString bytes = outer.toByteString(); - - - // The parser for inner / oneofInner message shouldn't be used if - // getInner / getOneofInner is not called. - LazyInnerMessageLite.PARSER = EasyMock.createStrictMock(Parser.class); - - EasyMock.replay(LazyInnerMessageLite.PARSER); - - LazyMessageLite deserialized = LazyMessageLite.parseFrom(bytes); - assertEquals(1, deserialized.getNum()); - assertEquals(421, deserialized.getNumWithDefault()); - - EasyMock.verify(LazyInnerMessageLite.PARSER); - } } diff --git a/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java b/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java index 94f4fcf5..035917c8 100644 --- a/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java +++ b/java/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java @@ -82,4 +82,27 @@ public class LiteEqualsAndHashTest extends TestCase { BarPrime barPrime = BarPrime.newBuilder().setName("bar").build(); assertFalse(bar.equals(barPrime)); } + + public void testEqualsAndHashCodeWithUnknownFields() throws InvalidProtocolBufferException { + Foo fooWithOnlyValue = Foo.newBuilder() + .setValue(1) + .build(); + + Foo fooWithValueAndExtension = fooWithOnlyValue.toBuilder() + .setValue(1) + .setExtension(Bar.fooExt, Bar.newBuilder() + .setName("name") + .build()) + .build(); + + Foo fooWithValueAndUnknownFields = Foo.parseFrom(fooWithValueAndExtension.toByteArray()); + + assertEqualsAndHashCodeAreFalse(fooWithOnlyValue, fooWithValueAndUnknownFields); + assertEqualsAndHashCodeAreFalse(fooWithValueAndExtension, fooWithValueAndUnknownFields); + } + + private void assertEqualsAndHashCodeAreFalse(Object o1, Object o2) { + assertFalse(o1.equals(o2)); + assertFalse(o1.hashCode() == o2.hashCode()); + } } diff --git a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java new file mode 100644 index 00000000..22dbeb76 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java @@ -0,0 +1,277 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import map_lite_test.MapForProto2TestProto.TestMap; +import map_lite_test.MapForProto2TestProto.TestMap.MessageValue; +import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue; + +import junit.framework.TestCase; + +/** + * Unit tests for map fields. + */ +public class MapForProto2LiteTest extends TestCase { + private void setMapValues(TestMap.Builder builder) { + builder.getMutableInt32ToInt32Field().put(1, 11); + builder.getMutableInt32ToInt32Field().put(2, 22); + builder.getMutableInt32ToInt32Field().put(3, 33); + + builder.getMutableInt32ToStringField().put(1, "11"); + builder.getMutableInt32ToStringField().put(2, "22"); + builder.getMutableInt32ToStringField().put(3, "33"); + + builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11")); + builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22")); + builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33")); + + builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR); + builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ); + + builder.getMutableInt32ToMessageField().put( + 1, MessageValue.newBuilder().setValue(11).build()); + builder.getMutableInt32ToMessageField().put( + 2, MessageValue.newBuilder().setValue(22).build()); + builder.getMutableInt32ToMessageField().put( + 3, MessageValue.newBuilder().setValue(33).build()); + + builder.getMutableStringToInt32Field().put("1", 11); + builder.getMutableStringToInt32Field().put("2", 22); + builder.getMutableStringToInt32Field().put("3", 33); + } + + private void assertMapValuesSet(TestMap message) { + assertEquals(3, message.getInt32ToInt32Field().size()); + assertEquals(11, message.getInt32ToInt32Field().get(1).intValue()); + assertEquals(22, message.getInt32ToInt32Field().get(2).intValue()); + assertEquals(33, message.getInt32ToInt32Field().get(3).intValue()); + + assertEquals(3, message.getInt32ToStringField().size()); + assertEquals("11", message.getInt32ToStringField().get(1)); + assertEquals("22", message.getInt32ToStringField().get(2)); + assertEquals("33", message.getInt32ToStringField().get(3)); + + assertEquals(3, message.getInt32ToBytesField().size()); + assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1)); + assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2)); + assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3)); + + assertEquals(3, message.getInt32ToEnumField().size()); + assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1)); + assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2)); + assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3)); + + assertEquals(3, message.getInt32ToMessageField().size()); + assertEquals(11, message.getInt32ToMessageField().get(1).getValue()); + assertEquals(22, message.getInt32ToMessageField().get(2).getValue()); + assertEquals(33, message.getInt32ToMessageField().get(3).getValue()); + + assertEquals(3, message.getStringToInt32Field().size()); + assertEquals(11, message.getStringToInt32Field().get("1").intValue()); + assertEquals(22, message.getStringToInt32Field().get("2").intValue()); + assertEquals(33, message.getStringToInt32Field().get("3").intValue()); + } + + private void updateMapValues(TestMap.Builder builder) { + builder.getMutableInt32ToInt32Field().put(1, 111); + builder.getMutableInt32ToInt32Field().remove(2); + builder.getMutableInt32ToInt32Field().put(4, 44); + + builder.getMutableInt32ToStringField().put(1, "111"); + builder.getMutableInt32ToStringField().remove(2); + builder.getMutableInt32ToStringField().put(4, "44"); + + builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111")); + builder.getMutableInt32ToBytesField().remove(2); + builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44")); + + builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR); + builder.getMutableInt32ToEnumField().remove(2); + builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX); + + builder.getMutableInt32ToMessageField().put( + 1, MessageValue.newBuilder().setValue(111).build()); + builder.getMutableInt32ToMessageField().remove(2); + builder.getMutableInt32ToMessageField().put( + 4, MessageValue.newBuilder().setValue(44).build()); + + builder.getMutableStringToInt32Field().put("1", 111); + builder.getMutableStringToInt32Field().remove("2"); + builder.getMutableStringToInt32Field().put("4", 44); + } + + private void assertMapValuesUpdated(TestMap message) { + assertEquals(3, message.getInt32ToInt32Field().size()); + assertEquals(111, message.getInt32ToInt32Field().get(1).intValue()); + assertEquals(33, message.getInt32ToInt32Field().get(3).intValue()); + assertEquals(44, message.getInt32ToInt32Field().get(4).intValue()); + + assertEquals(3, message.getInt32ToStringField().size()); + assertEquals("111", message.getInt32ToStringField().get(1)); + assertEquals("33", message.getInt32ToStringField().get(3)); + assertEquals("44", message.getInt32ToStringField().get(4)); + + assertEquals(3, message.getInt32ToBytesField().size()); + assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1)); + assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3)); + assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4)); + + assertEquals(3, message.getInt32ToEnumField().size()); + assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1)); + assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3)); + assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4)); + + assertEquals(3, message.getInt32ToMessageField().size()); + assertEquals(111, message.getInt32ToMessageField().get(1).getValue()); + assertEquals(33, message.getInt32ToMessageField().get(3).getValue()); + assertEquals(44, message.getInt32ToMessageField().get(4).getValue()); + + assertEquals(3, message.getStringToInt32Field().size()); + assertEquals(111, message.getStringToInt32Field().get("1").intValue()); + assertEquals(33, message.getStringToInt32Field().get("3").intValue()); + assertEquals(44, message.getStringToInt32Field().get("4").intValue()); + } + + private void assertMapValuesCleared(TestMap message) { + assertEquals(0, message.getInt32ToInt32Field().size()); + assertEquals(0, message.getInt32ToStringField().size()); + assertEquals(0, message.getInt32ToBytesField().size()); + assertEquals(0, message.getInt32ToEnumField().size()); + assertEquals(0, message.getInt32ToMessageField().size()); + assertEquals(0, message.getStringToInt32Field().size()); + } + + public void testGettersAndSetters() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + TestMap message = builder.build(); + assertMapValuesCleared(message); + + builder = message.toBuilder(); + setMapValues(builder); + message = builder.build(); + assertMapValuesSet(message); + + builder = message.toBuilder(); + updateMapValues(builder); + message = builder.build(); + assertMapValuesUpdated(message); + + builder = message.toBuilder(); + builder.clear(); + message = builder.build(); + assertMapValuesCleared(message); + } + + public void testSerializeAndParse() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesSet(message); + + builder = message.toBuilder(); + updateMapValues(builder); + message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesUpdated(message); + + builder = message.toBuilder(); + builder.clear(); + message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesCleared(message); + } + + public void testMergeFrom() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + TestMap.Builder other = TestMap.newBuilder(); + other.mergeFrom(message); + assertMapValuesSet(other.build()); + } + + public void testEqualsAndHashCode() throws Exception { + // Test that generated equals() and hashCode() will disregard the order + // of map entries when comparing/hashing map fields. + + // We can't control the order of elements in a HashMap. The best we can do + // here is to add elements in different order. + TestMap.Builder b1 = TestMap.newBuilder(); + b1.getMutableInt32ToInt32Field().put(1, 2); + b1.getMutableInt32ToInt32Field().put(3, 4); + b1.getMutableInt32ToInt32Field().put(5, 6); + TestMap m1 = b1.build(); + + TestMap.Builder b2 = TestMap.newBuilder(); + b2.getMutableInt32ToInt32Field().put(5, 6); + b2.getMutableInt32ToInt32Field().put(1, 2); + b2.getMutableInt32ToInt32Field().put(3, 4); + TestMap m2 = b2.build(); + + assertEquals(m1, m2); + assertEquals(m1.hashCode(), m2.hashCode()); + + // Make sure we did compare map fields. + b2.getMutableInt32ToInt32Field().put(1, 0); + m2 = b2.build(); + assertFalse(m1.equals(m2)); + // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed + // to be different. + } + + public void testUnknownEnumValues() throws Exception { + TestUnknownEnumValue.Builder builder = + TestUnknownEnumValue.newBuilder(); + builder.getMutableInt32ToInt32Field().put(1, 1); + builder.getMutableInt32ToInt32Field().put(2, 54321); + ByteString data = builder.build().toByteString(); + + TestMap message = TestMap.parseFrom(data); + // Entries with unknown enum values will be stored into UnknownFieldSet so + // there is only one entry in the map. + assertEquals(1, message.getInt32ToEnumField().size()); + assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1)); + // Serializing and parsing should preserve the unknown entry. + data = message.toByteString(); + TestUnknownEnumValue messageWithUnknownEnums = + TestUnknownEnumValue.parseFrom(data); + assertEquals(2, messageWithUnknownEnums.getInt32ToInt32Field().size()); + assertEquals(1, messageWithUnknownEnums.getInt32ToInt32Field().get(1).intValue()); + assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue()); + } + +} diff --git a/java/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/src/test/java/com/google/protobuf/MapForProto2Test.java new file mode 100644 index 00000000..d78c0d94 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/MapForProto2Test.java @@ -0,0 +1,488 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FieldDescriptor; +import map_test.MapForProto2TestProto.TestMap; +import map_test.MapForProto2TestProto.TestMap.MessageValue; +import map_test.MapForProto2TestProto.TestUnknownEnumValue; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Unit tests for map fields in proto2 protos. + */ +public class MapForProto2Test extends TestCase { + private void setMapValues(TestMap.Builder builder) { + builder.getMutableInt32ToInt32Field().put(1, 11); + builder.getMutableInt32ToInt32Field().put(2, 22); + builder.getMutableInt32ToInt32Field().put(3, 33); + + builder.getMutableInt32ToStringField().put(1, "11"); + builder.getMutableInt32ToStringField().put(2, "22"); + builder.getMutableInt32ToStringField().put(3, "33"); + + builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11")); + builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22")); + builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33")); + + builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR); + builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ); + + builder.getMutableInt32ToMessageField().put( + 1, MessageValue.newBuilder().setValue(11).build()); + builder.getMutableInt32ToMessageField().put( + 2, MessageValue.newBuilder().setValue(22).build()); + builder.getMutableInt32ToMessageField().put( + 3, MessageValue.newBuilder().setValue(33).build()); + + builder.getMutableStringToInt32Field().put("1", 11); + builder.getMutableStringToInt32Field().put("2", 22); + builder.getMutableStringToInt32Field().put("3", 33); + } + + private void assertMapValuesSet(TestMap message) { + assertEquals(3, message.getInt32ToInt32Field().size()); + assertEquals(11, message.getInt32ToInt32Field().get(1).intValue()); + assertEquals(22, message.getInt32ToInt32Field().get(2).intValue()); + assertEquals(33, message.getInt32ToInt32Field().get(3).intValue()); + + assertEquals(3, message.getInt32ToStringField().size()); + assertEquals("11", message.getInt32ToStringField().get(1)); + assertEquals("22", message.getInt32ToStringField().get(2)); + assertEquals("33", message.getInt32ToStringField().get(3)); + + assertEquals(3, message.getInt32ToBytesField().size()); + assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1)); + assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2)); + assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3)); + + assertEquals(3, message.getInt32ToEnumField().size()); + assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1)); + assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2)); + assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3)); + + assertEquals(3, message.getInt32ToMessageField().size()); + assertEquals(11, message.getInt32ToMessageField().get(1).getValue()); + assertEquals(22, message.getInt32ToMessageField().get(2).getValue()); + assertEquals(33, message.getInt32ToMessageField().get(3).getValue()); + + assertEquals(3, message.getStringToInt32Field().size()); + assertEquals(11, message.getStringToInt32Field().get("1").intValue()); + assertEquals(22, message.getStringToInt32Field().get("2").intValue()); + assertEquals(33, message.getStringToInt32Field().get("3").intValue()); + } + + private void updateMapValues(TestMap.Builder builder) { + builder.getMutableInt32ToInt32Field().put(1, 111); + builder.getMutableInt32ToInt32Field().remove(2); + builder.getMutableInt32ToInt32Field().put(4, 44); + + builder.getMutableInt32ToStringField().put(1, "111"); + builder.getMutableInt32ToStringField().remove(2); + builder.getMutableInt32ToStringField().put(4, "44"); + + builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111")); + builder.getMutableInt32ToBytesField().remove(2); + builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44")); + + builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR); + builder.getMutableInt32ToEnumField().remove(2); + builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX); + + builder.getMutableInt32ToMessageField().put( + 1, MessageValue.newBuilder().setValue(111).build()); + builder.getMutableInt32ToMessageField().remove(2); + builder.getMutableInt32ToMessageField().put( + 4, MessageValue.newBuilder().setValue(44).build()); + + builder.getMutableStringToInt32Field().put("1", 111); + builder.getMutableStringToInt32Field().remove("2"); + builder.getMutableStringToInt32Field().put("4", 44); + } + + private void assertMapValuesUpdated(TestMap message) { + assertEquals(3, message.getInt32ToInt32Field().size()); + assertEquals(111, message.getInt32ToInt32Field().get(1).intValue()); + assertEquals(33, message.getInt32ToInt32Field().get(3).intValue()); + assertEquals(44, message.getInt32ToInt32Field().get(4).intValue()); + + assertEquals(3, message.getInt32ToStringField().size()); + assertEquals("111", message.getInt32ToStringField().get(1)); + assertEquals("33", message.getInt32ToStringField().get(3)); + assertEquals("44", message.getInt32ToStringField().get(4)); + + assertEquals(3, message.getInt32ToBytesField().size()); + assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1)); + assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3)); + assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4)); + + assertEquals(3, message.getInt32ToEnumField().size()); + assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1)); + assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3)); + assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4)); + + assertEquals(3, message.getInt32ToMessageField().size()); + assertEquals(111, message.getInt32ToMessageField().get(1).getValue()); + assertEquals(33, message.getInt32ToMessageField().get(3).getValue()); + assertEquals(44, message.getInt32ToMessageField().get(4).getValue()); + + assertEquals(3, message.getStringToInt32Field().size()); + assertEquals(111, message.getStringToInt32Field().get("1").intValue()); + assertEquals(33, message.getStringToInt32Field().get("3").intValue()); + assertEquals(44, message.getStringToInt32Field().get("4").intValue()); + } + + private void assertMapValuesCleared(TestMap message) { + assertEquals(0, message.getInt32ToInt32Field().size()); + assertEquals(0, message.getInt32ToStringField().size()); + assertEquals(0, message.getInt32ToBytesField().size()); + assertEquals(0, message.getInt32ToEnumField().size()); + assertEquals(0, message.getInt32ToMessageField().size()); + assertEquals(0, message.getStringToInt32Field().size()); + } + + public void testGettersAndSetters() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + TestMap message = builder.build(); + assertMapValuesCleared(message); + + builder = message.toBuilder(); + setMapValues(builder); + message = builder.build(); + assertMapValuesSet(message); + + builder = message.toBuilder(); + updateMapValues(builder); + message = builder.build(); + assertMapValuesUpdated(message); + + builder = message.toBuilder(); + builder.clear(); + message = builder.build(); + assertMapValuesCleared(message); + } + + public void testSerializeAndParse() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesSet(message); + + builder = message.toBuilder(); + updateMapValues(builder); + message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesUpdated(message); + + builder = message.toBuilder(); + builder.clear(); + message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesCleared(message); + } + + public void testMergeFrom() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + TestMap.Builder other = TestMap.newBuilder(); + other.mergeFrom(message); + assertMapValuesSet(other.build()); + } + + public void testEqualsAndHashCode() throws Exception { + // Test that generated equals() and hashCode() will disregard the order + // of map entries when comparing/hashing map fields. + + // We can't control the order of elements in a HashMap. The best we can do + // here is to add elements in different order. + TestMap.Builder b1 = TestMap.newBuilder(); + b1.getMutableInt32ToInt32Field().put(1, 2); + b1.getMutableInt32ToInt32Field().put(3, 4); + b1.getMutableInt32ToInt32Field().put(5, 6); + TestMap m1 = b1.build(); + + TestMap.Builder b2 = TestMap.newBuilder(); + b2.getMutableInt32ToInt32Field().put(5, 6); + b2.getMutableInt32ToInt32Field().put(1, 2); + b2.getMutableInt32ToInt32Field().put(3, 4); + TestMap m2 = b2.build(); + + assertEquals(m1, m2); + assertEquals(m1.hashCode(), m2.hashCode()); + + // Make sure we did compare map fields. + b2.getMutableInt32ToInt32Field().put(1, 0); + m2 = b2.build(); + assertFalse(m1.equals(m2)); + // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed + // to be different. + } + + + // The following methods are used to test reflection API. + + private static FieldDescriptor f(String name) { + return TestMap.getDescriptor().findFieldByName(name); + } + + private static Object getFieldValue(Message mapEntry, String name) { + FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name); + return mapEntry.getField(field); + } + + private static Message.Builder setFieldValue( + Message.Builder mapEntry, String name, Object value) { + FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name); + mapEntry.setField(field, value); + return mapEntry; + } + + private static void assertHasMapValues(Message message, String name, Map<?, ?> values) { + FieldDescriptor field = f(name); + for (Object entry : (List<?>) message.getField(field)) { + Message mapEntry = (Message) entry; + Object key = getFieldValue(mapEntry, "key"); + Object value = getFieldValue(mapEntry, "value"); + assertTrue(values.containsKey(key)); + assertEquals(value, values.get(key)); + } + assertEquals(values.size(), message.getRepeatedFieldCount(field)); + for (int i = 0; i < message.getRepeatedFieldCount(field); i++) { + Message mapEntry = (Message) message.getRepeatedField(field, i); + Object key = getFieldValue(mapEntry, "key"); + Object value = getFieldValue(mapEntry, "value"); + assertTrue(values.containsKey(key)); + assertEquals(value, values.get(key)); + } + } + + private static <KeyType, ValueType> + Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) { + FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name); + Message.Builder entryBuilder = builder.newBuilderForField(field); + FieldDescriptor keyField = entryBuilder.getDescriptorForType().findFieldByName("key"); + FieldDescriptor valueField = entryBuilder.getDescriptorForType().findFieldByName("value"); + entryBuilder.setField(keyField, key); + entryBuilder.setField(valueField, value); + return entryBuilder.build(); + } + + private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) { + List<Message> entryList = new ArrayList<Message>(); + for (Map.Entry<?, ?> entry : values.entrySet()) { + entryList.add(newMapEntry(builder, name, entry.getKey(), entry.getValue())); + } + FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name); + builder.setField(field, entryList); + } + + private static <KeyType, ValueType> + Map<KeyType, ValueType> mapForValues( + KeyType key1, ValueType value1, KeyType key2, ValueType value2) { + Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } + + public void testReflectionApi() throws Exception { + // In reflection API, map fields are just repeated message fields. + TestMap.Builder builder = TestMap.newBuilder(); + builder.getMutableInt32ToInt32Field().put(1, 2); + builder.getMutableInt32ToInt32Field().put(3, 4); + builder.getMutableInt32ToMessageField().put( + 11, MessageValue.newBuilder().setValue(22).build()); + builder.getMutableInt32ToMessageField().put( + 33, MessageValue.newBuilder().setValue(44).build()); + TestMap message = builder.build(); + + // Test getField(), getRepeatedFieldCount(), getRepeatedField(). + assertHasMapValues(message, "int32_to_int32_field", + mapForValues(1, 2, 3, 4)); + assertHasMapValues(message, "int32_to_message_field", + mapForValues( + 11, MessageValue.newBuilder().setValue(22).build(), + 33, MessageValue.newBuilder().setValue(44).build())); + + // Test clearField() + builder.clearField(f("int32_to_int32_field")); + builder.clearField(f("int32_to_message_field")); + message = builder.build(); + assertEquals(0, message.getInt32ToInt32Field().size()); + assertEquals(0, message.getInt32ToMessageField().size()); + + // Test setField() + setMapValues(builder, "int32_to_int32_field", + mapForValues(11, 22, 33, 44)); + setMapValues(builder, "int32_to_message_field", + mapForValues( + 111, MessageValue.newBuilder().setValue(222).build(), + 333, MessageValue.newBuilder().setValue(444).build())); + message = builder.build(); + assertEquals(22, message.getInt32ToInt32Field().get(11).intValue()); + assertEquals(44, message.getInt32ToInt32Field().get(33).intValue()); + assertEquals(222, message.getInt32ToMessageField().get(111).getValue()); + assertEquals(444, message.getInt32ToMessageField().get(333).getValue()); + + // Test addRepeatedField + builder.addRepeatedField(f("int32_to_int32_field"), + newMapEntry(builder, "int32_to_int32_field", 55, 66)); + builder.addRepeatedField(f("int32_to_message_field"), + newMapEntry(builder, "int32_to_message_field", 555, + MessageValue.newBuilder().setValue(666).build())); + message = builder.build(); + assertEquals(66, message.getInt32ToInt32Field().get(55).intValue()); + assertEquals(666, message.getInt32ToMessageField().get(555).getValue()); + + // Test addRepeatedField (overriding existing values) + builder.addRepeatedField(f("int32_to_int32_field"), + newMapEntry(builder, "int32_to_int32_field", 55, 55)); + builder.addRepeatedField(f("int32_to_message_field"), + newMapEntry(builder, "int32_to_message_field", 555, + MessageValue.newBuilder().setValue(555).build())); + message = builder.build(); + assertEquals(55, message.getInt32ToInt32Field().get(55).intValue()); + assertEquals(555, message.getInt32ToMessageField().get(555).getValue()); + + // Test setRepeatedField + for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) { + Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i); + int oldKey = ((Integer) getFieldValue(mapEntry, "key")).intValue(); + int oldValue = ((Integer) getFieldValue(mapEntry, "value")).intValue(); + // Swap key with value for each entry. + Message.Builder mapEntryBuilder = mapEntry.toBuilder(); + setFieldValue(mapEntryBuilder, "key", oldValue); + setFieldValue(mapEntryBuilder, "value", oldKey); + builder.setRepeatedField(f("int32_to_int32_field"), i, mapEntryBuilder.build()); + } + message = builder.build(); + assertEquals(11, message.getInt32ToInt32Field().get(22).intValue()); + assertEquals(33, message.getInt32ToInt32Field().get(44).intValue()); + assertEquals(55, message.getInt32ToInt32Field().get(55).intValue()); + } + + public void testTextFormat() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + String textData = TextFormat.printToString(message); + + builder = TestMap.newBuilder(); + TextFormat.merge(textData, builder); + message = builder.build(); + + assertMapValuesSet(message); + } + + public void testDynamicMessage() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + Message dynamicDefaultInstance = + DynamicMessage.getDefaultInstance(TestMap.getDescriptor()); + Message dynamicMessage = dynamicDefaultInstance + .newBuilderForType().mergeFrom(message.toByteString()).build(); + + assertEquals(message, dynamicMessage); + assertEquals(message.hashCode(), dynamicMessage.hashCode()); + } + + public void testReflectionEqualsAndHashCode() throws Exception { + // Test that generated equals() and hashCode() will disregard the order + // of map entries when comparing/hashing map fields. + + // We use DynamicMessage to test reflection based equals()/hashCode(). + Message dynamicDefaultInstance = + DynamicMessage.getDefaultInstance(TestMap.getDescriptor()); + FieldDescriptor field = f("int32_to_int32_field"); + + Message.Builder b1 = dynamicDefaultInstance.newBuilderForType(); + b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2)); + b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4)); + b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6)); + Message m1 = b1.build(); + + Message.Builder b2 = dynamicDefaultInstance.newBuilderForType(); + b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6)); + b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2)); + b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4)); + Message m2 = b2.build(); + + assertEquals(m1, m2); + assertEquals(m1.hashCode(), m2.hashCode()); + + // Make sure we did compare map fields. + b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0)); + m2 = b2.build(); + assertFalse(m1.equals(m2)); + // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed + // to be different. + } + + public void testUnknownEnumValues() throws Exception { + TestUnknownEnumValue.Builder builder = + TestUnknownEnumValue.newBuilder(); + builder.getMutableInt32ToInt32Field().put(1, 1); + builder.getMutableInt32ToInt32Field().put(2, 54321); + ByteString data = builder.build().toByteString(); + + TestMap message = TestMap.parseFrom(data); + // Entries with unknown enum values will be stored into UnknownFieldSet so + // there is only one entry in the map. + assertEquals(1, message.getInt32ToEnumField().size()); + assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1)); + // UnknownFieldSet should not be empty. + assertFalse(message.getUnknownFields().asMap().isEmpty()); + // Serializing and parsing should preserve the unknown entry. + data = message.toByteString(); + TestUnknownEnumValue messageWithUnknownEnums = + TestUnknownEnumValue.parseFrom(data); + assertEquals(2, messageWithUnknownEnums.getInt32ToInt32Field().size()); + assertEquals(1, messageWithUnknownEnums.getInt32ToInt32Field().get(1).intValue()); + assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue()); + } + +} diff --git a/java/src/test/java/com/google/protobuf/MapTest.java b/java/src/test/java/com/google/protobuf/MapTest.java new file mode 100644 index 00000000..542a20e7 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/MapTest.java @@ -0,0 +1,569 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import map_test.MapTestProto.TestMap; +import map_test.MapTestProto.TestMap.MessageValue; +import map_test.MapTestProto.TestOnChangeEventPropagation; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Unit tests for map fields. + */ +public class MapTest extends TestCase { + private void setMapValues(TestMap.Builder builder) { + builder.getMutableInt32ToInt32Field().put(1, 11); + builder.getMutableInt32ToInt32Field().put(2, 22); + builder.getMutableInt32ToInt32Field().put(3, 33); + + builder.getMutableInt32ToStringField().put(1, "11"); + builder.getMutableInt32ToStringField().put(2, "22"); + builder.getMutableInt32ToStringField().put(3, "33"); + + builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11")); + builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22")); + builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33")); + + builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO); + builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR); + builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ); + + builder.getMutableInt32ToMessageField().put( + 1, MessageValue.newBuilder().setValue(11).build()); + builder.getMutableInt32ToMessageField().put( + 2, MessageValue.newBuilder().setValue(22).build()); + builder.getMutableInt32ToMessageField().put( + 3, MessageValue.newBuilder().setValue(33).build()); + + builder.getMutableStringToInt32Field().put("1", 11); + builder.getMutableStringToInt32Field().put("2", 22); + builder.getMutableStringToInt32Field().put("3", 33); + } + + private void assertMapValuesSet(TestMap message) { + assertEquals(3, message.getInt32ToInt32Field().size()); + assertEquals(11, message.getInt32ToInt32Field().get(1).intValue()); + assertEquals(22, message.getInt32ToInt32Field().get(2).intValue()); + assertEquals(33, message.getInt32ToInt32Field().get(3).intValue()); + + assertEquals(3, message.getInt32ToStringField().size()); + assertEquals("11", message.getInt32ToStringField().get(1)); + assertEquals("22", message.getInt32ToStringField().get(2)); + assertEquals("33", message.getInt32ToStringField().get(3)); + + assertEquals(3, message.getInt32ToBytesField().size()); + assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1)); + assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2)); + assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3)); + + assertEquals(3, message.getInt32ToEnumField().size()); + assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1)); + assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2)); + assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3)); + + assertEquals(3, message.getInt32ToMessageField().size()); + assertEquals(11, message.getInt32ToMessageField().get(1).getValue()); + assertEquals(22, message.getInt32ToMessageField().get(2).getValue()); + assertEquals(33, message.getInt32ToMessageField().get(3).getValue()); + + assertEquals(3, message.getStringToInt32Field().size()); + assertEquals(11, message.getStringToInt32Field().get("1").intValue()); + assertEquals(22, message.getStringToInt32Field().get("2").intValue()); + assertEquals(33, message.getStringToInt32Field().get("3").intValue()); + } + + private void updateMapValues(TestMap.Builder builder) { + builder.getMutableInt32ToInt32Field().put(1, 111); + builder.getMutableInt32ToInt32Field().remove(2); + builder.getMutableInt32ToInt32Field().put(4, 44); + + builder.getMutableInt32ToStringField().put(1, "111"); + builder.getMutableInt32ToStringField().remove(2); + builder.getMutableInt32ToStringField().put(4, "44"); + + builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111")); + builder.getMutableInt32ToBytesField().remove(2); + builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44")); + + builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR); + builder.getMutableInt32ToEnumField().remove(2); + builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX); + + builder.getMutableInt32ToMessageField().put( + 1, MessageValue.newBuilder().setValue(111).build()); + builder.getMutableInt32ToMessageField().remove(2); + builder.getMutableInt32ToMessageField().put( + 4, MessageValue.newBuilder().setValue(44).build()); + + builder.getMutableStringToInt32Field().put("1", 111); + builder.getMutableStringToInt32Field().remove("2"); + builder.getMutableStringToInt32Field().put("4", 44); + } + + private void assertMapValuesUpdated(TestMap message) { + assertEquals(3, message.getInt32ToInt32Field().size()); + assertEquals(111, message.getInt32ToInt32Field().get(1).intValue()); + assertEquals(33, message.getInt32ToInt32Field().get(3).intValue()); + assertEquals(44, message.getInt32ToInt32Field().get(4).intValue()); + + assertEquals(3, message.getInt32ToStringField().size()); + assertEquals("111", message.getInt32ToStringField().get(1)); + assertEquals("33", message.getInt32ToStringField().get(3)); + assertEquals("44", message.getInt32ToStringField().get(4)); + + assertEquals(3, message.getInt32ToBytesField().size()); + assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1)); + assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3)); + assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4)); + + assertEquals(3, message.getInt32ToEnumField().size()); + assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1)); + assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3)); + assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4)); + + assertEquals(3, message.getInt32ToMessageField().size()); + assertEquals(111, message.getInt32ToMessageField().get(1).getValue()); + assertEquals(33, message.getInt32ToMessageField().get(3).getValue()); + assertEquals(44, message.getInt32ToMessageField().get(4).getValue()); + + assertEquals(3, message.getStringToInt32Field().size()); + assertEquals(111, message.getStringToInt32Field().get("1").intValue()); + assertEquals(33, message.getStringToInt32Field().get("3").intValue()); + assertEquals(44, message.getStringToInt32Field().get("4").intValue()); + } + + private void assertMapValuesCleared(TestMap message) { + assertEquals(0, message.getInt32ToInt32Field().size()); + assertEquals(0, message.getInt32ToStringField().size()); + assertEquals(0, message.getInt32ToBytesField().size()); + assertEquals(0, message.getInt32ToEnumField().size()); + assertEquals(0, message.getInt32ToMessageField().size()); + assertEquals(0, message.getStringToInt32Field().size()); + } + + public void testGettersAndSetters() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + TestMap message = builder.build(); + assertMapValuesCleared(message); + + builder = message.toBuilder(); + setMapValues(builder); + message = builder.build(); + assertMapValuesSet(message); + + builder = message.toBuilder(); + updateMapValues(builder); + message = builder.build(); + assertMapValuesUpdated(message); + + builder = message.toBuilder(); + builder.clear(); + message = builder.build(); + assertMapValuesCleared(message); + } + + public void testSerializeAndParse() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesSet(message); + + builder = message.toBuilder(); + updateMapValues(builder); + message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesUpdated(message); + + builder = message.toBuilder(); + builder.clear(); + message = builder.build(); + assertEquals(message.getSerializedSize(), message.toByteString().size()); + message = TestMap.PARSER.parseFrom(message.toByteString()); + assertMapValuesCleared(message); + } + + public void testMergeFrom() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + TestMap.Builder other = TestMap.newBuilder(); + other.mergeFrom(message); + assertMapValuesSet(other.build()); + } + + public void testEqualsAndHashCode() throws Exception { + // Test that generated equals() and hashCode() will disregard the order + // of map entries when comparing/hashing map fields. + + // We can't control the order of elements in a HashMap. The best we can do + // here is to add elements in different order. + TestMap.Builder b1 = TestMap.newBuilder(); + b1.getMutableInt32ToInt32Field().put(1, 2); + b1.getMutableInt32ToInt32Field().put(3, 4); + b1.getMutableInt32ToInt32Field().put(5, 6); + TestMap m1 = b1.build(); + + TestMap.Builder b2 = TestMap.newBuilder(); + b2.getMutableInt32ToInt32Field().put(5, 6); + b2.getMutableInt32ToInt32Field().put(1, 2); + b2.getMutableInt32ToInt32Field().put(3, 4); + TestMap m2 = b2.build(); + + assertEquals(m1, m2); + assertEquals(m1.hashCode(), m2.hashCode()); + + // Make sure we did compare map fields. + b2.getMutableInt32ToInt32Field().put(1, 0); + m2 = b2.build(); + assertFalse(m1.equals(m2)); + // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed + // to be different. + } + + + public void testNestedBuilderOnChangeEventPropagation() { + TestOnChangeEventPropagation.Builder parent = + TestOnChangeEventPropagation.newBuilder(); + parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 2); + TestOnChangeEventPropagation message = parent.build(); + assertEquals(2, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue()); + + // Make a change using nested builder. + parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 3); + + // Should be able to observe the change. + message = parent.build(); + assertEquals(3, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue()); + + // Make another change using mergeFrom() + TestMap.Builder other = TestMap.newBuilder(); + other.getMutableInt32ToInt32Field().put(1, 4); + parent.getOptionalMessageBuilder().mergeFrom(other.build()); + + // Should be able to observe the change. + message = parent.build(); + assertEquals(4, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue()); + + // Make yet another change by clearing the nested builder. + parent.getOptionalMessageBuilder().clear(); + + // Should be able to observe the change. + message = parent.build(); + assertEquals(0, message.getOptionalMessage().getInt32ToInt32Field().size()); + } + + // The following methods are used to test reflection API. + + private static FieldDescriptor f(String name) { + return TestMap.getDescriptor().findFieldByName(name); + } + + private static Object getFieldValue(Message mapEntry, String name) { + FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name); + return mapEntry.getField(field); + } + + private static Message.Builder setFieldValue( + Message.Builder mapEntry, String name, Object value) { + FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name); + mapEntry.setField(field, value); + return mapEntry; + } + + private static void assertHasMapValues(Message message, String name, Map<?, ?> values) { + FieldDescriptor field = f(name); + for (Object entry : (List<?>) message.getField(field)) { + Message mapEntry = (Message) entry; + Object key = getFieldValue(mapEntry, "key"); + Object value = getFieldValue(mapEntry, "value"); + assertTrue(values.containsKey(key)); + assertEquals(value, values.get(key)); + } + assertEquals(values.size(), message.getRepeatedFieldCount(field)); + for (int i = 0; i < message.getRepeatedFieldCount(field); i++) { + Message mapEntry = (Message) message.getRepeatedField(field, i); + Object key = getFieldValue(mapEntry, "key"); + Object value = getFieldValue(mapEntry, "value"); + assertTrue(values.containsKey(key)); + assertEquals(value, values.get(key)); + } + } + + private static <KeyType, ValueType> + Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) { + FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name); + Message.Builder entryBuilder = builder.newBuilderForField(field); + FieldDescriptor keyField = entryBuilder.getDescriptorForType().findFieldByName("key"); + FieldDescriptor valueField = entryBuilder.getDescriptorForType().findFieldByName("value"); + entryBuilder.setField(keyField, key); + entryBuilder.setField(valueField, value); + return entryBuilder.build(); + } + + private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) { + List<Message> entryList = new ArrayList<Message>(); + for (Map.Entry<?, ?> entry : values.entrySet()) { + entryList.add(newMapEntry(builder, name, entry.getKey(), entry.getValue())); + } + FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name); + builder.setField(field, entryList); + } + + private static <KeyType, ValueType> + Map<KeyType, ValueType> mapForValues( + KeyType key1, ValueType value1, KeyType key2, ValueType value2) { + Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>(); + map.put(key1, value1); + map.put(key2, value2); + return map; + } + + public void testReflectionApi() throws Exception { + // In reflection API, map fields are just repeated message fields. + TestMap.Builder builder = TestMap.newBuilder(); + builder.getMutableInt32ToInt32Field().put(1, 2); + builder.getMutableInt32ToInt32Field().put(3, 4); + builder.getMutableInt32ToMessageField().put( + 11, MessageValue.newBuilder().setValue(22).build()); + builder.getMutableInt32ToMessageField().put( + 33, MessageValue.newBuilder().setValue(44).build()); + TestMap message = builder.build(); + + // Test getField(), getRepeatedFieldCount(), getRepeatedField(). + assertHasMapValues(message, "int32_to_int32_field", + mapForValues(1, 2, 3, 4)); + assertHasMapValues(message, "int32_to_message_field", + mapForValues( + 11, MessageValue.newBuilder().setValue(22).build(), + 33, MessageValue.newBuilder().setValue(44).build())); + + // Test clearField() + builder.clearField(f("int32_to_int32_field")); + builder.clearField(f("int32_to_message_field")); + message = builder.build(); + assertEquals(0, message.getInt32ToInt32Field().size()); + assertEquals(0, message.getInt32ToMessageField().size()); + + // Test setField() + setMapValues(builder, "int32_to_int32_field", + mapForValues(11, 22, 33, 44)); + setMapValues(builder, "int32_to_message_field", + mapForValues( + 111, MessageValue.newBuilder().setValue(222).build(), + 333, MessageValue.newBuilder().setValue(444).build())); + message = builder.build(); + assertEquals(22, message.getInt32ToInt32Field().get(11).intValue()); + assertEquals(44, message.getInt32ToInt32Field().get(33).intValue()); + assertEquals(222, message.getInt32ToMessageField().get(111).getValue()); + assertEquals(444, message.getInt32ToMessageField().get(333).getValue()); + + // Test addRepeatedField + builder.addRepeatedField(f("int32_to_int32_field"), + newMapEntry(builder, "int32_to_int32_field", 55, 66)); + builder.addRepeatedField(f("int32_to_message_field"), + newMapEntry(builder, "int32_to_message_field", 555, + MessageValue.newBuilder().setValue(666).build())); + message = builder.build(); + assertEquals(66, message.getInt32ToInt32Field().get(55).intValue()); + assertEquals(666, message.getInt32ToMessageField().get(555).getValue()); + + // Test addRepeatedField (overriding existing values) + builder.addRepeatedField(f("int32_to_int32_field"), + newMapEntry(builder, "int32_to_int32_field", 55, 55)); + builder.addRepeatedField(f("int32_to_message_field"), + newMapEntry(builder, "int32_to_message_field", 555, + MessageValue.newBuilder().setValue(555).build())); + message = builder.build(); + assertEquals(55, message.getInt32ToInt32Field().get(55).intValue()); + assertEquals(555, message.getInt32ToMessageField().get(555).getValue()); + + // Test setRepeatedField + for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) { + Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i); + int oldKey = ((Integer) getFieldValue(mapEntry, "key")).intValue(); + int oldValue = ((Integer) getFieldValue(mapEntry, "value")).intValue(); + // Swap key with value for each entry. + Message.Builder mapEntryBuilder = mapEntry.toBuilder(); + setFieldValue(mapEntryBuilder, "key", oldValue); + setFieldValue(mapEntryBuilder, "value", oldKey); + builder.setRepeatedField(f("int32_to_int32_field"), i, mapEntryBuilder.build()); + } + message = builder.build(); + assertEquals(11, message.getInt32ToInt32Field().get(22).intValue()); + assertEquals(33, message.getInt32ToInt32Field().get(44).intValue()); + assertEquals(55, message.getInt32ToInt32Field().get(55).intValue()); + } + + public void testTextFormat() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + String textData = TextFormat.printToString(message); + + builder = TestMap.newBuilder(); + TextFormat.merge(textData, builder); + message = builder.build(); + + assertMapValuesSet(message); + } + + public void testDynamicMessage() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + setMapValues(builder); + TestMap message = builder.build(); + + Message dynamicDefaultInstance = + DynamicMessage.getDefaultInstance(TestMap.getDescriptor()); + Message dynamicMessage = dynamicDefaultInstance + .newBuilderForType().mergeFrom(message.toByteString()).build(); + + assertEquals(message, dynamicMessage); + assertEquals(message.hashCode(), dynamicMessage.hashCode()); + } + + public void testReflectionEqualsAndHashCode() throws Exception { + // Test that generated equals() and hashCode() will disregard the order + // of map entries when comparing/hashing map fields. + + // We use DynamicMessage to test reflection based equals()/hashCode(). + Message dynamicDefaultInstance = + DynamicMessage.getDefaultInstance(TestMap.getDescriptor()); + FieldDescriptor field = f("int32_to_int32_field"); + + Message.Builder b1 = dynamicDefaultInstance.newBuilderForType(); + b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2)); + b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4)); + b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6)); + Message m1 = b1.build(); + + Message.Builder b2 = dynamicDefaultInstance.newBuilderForType(); + b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6)); + b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2)); + b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4)); + Message m2 = b2.build(); + + assertEquals(m1, m2); + assertEquals(m1.hashCode(), m2.hashCode()); + + // Make sure we did compare map fields. + b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0)); + m2 = b2.build(); + assertFalse(m1.equals(m2)); + // Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed + // to be different. + } + + public void testUnknownEnumValues() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + builder.getMutableInt32ToEnumFieldValue().put(0, 0); + builder.getMutableInt32ToEnumFieldValue().put(1, 1); + builder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value. + TestMap message = builder.build(); + + assertEquals(TestMap.EnumValue.FOO, + message.getInt32ToEnumField().get(0)); + assertEquals(TestMap.EnumValue.BAR, + message.getInt32ToEnumField().get(1)); + assertEquals(TestMap.EnumValue.UNRECOGNIZED, + message.getInt32ToEnumField().get(2)); + assertEquals(1000, message.getInt32ToEnumFieldValue().get(2).intValue()); + + // Unknown enum values should be preserved after: + // 1. Serialization and parsing. + // 2. toBuild(). + // 3. mergeFrom(). + message = TestMap.parseFrom(message.toByteString()); + assertEquals(1000, message.getInt32ToEnumFieldValue().get(2).intValue()); + builder = message.toBuilder(); + assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue()); + builder = TestMap.newBuilder().mergeFrom(message); + assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue()); + + // hashCode()/equals() should take unknown enum values into account. + builder.getMutableInt32ToEnumFieldValue().put(2, 1001); + TestMap message2 = builder.build(); + assertFalse(message.hashCode() == message2.hashCode()); + assertFalse(message.equals(message2)); + // Unknown values will be converted to UNRECOGNIZED so the resulted enum map + // should be the same. + assertTrue(message.getInt32ToEnumField().equals(message2.getInt32ToEnumField())); + } + + public void testUnknownEnumValuesInReflectionApi() throws Exception { + Descriptor descriptor = TestMap.getDescriptor(); + EnumDescriptor enumDescriptor = TestMap.EnumValue.getDescriptor(); + FieldDescriptor field = descriptor.findFieldByName("int32_to_enum_field"); + + Map<Integer, Integer> data = new HashMap<Integer, Integer>(); + data.put(0, 0); + data.put(1, 1); + data.put(2, 1000); // unknown value. + + TestMap.Builder builder = TestMap.newBuilder(); + for (Map.Entry<Integer, Integer> entry : data.entrySet()) { + builder.getMutableInt32ToEnumFieldValue().put(entry.getKey(), entry.getValue()); + } + + // Try to read unknown enum values using reflection API. + for (int i = 0; i < builder.getRepeatedFieldCount(field); i++) { + Message mapEntry = (Message) builder.getRepeatedField(field, i); + int key = ((Integer) getFieldValue(mapEntry, "key")).intValue(); + int value = ((EnumValueDescriptor) getFieldValue(mapEntry, "value")).getNumber(); + assertEquals(data.get(key).intValue(), value); + Message.Builder mapEntryBuilder = mapEntry.toBuilder(); + // Increase the value by 1. + setFieldValue(mapEntryBuilder, "value", + enumDescriptor.findValueByNumberCreatingIfUnknown(value + 1)); + builder.setRepeatedField(field, i, mapEntryBuilder.build()); + } + + // Verify that enum values have been successfully updated. + TestMap message = builder.build(); + for (Map.Entry<Integer, Integer> entry : message.getInt32ToEnumFieldValue().entrySet()) { + assertEquals(data.get(entry.getKey()) + 1, entry.getValue().intValue()); + } + } +} diff --git a/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java b/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java index 614ac7fe..2c60fe0e 100644 --- a/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java +++ b/java/src/test/java/com/google/protobuf/TestBadIdentifiers.java @@ -72,7 +72,7 @@ public class TestBadIdentifiers extends TestCase { assertEquals(0, message.getMessageField5Count()); assertEquals(0, message.getInt32FieldCount11()); - assertEquals(1, message.getEnumFieldCount12().getNumber()); + assertEquals(0, message.getEnumFieldCount12().getNumber()); assertEquals("", message.getStringFieldCount13()); assertEquals(ByteString.EMPTY, message.getBytesFieldCount14()); assertEquals(0, message.getMessageFieldCount15().getSerializedSize()); diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java index 152bdadc..82f9582f 100644 --- a/java/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java @@ -73,7 +73,7 @@ public class TextFormatTest extends TestCase { private static String exoticText = "repeated_int32: -1\n" + "repeated_int32: -2147483648\n" + - "repeated_int64: -1\n" + + "repeated_int64: -1,\n" + "repeated_int64: -9223372036854775808\n" + "repeated_uint32: 4294967295\n" + "repeated_uint32: 2147483648\n" + @@ -101,7 +101,7 @@ public class TextFormatTest extends TestCase { private static String canonicalExoticText = exoticText.replace(": .", ": 0.").replace(": -.", ": -0.") // short-form double - .replace("23e", "23E").replace("E+", "E").replace("0.23E17", "2.3E16"); + .replace("23e", "23E").replace("E+", "E").replace("0.23E17", "2.3E16").replace(",", ""); private String messageSetText = "[protobuf_unittest.TestMessageSetExtension1] {\n" + @@ -119,6 +119,7 @@ public class TextFormatTest extends TestCase { " i: 456\n" + "}\n"; + private final TextFormat.Parser parserWithOverwriteForbidden = TextFormat.Parser.newBuilder() .setSingularOverwritePolicy( @@ -460,6 +461,7 @@ public class TextFormatTest extends TestCase { } } + private void assertParseErrorWithOverwriteForbidden(String error, String text) { TestAllTypes.Builder builder = TestAllTypes.newBuilder(); @@ -553,10 +555,10 @@ public class TextFormatTest extends TestCase { public void testEscape() throws Exception { // Escape sequences. - assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"", - TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\""))); - assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"", - TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\"")); + assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177", + TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\177"))); + assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\177", + TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\"\177")); assertEquals(bytes("\0\001\007\b\f\n\r\t\013\\\'\""), TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); assertEquals("\0\001\007\b\f\n\r\t\013\\\'\"", @@ -900,6 +902,7 @@ public class TextFormatTest extends TestCase { .build())); } + public void testParseNonRepeatedFields() throws Exception { assertParseSuccessWithOverwriteForbidden( "repeated_int32: 1\n" + diff --git a/java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java b/java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java new file mode 100644 index 00000000..8f45976f --- /dev/null +++ b/java/src/test/java/com/google/protobuf/UnknownEnumValueTest.java @@ -0,0 +1,255 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.FieldPresenceTestProto.TestAllTypes; +import com.google.protobuf.TextFormat.ParseException; + +import junit.framework.TestCase; + +/** + * Unit tests for protos that keep unknown enum values rather than discard + * them as unknown fields. + */ +public class UnknownEnumValueTest extends TestCase { + public void testUnknownEnumValues() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.setOptionalNestedEnumValue(4321); + builder.addRepeatedNestedEnumValue(5432); + builder.addPackedNestedEnumValue(6543); + TestAllTypes message = builder.build(); + assertEquals(4321, message.getOptionalNestedEnumValue()); + assertEquals(5432, message.getRepeatedNestedEnumValue(0)); + assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue()); + assertEquals(6543, message.getPackedNestedEnumValue(0)); + // Returns UNRECOGNIZED if an enum type is requested. + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum()); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0)); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0)); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0)); + + // Test serialization and parsing. + ByteString data = message.toByteString(); + message = TestAllTypes.parseFrom(data); + assertEquals(4321, message.getOptionalNestedEnumValue()); + assertEquals(5432, message.getRepeatedNestedEnumValue(0)); + assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue()); + assertEquals(6543, message.getPackedNestedEnumValue(0)); + // Returns UNRECOGNIZED if an enum type is requested. + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum()); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0)); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0)); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0)); + + // Test toBuilder(). + builder = message.toBuilder(); + assertEquals(4321, builder.getOptionalNestedEnumValue()); + assertEquals(5432, builder.getRepeatedNestedEnumValue(0)); + assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue()); + assertEquals(6543, builder.getPackedNestedEnumValue(0)); + // Returns UNRECOGNIZED if an enum type is requested. + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum()); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0)); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0)); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0)); + + // Test mergeFrom(). + builder = TestAllTypes.newBuilder().mergeFrom(message); + assertEquals(4321, builder.getOptionalNestedEnumValue()); + assertEquals(5432, builder.getRepeatedNestedEnumValue(0)); + assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue()); + assertEquals(6543, builder.getPackedNestedEnumValue(0)); + // Returns UNRECOGNIZED if an enum type is requested. + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum()); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0)); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0)); + assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0)); + + // Test equals() and hashCode() + TestAllTypes sameMessage = builder.build(); + assertEquals(message, sameMessage); + assertEquals(message.hashCode(), sameMessage.hashCode()); + + // Getting the numeric value of UNRECOGNIZED will throw an exception. + try { + TestAllTypes.NestedEnum.UNRECOGNIZED.getNumber(); + fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + // Setting an enum field to an UNRECOGNIZED value will throw an exception. + try { + builder.setOptionalNestedEnum(builder.getOptionalNestedEnum()); + fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + try { + builder.addRepeatedNestedEnum(builder.getOptionalNestedEnum()); + fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + } + + public void testUnknownEnumValueInReflectionApi() throws Exception { + Descriptor descriptor = TestAllTypes.getDescriptor(); + FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum"); + FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum"); + FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum"); + EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); + + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.setField(optionalNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(4321)); + builder.addRepeatedField(repeatedNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(5432)); + builder.addRepeatedField(packedNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(6543)); + TestAllTypes message = builder.build(); + + // Getters will return unknown enum values as EnumValueDescriptor. + EnumValueDescriptor unknown4321 = + (EnumValueDescriptor) message.getField(optionalNestedEnumField); + EnumValueDescriptor unknown5432 = + (EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0); + EnumValueDescriptor unknown6543 = + (EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0); + assertEquals(4321, unknown4321.getNumber()); + assertEquals(5432, unknown5432.getNumber()); + assertEquals(6543, unknown6543.getNumber()); + + // Unknown EnumValueDescriptor will map to UNRECOGNIZED. + assertEquals( + TestAllTypes.NestedEnum.valueOf(unknown4321), + TestAllTypes.NestedEnum.UNRECOGNIZED); + assertEquals( + TestAllTypes.NestedEnum.valueOf(unknown5432), + TestAllTypes.NestedEnum.UNRECOGNIZED); + assertEquals( + TestAllTypes.NestedEnum.valueOf(unknown6543), + TestAllTypes.NestedEnum.UNRECOGNIZED); + + // Setters also accept unknown EnumValueDescriptor. + builder.setField(optionalNestedEnumField, unknown6543); + builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321); + builder.setRepeatedField(packedNestedEnumField, 0, unknown5432); + message = builder.build(); + // Like other descriptors, unknown EnumValueDescriptor can be compared by + // object identity. + assertTrue(unknown6543 == message.getField(optionalNestedEnumField)); + assertTrue(unknown4321 == message.getRepeatedField(repeatedNestedEnumField, 0)); + assertTrue(unknown5432 == message.getRepeatedField(packedNestedEnumField, 0)); + } + + public void testUnknownEnumValueWithDynamicMessage() throws Exception { + Descriptor descriptor = TestAllTypes.getDescriptor(); + FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum"); + FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum"); + FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum"); + EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); + + Message dynamicMessageDefaultInstance = DynamicMessage.getDefaultInstance(descriptor); + + Message.Builder builder = dynamicMessageDefaultInstance.newBuilderForType(); + builder.setField(optionalNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(4321)); + builder.addRepeatedField(repeatedNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(5432)); + builder.addRepeatedField(packedNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(6543)); + Message message = builder.build(); + assertEquals(4321, + ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber()); + assertEquals(5432, + ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber()); + assertEquals(6543, + ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber()); + + // Test reflection based serialization/parsing implementation. + ByteString data = message.toByteString(); + message = dynamicMessageDefaultInstance + .newBuilderForType() + .mergeFrom(data) + .build(); + assertEquals(4321, + ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber()); + assertEquals(5432, + ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber()); + assertEquals(6543, + ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber()); + + // Test reflection based equals()/hashCode(). + builder = dynamicMessageDefaultInstance.newBuilderForType(); + builder.setField(optionalNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(4321)); + builder.addRepeatedField(repeatedNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(5432)); + builder.addRepeatedField(packedNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(6543)); + Message sameMessage = builder.build(); + assertEquals(message, sameMessage); + assertEquals(message.hashCode(), sameMessage.hashCode()); + builder.setField(optionalNestedEnumField, + enumType.findValueByNumberCreatingIfUnknown(0)); + Message differentMessage = builder.build(); + assertFalse(message.equals(differentMessage)); + } + + public void testUnknownEnumValuesInTextFormat() { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + builder.setOptionalNestedEnumValue(4321); + builder.addRepeatedNestedEnumValue(5432); + builder.addPackedNestedEnumValue(6543); + TestAllTypes message = builder.build(); + + // We can print a message with unknown enum values. + String textData = TextFormat.printToString(message); + assertEquals( + "optional_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_4321\n" + + "repeated_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_5432\n" + + "packed_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_6543\n", textData); + + // Parsing unknown enum values will fail just like parsing other kinds of + // unknown fields. + try { + TextFormat.merge(textData, builder); + fail(); + } catch (ParseException e) { + // expected. + } + } +} diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java b/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java new file mode 100644 index 00000000..6372b7a7 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java @@ -0,0 +1,317 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar; +import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * Tests for {@link UnknownFieldSetLite}. + * + * @author dweis@google.com (Daniel Weis) + */ +public class UnknownFieldSetLiteTest extends TestCase { + + public void testNoDataIsDefaultInstance() { + assertSame( + UnknownFieldSetLite.getDefaultInstance(), + UnknownFieldSetLite.newBuilder() + .build()); + } + + public void testDefaultInstance() { + UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance(); + + assertEquals(0, unknownFields.getSerializedSize()); + assertEquals(ByteString.EMPTY, toByteString(unknownFields)); + } + + public void testMergeFieldFrom() throws IOException { + Foo foo = Foo.newBuilder() + .setValue(2) + .build(); + + CodedInputStream input = CodedInputStream.newInstance(foo.toByteArray()); + + UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); + builder.mergeFieldFrom(input.readTag(), input); + + assertEquals(foo.toByteString(), toByteString(builder.build())); + } + + public void testSerializedSize() throws IOException { + Foo foo = Foo.newBuilder() + .setValue(2) + .build(); + + CodedInputStream input = CodedInputStream.newInstance(foo.toByteArray()); + + UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); + builder.mergeFieldFrom(input.readTag(), input); + + assertEquals(foo.toByteString().size(), builder.build().getSerializedSize()); + } + + public void testMergeVarintField() throws IOException { + UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); + builder.mergeVarintField(10, 2); + + CodedInputStream input = + CodedInputStream.newInstance(toByteString(builder.build()).toByteArray()); + + int tag = input.readTag(); + assertEquals(10, WireFormat.getTagFieldNumber(tag)); + assertEquals(WireFormat.WIRETYPE_VARINT, WireFormat.getTagWireType(tag)); + assertEquals(2, input.readUInt64()); + assertTrue(input.isAtEnd()); + } + + public void testMergeVarintField_negative() throws IOException { + UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); + builder.mergeVarintField(10, -6); + + CodedInputStream input = + CodedInputStream.newInstance(toByteString(builder.build()).toByteArray()); + + int tag = input.readTag(); + assertEquals(10, WireFormat.getTagFieldNumber(tag)); + assertEquals(WireFormat.WIRETYPE_VARINT, WireFormat.getTagWireType(tag)); + assertEquals(-6, input.readUInt64()); + assertTrue(input.isAtEnd()); + } + + public void testEqualsAndHashCode() { + UnknownFieldSetLite.Builder builder1 = UnknownFieldSetLite.newBuilder(); + builder1.mergeVarintField(10, 2); + UnknownFieldSetLite unknownFields1 = builder1.build(); + + UnknownFieldSetLite.Builder builder2 = UnknownFieldSetLite.newBuilder(); + builder2.mergeVarintField(10, 2); + UnknownFieldSetLite unknownFields2 = builder2.build(); + + assertEquals(unknownFields1, unknownFields2); + assertEquals(unknownFields1.hashCode(), unknownFields2.hashCode()); + assertFalse(unknownFields1.equals(UnknownFieldSetLite.getDefaultInstance())); + assertFalse(unknownFields1.hashCode() == UnknownFieldSetLite.getDefaultInstance().hashCode()); + } + + public void testConcat() throws IOException { + UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); + builder.mergeVarintField(10, 2); + UnknownFieldSetLite unknownFields = builder.build(); + + unknownFields = UnknownFieldSetLite.concat(unknownFields, unknownFields); + + CodedInputStream input = + CodedInputStream.newInstance(toByteString(unknownFields).toByteArray()); + + int tag = input.readTag(); + assertEquals(10, WireFormat.getTagFieldNumber(tag)); + assertEquals(WireFormat.WIRETYPE_VARINT, WireFormat.getTagWireType(tag)); + assertEquals(2, input.readUInt64()); + assertFalse(input.isAtEnd()); + input.readTag(); + assertEquals(10, WireFormat.getTagFieldNumber(tag)); + assertEquals(WireFormat.WIRETYPE_VARINT, WireFormat.getTagWireType(tag)); + assertEquals(2, input.readUInt64()); + assertTrue(input.isAtEnd()); + } + + public void testConcat_empty() { + UnknownFieldSetLite unknownFields = UnknownFieldSetLite.concat( + UnknownFieldSetLite.getDefaultInstance(), UnknownFieldSetLite.getDefaultInstance()); + + assertEquals(0, unknownFields.getSerializedSize()); + assertEquals(ByteString.EMPTY, toByteString(unknownFields)); + } + + public void testBuilderReuse() throws IOException { + UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); + builder.mergeVarintField(10, 2); + builder.build(); + + try { + builder.build(); + fail(); + } catch (IllegalStateException e) { + // Expected. + } + + try { + builder.mergeFieldFrom(0, CodedInputStream.newInstance(new byte[0])); + fail(); + } catch (IllegalStateException e) { + // Expected. + } + + try { + builder.mergeVarintField(5, 1); + fail(); + } catch (IllegalStateException e) { + // Expected. + } + } + + public void testBuilderReuse_empty() { + UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder(); + builder.build(); + + try { + builder.build(); + fail(); + } catch (IllegalStateException e) { + // Expected. + } + } + + public void testRoundTrips() throws InvalidProtocolBufferException { + Foo foo = Foo.newBuilder() + .setValue(1) + .setExtension(Bar.fooExt, Bar.newBuilder() + .setName("name") + .build()) + .setExtension(LiteEqualsAndHash.varint, 22) + .setExtension(LiteEqualsAndHash.fixed32, 44) + .setExtension(LiteEqualsAndHash.fixed64, 66L) + .setExtension(LiteEqualsAndHash.myGroup, LiteEqualsAndHash.MyGroup.newBuilder() + .setGroupValue("value") + .build()) + .build(); + + Foo copy = Foo.parseFrom(foo.toByteArray()); + + assertEquals(foo.getSerializedSize(), copy.getSerializedSize()); + assertFalse(foo.equals(copy)); + + Foo secondCopy = Foo.parseFrom(foo.toByteArray()); + assertEquals(copy, secondCopy); + + ExtensionRegistryLite extensionRegistry = ExtensionRegistryLite.newInstance(); + LiteEqualsAndHash.registerAllExtensions(extensionRegistry); + Foo copyOfCopy = Foo.parseFrom(copy.toByteArray(), extensionRegistry); + + assertEquals(foo, copyOfCopy); + } + + public void testMalformedBytes() { + try { + Foo.parseFrom("this is a malformed protocol buffer".getBytes(StandardCharsets.UTF_8)); + fail(); + } catch (InvalidProtocolBufferException e) { + // Expected. + } + } + + public void testMissingStartGroupTag() throws IOException { + ByteString.Output byteStringOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(byteStringOutput); + output.writeGroupNoTag(Foo.newBuilder().setValue(11).build()); + output.writeTag(100, WireFormat.WIRETYPE_END_GROUP); + output.flush(); + + try { + Foo.parseFrom(byteStringOutput.toByteString()); + fail(); + } catch (InvalidProtocolBufferException e) { + // Expected. + } + } + + public void testMissingEndGroupTag() throws IOException { + ByteString.Output byteStringOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(byteStringOutput); + output.writeTag(100, WireFormat.WIRETYPE_START_GROUP); + output.writeGroupNoTag(Foo.newBuilder().setValue(11).build()); + output.flush(); + + try { + Foo.parseFrom(byteStringOutput.toByteString()); + fail(); + } catch (InvalidProtocolBufferException e) { + // Expected. + } + } + + public void testMismatchingGroupTags() throws IOException { + ByteString.Output byteStringOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(byteStringOutput); + output.writeTag(100, WireFormat.WIRETYPE_START_GROUP); + output.writeGroupNoTag(Foo.newBuilder().setValue(11).build()); + output.writeTag(101, WireFormat.WIRETYPE_END_GROUP); + output.flush(); + + try { + Foo.parseFrom(byteStringOutput.toByteString()); + fail(); + } catch (InvalidProtocolBufferException e) { + // Expected. + } + } + + public void testTruncatedInput() { + Foo foo = Foo.newBuilder() + .setValue(1) + .setExtension(Bar.fooExt, Bar.newBuilder() + .setName("name") + .build()) + .setExtension(LiteEqualsAndHash.varint, 22) + .setExtension(LiteEqualsAndHash.myGroup, LiteEqualsAndHash.MyGroup.newBuilder() + .setGroupValue("value") + .build()) + .build(); + + try { + Foo.parseFrom(foo.toByteString().substring(0, foo.toByteString().size() - 10)); + fail(); + } catch (InvalidProtocolBufferException e) { + // Expected. + } + } + + private ByteString toByteString(UnknownFieldSetLite unknownFields) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(byteArrayOutputStream); + try { + unknownFields.writeTo(output); + output.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return ByteString.copyFrom(byteArrayOutputStream.toByteArray()); + } +} diff --git a/java/src/test/java/com/google/protobuf/field_presence_test.proto b/java/src/test/java/com/google/protobuf/field_presence_test.proto new file mode 100644 index 00000000..8d8ea8ef --- /dev/null +++ b/java/src/test/java/com/google/protobuf/field_presence_test.proto @@ -0,0 +1,93 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package field_presence_test; + +import "google/protobuf/unittest.proto"; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "FieldPresenceTestProto"; +option java_generate_equals_and_hash = true; + +message TestAllTypes { + enum NestedEnum { + FOO = 0; + BAR = 1; + BAZ = 2; + } + message NestedMessage { + optional int32 value = 1; + } + + optional int32 optional_int32 = 1; + optional string optional_string = 2; + optional bytes optional_bytes = 3; + optional NestedEnum optional_nested_enum = 4; + optional NestedMessage optional_nested_message = 5; + optional protobuf_unittest.TestRequired optional_proto2_message = 6; + + oneof oneof_field { + int32 oneof_int32 = 11; + uint32 oneof_uint32 = 12; + string oneof_string = 13; + bytes oneof_bytes = 14; + NestedEnum oneof_nested_enum = 15; + NestedMessage oneof_nested_message = 16; + protobuf_unittest.TestRequired oneof_proto2_message = 17; + } + + repeated int32 repeated_int32 = 21; + repeated string repeated_string = 22; + repeated bytes repeated_bytes = 23; + repeated NestedEnum repeated_nested_enum = 24; + repeated NestedMessage repeated_nested_message = 25; + repeated protobuf_unittest.TestRequired repeated_proto2_message = 26; + repeated NestedEnum packed_nested_enum = 27 [packed = true]; +} + +message TestOptionalFieldsOnly { + optional int32 optional_int32 = 1; + optional string optional_string = 2; + optional bytes optional_bytes = 3; + optional TestAllTypes.NestedEnum optional_nested_enum = 4; + optional TestAllTypes.NestedMessage optional_nested_message = 5; + optional protobuf_unittest.TestRequired optional_proto2_message = 6; +} + +message TestRepeatedFieldsOnly { + repeated int32 repeated_int32 = 21; + repeated string repeated_string = 22; + repeated bytes repeated_bytes = 23; + repeated TestAllTypes.NestedEnum repeated_nested_enum = 24; + repeated TestAllTypes.NestedMessage repeated_nested_message = 25; + repeated protobuf_unittest.TestRequired repeated_proto2_message = 26; +} diff --git a/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto b/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto index baed4e10..015dc267 100644 --- a/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto +++ b/java/src/test/java/com/google/protobuf/lazy_fields_lite.proto @@ -32,6 +32,7 @@ // // A proto file with lazy fields +syntax = "proto2"; package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto b/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto index 68615672..86837250 100644 --- a/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto +++ b/java/src/test/java/com/google/protobuf/lite_equals_and_hash.proto @@ -30,6 +30,7 @@ // Author: pbogle@google.com (Phil Bogle) +syntax = "proto2"; package protobuf_unittest.lite_equals_and_hash; @@ -41,9 +42,15 @@ option optimize_for = LITE_RUNTIME; message Foo { optional int32 value = 1; repeated Bar bar = 2; + + extensions 100 to max; } message Bar { + extend Foo { + optional Bar foo_ext = 100; + } + optional string name = 1; } @@ -53,3 +60,13 @@ message BarPrime { message Empty { } + +extend Foo { + optional int32 varint = 101; + optional fixed32 fixed32 = 102; + optional fixed64 fixed64 = 103; + optional group MyGroup = 104 { + optional string group_value = 1; + } +} + diff --git a/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto b/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto new file mode 100644 index 00000000..4f23a4a8 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/map_for_proto2_lite_test.proto @@ -0,0 +1,63 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; + + +option java_outer_classname = "MapForProto2TestProto"; +option java_generate_equals_and_hash = true; + +message TestMap { + message MessageValue { + optional int32 value = 1; + } + enum EnumValue { + FOO = 0; + BAR = 1; + BAZ = 2; + QUX = 3; + } + + map<int32, int32> int32_to_int32_field = 1; + map<int32, string> int32_to_string_field = 2; + map<int32, bytes> int32_to_bytes_field = 3; + map<int32, EnumValue> int32_to_enum_field = 4; + map<int32, MessageValue> int32_to_message_field = 5; + map<string, int32> string_to_int32_field = 6; +} + +message TestUnknownEnumValue { + // Wire-compatible with TestMap.int32_to_enum_field so we can test the + // parsing behavior of TestMap regarding unknown enum values. + map<int32, int32> int32_to_int32_field = 4; +} +package map_for_proto2_lite_test; +option java_package = "map_lite_test"; +option optimize_for = LITE_RUNTIME; diff --git a/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto b/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto new file mode 100644 index 00000000..36dfbd10 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/map_for_proto2_test.proto @@ -0,0 +1,62 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; + +package map_for_proto2_test; + +option java_package = "map_test"; +option java_outer_classname = "MapForProto2TestProto"; +option java_generate_equals_and_hash = true; + +message TestMap { + message MessageValue { + optional int32 value = 1; + } + enum EnumValue { + FOO = 0; + BAR = 1; + BAZ = 2; + QUX = 3; + } + + map<int32, int32> int32_to_int32_field = 1; + map<int32, string> int32_to_string_field = 2; + map<int32, bytes> int32_to_bytes_field = 3; + map<int32, EnumValue> int32_to_enum_field = 4; + map<int32, MessageValue> int32_to_message_field = 5; + map<string, int32> string_to_int32_field = 6; +} + +message TestUnknownEnumValue { + // Wire-compatible with TestMap.int32_to_enum_field so we can test the + // parsing behavior of TestMap regarding unknown enum values. + map<int32, int32> int32_to_int32_field = 4; +} diff --git a/java/src/test/java/com/google/protobuf/map_test.proto b/java/src/test/java/com/google/protobuf/map_test.proto new file mode 100644 index 00000000..105ee3f8 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/map_test.proto @@ -0,0 +1,63 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package map_test; + +option java_package = "map_test"; +option java_outer_classname = "MapTestProto"; +option java_generate_equals_and_hash = true; + +message TestMap { + message MessageValue { + optional int32 value = 1; + } + enum EnumValue { + FOO = 0; + BAR = 1; + BAZ = 2; + QUX = 3; + } + + map<int32, int32> int32_to_int32_field = 1; + map<int32, string> int32_to_string_field = 2; + map<int32, bytes> int32_to_bytes_field = 3; + map<int32, EnumValue> int32_to_enum_field = 4; + map<int32, MessageValue> int32_to_message_field = 5; + map<string, int32> string_to_int32_field = 6; +} + +// Used to test that a nested bulider containing map fields will properly +// propagate the onChange event and mark its parent dirty when a change +// is made to a map field. +message TestOnChangeEventPropagation { + optional TestMap optional_message = 1; +} diff --git a/java/src/test/java/com/google/protobuf/multiple_files_test.proto b/java/src/test/java/com/google/protobuf/multiple_files_test.proto index 7ec89a05..92790506 100644 --- a/java/src/test/java/com/google/protobuf/multiple_files_test.proto +++ b/java/src/test/java/com/google/protobuf/multiple_files_test.proto @@ -32,6 +32,7 @@ // // A proto file which tests the java_multiple_files option. +syntax = "proto2"; // Some generic_services option(s) added automatically. // See: http://go/proto2-generic-services-default diff --git a/java/src/test/java/com/google/protobuf/nested_builders_test.proto b/java/src/test/java/com/google/protobuf/nested_builders_test.proto index 8ab4a433..a5dd66d8 100644 --- a/java/src/test/java/com/google/protobuf/nested_builders_test.proto +++ b/java/src/test/java/com/google/protobuf/nested_builders_test.proto @@ -30,6 +30,7 @@ // Author: jonp@google.com (Jon Perlow) // +syntax = "proto2"; package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/nested_extension.proto b/java/src/test/java/com/google/protobuf/nested_extension.proto index 86492867..704e649e 100644 --- a/java/src/test/java/com/google/protobuf/nested_extension.proto +++ b/java/src/test/java/com/google/protobuf/nested_extension.proto @@ -33,6 +33,7 @@ // A proto file with nested extensions. Note that this must be defined in // a separate file to properly test the initialization of the outer class. +syntax = "proto2"; import "com/google/protobuf/non_nested_extension.proto"; diff --git a/java/src/test/java/com/google/protobuf/nested_extension_lite.proto b/java/src/test/java/com/google/protobuf/nested_extension_lite.proto index 9c686829..a95c38b2 100644 --- a/java/src/test/java/com/google/protobuf/nested_extension_lite.proto +++ b/java/src/test/java/com/google/protobuf/nested_extension_lite.proto @@ -34,6 +34,7 @@ // this must be defined in a separate file to properly test the initialization // of the outer class. +syntax = "proto2"; package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension.proto b/java/src/test/java/com/google/protobuf/non_nested_extension.proto index cb2f8b00..31fac552 100644 --- a/java/src/test/java/com/google/protobuf/non_nested_extension.proto +++ b/java/src/test/java/com/google/protobuf/non_nested_extension.proto @@ -32,6 +32,7 @@ // // A proto file with extensions. +syntax = "proto2"; package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto b/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto index c1f744b8..37c369ed 100644 --- a/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto +++ b/java/src/test/java/com/google/protobuf/non_nested_extension_lite.proto @@ -32,6 +32,7 @@ // // A proto file with extensions for a MessageLite messages. +syntax = "proto2"; package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test.proto b/java/src/test/java/com/google/protobuf/outer_class_name_test.proto index c5114f89..42083681 100644 --- a/java/src/test/java/com/google/protobuf/outer_class_name_test.proto +++ b/java/src/test/java/com/google/protobuf/outer_class_name_test.proto @@ -28,6 +28,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +syntax = "proto2"; + package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto b/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto index 741cd281..3e5956b0 100644 --- a/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto +++ b/java/src/test/java/com/google/protobuf/outer_class_name_test2.proto @@ -28,6 +28,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +syntax = "proto2"; + package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto b/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto index 5d5d4ac9..74a8ba3c 100644 --- a/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto +++ b/java/src/test/java/com/google/protobuf/outer_class_name_test3.proto @@ -28,6 +28,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +syntax = "proto2"; + package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto index 202e8c91..67035fd5 100644 --- a/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto +++ b/java/src/test/java/com/google/protobuf/test_bad_identifiers.proto @@ -33,6 +33,7 @@ // This file tests that various identifiers work as field and type names even // though the same identifiers are used internally by the java code generator. +syntax = "proto2"; // Some generic_services option(s) added automatically. // See: http://go/proto2-generic-services-default @@ -59,12 +60,14 @@ message Descriptor { } optional NestedDescriptor nested_descriptor = 2; enum NestedEnum { + UNKNOWN = 0; FOO = 1; } } message Parser { enum ParserEnum { + UNKNOWN = 0; PARSER = 1; } optional ParserEnum parser = 1; @@ -72,6 +75,7 @@ message Parser { message Deprecated { enum TestEnum { + UNKNOWN = 0; FOO = 1; // Test if @Deprecated annotation conflicts with Deprecated message name. @@ -118,6 +122,7 @@ service TestConflictingMethodNames { message TestConflictingFieldNames { enum TestEnum { + UNKNOWN = 0; FOO = 1; } message TestMessage { @@ -142,16 +147,23 @@ message TestConflictingFieldNames { // This field conflicts with "int32_field" as they both generate // the method getInt32FieldList(). - required int32 int32_field_list = 31; + required int32 int32_field_list = 31; // NO_PROTO3 - extensions 1000 to max; + extensions 1000 to max; // NO_PROTO3 repeated int64 int64_field = 41; - extend TestConflictingFieldNames { + extend TestConflictingFieldNames { // NO_PROTO3 // We don't generate accessors for extensions so the following extension // fields don't conflict with the repeated field "int64_field". - optional int64 int64_field_count = 1001; - optional int64 int64_field_list = 1002; - } + optional int64 int64_field_count = 1001; // NO_PROTO3 + optional int64 int64_field_list = 1002; // NO_PROTO3 + } // NO_PROTO3 } +message TestMapField { + message MapField {} + message Pair {} + message Message {} + + map<int32, int32> map_field = 1; +} diff --git a/java/src/test/java/com/google/protobuf/test_check_utf8.proto b/java/src/test/java/com/google/protobuf/test_check_utf8.proto index 206946d8..119c1dcb 100644 --- a/java/src/test/java/com/google/protobuf/test_check_utf8.proto +++ b/java/src/test/java/com/google/protobuf/test_check_utf8.proto @@ -31,6 +31,7 @@ // Author: Jacob Butcher (jbaum@google.com) // // Test file option java_string_check_utf8. +syntax = "proto2"; package proto2_test_check_utf8; diff --git a/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto b/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto index fa057a88..f06d76d6 100644 --- a/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto +++ b/java/src/test/java/com/google/protobuf/test_check_utf8_size.proto @@ -31,6 +31,7 @@ // Author: Jacob Butcher (jbaum@google.com) // // Test file option java_string_check_utf8. +syntax = "proto2"; package proto2_test_check_utf8_size; diff --git a/java/src/test/java/com/google/protobuf/test_custom_options.proto b/java/src/test/java/com/google/protobuf/test_custom_options.proto index f6a5ecd4..f8efd455 100644 --- a/java/src/test/java/com/google/protobuf/test_custom_options.proto +++ b/java/src/test/java/com/google/protobuf/test_custom_options.proto @@ -32,6 +32,7 @@ // // Test that custom options defined in a proto file's dependencies are properly // initialized. +syntax = "proto2"; package protobuf_unittest; diff --git a/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto b/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto index 72e05a36..645f57b4 100644 --- a/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto +++ b/java/src/test/java/com/google/protobuf/test_extra_interfaces.proto @@ -29,6 +29,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Author: Darick Tong (darick@google.com) +syntax = "proto2"; package protobuf_unittest; |