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.Proto2UnknownEnumValuesTestProto.Proto2EnumMessage; 39 import com.google.protobuf.Proto2UnknownEnumValuesTestProto.Proto2EnumMessageWithEnumSubset; 40 import com.google.protobuf.Proto2UnknownEnumValuesTestProto.Proto2TestEnum; 41 import com.google.protobuf.Proto2UnknownEnumValuesTestProto.Proto2TestEnumSubset; 42 import com.google.protobuf.TextFormat.ParseException; 43 import junit.framework.TestCase; 44 45 /** 46 * Unit tests for protos that keep unknown enum values rather than discard them as unknown fields. 47 */ 48 public class UnknownEnumValueTest extends TestCase { 49 testUnknownEnumValues()50 public void testUnknownEnumValues() throws Exception { 51 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 52 builder.setOptionalNestedEnumValue(4321); 53 builder.addRepeatedNestedEnumValue(5432); 54 builder.addPackedNestedEnumValue(6543); 55 TestAllTypes message = builder.build(); 56 assertEquals(4321, message.getOptionalNestedEnumValue()); 57 assertEquals(5432, message.getRepeatedNestedEnumValue(0)); 58 assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue()); 59 assertEquals(6543, message.getPackedNestedEnumValue(0)); 60 // Returns UNRECOGNIZED if an enum type is requested. 61 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum()); 62 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0)); 63 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0)); 64 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0)); 65 66 // Test serialization and parsing. 67 ByteString data = message.toByteString(); 68 message = TestAllTypes.parseFrom(data); 69 assertEquals(4321, message.getOptionalNestedEnumValue()); 70 assertEquals(5432, message.getRepeatedNestedEnumValue(0)); 71 assertEquals(5432, message.getRepeatedNestedEnumValueList().get(0).intValue()); 72 assertEquals(6543, message.getPackedNestedEnumValue(0)); 73 // Returns UNRECOGNIZED if an enum type is requested. 74 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getOptionalNestedEnum()); 75 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnum(0)); 76 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getRepeatedNestedEnumList().get(0)); 77 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, message.getPackedNestedEnum(0)); 78 79 // Test toBuilder(). 80 builder = message.toBuilder(); 81 assertEquals(4321, builder.getOptionalNestedEnumValue()); 82 assertEquals(5432, builder.getRepeatedNestedEnumValue(0)); 83 assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue()); 84 assertEquals(6543, builder.getPackedNestedEnumValue(0)); 85 // Returns UNRECOGNIZED if an enum type is requested. 86 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum()); 87 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0)); 88 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0)); 89 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0)); 90 91 // Test mergeFrom(). 92 builder = TestAllTypes.newBuilder().mergeFrom(message); 93 assertEquals(4321, builder.getOptionalNestedEnumValue()); 94 assertEquals(5432, builder.getRepeatedNestedEnumValue(0)); 95 assertEquals(5432, builder.getRepeatedNestedEnumValueList().get(0).intValue()); 96 assertEquals(6543, builder.getPackedNestedEnumValue(0)); 97 // Returns UNRECOGNIZED if an enum type is requested. 98 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getOptionalNestedEnum()); 99 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnum(0)); 100 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getRepeatedNestedEnumList().get(0)); 101 assertEquals(TestAllTypes.NestedEnum.UNRECOGNIZED, builder.getPackedNestedEnum(0)); 102 103 // Test equals() and hashCode() 104 TestAllTypes sameMessage = builder.build(); 105 assertEquals(message, sameMessage); 106 assertEquals(message.hashCode(), sameMessage.hashCode()); 107 108 // Getting the numeric value of UNRECOGNIZED will throw an exception. 109 try { 110 TestAllTypes.NestedEnum.UNRECOGNIZED.getNumber(); 111 fail("Exception is expected."); 112 } catch (IllegalArgumentException e) { 113 // Expected. 114 } 115 116 // Setting an enum field to an UNRECOGNIZED value will throw an exception. 117 try { 118 builder.setOptionalNestedEnum(builder.getOptionalNestedEnum()); 119 fail("Exception is expected."); 120 } catch (IllegalArgumentException e) { 121 // Expected. 122 } 123 try { 124 builder.addRepeatedNestedEnum(builder.getOptionalNestedEnum()); 125 fail("Exception is expected."); 126 } catch (IllegalArgumentException e) { 127 // Expected. 128 } 129 } 130 testUnknownEnumValueInReflectionApi()131 public void testUnknownEnumValueInReflectionApi() throws Exception { 132 Descriptor descriptor = TestAllTypes.getDescriptor(); 133 FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum"); 134 FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum"); 135 FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum"); 136 EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); 137 138 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 139 builder.setField(optionalNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(4321)); 140 builder.addRepeatedField( 141 repeatedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(5432)); 142 builder.addRepeatedField( 143 packedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(6543)); 144 TestAllTypes message = builder.build(); 145 146 // Getters will return unknown enum values as EnumValueDescriptor. 147 EnumValueDescriptor unknown4321 = 148 (EnumValueDescriptor) message.getField(optionalNestedEnumField); 149 EnumValueDescriptor unknown5432 = 150 (EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0); 151 EnumValueDescriptor unknown6543 = 152 (EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0); 153 assertEquals(4321, unknown4321.getNumber()); 154 assertEquals(5432, unknown5432.getNumber()); 155 assertEquals(6543, unknown6543.getNumber()); 156 157 // Unknown EnumValueDescriptor will map to UNRECOGNIZED. 158 assertEquals( 159 TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown4321)); 160 assertEquals( 161 TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown5432)); 162 assertEquals( 163 TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown6543)); 164 165 // Setters also accept unknown EnumValueDescriptor. 166 builder.setField(optionalNestedEnumField, unknown6543); 167 builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321); 168 builder.setRepeatedField(packedNestedEnumField, 0, unknown5432); 169 message = builder.build(); 170 // Like other descriptors, unknown EnumValueDescriptor can be compared by 171 // object identity. 172 assertSame(message.getField(optionalNestedEnumField), unknown6543); 173 assertSame(message.getRepeatedField(repeatedNestedEnumField, 0), unknown4321); 174 assertSame(message.getRepeatedField(packedNestedEnumField, 0), unknown5432); 175 } 176 testUnknownEnumValueWithDynamicMessage()177 public void testUnknownEnumValueWithDynamicMessage() throws Exception { 178 Descriptor descriptor = TestAllTypes.getDescriptor(); 179 FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum"); 180 FieldDescriptor repeatedNestedEnumField = descriptor.findFieldByName("repeated_nested_enum"); 181 FieldDescriptor packedNestedEnumField = descriptor.findFieldByName("packed_nested_enum"); 182 EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); 183 184 Message dynamicMessageDefaultInstance = DynamicMessage.getDefaultInstance(descriptor); 185 186 Message.Builder builder = dynamicMessageDefaultInstance.newBuilderForType(); 187 builder.setField(optionalNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(4321)); 188 builder.addRepeatedField( 189 repeatedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(5432)); 190 builder.addRepeatedField( 191 packedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(6543)); 192 Message message = builder.build(); 193 assertEquals( 194 4321, ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber()); 195 assertEquals( 196 5432, 197 ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber()); 198 assertEquals( 199 6543, 200 ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber()); 201 202 // Test reflection based serialization/parsing implementation. 203 ByteString data = message.toByteString(); 204 message = dynamicMessageDefaultInstance.newBuilderForType().mergeFrom(data).build(); 205 assertEquals( 206 4321, ((EnumValueDescriptor) message.getField(optionalNestedEnumField)).getNumber()); 207 assertEquals( 208 5432, 209 ((EnumValueDescriptor) message.getRepeatedField(repeatedNestedEnumField, 0)).getNumber()); 210 assertEquals( 211 6543, 212 ((EnumValueDescriptor) message.getRepeatedField(packedNestedEnumField, 0)).getNumber()); 213 214 // Test reflection based equals()/hashCode(). 215 builder = dynamicMessageDefaultInstance.newBuilderForType(); 216 builder.setField(optionalNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(4321)); 217 builder.addRepeatedField( 218 repeatedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(5432)); 219 builder.addRepeatedField( 220 packedNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(6543)); 221 Message sameMessage = builder.build(); 222 assertEquals(message, sameMessage); 223 assertEquals(message.hashCode(), sameMessage.hashCode()); 224 builder.setField(optionalNestedEnumField, enumType.findValueByNumberCreatingIfUnknown(0)); 225 Message differentMessage = builder.build(); 226 assertFalse(message.equals(differentMessage)); 227 } 228 testUnknownEnumValuesInTextFormat()229 public void testUnknownEnumValuesInTextFormat() { 230 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 231 builder.setOptionalNestedEnumValue(4321); 232 builder.addRepeatedNestedEnumValue(5432); 233 builder.addPackedNestedEnumValue(6543); 234 TestAllTypes message = builder.build(); 235 236 // We can print a message with unknown enum values. 237 String textData = TextFormat.printer().printToString(message); 238 assertEquals( 239 "optional_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_4321\n" 240 + "repeated_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_5432\n" 241 + "packed_nested_enum: UNKNOWN_ENUM_VALUE_NestedEnum_6543\n", 242 textData); 243 244 // Parsing unknown enum values will fail just like parsing other kinds of 245 // unknown fields. 246 try { 247 TextFormat.merge(textData, builder); 248 fail(); 249 } catch (ParseException e) { 250 // expected. 251 } 252 } 253 testUnknownEnumValuesInProto2()254 public void testUnknownEnumValuesInProto2() throws Exception { 255 Proto2EnumMessage.Builder sourceMessage = Proto2EnumMessage.newBuilder(); 256 sourceMessage 257 .addRepeatedPackedEnum(Proto2TestEnum.ZERO) 258 .addRepeatedPackedEnum(Proto2TestEnum.TWO) // Unknown in parsed proto 259 .addRepeatedPackedEnum(Proto2TestEnum.ONE); 260 261 Proto2EnumMessageWithEnumSubset destMessage = 262 Proto2EnumMessageWithEnumSubset.parseFrom(sourceMessage.build().toByteArray()); 263 264 // Known enum values should be preserved. 265 assertEquals(2, destMessage.getRepeatedPackedEnumCount()); 266 assertEquals(Proto2TestEnumSubset.TESTENUM_SUBSET_ZERO, destMessage.getRepeatedPackedEnum(0)); 267 assertEquals(Proto2TestEnumSubset.TESTENUM_SUBSET_ONE, destMessage.getRepeatedPackedEnum(1)); 268 269 // Unknown enum values should be found in UnknownFieldSet. 270 UnknownFieldSet unknown = destMessage.getUnknownFields(); 271 assertEquals( 272 Proto2TestEnum.TWO_VALUE, 273 unknown 274 .getField(Proto2EnumMessageWithEnumSubset.REPEATED_PACKED_ENUM_FIELD_NUMBER) 275 .getVarintList() 276 .get(0) 277 .longValue()); 278 } 279 testUnknownEnumValuesInProto2WithDynamicMessage()280 public void testUnknownEnumValuesInProto2WithDynamicMessage() throws Exception { 281 Descriptor descriptor = Proto2EnumMessageWithEnumSubset.getDescriptor(); 282 FieldDescriptor repeatedPackedField = descriptor.findFieldByName("repeated_packed_enum"); 283 284 Proto2EnumMessage.Builder sourceMessage = Proto2EnumMessage.newBuilder(); 285 sourceMessage 286 .addRepeatedPackedEnum(Proto2TestEnum.ZERO) 287 .addRepeatedPackedEnum(Proto2TestEnum.TWO) // Unknown in parsed proto 288 .addRepeatedPackedEnum(Proto2TestEnum.ONE); 289 290 DynamicMessage message = 291 DynamicMessage.parseFrom( 292 Proto2EnumMessageWithEnumSubset.getDescriptor(), sourceMessage.build().toByteArray()); 293 294 // Known enum values should be preserved. 295 assertEquals(2, message.getRepeatedFieldCount(repeatedPackedField)); 296 EnumValueDescriptor enumValue0 = 297 (EnumValueDescriptor) message.getRepeatedField(repeatedPackedField, 0); 298 EnumValueDescriptor enumValue1 = 299 (EnumValueDescriptor) message.getRepeatedField(repeatedPackedField, 1); 300 301 assertEquals(Proto2TestEnumSubset.TESTENUM_SUBSET_ZERO_VALUE, enumValue0.getNumber()); 302 assertEquals(Proto2TestEnumSubset.TESTENUM_SUBSET_ONE_VALUE, enumValue1.getNumber()); 303 304 // Unknown enum values should be found in UnknownFieldSet. 305 UnknownFieldSet unknown = message.getUnknownFields(); 306 assertEquals( 307 Proto2TestEnum.TWO_VALUE, 308 unknown.getField(repeatedPackedField.getNumber()).getVarintList().get(0).longValue()); 309 } 310 } 311