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
31package com.google.protobuf;
32
33import com.google.protobuf.Descriptors.Descriptor;
34import com.google.protobuf.Descriptors.EnumDescriptor;
35import com.google.protobuf.Descriptors.EnumValueDescriptor;
36import com.google.protobuf.Descriptors.FieldDescriptor;
37import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
38import com.google.protobuf.TextFormat.ParseException;
39
40import junit.framework.TestCase;
41
42/**
43 * Unit tests for protos that keep unknown enum values rather than discard
44 * them as unknown fields.
45 */
46public class UnknownEnumValueTest extends TestCase {
47  public void testUnknownEnumValues() throws Exception {
48    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
49    builder.setOptionalNestedEnumValue(4321);
50    builder.addRepeatedNestedEnumValue(5432);
51    builder.addPackedNestedEnumValue(6543);
52    TestAllTypes message = builder.build();
53    assertEquals(4321, message.getOptionalNestedEnumValue());
54    assertEquals(5432, message.getRepeatedNestedEnumValue(0));
55    assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue());
56    assertEquals(6543, message.getPackedNestedEnumValue(0));
57    // Returns UNRECOGNIZED if an enum type is requested.
58    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum());
59    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0));
60    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0));
61    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0));
62
63    // Test serialization and parsing.
64    ByteString data = message.toByteString();
65    message = TestAllTypes.parseFrom(data);
66    assertEquals(4321, message.getOptionalNestedEnumValue());
67    assertEquals(5432, message.getRepeatedNestedEnumValue(0));
68    assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue());
69    assertEquals(6543, message.getPackedNestedEnumValue(0));
70    // Returns UNRECOGNIZED if an enum type is requested.
71    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum());
72    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0));
73    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0));
74    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0));
75
76    // Test toBuilder().
77    builder = message.toBuilder();
78    assertEquals(4321, builder.getOptionalNestedEnumValue());
79    assertEquals(5432, builder.getRepeatedNestedEnumValue(0));
80    assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue());
81    assertEquals(6543, builder.getPackedNestedEnumValue(0));
82    // Returns UNRECOGNIZED if an enum type is requested.
83    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum());
84    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0));
85    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0));
86    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0));
87
88    // Test mergeFrom().
89    builder = TestAllTypes.newBuilder().mergeFrom(message);
90    assertEquals(4321, builder.getOptionalNestedEnumValue());
91    assertEquals(5432, builder.getRepeatedNestedEnumValue(0));
92    assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue());
93    assertEquals(6543, builder.getPackedNestedEnumValue(0));
94    // Returns UNRECOGNIZED if an enum type is requested.
95    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum());
96    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0));
97    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0));
98    assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0));
99
100    // Test equals() and hashCode()
101    TestAllTypes sameMessage = builder.build();
102    assertEquals(message, sameMessage);
103    assertEquals(message.hashCode(), sameMessage.hashCode());
104
105    // Getting the numeric value of UNRECOGNIZED will throw an exception.
106    try {
107      TestAllTypes.NestedEnum.UNRECOGNIZED.getNumber();
108      fail("Exception is expected.");
109    } catch (IllegalArgumentException e) {
110      // Expected.
111    }
112
113    // Setting an enum field to an UNRECOGNIZED value will throw an exception.
114    try {
115      builder.setOptionalNestedEnum(builder.getOptionalNestedEnum());
116      fail("Exception is expected.");
117    } catch (IllegalArgumentException e) {
118      // Expected.
119    }
120    try {
121      builder.addRepeatedNestedEnum(builder.getOptionalNestedEnum());
122      fail("Exception is expected.");
123    } catch (IllegalArgumentException e) {
124      // Expected.
125    }
126  }
127
128  public void testUnknownEnumValueInReflectionApi() throws Exception {
129    Descriptor descriptor = TestAllTypes.getDescriptor();
130    FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
131    FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum");
132    FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum");
133    EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
134
135    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
136    builder.setField(optionalNestedEnumField,
137        enumType.findValueByNumberCreatingIfUnknown(4321));
138    builder.addRepeatedField(repeatedNestedEnumField,
139        enumType.findValueByNumberCreatingIfUnknown(5432));
140    builder.addRepeatedField(packedNestedEnumField,
141        enumType.findValueByNumberCreatingIfUnknown(6543));
142    TestAllTypes message = builder.build();
143
144    // Getters will return unknown enum values as EnumValueDescriptor.
145    EnumValueDescriptor unknown4321 =
146        (EnumValueDescriptor) message.getField(optionalNestedEnumField);
147    EnumValueDescriptor unknown5432 =
148        (EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0);
149    EnumValueDescriptor unknown6543 =
150        (EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0);
151    assertEquals(4321, unknown4321.getNumber());
152    assertEquals(5432, unknown5432.getNumber());
153    assertEquals(6543, unknown6543.getNumber());
154
155    // Unknown EnumValueDescriptor will map to UNRECOGNIZED.
156    assertEquals(
157        TestAllTypes.NestedEnum.valueOf(unknown4321),
158        TestAllTypes.NestedEnum.UNRECOGNIZED);
159    assertEquals(
160        TestAllTypes.NestedEnum.valueOf(unknown5432),
161        TestAllTypes.NestedEnum.UNRECOGNIZED);
162    assertEquals(
163        TestAllTypes.NestedEnum.valueOf(unknown6543),
164        TestAllTypes.NestedEnum.UNRECOGNIZED);
165
166    // Setters also accept unknown EnumValueDescriptor.
167    builder.setField(optionalNestedEnumField, unknown6543);
168    builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321);
169    builder.setRepeatedField(packedNestedEnumField, 0, unknown5432);
170    message = builder.build();
171    // Like other descriptors, unknown EnumValueDescriptor can be compared by
172    // object identity.
173    assertTrue(unknown6543 == message.getField(optionalNestedEnumField));
174    assertTrue(unknown4321 == message.getRepeatedField(repeatedNestedEnumField, 0));
175    assertTrue(unknown5432 == message.getRepeatedField(packedNestedEnumField, 0));
176  }
177
178  public void testUnknownEnumValueWithDynamicMessage() throws Exception {
179    Descriptor descriptor = TestAllTypes.getDescriptor();
180    FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
181    FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum");
182    FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum");
183    EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
184
185    Message dynamicMessageDefaultInstance = DynamicMessage.getDefaultInstance(descriptor);
186
187    Message.Builder builder = dynamicMessageDefaultInstance.newBuilderForType();
188    builder.setField(optionalNestedEnumField,
189        enumType.findValueByNumberCreatingIfUnknown(4321));
190    builder.addRepeatedField(repeatedNestedEnumField,
191        enumType.findValueByNumberCreatingIfUnknown(5432));
192    builder.addRepeatedField(packedNestedEnumField,
193        enumType.findValueByNumberCreatingIfUnknown(6543));
194    Message message = builder.build();
195    assertEquals(4321,
196        ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber());
197    assertEquals(5432,
198        ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber());
199    assertEquals(6543,
200        ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber());
201
202    // Test reflection based serialization/parsing implementation.
203    ByteString data = message.toByteString();
204    message = dynamicMessageDefaultInstance
205        .newBuilderForType()
206        .mergeFrom(data)
207        .build();
208    assertEquals(4321,
209        ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber());
210    assertEquals(5432,
211        ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber());
212    assertEquals(6543,
213        ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber());
214
215    // Test reflection based equals()/hashCode().
216    builder = dynamicMessageDefaultInstance.newBuilderForType();
217    builder.setField(optionalNestedEnumField,
218        enumType.findValueByNumberCreatingIfUnknown(4321));
219    builder.addRepeatedField(repeatedNestedEnumField,
220        enumType.findValueByNumberCreatingIfUnknown(5432));
221    builder.addRepeatedField(packedNestedEnumField,
222        enumType.findValueByNumberCreatingIfUnknown(6543));
223    Message sameMessage = builder.build();
224    assertEquals(message, sameMessage);
225    assertEquals(message.hashCode(), sameMessage.hashCode());
226    builder.setField(optionalNestedEnumField,
227        enumType.findValueByNumberCreatingIfUnknown(0));
228    Message differentMessage = builder.build();
229    assertFalse(message.equals(differentMessage));
230  }
231
232  public void testUnknownEnumValuesInTextFormat() {
233    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
234    builder.setOptionalNestedEnumValue(4321);
235    builder.addRepeatedNestedEnumValue(5432);
236    builder.addPackedNestedEnumValue(6543);
237    TestAllTypes message = builder.build();
238
239    // We can print a message with unknown enum values.
240    String textData = TextFormat.printToString(message);
241    assertEquals(
242        "optional_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_4321\n"
243        + "repeated_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_5432\n"
244        + "packed_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_6543\n", textData);
245
246    // Parsing unknown enum values will fail just like parsing other kinds of
247    // unknown fields.
248    try {
249      TextFormat.merge(textData, builder);
250      fail();
251    } catch (ParseException e) {
252      // expected.
253    }
254  }
255}
256