| // 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 protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize; |
| import protobuf_unittest.UnittestProto; |
| import protobuf_unittest.UnittestProto.ForeignMessage; |
| import protobuf_unittest.UnittestProto.TestAllExtensions; |
| import protobuf_unittest.UnittestProto.TestAllTypes; |
| import protobuf_unittest.UnittestProto.TestPackedTypes; |
| import protobuf_unittest.UnittestProto.TestRequired; |
| import protobuf_unittest.UnittestProto.TestRequiredForeign; |
| import protobuf_unittest.UnittestProto.TestUnpackedTypes; |
| import junit.framework.TestCase; |
| |
| import java.util.Map; |
| |
| /** |
| * Unit test for {@link AbstractMessage}. |
| * |
| * @author kenton@google.com Kenton Varda |
| */ |
| public class AbstractMessageTest extends TestCase { |
| /** |
| * Extends AbstractMessage and wraps some other message object. The methods |
| * of the Message interface which aren't explicitly implemented by |
| * AbstractMessage are forwarded to the wrapped object. This allows us to |
| * test that AbstractMessage's implementations work even if the wrapped |
| * object does not use them. |
| */ |
| private static class AbstractMessageWrapper extends AbstractMessage { |
| private final Message wrappedMessage; |
| |
| public AbstractMessageWrapper(Message wrappedMessage) { |
| this.wrappedMessage = wrappedMessage; |
| } |
| |
| @Override |
| public Descriptors.Descriptor getDescriptorForType() { |
| return wrappedMessage.getDescriptorForType(); |
| } |
| @Override |
| public AbstractMessageWrapper getDefaultInstanceForType() { |
| return new AbstractMessageWrapper( |
| wrappedMessage.getDefaultInstanceForType()); |
| } |
| @Override |
| public Map<Descriptors.FieldDescriptor, Object> getAllFields() { |
| return wrappedMessage.getAllFields(); |
| } |
| @Override |
| public boolean hasField(Descriptors.FieldDescriptor field) { |
| return wrappedMessage.hasField(field); |
| } |
| @Override |
| public Object getField(Descriptors.FieldDescriptor field) { |
| return wrappedMessage.getField(field); |
| } |
| @Override |
| public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { |
| return wrappedMessage.getRepeatedFieldCount(field); |
| } |
| @Override |
| public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) { |
| return wrappedMessage.getRepeatedField(field, index); |
| } |
| @Override |
| public UnknownFieldSet getUnknownFields() { |
| return wrappedMessage.getUnknownFields(); |
| } |
| @Override |
| public Builder newBuilderForType() { |
| return new Builder(wrappedMessage.newBuilderForType()); |
| } |
| @Override |
| public Builder toBuilder() { |
| return new Builder(wrappedMessage.toBuilder()); |
| } |
| |
| static class Builder extends AbstractMessage.Builder<Builder> { |
| private final Message.Builder wrappedBuilder; |
| |
| public Builder(Message.Builder wrappedBuilder) { |
| this.wrappedBuilder = wrappedBuilder; |
| } |
| |
| @Override |
| public AbstractMessageWrapper build() { |
| return new AbstractMessageWrapper(wrappedBuilder.build()); |
| } |
| @Override |
| public AbstractMessageWrapper buildPartial() { |
| return new AbstractMessageWrapper(wrappedBuilder.buildPartial()); |
| } |
| @Override |
| public Builder clone() { |
| return new Builder(wrappedBuilder.clone()); |
| } |
| @Override |
| public boolean isInitialized() { |
| return clone().buildPartial().isInitialized(); |
| } |
| @Override |
| public Descriptors.Descriptor getDescriptorForType() { |
| return wrappedBuilder.getDescriptorForType(); |
| } |
| @Override |
| public AbstractMessageWrapper getDefaultInstanceForType() { |
| return new AbstractMessageWrapper( |
| wrappedBuilder.getDefaultInstanceForType()); |
| } |
| @Override |
| public Map<Descriptors.FieldDescriptor, Object> getAllFields() { |
| return wrappedBuilder.getAllFields(); |
| } |
| @Override |
| public Builder newBuilderForField(Descriptors.FieldDescriptor field) { |
| return new Builder(wrappedBuilder.newBuilderForField(field)); |
| } |
| @Override |
| public boolean hasField(Descriptors.FieldDescriptor field) { |
| return wrappedBuilder.hasField(field); |
| } |
| @Override |
| public Object getField(Descriptors.FieldDescriptor field) { |
| return wrappedBuilder.getField(field); |
| } |
| @Override |
| public Builder setField(Descriptors.FieldDescriptor field, Object value) { |
| wrappedBuilder.setField(field, value); |
| return this; |
| } |
| @Override |
| public Builder clearField(Descriptors.FieldDescriptor field) { |
| wrappedBuilder.clearField(field); |
| return this; |
| } |
| @Override |
| public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { |
| return wrappedBuilder.getRepeatedFieldCount(field); |
| } |
| @Override |
| public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) { |
| return wrappedBuilder.getRepeatedField(field, index); |
| } |
| @Override |
| public Builder setRepeatedField(Descriptors.FieldDescriptor field, int index, Object value) { |
| wrappedBuilder.setRepeatedField(field, index, value); |
| return this; |
| } |
| @Override |
| public Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value) { |
| wrappedBuilder.addRepeatedField(field, value); |
| return this; |
| } |
| @Override |
| public UnknownFieldSet getUnknownFields() { |
| return wrappedBuilder.getUnknownFields(); |
| } |
| @Override |
| public Builder setUnknownFields(UnknownFieldSet unknownFields) { |
| wrappedBuilder.setUnknownFields(unknownFields); |
| return this; |
| } |
| @Override |
| public Message.Builder getFieldBuilder(FieldDescriptor field) { |
| return wrappedBuilder.getFieldBuilder(field); |
| } |
| } |
| @Override |
| public Parser<? extends Message> getParserForType() { |
| return wrappedMessage.getParserForType(); |
| } |
| } |
| |
| // ================================================================= |
| |
| TestUtil.ReflectionTester reflectionTester = |
| new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null); |
| |
| TestUtil.ReflectionTester extensionsReflectionTester = |
| new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(), |
| TestUtil.getExtensionRegistry()); |
| |
| public void testClear() throws Exception { |
| AbstractMessageWrapper message = |
| new AbstractMessageWrapper.Builder( |
| TestAllTypes.newBuilder(TestUtil.getAllSet())) |
| .clear().build(); |
| TestUtil.assertClear((TestAllTypes) message.wrappedMessage); |
| } |
| |
| public void testCopy() throws Exception { |
| AbstractMessageWrapper message = |
| new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder()) |
| .mergeFrom(TestUtil.getAllSet()).build(); |
| TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage); |
| } |
| |
| public void testSerializedSize() throws Exception { |
| TestAllTypes message = TestUtil.getAllSet(); |
| Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet()); |
| |
| assertEquals(message.getSerializedSize(), |
| abstractMessage.getSerializedSize()); |
| } |
| |
| public void testSerialization() throws Exception { |
| Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet()); |
| |
| TestUtil.assertAllFieldsSet( |
| TestAllTypes.parseFrom(abstractMessage.toByteString())); |
| |
| assertEquals(TestUtil.getAllSet().toByteString(), |
| abstractMessage.toByteString()); |
| } |
| |
| public void testParsing() throws Exception { |
| AbstractMessageWrapper.Builder builder = |
| new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder()); |
| AbstractMessageWrapper message = |
| builder.mergeFrom(TestUtil.getAllSet().toByteString()).build(); |
| TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage); |
| } |
| |
| public void testParsingUninitialized() throws Exception { |
| TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder(); |
| builder.getOptionalMessageBuilder().setDummy2(10); |
| ByteString bytes = builder.buildPartial().toByteString(); |
| Message.Builder abstractMessageBuilder = |
| new AbstractMessageWrapper.Builder(TestRequiredForeign.newBuilder()); |
| // mergeFrom() should not throw initialization error. |
| abstractMessageBuilder.mergeFrom(bytes).buildPartial(); |
| try { |
| abstractMessageBuilder.mergeFrom(bytes).build(); |
| fail(); |
| } catch (UninitializedMessageException ex) { |
| // pass |
| } |
| |
| // test DynamicMessage directly. |
| Message.Builder dynamicMessageBuilder = DynamicMessage.newBuilder( |
| TestRequiredForeign.getDescriptor()); |
| // mergeFrom() should not throw initialization error. |
| dynamicMessageBuilder.mergeFrom(bytes).buildPartial(); |
| try { |
| dynamicMessageBuilder.mergeFrom(bytes).build(); |
| fail(); |
| } catch (UninitializedMessageException ex) { |
| // pass |
| } |
| } |
| |
| public void testPackedSerialization() throws Exception { |
| Message abstractMessage = |
| new AbstractMessageWrapper(TestUtil.getPackedSet()); |
| |
| TestUtil.assertPackedFieldsSet( |
| TestPackedTypes.parseFrom(abstractMessage.toByteString())); |
| |
| assertEquals(TestUtil.getPackedSet().toByteString(), |
| abstractMessage.toByteString()); |
| } |
| |
| public void testPackedParsing() throws Exception { |
| AbstractMessageWrapper.Builder builder = |
| new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder()); |
| AbstractMessageWrapper message = |
| builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build(); |
| TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage); |
| } |
| |
| public void testUnpackedSerialization() throws Exception { |
| Message abstractMessage = |
| new AbstractMessageWrapper(TestUtil.getUnpackedSet()); |
| |
| TestUtil.assertUnpackedFieldsSet( |
| TestUnpackedTypes.parseFrom(abstractMessage.toByteString())); |
| |
| assertEquals(TestUtil.getUnpackedSet().toByteString(), |
| abstractMessage.toByteString()); |
| } |
| |
| public void testParsePackedToUnpacked() throws Exception { |
| AbstractMessageWrapper.Builder builder = |
| new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder()); |
| AbstractMessageWrapper message = |
| builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build(); |
| TestUtil.assertUnpackedFieldsSet( |
| (TestUnpackedTypes) message.wrappedMessage); |
| } |
| |
| public void testParseUnpackedToPacked() throws Exception { |
| AbstractMessageWrapper.Builder builder = |
| new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder()); |
| AbstractMessageWrapper message = |
| builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build(); |
| TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage); |
| } |
| |
| public void testUnpackedParsing() throws Exception { |
| AbstractMessageWrapper.Builder builder = |
| new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder()); |
| AbstractMessageWrapper message = |
| builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build(); |
| TestUtil.assertUnpackedFieldsSet( |
| (TestUnpackedTypes) message.wrappedMessage); |
| } |
| |
| public void testOptimizedForSize() throws Exception { |
| // We're mostly only checking that this class was compiled successfully. |
| TestOptimizedForSize message = |
| TestOptimizedForSize.newBuilder().setI(1).build(); |
| message = TestOptimizedForSize.parseFrom(message.toByteString()); |
| assertEquals(2, message.getSerializedSize()); |
| } |
| |
| // ----------------------------------------------------------------- |
| // Tests for isInitialized(). |
| |
| private static final TestRequired TEST_REQUIRED_UNINITIALIZED = |
| TestRequired.getDefaultInstance(); |
| private static final TestRequired TEST_REQUIRED_INITIALIZED = |
| TestRequired.newBuilder().setA(1).setB(2).setC(3).build(); |
| |
| public void testIsInitialized() throws Exception { |
| TestRequired.Builder builder = TestRequired.newBuilder(); |
| AbstractMessageWrapper.Builder abstractBuilder = |
| new AbstractMessageWrapper.Builder(builder); |
| |
| assertFalse(abstractBuilder.isInitialized()); |
| assertEquals("a, b, c", abstractBuilder.getInitializationErrorString()); |
| builder.setA(1); |
| assertFalse(abstractBuilder.isInitialized()); |
| assertEquals("b, c", abstractBuilder.getInitializationErrorString()); |
| builder.setB(1); |
| assertFalse(abstractBuilder.isInitialized()); |
| assertEquals("c", abstractBuilder.getInitializationErrorString()); |
| builder.setC(1); |
| assertTrue(abstractBuilder.isInitialized()); |
| assertEquals("", abstractBuilder.getInitializationErrorString()); |
| } |
| |
| public void testForeignIsInitialized() throws Exception { |
| TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder(); |
| AbstractMessageWrapper.Builder abstractBuilder = |
| new AbstractMessageWrapper.Builder(builder); |
| |
| assertTrue(abstractBuilder.isInitialized()); |
| assertEquals("", abstractBuilder.getInitializationErrorString()); |
| |
| builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED); |
| assertFalse(abstractBuilder.isInitialized()); |
| assertEquals( |
| "optional_message.a, optional_message.b, optional_message.c", |
| abstractBuilder.getInitializationErrorString()); |
| |
| builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED); |
| assertTrue(abstractBuilder.isInitialized()); |
| assertEquals("", abstractBuilder.getInitializationErrorString()); |
| |
| builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED); |
| assertFalse(abstractBuilder.isInitialized()); |
| assertEquals( |
| "repeated_message[0].a, repeated_message[0].b, repeated_message[0].c", |
| abstractBuilder.getInitializationErrorString()); |
| |
| builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED); |
| assertTrue(abstractBuilder.isInitialized()); |
| assertEquals("", abstractBuilder.getInitializationErrorString()); |
| } |
| |
| // ----------------------------------------------------------------- |
| // Tests for mergeFrom |
| |
| static final TestAllTypes MERGE_SOURCE = |
| TestAllTypes.newBuilder() |
| .setOptionalInt32(1) |
| .setOptionalString("foo") |
| .setOptionalForeignMessage(ForeignMessage.getDefaultInstance()) |
| .addRepeatedString("bar") |
| .build(); |
| |
| static final TestAllTypes MERGE_DEST = |
| TestAllTypes.newBuilder() |
| .setOptionalInt64(2) |
| .setOptionalString("baz") |
| .setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build()) |
| .addRepeatedString("qux") |
| .build(); |
| |
| static final String MERGE_RESULT_TEXT = |
| "optional_int32: 1\n" + |
| "optional_int64: 2\n" + |
| "optional_string: \"foo\"\n" + |
| "optional_foreign_message {\n" + |
| " c: 3\n" + |
| "}\n" + |
| "repeated_string: \"qux\"\n" + |
| "repeated_string: \"bar\"\n"; |
| |
| public void testMergeFrom() throws Exception { |
| AbstractMessageWrapper result = |
| new AbstractMessageWrapper.Builder( |
| TestAllTypes.newBuilder(MERGE_DEST)) |
| .mergeFrom(MERGE_SOURCE).build(); |
| |
| assertEquals(MERGE_RESULT_TEXT, result.toString()); |
| } |
| |
| // ----------------------------------------------------------------- |
| // Tests for equals and hashCode |
| |
| public void testEqualsAndHashCode() throws Exception { |
| TestAllTypes a = TestUtil.getAllSet(); |
| TestAllTypes b = TestAllTypes.newBuilder().build(); |
| TestAllTypes c = TestAllTypes.newBuilder(b).addRepeatedString("x").build(); |
| TestAllTypes d = TestAllTypes.newBuilder(c).addRepeatedString("y").build(); |
| TestAllExtensions e = TestUtil.getAllExtensionsSet(); |
| TestAllExtensions f = TestAllExtensions.newBuilder(e) |
| .addExtension(UnittestProto.repeatedInt32Extension, 999).build(); |
| |
| checkEqualsIsConsistent(a); |
| checkEqualsIsConsistent(b); |
| checkEqualsIsConsistent(c); |
| checkEqualsIsConsistent(d); |
| checkEqualsIsConsistent(e); |
| checkEqualsIsConsistent(f); |
| |
| checkNotEqual(a, b); |
| checkNotEqual(a, c); |
| checkNotEqual(a, d); |
| checkNotEqual(a, e); |
| checkNotEqual(a, f); |
| |
| checkNotEqual(b, c); |
| checkNotEqual(b, d); |
| checkNotEqual(b, e); |
| checkNotEqual(b, f); |
| |
| checkNotEqual(c, d); |
| checkNotEqual(c, e); |
| checkNotEqual(c, f); |
| |
| checkNotEqual(d, e); |
| checkNotEqual(d, f); |
| |
| checkNotEqual(e, f); |
| |
| // Deserializing into the TestEmptyMessage such that every field |
| // is an {@link UnknownFieldSet.Field}. |
| UnittestProto.TestEmptyMessage eUnknownFields = |
| UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray()); |
| UnittestProto.TestEmptyMessage fUnknownFields = |
| UnittestProto.TestEmptyMessage.parseFrom(f.toByteArray()); |
| checkNotEqual(eUnknownFields, fUnknownFields); |
| checkEqualsIsConsistent(eUnknownFields); |
| checkEqualsIsConsistent(fUnknownFields); |
| |
| // Subsequent reconstitutions should be identical |
| UnittestProto.TestEmptyMessage eUnknownFields2 = |
| UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray()); |
| checkEqualsIsConsistent(eUnknownFields, eUnknownFields2); |
| } |
| |
| |
| /** |
| * Asserts that the given proto has symmetric equals and hashCode methods. |
| */ |
| private void checkEqualsIsConsistent(Message message) { |
| // Object should be equal to itself. |
| assertEquals(message, message); |
| |
| // Object should be equal to a dynamic copy of itself. |
| DynamicMessage dynamic = DynamicMessage.newBuilder(message).build(); |
| checkEqualsIsConsistent(message, dynamic); |
| } |
| |
| /** |
| * Asserts that the given protos are equal and have the same hash code. |
| */ |
| private void checkEqualsIsConsistent(Message message1, Message message2) { |
| assertEquals(message1, message2); |
| assertEquals(message2, message1); |
| assertEquals(message2.hashCode(), message1.hashCode()); |
| } |
| |
| /** |
| * Asserts that the given protos are not equal and have different hash codes. |
| * |
| * @warning It's valid for non-equal objects to have the same hash code, so |
| * this test is stricter than it needs to be. However, this should happen |
| * relatively rarely. |
| */ |
| private void checkNotEqual(Message m1, Message m2) { |
| String equalsError = String.format("%s should not be equal to %s", m1, m2); |
| assertFalse(equalsError, m1.equals(m2)); |
| assertFalse(equalsError, m2.equals(m1)); |
| |
| assertFalse( |
| String.format("%s should have a different hash code from %s", m1, m2), |
| m1.hashCode() == m2.hashCode()); |
| } |
| |
| public void testCheckByteStringIsUtf8OnUtf8() { |
| ByteString byteString = ByteString.copyFromUtf8("some text"); |
| AbstractMessageLite.checkByteStringIsUtf8(byteString); |
| // No exception thrown. |
| } |
| |
| public void testCheckByteStringIsUtf8OnNonUtf8() { |
| ByteString byteString = |
| ByteString.copyFrom(new byte[]{(byte) 0x80}); // A lone continuation byte. |
| try { |
| AbstractMessageLite.checkByteStringIsUtf8(byteString); |
| fail("Expected AbstractMessageLite.checkByteStringIsUtf8 to throw IllegalArgumentException"); |
| } catch (IllegalArgumentException exception) { |
| assertEquals("Byte string is not UTF-8.", exception.getMessage()); |
| } |
| } |
| |
| } |