// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // http://code.google.com/p/protobuf/ // // 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.UnittestProto; import protobuf_unittest.UnittestProto.TestAllExtensions; import protobuf_unittest.UnittestProto.TestAllTypes; import protobuf_unittest.UnittestProto.TestEmptyMessage; import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions; import junit.framework.TestCase; import java.util.Arrays; import java.util.Map; /** * Tests related to unknown field handling. * * @author kenton@google.com (Kenton Varda) */ public class UnknownFieldSetTest extends TestCase { public void setUp() throws Exception { descriptor = TestAllTypes.getDescriptor(); allFields = TestUtil.getAllSet(); allFieldsData = allFields.toByteString(); emptyMessage = TestEmptyMessage.parseFrom(allFieldsData); unknownFields = emptyMessage.getUnknownFields(); } UnknownFieldSet.Field getField(String name) { Descriptors.FieldDescriptor field = descriptor.findFieldByName(name); assertNotNull(field); return unknownFields.getField(field.getNumber()); } // Constructs a protocol buffer which contains fields with all the same // numbers as allFieldsData except that each field is some other wire // type. ByteString getBizarroData() throws Exception { UnknownFieldSet.Builder bizarroFields = UnknownFieldSet.newBuilder(); UnknownFieldSet.Field varintField = UnknownFieldSet.Field.newBuilder().addVarint(1).build(); UnknownFieldSet.Field fixed32Field = UnknownFieldSet.Field.newBuilder().addFixed32(1).build(); for (Map.Entry<Integer, UnknownFieldSet.Field> entry : unknownFields.asMap().entrySet()) { if (entry.getValue().getVarintList().isEmpty()) { // Original field is not a varint, so use a varint. bizarroFields.addField(entry.getKey(), varintField); } else { // Original field *is* a varint, so use something else. bizarroFields.addField(entry.getKey(), fixed32Field); } } return bizarroFields.build().toByteString(); } Descriptors.Descriptor descriptor; TestAllTypes allFields; ByteString allFieldsData; // An empty message that has been parsed from allFieldsData. So, it has // unknown fields of every type. TestEmptyMessage emptyMessage; UnknownFieldSet unknownFields; // ================================================================= public void testVarint() throws Exception { UnknownFieldSet.Field field = getField("optional_int32"); assertEquals(1, field.getVarintList().size()); assertEquals(allFields.getOptionalInt32(), (long) field.getVarintList().get(0)); } public void testFixed32() throws Exception { UnknownFieldSet.Field field = getField("optional_fixed32"); assertEquals(1, field.getFixed32List().size()); assertEquals(allFields.getOptionalFixed32(), (int) field.getFixed32List().get(0)); } public void testFixed64() throws Exception { UnknownFieldSet.Field field = getField("optional_fixed64"); assertEquals(1, field.getFixed64List().size()); assertEquals(allFields.getOptionalFixed64(), (long) field.getFixed64List().get(0)); } public void testLengthDelimited() throws Exception { UnknownFieldSet.Field field = getField("optional_bytes"); assertEquals(1, field.getLengthDelimitedList().size()); assertEquals(allFields.getOptionalBytes(), field.getLengthDelimitedList().get(0)); } public void testGroup() throws Exception { Descriptors.FieldDescriptor nestedFieldDescriptor = TestAllTypes.OptionalGroup.getDescriptor().findFieldByName("a"); assertNotNull(nestedFieldDescriptor); UnknownFieldSet.Field field = getField("optionalgroup"); assertEquals(1, field.getGroupList().size()); UnknownFieldSet group = field.getGroupList().get(0); assertEquals(1, group.asMap().size()); assertTrue(group.hasField(nestedFieldDescriptor.getNumber())); UnknownFieldSet.Field nestedField = group.getField(nestedFieldDescriptor.getNumber()); assertEquals(1, nestedField.getVarintList().size()); assertEquals(allFields.getOptionalGroup().getA(), (long) nestedField.getVarintList().get(0)); } public void testSerialize() throws Exception { // Check that serializing the UnknownFieldSet produces the original data // again. ByteString data = emptyMessage.toByteString(); assertEquals(allFieldsData, data); } public void testCopyFrom() throws Exception { TestEmptyMessage message = TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).build(); assertEquals(emptyMessage.toString(), message.toString()); } public void testMergeFrom() throws Exception { TestEmptyMessage source = TestEmptyMessage.newBuilder() .setUnknownFields( UnknownFieldSet.newBuilder() .addField(2, UnknownFieldSet.Field.newBuilder() .addVarint(2).build()) .addField(3, UnknownFieldSet.Field.newBuilder() .addVarint(4).build()) .build()) .build(); TestEmptyMessage destination = TestEmptyMessage.newBuilder() .setUnknownFields( UnknownFieldSet.newBuilder() .addField(1, UnknownFieldSet.Field.newBuilder() .addVarint(1).build()) .addField(3, UnknownFieldSet.Field.newBuilder() .addVarint(3).build()) .build()) .mergeFrom(source) .build(); assertEquals( "1: 1\n" + "2: 2\n" + "3: 3\n" + "3: 4\n", destination.toString()); } public void testClear() throws Exception { UnknownFieldSet fields = UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clear().build(); assertTrue(fields.asMap().isEmpty()); } public void testClearMessage() throws Exception { TestEmptyMessage message = TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).clear().build(); assertEquals(0, message.getSerializedSize()); } public void testParseKnownAndUnknown() throws Exception { // Test mixing known and unknown fields when parsing. UnknownFieldSet fields = UnknownFieldSet.newBuilder(unknownFields) .addField(123456, UnknownFieldSet.Field.newBuilder().addVarint(654321).build()) .build(); ByteString data = fields.toByteString(); TestAllTypes destination = TestAllTypes.parseFrom(data); TestUtil.assertAllFieldsSet(destination); assertEquals(1, destination.getUnknownFields().asMap().size()); UnknownFieldSet.Field field = destination.getUnknownFields().getField(123456); assertEquals(1, field.getVarintList().size()); assertEquals(654321, (long) field.getVarintList().get(0)); } public void testWrongTypeTreatedAsUnknown() throws Exception { // Test that fields of the wrong wire type are treated like unknown fields // when parsing. ByteString bizarroData = getBizarroData(); TestAllTypes allTypesMessage = TestAllTypes.parseFrom(bizarroData); TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData); // All fields should have been interpreted as unknown, so the debug strings // should be the same. assertEquals(emptyMessage.toString(), allTypesMessage.toString()); } public void testUnknownExtensions() throws Exception { // Make sure fields are properly parsed to the UnknownFieldSet even when // they are declared as extension numbers. TestEmptyMessageWithExtensions message = TestEmptyMessageWithExtensions.parseFrom(allFieldsData); assertEquals(unknownFields.asMap().size(), message.getUnknownFields().asMap().size()); assertEquals(allFieldsData, message.toByteString()); } public void testWrongExtensionTypeTreatedAsUnknown() throws Exception { // Test that fields of the wrong wire type are treated like unknown fields // when parsing extensions. ByteString bizarroData = getBizarroData(); TestAllExtensions allExtensionsMessage = TestAllExtensions.parseFrom(bizarroData); TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData); // All fields should have been interpreted as unknown, so the debug strings // should be the same. assertEquals(emptyMessage.toString(), allExtensionsMessage.toString()); } public void testParseUnknownEnumValue() throws Exception { Descriptors.FieldDescriptor singularField = TestAllTypes.getDescriptor().findFieldByName("optional_nested_enum"); Descriptors.FieldDescriptor repeatedField = TestAllTypes.getDescriptor().findFieldByName("repeated_nested_enum"); assertNotNull(singularField); assertNotNull(repeatedField); ByteString data = UnknownFieldSet.newBuilder() .addField(singularField.getNumber(), UnknownFieldSet.Field.newBuilder() .addVarint(TestAllTypes.NestedEnum.BAR.getNumber()) .addVarint(5) // not valid .build()) .addField(repeatedField.getNumber(), UnknownFieldSet.Field.newBuilder() .addVarint(TestAllTypes.NestedEnum.FOO.getNumber()) .addVarint(4) // not valid .addVarint(TestAllTypes.NestedEnum.BAZ.getNumber()) .addVarint(6) // not valid .build()) .build() .toByteString(); { TestAllTypes message = TestAllTypes.parseFrom(data); assertEquals(TestAllTypes.NestedEnum.BAR, message.getOptionalNestedEnum()); assertEquals( Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ), message.getRepeatedNestedEnumList()); assertEquals(Arrays.asList(5L), message.getUnknownFields() .getField(singularField.getNumber()) .getVarintList()); assertEquals(Arrays.asList(4L, 6L), message.getUnknownFields() .getField(repeatedField.getNumber()) .getVarintList()); } { TestAllExtensions message = TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry()); assertEquals(TestAllTypes.NestedEnum.BAR, message.getExtension(UnittestProto.optionalNestedEnumExtension)); assertEquals( Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ), message.getExtension(UnittestProto.repeatedNestedEnumExtension)); assertEquals(Arrays.asList(5L), message.getUnknownFields() .getField(singularField.getNumber()) .getVarintList()); assertEquals(Arrays.asList(4L, 6L), message.getUnknownFields() .getField(repeatedField.getNumber()) .getVarintList()); } } public void testLargeVarint() throws Exception { ByteString data = UnknownFieldSet.newBuilder() .addField(1, UnknownFieldSet.Field.newBuilder() .addVarint(0x7FFFFFFFFFFFFFFFL) .build()) .build() .toByteString(); UnknownFieldSet parsed = UnknownFieldSet.parseFrom(data); UnknownFieldSet.Field field = parsed.getField(1); assertEquals(1, field.getVarintList().size()); assertEquals(0x7FFFFFFFFFFFFFFFL, (long)field.getVarintList().get(0)); } public void testEqualsAndHashCode() { UnknownFieldSet.Field fixed32Field = UnknownFieldSet.Field.newBuilder() .addFixed32(1) .build(); UnknownFieldSet.Field fixed64Field = UnknownFieldSet.Field.newBuilder() .addFixed64(1) .build(); UnknownFieldSet.Field varIntField = UnknownFieldSet.Field.newBuilder() .addVarint(1) .build(); UnknownFieldSet.Field lengthDelimitedField = UnknownFieldSet.Field.newBuilder() .addLengthDelimited(ByteString.EMPTY) .build(); UnknownFieldSet.Field groupField = UnknownFieldSet.Field.newBuilder() .addGroup(unknownFields) .build(); UnknownFieldSet a = UnknownFieldSet.newBuilder() .addField(1, fixed32Field) .build(); UnknownFieldSet b = UnknownFieldSet.newBuilder() .addField(1, fixed64Field) .build(); UnknownFieldSet c = UnknownFieldSet.newBuilder() .addField(1, varIntField) .build(); UnknownFieldSet d = UnknownFieldSet.newBuilder() .addField(1, lengthDelimitedField) .build(); UnknownFieldSet e = UnknownFieldSet.newBuilder() .addField(1, groupField) .build(); checkEqualsIsConsistent(a); checkEqualsIsConsistent(b); checkEqualsIsConsistent(c); checkEqualsIsConsistent(d); checkEqualsIsConsistent(e); checkNotEqual(a, b); checkNotEqual(a, c); checkNotEqual(a, d); checkNotEqual(a, e); checkNotEqual(b, c); checkNotEqual(b, d); checkNotEqual(b, e); checkNotEqual(c, d); checkNotEqual(c, e); checkNotEqual(d, e); } /** * Asserts that the given field sets 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(UnknownFieldSet s1, UnknownFieldSet s2) { String equalsError = String.format("%s should not be equal to %s", s1, s2); assertFalse(equalsError, s1.equals(s2)); assertFalse(equalsError, s2.equals(s1)); assertFalse( String.format("%s should have a different hash code from %s", s1, s2), s1.hashCode() == s2.hashCode()); } /** * Asserts that the given field sets are equal and have identical hash codes. */ private void checkEqualsIsConsistent(UnknownFieldSet set) { // Object should be equal to itself. assertEquals(set, set); // Object should be equal to a copy of itself. UnknownFieldSet copy = UnknownFieldSet.newBuilder(set).build(); assertEquals(set, copy); assertEquals(copy, set); assertEquals(set.hashCode(), copy.hashCode()); } }