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.Descriptor;
34 import com.google.protobuf.Descriptors.EnumDescriptor;
35 import com.google.protobuf.Descriptors.EnumValueDescriptor;
36 import com.google.protobuf.Descriptors.FieldDescriptor;
37 import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
38 import com.google.protobuf.TextFormat.ParseException;
39 
40 import junit.framework.TestCase;
41 
42 /**
43  * Unit tests for protos that keep unknown enum values rather than discard
44  * them as unknown fields.
45  */
46 public class UnknownEnumValueTest extends TestCase {
testUnknownEnumValues()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 
testUnknownEnumValueInReflectionApi()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 
testUnknownEnumValueWithDynamicMessage()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 
testUnknownEnumValuesInTextFormat()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