Home | History | Annotate | Download | only in protobuf
      1 // Protocol Buffers - Google's data interchange format
      2 // Copyright 2008 Google Inc.  All rights reserved.
      3 // https://developers.google.com/protocol-buffers/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are
      7 // met:
      8 //
      9 //     * Redistributions of source code must retain the above copyright
     10 // notice, this list of conditions and the following disclaimer.
     11 //     * Redistributions in binary form must reproduce the above
     12 // copyright notice, this list of conditions and the following disclaimer
     13 // in the documentation and/or other materials provided with the
     14 // distribution.
     15 //     * Neither the name of Google Inc. nor the names of its
     16 // contributors may be used to endorse or promote products derived from
     17 // this software without specific prior written permission.
     18 //
     19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30 
     31 package com.google.protobuf;
     32 
     33 import com.google.protobuf.Descriptors.FieldDescriptor;
     34 import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
     35 import protobuf_unittest.UnittestProto;
     36 import protobuf_unittest.UnittestProto.ForeignMessage;
     37 import protobuf_unittest.UnittestProto.TestAllExtensions;
     38 import protobuf_unittest.UnittestProto.TestAllTypes;
     39 import protobuf_unittest.UnittestProto.TestPackedTypes;
     40 import protobuf_unittest.UnittestProto.TestRequired;
     41 import protobuf_unittest.UnittestProto.TestRequiredForeign;
     42 import protobuf_unittest.UnittestProto.TestUnpackedTypes;
     43 
     44 import junit.framework.TestCase;
     45 
     46 import java.util.Map;
     47 
     48 /**
     49  * Unit test for {@link AbstractMessage}.
     50  *
     51  * @author kenton (at) google.com Kenton Varda
     52  */
     53 public class AbstractMessageTest extends TestCase {
     54   /**
     55    * Extends AbstractMessage and wraps some other message object.  The methods
     56    * of the Message interface which aren't explicitly implemented by
     57    * AbstractMessage are forwarded to the wrapped object.  This allows us to
     58    * test that AbstractMessage's implementations work even if the wrapped
     59    * object does not use them.
     60    */
     61   private static class AbstractMessageWrapper extends AbstractMessage {
     62     private final Message wrappedMessage;
     63 
     64     public AbstractMessageWrapper(Message wrappedMessage) {
     65       this.wrappedMessage = wrappedMessage;
     66     }
     67 
     68     public Descriptors.Descriptor getDescriptorForType() {
     69       return wrappedMessage.getDescriptorForType();
     70     }
     71     public AbstractMessageWrapper getDefaultInstanceForType() {
     72       return new AbstractMessageWrapper(
     73         wrappedMessage.getDefaultInstanceForType());
     74     }
     75     public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
     76       return wrappedMessage.getAllFields();
     77     }
     78     public boolean hasField(Descriptors.FieldDescriptor field) {
     79       return wrappedMessage.hasField(field);
     80     }
     81     public Object getField(Descriptors.FieldDescriptor field) {
     82       return wrappedMessage.getField(field);
     83     }
     84     public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
     85       return wrappedMessage.getRepeatedFieldCount(field);
     86     }
     87     public Object getRepeatedField(
     88         Descriptors.FieldDescriptor field, int index) {
     89       return wrappedMessage.getRepeatedField(field, index);
     90     }
     91     public UnknownFieldSet getUnknownFields() {
     92       return wrappedMessage.getUnknownFields();
     93     }
     94     public Builder newBuilderForType() {
     95       return new Builder(wrappedMessage.newBuilderForType());
     96     }
     97     public Builder toBuilder() {
     98       return new Builder(wrappedMessage.toBuilder());
     99     }
    100 
    101     static class Builder extends AbstractMessage.Builder<Builder> {
    102       private final Message.Builder wrappedBuilder;
    103 
    104       public Builder(Message.Builder wrappedBuilder) {
    105         this.wrappedBuilder = wrappedBuilder;
    106       }
    107 
    108       public AbstractMessageWrapper build() {
    109         return new AbstractMessageWrapper(wrappedBuilder.build());
    110       }
    111       public AbstractMessageWrapper buildPartial() {
    112         return new AbstractMessageWrapper(wrappedBuilder.buildPartial());
    113       }
    114       public Builder clone() {
    115         return new Builder(wrappedBuilder.clone());
    116       }
    117       public boolean isInitialized() {
    118         return clone().buildPartial().isInitialized();
    119       }
    120       public Descriptors.Descriptor getDescriptorForType() {
    121         return wrappedBuilder.getDescriptorForType();
    122       }
    123       public AbstractMessageWrapper getDefaultInstanceForType() {
    124         return new AbstractMessageWrapper(
    125           wrappedBuilder.getDefaultInstanceForType());
    126       }
    127       public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
    128         return wrappedBuilder.getAllFields();
    129       }
    130       public Builder newBuilderForField(Descriptors.FieldDescriptor field) {
    131         return new Builder(wrappedBuilder.newBuilderForField(field));
    132       }
    133       public boolean hasField(Descriptors.FieldDescriptor field) {
    134         return wrappedBuilder.hasField(field);
    135       }
    136       public Object getField(Descriptors.FieldDescriptor field) {
    137         return wrappedBuilder.getField(field);
    138       }
    139       public Builder setField(Descriptors.FieldDescriptor field, Object value) {
    140         wrappedBuilder.setField(field, value);
    141         return this;
    142       }
    143       public Builder clearField(Descriptors.FieldDescriptor field) {
    144         wrappedBuilder.clearField(field);
    145         return this;
    146       }
    147       public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
    148         return wrappedBuilder.getRepeatedFieldCount(field);
    149       }
    150       public Object getRepeatedField(
    151           Descriptors.FieldDescriptor field, int index) {
    152         return wrappedBuilder.getRepeatedField(field, index);
    153       }
    154       public Builder setRepeatedField(Descriptors.FieldDescriptor field,
    155                                       int index, Object value) {
    156         wrappedBuilder.setRepeatedField(field, index, value);
    157         return this;
    158       }
    159       public Builder addRepeatedField(
    160           Descriptors.FieldDescriptor field, Object value) {
    161         wrappedBuilder.addRepeatedField(field, value);
    162         return this;
    163       }
    164       public UnknownFieldSet getUnknownFields() {
    165         return wrappedBuilder.getUnknownFields();
    166       }
    167       public Builder setUnknownFields(UnknownFieldSet unknownFields) {
    168         wrappedBuilder.setUnknownFields(unknownFields);
    169         return this;
    170       }
    171       @Override
    172       public Message.Builder getFieldBuilder(FieldDescriptor field) {
    173         return wrappedBuilder.getFieldBuilder(field);
    174       }
    175     }
    176     public Parser<? extends Message> getParserForType() {
    177       return wrappedMessage.getParserForType();
    178     }
    179   }
    180 
    181   // =================================================================
    182 
    183   TestUtil.ReflectionTester reflectionTester =
    184     new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null);
    185 
    186   TestUtil.ReflectionTester extensionsReflectionTester =
    187     new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(),
    188                                   TestUtil.getExtensionRegistry());
    189 
    190   public void testClear() throws Exception {
    191     AbstractMessageWrapper message =
    192       new AbstractMessageWrapper.Builder(
    193           TestAllTypes.newBuilder(TestUtil.getAllSet()))
    194         .clear().build();
    195     TestUtil.assertClear((TestAllTypes) message.wrappedMessage);
    196   }
    197 
    198   public void testCopy() throws Exception {
    199     AbstractMessageWrapper message =
    200       new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder())
    201         .mergeFrom(TestUtil.getAllSet()).build();
    202     TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage);
    203   }
    204 
    205   public void testSerializedSize() throws Exception {
    206     TestAllTypes message = TestUtil.getAllSet();
    207     Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet());
    208 
    209     assertEquals(message.getSerializedSize(),
    210                  abstractMessage.getSerializedSize());
    211   }
    212 
    213   public void testSerialization() throws Exception {
    214     Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet());
    215 
    216     TestUtil.assertAllFieldsSet(
    217       TestAllTypes.parseFrom(abstractMessage.toByteString()));
    218 
    219     assertEquals(TestUtil.getAllSet().toByteString(),
    220                  abstractMessage.toByteString());
    221   }
    222 
    223   public void testParsing() throws Exception {
    224     AbstractMessageWrapper.Builder builder =
    225       new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder());
    226     AbstractMessageWrapper message =
    227       builder.mergeFrom(TestUtil.getAllSet().toByteString()).build();
    228     TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage);
    229   }
    230 
    231   public void testParsingUninitialized() throws Exception {
    232     TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder();
    233     builder.getOptionalMessageBuilder().setDummy2(10);
    234     ByteString bytes = builder.buildPartial().toByteString();
    235     Message.Builder abstractMessageBuilder =
    236         new AbstractMessageWrapper.Builder(TestRequiredForeign.newBuilder());
    237     // mergeFrom() should not throw initialization error.
    238     abstractMessageBuilder.mergeFrom(bytes).buildPartial();
    239     try {
    240       abstractMessageBuilder.mergeFrom(bytes).build();
    241       fail();
    242     } catch (UninitializedMessageException ex) {
    243       // pass
    244     }
    245 
    246     // test DynamicMessage directly.
    247     Message.Builder dynamicMessageBuilder = DynamicMessage.newBuilder(
    248         TestRequiredForeign.getDescriptor());
    249     // mergeFrom() should not throw initialization error.
    250     dynamicMessageBuilder.mergeFrom(bytes).buildPartial();
    251     try {
    252       dynamicMessageBuilder.mergeFrom(bytes).build();
    253       fail();
    254     } catch (UninitializedMessageException ex) {
    255       // pass
    256     }
    257   }
    258 
    259   public void testPackedSerialization() throws Exception {
    260     Message abstractMessage =
    261         new AbstractMessageWrapper(TestUtil.getPackedSet());
    262 
    263     TestUtil.assertPackedFieldsSet(
    264       TestPackedTypes.parseFrom(abstractMessage.toByteString()));
    265 
    266     assertEquals(TestUtil.getPackedSet().toByteString(),
    267                  abstractMessage.toByteString());
    268   }
    269 
    270   public void testPackedParsing() throws Exception {
    271     AbstractMessageWrapper.Builder builder =
    272       new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder());
    273     AbstractMessageWrapper message =
    274       builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build();
    275     TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage);
    276   }
    277 
    278   public void testUnpackedSerialization() throws Exception {
    279     Message abstractMessage =
    280       new AbstractMessageWrapper(TestUtil.getUnpackedSet());
    281 
    282     TestUtil.assertUnpackedFieldsSet(
    283       TestUnpackedTypes.parseFrom(abstractMessage.toByteString()));
    284 
    285     assertEquals(TestUtil.getUnpackedSet().toByteString(),
    286                  abstractMessage.toByteString());
    287   }
    288 
    289   public void testParsePackedToUnpacked() throws Exception {
    290     AbstractMessageWrapper.Builder builder =
    291       new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder());
    292     AbstractMessageWrapper message =
    293       builder.mergeFrom(TestUtil.getPackedSet().toByteString()).build();
    294     TestUtil.assertUnpackedFieldsSet(
    295       (TestUnpackedTypes) message.wrappedMessage);
    296   }
    297 
    298   public void testParseUnpackedToPacked() throws Exception {
    299     AbstractMessageWrapper.Builder builder =
    300       new AbstractMessageWrapper.Builder(TestPackedTypes.newBuilder());
    301     AbstractMessageWrapper message =
    302       builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build();
    303     TestUtil.assertPackedFieldsSet((TestPackedTypes) message.wrappedMessage);
    304   }
    305 
    306   public void testUnpackedParsing() throws Exception {
    307     AbstractMessageWrapper.Builder builder =
    308       new AbstractMessageWrapper.Builder(TestUnpackedTypes.newBuilder());
    309     AbstractMessageWrapper message =
    310       builder.mergeFrom(TestUtil.getUnpackedSet().toByteString()).build();
    311     TestUtil.assertUnpackedFieldsSet(
    312       (TestUnpackedTypes) message.wrappedMessage);
    313   }
    314 
    315   public void testOptimizedForSize() throws Exception {
    316     // We're mostly only checking that this class was compiled successfully.
    317     TestOptimizedForSize message =
    318       TestOptimizedForSize.newBuilder().setI(1).build();
    319     message = TestOptimizedForSize.parseFrom(message.toByteString());
    320     assertEquals(2, message.getSerializedSize());
    321   }
    322 
    323   // -----------------------------------------------------------------
    324   // Tests for isInitialized().
    325 
    326   private static final TestRequired TEST_REQUIRED_UNINITIALIZED =
    327     TestRequired.getDefaultInstance();
    328   private static final TestRequired TEST_REQUIRED_INITIALIZED =
    329     TestRequired.newBuilder().setA(1).setB(2).setC(3).build();
    330 
    331   public void testIsInitialized() throws Exception {
    332     TestRequired.Builder builder = TestRequired.newBuilder();
    333     AbstractMessageWrapper.Builder abstractBuilder =
    334       new AbstractMessageWrapper.Builder(builder);
    335 
    336     assertFalse(abstractBuilder.isInitialized());
    337     assertEquals("a, b, c", abstractBuilder.getInitializationErrorString());
    338     builder.setA(1);
    339     assertFalse(abstractBuilder.isInitialized());
    340     assertEquals("b, c", abstractBuilder.getInitializationErrorString());
    341     builder.setB(1);
    342     assertFalse(abstractBuilder.isInitialized());
    343     assertEquals("c", abstractBuilder.getInitializationErrorString());
    344     builder.setC(1);
    345     assertTrue(abstractBuilder.isInitialized());
    346     assertEquals("", abstractBuilder.getInitializationErrorString());
    347   }
    348 
    349   public void testForeignIsInitialized() throws Exception {
    350     TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder();
    351     AbstractMessageWrapper.Builder abstractBuilder =
    352       new AbstractMessageWrapper.Builder(builder);
    353 
    354     assertTrue(abstractBuilder.isInitialized());
    355     assertEquals("", abstractBuilder.getInitializationErrorString());
    356 
    357     builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED);
    358     assertFalse(abstractBuilder.isInitialized());
    359     assertEquals(
    360         "optional_message.a, optional_message.b, optional_message.c",
    361         abstractBuilder.getInitializationErrorString());
    362 
    363     builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED);
    364     assertTrue(abstractBuilder.isInitialized());
    365     assertEquals("", abstractBuilder.getInitializationErrorString());
    366 
    367     builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED);
    368     assertFalse(abstractBuilder.isInitialized());
    369     assertEquals(
    370         "repeated_message[0].a, repeated_message[0].b, repeated_message[0].c",
    371         abstractBuilder.getInitializationErrorString());
    372 
    373     builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED);
    374     assertTrue(abstractBuilder.isInitialized());
    375     assertEquals("", abstractBuilder.getInitializationErrorString());
    376   }
    377 
    378   // -----------------------------------------------------------------
    379   // Tests for mergeFrom
    380 
    381   static final TestAllTypes MERGE_SOURCE =
    382     TestAllTypes.newBuilder()
    383       .setOptionalInt32(1)
    384       .setOptionalString("foo")
    385       .setOptionalForeignMessage(ForeignMessage.getDefaultInstance())
    386       .addRepeatedString("bar")
    387       .build();
    388 
    389   static final TestAllTypes MERGE_DEST =
    390     TestAllTypes.newBuilder()
    391       .setOptionalInt64(2)
    392       .setOptionalString("baz")
    393       .setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build())
    394       .addRepeatedString("qux")
    395       .build();
    396 
    397   static final String MERGE_RESULT_TEXT =
    398       "optional_int32: 1\n" +
    399       "optional_int64: 2\n" +
    400       "optional_string: \"foo\"\n" +
    401       "optional_foreign_message {\n" +
    402       "  c: 3\n" +
    403       "}\n" +
    404       "repeated_string: \"qux\"\n" +
    405       "repeated_string: \"bar\"\n";
    406 
    407   public void testMergeFrom() throws Exception {
    408     AbstractMessageWrapper result =
    409       new AbstractMessageWrapper.Builder(
    410         TestAllTypes.newBuilder(MERGE_DEST))
    411       .mergeFrom(MERGE_SOURCE).build();
    412 
    413     assertEquals(MERGE_RESULT_TEXT, result.toString());
    414   }
    415 
    416   // -----------------------------------------------------------------
    417   // Tests for equals and hashCode
    418 
    419   public void testEqualsAndHashCode() throws Exception {
    420     TestAllTypes a = TestUtil.getAllSet();
    421     TestAllTypes b = TestAllTypes.newBuilder().build();
    422     TestAllTypes c = TestAllTypes.newBuilder(b).addRepeatedString("x").build();
    423     TestAllTypes d = TestAllTypes.newBuilder(c).addRepeatedString("y").build();
    424     TestAllExtensions e = TestUtil.getAllExtensionsSet();
    425     TestAllExtensions f = TestAllExtensions.newBuilder(e)
    426         .addExtension(UnittestProto.repeatedInt32Extension, 999).build();
    427 
    428     checkEqualsIsConsistent(a);
    429     checkEqualsIsConsistent(b);
    430     checkEqualsIsConsistent(c);
    431     checkEqualsIsConsistent(d);
    432     checkEqualsIsConsistent(e);
    433     checkEqualsIsConsistent(f);
    434 
    435     checkNotEqual(a, b);
    436     checkNotEqual(a, c);
    437     checkNotEqual(a, d);
    438     checkNotEqual(a, e);
    439     checkNotEqual(a, f);
    440 
    441     checkNotEqual(b, c);
    442     checkNotEqual(b, d);
    443     checkNotEqual(b, e);
    444     checkNotEqual(b, f);
    445 
    446     checkNotEqual(c, d);
    447     checkNotEqual(c, e);
    448     checkNotEqual(c, f);
    449 
    450     checkNotEqual(d, e);
    451     checkNotEqual(d, f);
    452 
    453     checkNotEqual(e, f);
    454 
    455     // Deserializing into the TestEmptyMessage such that every field
    456     // is an {@link UnknownFieldSet.Field}.
    457     UnittestProto.TestEmptyMessage eUnknownFields =
    458         UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
    459     UnittestProto.TestEmptyMessage fUnknownFields =
    460         UnittestProto.TestEmptyMessage.parseFrom(f.toByteArray());
    461     checkNotEqual(eUnknownFields, fUnknownFields);
    462     checkEqualsIsConsistent(eUnknownFields);
    463     checkEqualsIsConsistent(fUnknownFields);
    464 
    465     // Subsequent reconstitutions should be identical
    466     UnittestProto.TestEmptyMessage eUnknownFields2 =
    467         UnittestProto.TestEmptyMessage.parseFrom(e.toByteArray());
    468     checkEqualsIsConsistent(eUnknownFields, eUnknownFields2);
    469   }
    470 
    471 
    472   /**
    473    * Asserts that the given proto has symmetric equals and hashCode methods.
    474    */
    475   private void checkEqualsIsConsistent(Message message) {
    476     // Object should be equal to itself.
    477     assertEquals(message, message);
    478 
    479     // Object should be equal to a dynamic copy of itself.
    480     DynamicMessage dynamic = DynamicMessage.newBuilder(message).build();
    481     checkEqualsIsConsistent(message, dynamic);
    482   }
    483 
    484   /**
    485    * Asserts that the given protos are equal and have the same hash code.
    486    */
    487   private void checkEqualsIsConsistent(Message message1, Message message2) {
    488     assertEquals(message1, message2);
    489     assertEquals(message2, message1);
    490     assertEquals(message2.hashCode(), message1.hashCode());
    491   }
    492 
    493   /**
    494    * Asserts that the given protos are not equal and have different hash codes.
    495    *
    496    * @warning It's valid for non-equal objects to have the same hash code, so
    497    *   this test is stricter than it needs to be. However, this should happen
    498    *   relatively rarely.
    499    */
    500   private void checkNotEqual(Message m1, Message m2) {
    501     String equalsError = String.format("%s should not be equal to %s", m1, m2);
    502     assertFalse(equalsError, m1.equals(m2));
    503     assertFalse(equalsError, m2.equals(m1));
    504 
    505     assertFalse(
    506         String.format("%s should have a different hash code from %s", m1, m2),
    507         m1.hashCode() == m2.hashCode());
    508   }
    509 
    510   public void testCheckByteStringIsUtf8OnUtf8() {
    511     ByteString byteString = ByteString.copyFromUtf8("some text");
    512     AbstractMessageLite.checkByteStringIsUtf8(byteString);
    513     // No exception thrown.
    514   }
    515 
    516   public void testCheckByteStringIsUtf8OnNonUtf8() {
    517     ByteString byteString =
    518         ByteString.copyFrom(new byte[]{(byte) 0x80}); // A lone continuation byte.
    519     try {
    520       AbstractMessageLite.checkByteStringIsUtf8(byteString);
    521       fail("Expected AbstractMessageLite.checkByteStringIsUtf8 to throw IllegalArgumentException");
    522     } catch (IllegalArgumentException exception) {
    523       assertEquals("Byte string is not UTF-8.", exception.getMessage());
    524     }
    525   }
    526 
    527 }
    528