1// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.  All rights reserved.
3// http://code.google.com/p/protobuf/
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
31package com.google.protobuf;
32
33import protobuf_unittest.UnittestProto;
34import protobuf_unittest.UnittestProto.TestAllExtensions;
35import protobuf_unittest.UnittestProto.TestAllTypes;
36import protobuf_unittest.UnittestProto.TestEmptyMessage;
37import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions;
38
39import junit.framework.TestCase;
40
41import java.util.Arrays;
42import java.util.Map;
43
44/**
45 * Tests related to unknown field handling.
46 *
47 * @author kenton@google.com (Kenton Varda)
48 */
49public class UnknownFieldSetTest extends TestCase {
50  public void setUp() throws Exception {
51    descriptor = TestAllTypes.getDescriptor();
52    allFields = TestUtil.getAllSet();
53    allFieldsData = allFields.toByteString();
54    emptyMessage = TestEmptyMessage.parseFrom(allFieldsData);
55    unknownFields = emptyMessage.getUnknownFields();
56  }
57
58  UnknownFieldSet.Field getField(String name) {
59    Descriptors.FieldDescriptor field = descriptor.findFieldByName(name);
60    assertNotNull(field);
61    return unknownFields.getField(field.getNumber());
62  }
63
64  // Constructs a protocol buffer which contains fields with all the same
65  // numbers as allFieldsData except that each field is some other wire
66  // type.
67  ByteString getBizarroData() throws Exception {
68    UnknownFieldSet.Builder bizarroFields = UnknownFieldSet.newBuilder();
69
70    UnknownFieldSet.Field varintField =
71      UnknownFieldSet.Field.newBuilder().addVarint(1).build();
72    UnknownFieldSet.Field fixed32Field =
73      UnknownFieldSet.Field.newBuilder().addFixed32(1).build();
74
75    for (Map.Entry<Integer, UnknownFieldSet.Field> entry :
76         unknownFields.asMap().entrySet()) {
77      if (entry.getValue().getVarintList().isEmpty()) {
78        // Original field is not a varint, so use a varint.
79        bizarroFields.addField(entry.getKey(), varintField);
80      } else {
81        // Original field *is* a varint, so use something else.
82        bizarroFields.addField(entry.getKey(), fixed32Field);
83      }
84    }
85
86    return bizarroFields.build().toByteString();
87  }
88
89  Descriptors.Descriptor descriptor;
90  TestAllTypes allFields;
91  ByteString allFieldsData;
92
93  // An empty message that has been parsed from allFieldsData.  So, it has
94  // unknown fields of every type.
95  TestEmptyMessage emptyMessage;
96  UnknownFieldSet unknownFields;
97
98  // =================================================================
99
100  public void testVarint() throws Exception {
101    UnknownFieldSet.Field field = getField("optional_int32");
102    assertEquals(1, field.getVarintList().size());
103    assertEquals(allFields.getOptionalInt32(),
104                 (long) field.getVarintList().get(0));
105  }
106
107  public void testFixed32() throws Exception {
108    UnknownFieldSet.Field field = getField("optional_fixed32");
109    assertEquals(1, field.getFixed32List().size());
110    assertEquals(allFields.getOptionalFixed32(),
111                 (int) field.getFixed32List().get(0));
112  }
113
114  public void testFixed64() throws Exception {
115    UnknownFieldSet.Field field = getField("optional_fixed64");
116    assertEquals(1, field.getFixed64List().size());
117    assertEquals(allFields.getOptionalFixed64(),
118                 (long) field.getFixed64List().get(0));
119  }
120
121  public void testLengthDelimited() throws Exception {
122    UnknownFieldSet.Field field = getField("optional_bytes");
123    assertEquals(1, field.getLengthDelimitedList().size());
124    assertEquals(allFields.getOptionalBytes(),
125                 field.getLengthDelimitedList().get(0));
126  }
127
128  public void testGroup() throws Exception {
129    Descriptors.FieldDescriptor nestedFieldDescriptor =
130      TestAllTypes.OptionalGroup.getDescriptor().findFieldByName("a");
131    assertNotNull(nestedFieldDescriptor);
132
133    UnknownFieldSet.Field field = getField("optionalgroup");
134    assertEquals(1, field.getGroupList().size());
135
136    UnknownFieldSet group = field.getGroupList().get(0);
137    assertEquals(1, group.asMap().size());
138    assertTrue(group.hasField(nestedFieldDescriptor.getNumber()));
139
140    UnknownFieldSet.Field nestedField =
141      group.getField(nestedFieldDescriptor.getNumber());
142    assertEquals(1, nestedField.getVarintList().size());
143    assertEquals(allFields.getOptionalGroup().getA(),
144                 (long) nestedField.getVarintList().get(0));
145  }
146
147  public void testSerialize() throws Exception {
148    // Check that serializing the UnknownFieldSet produces the original data
149    // again.
150    ByteString data = emptyMessage.toByteString();
151    assertEquals(allFieldsData, data);
152  }
153
154  public void testCopyFrom() throws Exception {
155    TestEmptyMessage message =
156      TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).build();
157
158    assertEquals(emptyMessage.toString(), message.toString());
159  }
160
161  public void testMergeFrom() throws Exception {
162    TestEmptyMessage source =
163      TestEmptyMessage.newBuilder()
164        .setUnknownFields(
165          UnknownFieldSet.newBuilder()
166            .addField(2,
167              UnknownFieldSet.Field.newBuilder()
168                .addVarint(2).build())
169            .addField(3,
170              UnknownFieldSet.Field.newBuilder()
171                .addVarint(4).build())
172            .build())
173        .build();
174    TestEmptyMessage destination =
175      TestEmptyMessage.newBuilder()
176        .setUnknownFields(
177          UnknownFieldSet.newBuilder()
178            .addField(1,
179              UnknownFieldSet.Field.newBuilder()
180                .addVarint(1).build())
181            .addField(3,
182              UnknownFieldSet.Field.newBuilder()
183                .addVarint(3).build())
184            .build())
185        .mergeFrom(source)
186        .build();
187
188    assertEquals(
189      "1: 1\n" +
190      "2: 2\n" +
191      "3: 3\n" +
192      "3: 4\n",
193      destination.toString());
194  }
195
196  public void testClear() throws Exception {
197    UnknownFieldSet fields =
198      UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clear().build();
199    assertTrue(fields.asMap().isEmpty());
200  }
201
202  public void testClearMessage() throws Exception {
203    TestEmptyMessage message =
204      TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).clear().build();
205    assertEquals(0, message.getSerializedSize());
206  }
207
208  public void testParseKnownAndUnknown() throws Exception {
209    // Test mixing known and unknown fields when parsing.
210
211    UnknownFieldSet fields =
212      UnknownFieldSet.newBuilder(unknownFields)
213        .addField(123456,
214          UnknownFieldSet.Field.newBuilder().addVarint(654321).build())
215        .build();
216
217    ByteString data = fields.toByteString();
218    TestAllTypes destination = TestAllTypes.parseFrom(data);
219
220    TestUtil.assertAllFieldsSet(destination);
221    assertEquals(1, destination.getUnknownFields().asMap().size());
222
223    UnknownFieldSet.Field field =
224      destination.getUnknownFields().getField(123456);
225    assertEquals(1, field.getVarintList().size());
226    assertEquals(654321, (long) field.getVarintList().get(0));
227  }
228
229  public void testWrongTypeTreatedAsUnknown() throws Exception {
230    // Test that fields of the wrong wire type are treated like unknown fields
231    // when parsing.
232
233    ByteString bizarroData = getBizarroData();
234    TestAllTypes allTypesMessage = TestAllTypes.parseFrom(bizarroData);
235    TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData);
236
237    // All fields should have been interpreted as unknown, so the debug strings
238    // should be the same.
239    assertEquals(emptyMessage.toString(), allTypesMessage.toString());
240  }
241
242  public void testUnknownExtensions() throws Exception {
243    // Make sure fields are properly parsed to the UnknownFieldSet even when
244    // they are declared as extension numbers.
245
246    TestEmptyMessageWithExtensions message =
247      TestEmptyMessageWithExtensions.parseFrom(allFieldsData);
248
249    assertEquals(unknownFields.asMap().size(),
250                 message.getUnknownFields().asMap().size());
251    assertEquals(allFieldsData, message.toByteString());
252  }
253
254  public void testWrongExtensionTypeTreatedAsUnknown() throws Exception {
255    // Test that fields of the wrong wire type are treated like unknown fields
256    // when parsing extensions.
257
258    ByteString bizarroData = getBizarroData();
259    TestAllExtensions allExtensionsMessage =
260      TestAllExtensions.parseFrom(bizarroData);
261    TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData);
262
263    // All fields should have been interpreted as unknown, so the debug strings
264    // should be the same.
265    assertEquals(emptyMessage.toString(),
266                 allExtensionsMessage.toString());
267  }
268
269  public void testParseUnknownEnumValue() throws Exception {
270    Descriptors.FieldDescriptor singularField =
271      TestAllTypes.getDescriptor().findFieldByName("optional_nested_enum");
272    Descriptors.FieldDescriptor repeatedField =
273      TestAllTypes.getDescriptor().findFieldByName("repeated_nested_enum");
274    assertNotNull(singularField);
275    assertNotNull(repeatedField);
276
277    ByteString data =
278      UnknownFieldSet.newBuilder()
279        .addField(singularField.getNumber(),
280          UnknownFieldSet.Field.newBuilder()
281            .addVarint(TestAllTypes.NestedEnum.BAR.getNumber())
282            .addVarint(5)   // not valid
283            .build())
284        .addField(repeatedField.getNumber(),
285          UnknownFieldSet.Field.newBuilder()
286            .addVarint(TestAllTypes.NestedEnum.FOO.getNumber())
287            .addVarint(4)   // not valid
288            .addVarint(TestAllTypes.NestedEnum.BAZ.getNumber())
289            .addVarint(6)   // not valid
290            .build())
291        .build()
292        .toByteString();
293
294    {
295      TestAllTypes message = TestAllTypes.parseFrom(data);
296      assertEquals(TestAllTypes.NestedEnum.BAR,
297                   message.getOptionalNestedEnum());
298      assertEquals(
299        Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
300        message.getRepeatedNestedEnumList());
301      assertEquals(Arrays.asList(5L),
302                   message.getUnknownFields()
303                          .getField(singularField.getNumber())
304                          .getVarintList());
305      assertEquals(Arrays.asList(4L, 6L),
306                   message.getUnknownFields()
307                          .getField(repeatedField.getNumber())
308                          .getVarintList());
309    }
310
311    {
312      TestAllExtensions message =
313        TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
314      assertEquals(TestAllTypes.NestedEnum.BAR,
315        message.getExtension(UnittestProto.optionalNestedEnumExtension));
316      assertEquals(
317        Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
318        message.getExtension(UnittestProto.repeatedNestedEnumExtension));
319      assertEquals(Arrays.asList(5L),
320                   message.getUnknownFields()
321                          .getField(singularField.getNumber())
322                          .getVarintList());
323      assertEquals(Arrays.asList(4L, 6L),
324                   message.getUnknownFields()
325                          .getField(repeatedField.getNumber())
326                          .getVarintList());
327    }
328  }
329
330  public void testLargeVarint() throws Exception {
331    ByteString data =
332      UnknownFieldSet.newBuilder()
333        .addField(1,
334          UnknownFieldSet.Field.newBuilder()
335            .addVarint(0x7FFFFFFFFFFFFFFFL)
336            .build())
337        .build()
338        .toByteString();
339    UnknownFieldSet parsed = UnknownFieldSet.parseFrom(data);
340    UnknownFieldSet.Field field = parsed.getField(1);
341    assertEquals(1, field.getVarintList().size());
342    assertEquals(0x7FFFFFFFFFFFFFFFL, (long)field.getVarintList().get(0));
343  }
344
345  public void testEqualsAndHashCode() {
346    UnknownFieldSet.Field fixed32Field =
347        UnknownFieldSet.Field.newBuilder()
348            .addFixed32(1)
349            .build();
350    UnknownFieldSet.Field fixed64Field =
351        UnknownFieldSet.Field.newBuilder()
352            .addFixed64(1)
353            .build();
354    UnknownFieldSet.Field varIntField =
355        UnknownFieldSet.Field.newBuilder()
356            .addVarint(1)
357            .build();
358    UnknownFieldSet.Field lengthDelimitedField =
359        UnknownFieldSet.Field.newBuilder()
360            .addLengthDelimited(ByteString.EMPTY)
361            .build();
362    UnknownFieldSet.Field groupField =
363        UnknownFieldSet.Field.newBuilder()
364            .addGroup(unknownFields)
365            .build();
366
367    UnknownFieldSet a =
368        UnknownFieldSet.newBuilder()
369            .addField(1, fixed32Field)
370            .build();
371    UnknownFieldSet b =
372        UnknownFieldSet.newBuilder()
373            .addField(1, fixed64Field)
374            .build();
375    UnknownFieldSet c =
376        UnknownFieldSet.newBuilder()
377            .addField(1, varIntField)
378            .build();
379    UnknownFieldSet d =
380        UnknownFieldSet.newBuilder()
381            .addField(1, lengthDelimitedField)
382            .build();
383    UnknownFieldSet e =
384        UnknownFieldSet.newBuilder()
385            .addField(1, groupField)
386            .build();
387
388    checkEqualsIsConsistent(a);
389    checkEqualsIsConsistent(b);
390    checkEqualsIsConsistent(c);
391    checkEqualsIsConsistent(d);
392    checkEqualsIsConsistent(e);
393
394    checkNotEqual(a, b);
395    checkNotEqual(a, c);
396    checkNotEqual(a, d);
397    checkNotEqual(a, e);
398    checkNotEqual(b, c);
399    checkNotEqual(b, d);
400    checkNotEqual(b, e);
401    checkNotEqual(c, d);
402    checkNotEqual(c, e);
403    checkNotEqual(d, e);
404  }
405
406  /**
407   * Asserts that the given field sets are not equal and have different
408   * hash codes.
409   *
410   * @warning It's valid for non-equal objects to have the same hash code, so
411   *   this test is stricter than it needs to be. However, this should happen
412   *   relatively rarely.
413   */
414  private void checkNotEqual(UnknownFieldSet s1, UnknownFieldSet s2) {
415    String equalsError = String.format("%s should not be equal to %s", s1, s2);
416    assertFalse(equalsError, s1.equals(s2));
417    assertFalse(equalsError, s2.equals(s1));
418
419    assertFalse(
420        String.format("%s should have a different hash code from %s", s1, s2),
421        s1.hashCode() == s2.hashCode());
422  }
423
424  /**
425   * Asserts that the given field sets are equal and have identical hash codes.
426   */
427  private void checkEqualsIsConsistent(UnknownFieldSet set) {
428    // Object should be equal to itself.
429    assertEquals(set, set);
430
431    // Object should be equal to a copy of itself.
432    UnknownFieldSet copy = UnknownFieldSet.newBuilder(set).build();
433    assertEquals(set, copy);
434    assertEquals(copy, set);
435    assertEquals(set.hashCode(), copy.hashCode());
436  }
437}
438