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