• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 package com.google.protobuf;
9 
10 import static com.google.common.truth.Truth.assertThat;
11 import static com.google.common.truth.Truth.assertWithMessage;
12 
13 import com.google.protobuf.Descriptors.Descriptor;
14 import com.google.protobuf.Descriptors.EnumDescriptor;
15 import com.google.protobuf.Descriptors.EnumValueDescriptor;
16 import com.google.protobuf.Descriptors.FieldDescriptor;
17 import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
18 import com.google.protobuf.Proto2UnknownEnumValuesTestProto.Proto2EnumMessage;
19 import com.google.protobuf.Proto2UnknownEnumValuesTestProto.Proto2EnumMessageWithEnumSubset;
20 import com.google.protobuf.Proto2UnknownEnumValuesTestProto.Proto2TestEnum;
21 import com.google.protobuf.Proto2UnknownEnumValuesTestProto.Proto2TestEnumSubset;
22 import org.junit.Test;
23 import org.junit.runner.RunWith;
24 import org.junit.runners.JUnit4;
25 
26 /**
27  * Unit tests for protos that keep unknown enum values rather than discard them as unknown fields.
28  */
29 @RunWith(JUnit4.class)
30 public class UnknownEnumValueTest {
31 
32   @Test
33   @SuppressWarnings("ProtoNewBuilderMergeFrom")
testUnknownEnumValues()34   public void testUnknownEnumValues() throws Exception {
35     assertThat(TestAllTypes.NestedEnum.getDescriptor().isClosed()).isFalse();
36     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
37     builder.setOptionalNestedEnumValue(4321);
38     builder.addRepeatedNestedEnumValue(5432);
39     builder.addPackedNestedEnumValue(6543);
40     TestAllTypes message = builder.build();
41     assertThat(message.getOptionalNestedEnumValue()).isEqualTo(4321);
42     assertThat(message.getRepeatedNestedEnumValue(0)).isEqualTo(5432);
43     assertThat(message.getRepeatedNestedEnumValueList().get(0).intValue()).isEqualTo(5432);
44     assertThat(message.getPackedNestedEnumValue(0)).isEqualTo(6543);
45     // Returns UNRECOGNIZED if an enum type is requested.
46     assertThat(message.getOptionalNestedEnum()).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
47     assertThat(message.getRepeatedNestedEnum(0)).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
48     assertThat(message.getRepeatedNestedEnumList().get(0))
49         .isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
50     assertThat(message.getPackedNestedEnum(0)).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
51 
52     // Test serialization and parsing.
53     ByteString data = message.toByteString();
54     message = TestAllTypes.parseFrom(data);
55     assertThat(message.getOptionalNestedEnumValue()).isEqualTo(4321);
56     assertThat(message.getRepeatedNestedEnumValue(0)).isEqualTo(5432);
57     assertThat(message.getRepeatedNestedEnumValueList().get(0).intValue()).isEqualTo(5432);
58     assertThat(message.getPackedNestedEnumValue(0)).isEqualTo(6543);
59     // Returns UNRECOGNIZED if an enum type is requested.
60     assertThat(message.getOptionalNestedEnum()).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
61     assertThat(message.getRepeatedNestedEnum(0)).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
62     assertThat(message.getRepeatedNestedEnumList().get(0))
63         .isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
64     assertThat(message.getPackedNestedEnum(0)).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
65 
66     // Test toBuilder().
67     builder = message.toBuilder();
68     assertThat(builder.getOptionalNestedEnumValue()).isEqualTo(4321);
69     assertThat(builder.getRepeatedNestedEnumValue(0)).isEqualTo(5432);
70     assertThat(builder.getRepeatedNestedEnumValueList().get(0).intValue()).isEqualTo(5432);
71     assertThat(builder.getPackedNestedEnumValue(0)).isEqualTo(6543);
72     // Returns UNRECOGNIZED if an enum type is requested.
73     assertThat(builder.getOptionalNestedEnum()).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
74     assertThat(builder.getRepeatedNestedEnum(0)).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
75     assertThat(builder.getRepeatedNestedEnumList().get(0))
76         .isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
77     assertThat(builder.getPackedNestedEnum(0)).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
78 
79     // Test mergeFrom().
80     builder = TestAllTypes.newBuilder().mergeFrom(message);
81     assertThat(builder.getOptionalNestedEnumValue()).isEqualTo(4321);
82     assertThat(builder.getRepeatedNestedEnumValue(0)).isEqualTo(5432);
83     assertThat(builder.getRepeatedNestedEnumValueList().get(0).intValue()).isEqualTo(5432);
84     assertThat(builder.getPackedNestedEnumValue(0)).isEqualTo(6543);
85     // Returns UNRECOGNIZED if an enum type is requested.
86     assertThat(builder.getOptionalNestedEnum()).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
87     assertThat(builder.getRepeatedNestedEnum(0)).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
88     assertThat(builder.getRepeatedNestedEnumList().get(0))
89         .isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
90     assertThat(builder.getPackedNestedEnum(0)).isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
91 
92     // Test equals() and hashCode()
93     TestAllTypes sameMessage = builder.build();
94     assertThat(sameMessage).isEqualTo(message);
95     assertThat(sameMessage.hashCode()).isEqualTo(message.hashCode());
96 
97     // Getting the numeric value of UNRECOGNIZED will throw an exception.
98     try {
99       TestAllTypes.NestedEnum.UNRECOGNIZED.getNumber();
100       assertWithMessage("Exception is expected.").fail();
101     } catch (IllegalArgumentException e) {
102       // Expected.
103     }
104 
105     // Setting an enum field to an UNRECOGNIZED value will throw an exception.
106     try {
107       builder.setOptionalNestedEnum(builder.getOptionalNestedEnum());
108       assertWithMessage("Exception is expected.").fail();
109     } catch (IllegalArgumentException e) {
110       // Expected.
111     }
112     try {
113       builder.addRepeatedNestedEnum(builder.getOptionalNestedEnum());
114       assertWithMessage("Exception is expected.").fail();
115     } catch (IllegalArgumentException e) {
116       // Expected.
117     }
118   }
119 
120   @Test
testUnknownEnumValueInReflectionApi()121   public void testUnknownEnumValueInReflectionApi() throws Exception {
122     Descriptor descriptor = TestAllTypes.getDescriptor();
123     FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
124     FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum");
125     FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum");
126     EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
127 
128     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
129     builder.setField(optionalNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(4321));
130     builder.addRepeatedField(
131         repeatedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(5432));
132     builder.addRepeatedField(
133         packedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(6543));
134     TestAllTypes message = builder.build();
135 
136     // Getters will return unknown enum values as EnumValueDescriptor.
137     EnumValueDescriptor unknown4321 =
138         (EnumValueDescriptor) message.getField(optionalNestedEnumField);
139     EnumValueDescriptor unknown5432 =
140         (EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0);
141     EnumValueDescriptor unknown6543 =
142         (EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0);
143     assertThat(unknown4321.getNumber()).isEqualTo(4321);
144     assertThat(unknown5432.getNumber()).isEqualTo(5432);
145     assertThat(unknown6543.getNumber()).isEqualTo(6543);
146 
147     // Unknown EnumValueDescriptor will map to UNRECOGNIZED.
148     assertThat(TestAllTypes.NestedEnum.valueOf(unknown4321))
149         .isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
150     assertThat(TestAllTypes.NestedEnum.valueOf(unknown5432))
151         .isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
152     assertThat(TestAllTypes.NestedEnum.valueOf(unknown6543))
153         .isEqualTo(TestAllTypes.NestedEnum.UNRECOGNIZED);
154 
155     // Setters also accept unknown EnumValueDescriptor.
156     builder.setField(optionalNestedEnumField, unknown6543);
157     builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321);
158     builder.setRepeatedField(packedNestedEnumField, 0, unknown5432);
159     message = builder.build();
160     // Like other descriptors, unknown EnumValueDescriptor can be compared by
161     // object identity.
162     assertThat(unknown6543).isSameInstanceAs(message.getField(optionalNestedEnumField));
163     assertThat(unknown4321).isSameInstanceAs(message.getRepeatedField(repeatedNestedEnumField, 0));
164     assertThat(unknown5432).isSameInstanceAs(message.getRepeatedField(packedNestedEnumField, 0));
165   }
166 
167   @Test
testUnknownEnumValueWithDynamicMessage()168   public void testUnknownEnumValueWithDynamicMessage() throws Exception {
169     Descriptor descriptor = TestAllTypes.getDescriptor();
170     FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
171     FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum");
172     FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum");
173     EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor();
174 
175     Message dynamicMessageDefaultInstance = DynamicMessage.getDefaultInstance(descriptor);
176 
177     Message.Builder builder = dynamicMessageDefaultInstance.newBuilderForType();
178     builder.setField(optionalNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(4321));
179     builder.addRepeatedField(
180         repeatedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(5432));
181     builder.addRepeatedField(
182         packedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(6543));
183     Message message = builder.build();
184     assertThat(((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber())
185         .isEqualTo(4321);
186     assertThat(
187             ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0))
188                 .getNumber())
189         .isEqualTo(5432);
190     assertThat(
191             ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber())
192         .isEqualTo(6543);
193 
194     // Test reflection based serialization/parsing implementation.
195     ByteString data = message.toByteString();
196     message = dynamicMessageDefaultInstance.newBuilderForType().mergeFrom(data).build();
197     assertThat(((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber())
198         .isEqualTo(4321);
199     assertThat(
200             ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0))
201                 .getNumber())
202         .isEqualTo(5432);
203     assertThat(
204             ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber())
205         .isEqualTo(6543);
206 
207     // Test reflection based equals()/hashCode().
208     builder = dynamicMessageDefaultInstance.newBuilderForType();
209     builder.setField(optionalNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(4321));
210     builder.addRepeatedField(
211         repeatedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(5432));
212     builder.addRepeatedField(
213         packedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(6543));
214     Message sameMessage = builder.build();
215     assertThat(sameMessage).isEqualTo(message);
216     assertThat(sameMessage.hashCode()).isEqualTo(message.hashCode());
217     builder.setField(optionalNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(0));
218     Message differentMessage = builder.build();
219     assertThat(message.equals(differentMessage)).isFalse();
220   }
221 
222   @Test
testUnknownEnumValuesInTextFormat()223   public void testUnknownEnumValuesInTextFormat() throws Exception {
224     TestAllTypes.Builder builder = TestAllTypes.newBuilder();
225     builder.setOptionalNestedEnumValue(4321);
226     builder.addRepeatedNestedEnumValue(5432);
227     builder.addPackedNestedEnumValue(6543);
228     TestAllTypes message = builder.build();
229 
230     // We can print a message with unknown enum values.
231     String textData = TextFormat.printer().printToString(message);
232     assertThat(textData)
233         .isEqualTo(
234             "optional_nested_enum: 4321\n"
235                 + "repeated_nested_enum: 5432\n"
236                 + "packed_nested_enum: 6543\n");
237 
238     builder.clear();
239     TextFormat.merge(textData, builder);
240     assertThat(message.equals(builder.build())).isTrue();
241   }
242 
243   @Test
testUnknownEnumValuesInProto2()244   public void testUnknownEnumValuesInProto2() throws Exception {
245     assertThat(Proto2TestEnum.getDescriptor().isClosed()).isTrue();
246     Proto2EnumMessage.Builder sourceMessage = Proto2EnumMessage.newBuilder();
247     sourceMessage
248         .addRepeatedPackedEnum(Proto2TestEnum.ZERO)
249         .addRepeatedPackedEnum(Proto2TestEnum.TWO) // Unknown in parsed proto
250         .addRepeatedPackedEnum(Proto2TestEnum.ONE);
251 
252     Proto2EnumMessageWithEnumSubset destMessage =
253         Proto2EnumMessageWithEnumSubset.parseFrom(sourceMessage.build().toByteArray());
254 
255     // Known enum values should be preserved.
256     assertThat(destMessage.getRepeatedPackedEnumCount()).isEqualTo(2);
257     assertThat(destMessage.getRepeatedPackedEnum(0))
258         .isEqualTo(Proto2TestEnumSubset.TESTENUM_SUBSET_ZERO);
259     assertThat(destMessage.getRepeatedPackedEnum(1))
260         .isEqualTo(Proto2TestEnumSubset.TESTENUM_SUBSET_ONE);
261 
262     // Unknown enum values should be found in UnknownFieldSet.
263     UnknownFieldSet unknown = destMessage.getUnknownFields();
264     assertThat(
265             unknown
266                 .getField(Proto2EnumMessageWithEnumSubset.REPEATED_PACKED_ENUM_FIELD_NUMBER)
267                 .getVarintList()
268                 .get(0)
269                 .longValue())
270         .isEqualTo(Proto2TestEnum.TWO_VALUE);
271   }
272 
273   @Test
testUnknownEnumValuesInProto2WithDynamicMessage()274   public void testUnknownEnumValuesInProto2WithDynamicMessage() throws Exception {
275     Descriptor descriptor = Proto2EnumMessageWithEnumSubset.getDescriptor();
276     FieldDescriptor repeatedPackedField = descriptor.findFieldByName("repeated_packed_enum");
277 
278     Proto2EnumMessage.Builder sourceMessage = Proto2EnumMessage.newBuilder();
279     sourceMessage
280         .addRepeatedPackedEnum(Proto2TestEnum.ZERO)
281         .addRepeatedPackedEnum(Proto2TestEnum.TWO) // Unknown in parsed proto
282         .addRepeatedPackedEnum(Proto2TestEnum.ONE);
283 
284     DynamicMessage message =
285         DynamicMessage.parseFrom(
286             Proto2EnumMessageWithEnumSubset.getDescriptor(), sourceMessage.build().toByteArray());
287 
288     // Known enum values should be preserved.
289     assertThat(message.getRepeatedFieldCount(repeatedPackedField)).isEqualTo(2);
290     EnumValueDescriptor enumValue0 =
291         (EnumValueDescriptor) message.getRepeatedField(repeatedPackedField, 0);
292     EnumValueDescriptor enumValue1 =
293         (EnumValueDescriptor) message.getRepeatedField(repeatedPackedField, 1);
294 
295     assertThat(enumValue0.getNumber()).isEqualTo(Proto2TestEnumSubset.TESTENUM_SUBSET_ZERO_VALUE);
296     assertThat(enumValue1.getNumber()).isEqualTo(Proto2TestEnumSubset.TESTENUM_SUBSET_ONE_VALUE);
297 
298     // Unknown enum values should be found in UnknownFieldSet.
299     UnknownFieldSet unknown = message.getUnknownFields();
300     assertThat(unknown.getField(repeatedPackedField.getNumber()).getVarintList().get(0).longValue())
301         .isEqualTo(Proto2TestEnum.TWO_VALUE);
302   }
303 }
304