content: // 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.test;
  import 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());
    }
  }
  