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