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.FieldPresenceTestProto.TestOptionalFieldsOnly; 39 import com.google.protobuf.FieldPresenceTestProto.TestRepeatedFieldsOnly; 40 import protobuf_unittest.UnittestProto; 41 import junit.framework.TestCase; 42 43 /** 44 * Unit tests for protos that doesn't support field presence test for optional non-message fields. 45 */ 46 public class FieldPresenceTest extends TestCase { hasMethod(Class<?> clazz, String name)47 private static boolean hasMethod(Class<?> clazz, String name) { 48 try { 49 if (clazz.getMethod(name) != null) { 50 return true; 51 } else { 52 return false; 53 } 54 } catch (NoSuchMethodException e) { 55 return false; 56 } 57 } 58 assertHasMethodRemoved( Class<?> classWithFieldPresence, Class<?> classWithoutFieldPresence, String camelName)59 private static void assertHasMethodRemoved( 60 Class<?> classWithFieldPresence, Class<?> classWithoutFieldPresence, String camelName) { 61 assertTrue(hasMethod(classWithFieldPresence, "get" + camelName)); 62 assertTrue(hasMethod(classWithFieldPresence, "has" + camelName)); 63 assertTrue(hasMethod(classWithoutFieldPresence, "get" + camelName)); 64 assertFalse(hasMethod(classWithoutFieldPresence, "has" + camelName)); 65 } 66 testHasMethod()67 public void testHasMethod() { 68 // Optional non-message fields don't have a hasFoo() method generated. 69 assertHasMethodRemoved(UnittestProto.TestAllTypes.class, TestAllTypes.class, "OptionalInt32"); 70 assertHasMethodRemoved(UnittestProto.TestAllTypes.class, TestAllTypes.class, "OptionalString"); 71 assertHasMethodRemoved(UnittestProto.TestAllTypes.class, TestAllTypes.class, "OptionalBytes"); 72 assertHasMethodRemoved( 73 UnittestProto.TestAllTypes.class, TestAllTypes.class, "OptionalNestedEnum"); 74 75 assertHasMethodRemoved( 76 UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, "OptionalInt32"); 77 assertHasMethodRemoved( 78 UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, "OptionalString"); 79 assertHasMethodRemoved( 80 UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, "OptionalBytes"); 81 assertHasMethodRemoved( 82 UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, "OptionalNestedEnum"); 83 84 // message fields still have the hasFoo() method generated. 85 assertFalse(TestAllTypes.getDefaultInstance().hasOptionalNestedMessage()); 86 assertFalse(TestAllTypes.newBuilder().hasOptionalNestedMessage()); 87 88 // oneof fields don't have hasFoo() methods for non-message types. 89 assertHasMethodRemoved(UnittestProto.TestAllTypes.class, TestAllTypes.class, "OneofUint32"); 90 assertHasMethodRemoved(UnittestProto.TestAllTypes.class, TestAllTypes.class, "OneofString"); 91 assertHasMethodRemoved(UnittestProto.TestAllTypes.class, TestAllTypes.class, "OneofBytes"); 92 assertFalse(TestAllTypes.getDefaultInstance().hasOneofNestedMessage()); 93 assertFalse(TestAllTypes.newBuilder().hasOneofNestedMessage()); 94 95 assertHasMethodRemoved( 96 UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, "OneofUint32"); 97 assertHasMethodRemoved( 98 UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, "OneofString"); 99 assertHasMethodRemoved( 100 UnittestProto.TestAllTypes.Builder.class, TestAllTypes.Builder.class, "OneofBytes"); 101 } 102 testOneofEquals()103 public void testOneofEquals() throws Exception { 104 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 105 TestAllTypes message1 = builder.build(); 106 // Set message2's oneof_uint32 field to defalut value. The two 107 // messages should be different when check with oneof case. 108 builder.setOneofUint32(0); 109 TestAllTypes message2 = builder.build(); 110 assertFalse(message1.equals(message2)); 111 } 112 testLazyField()113 public void testLazyField() throws Exception { 114 // Test default constructed message. 115 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 116 TestAllTypes message = builder.build(); 117 assertFalse(message.hasOptionalLazyMessage()); 118 assertEquals(0, message.getSerializedSize()); 119 assertEquals(ByteString.EMPTY, message.toByteString()); 120 121 // Set default instance to the field. 122 builder.setOptionalLazyMessage(TestAllTypes.NestedMessage.getDefaultInstance()); 123 message = builder.build(); 124 assertTrue(message.hasOptionalLazyMessage()); 125 assertEquals(2, message.getSerializedSize()); 126 127 // Test parse zero-length from wire sets the presence. 128 TestAllTypes parsed = TestAllTypes.parseFrom(message.toByteString()); 129 assertTrue(parsed.hasOptionalLazyMessage()); 130 assertEquals(message.getOptionalLazyMessage(), parsed.getOptionalLazyMessage()); 131 } 132 testFieldPresence()133 public void testFieldPresence() { 134 // Optional non-message fields set to their default value are treated the 135 // same way as not set. 136 137 // Serialization will ignore such fields. 138 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 139 builder.setOptionalInt32(0); 140 builder.setOptionalString(""); 141 builder.setOptionalBytes(ByteString.EMPTY); 142 builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO); 143 TestAllTypes message = builder.build(); 144 assertEquals(0, message.getSerializedSize()); 145 146 // mergeFrom() will ignore such fields. 147 TestAllTypes.Builder a = TestAllTypes.newBuilder(); 148 a.setOptionalInt32(1); 149 a.setOptionalString("x"); 150 a.setOptionalBytes(ByteString.copyFromUtf8("y")); 151 a.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR); 152 TestAllTypes.Builder b = TestAllTypes.newBuilder(); 153 b.setOptionalInt32(0); 154 b.setOptionalString(""); 155 b.setOptionalBytes(ByteString.EMPTY); 156 b.setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO); 157 a.mergeFrom(b.build()); 158 message = a.build(); 159 assertEquals(1, message.getOptionalInt32()); 160 assertEquals("x", message.getOptionalString()); 161 assertEquals(ByteString.copyFromUtf8("y"), message.getOptionalBytes()); 162 assertEquals(TestAllTypes.NestedEnum.BAR, message.getOptionalNestedEnum()); 163 164 // equals()/hashCode() should produce the same results. 165 TestAllTypes empty = TestAllTypes.getDefaultInstance(); 166 message = builder.build(); 167 assertEquals(message, empty); 168 assertEquals(empty, message); 169 assertEquals(empty.hashCode(), message.hashCode()); 170 } 171 testFieldPresenceByReflection()172 public void testFieldPresenceByReflection() { 173 Descriptor descriptor = TestAllTypes.getDescriptor(); 174 FieldDescriptor optionalInt32Field = descriptor.findFieldByName("optional_int32"); 175 FieldDescriptor optionalStringField = descriptor.findFieldByName("optional_string"); 176 FieldDescriptor optionalBytesField = descriptor.findFieldByName("optional_bytes"); 177 FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum"); 178 179 // Field not present. 180 TestAllTypes message = TestAllTypes.getDefaultInstance(); 181 assertFalse(message.hasField(optionalInt32Field)); 182 assertFalse(message.hasField(optionalStringField)); 183 assertFalse(message.hasField(optionalBytesField)); 184 assertFalse(message.hasField(optionalNestedEnumField)); 185 assertEquals(0, message.getAllFields().size()); 186 187 // Field set to default value is seen as not present. 188 message = 189 TestAllTypes.newBuilder() 190 .setOptionalInt32(0) 191 .setOptionalString("") 192 .setOptionalBytes(ByteString.EMPTY) 193 .setOptionalNestedEnum(TestAllTypes.NestedEnum.FOO) 194 .build(); 195 assertFalse(message.hasField(optionalInt32Field)); 196 assertFalse(message.hasField(optionalStringField)); 197 assertFalse(message.hasField(optionalBytesField)); 198 assertFalse(message.hasField(optionalNestedEnumField)); 199 assertEquals(0, message.getAllFields().size()); 200 201 // Field set to non-default value is seen as present. 202 message = 203 TestAllTypes.newBuilder() 204 .setOptionalInt32(1) 205 .setOptionalString("x") 206 .setOptionalBytes(ByteString.copyFromUtf8("y")) 207 .setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR) 208 .build(); 209 assertTrue(message.hasField(optionalInt32Field)); 210 assertTrue(message.hasField(optionalStringField)); 211 assertTrue(message.hasField(optionalBytesField)); 212 assertTrue(message.hasField(optionalNestedEnumField)); 213 assertEquals(4, message.getAllFields().size()); 214 } 215 testFieldPresenceDynamicMessage()216 public void testFieldPresenceDynamicMessage() { 217 Descriptor descriptor = TestAllTypes.getDescriptor(); 218 FieldDescriptor optionalInt32Field = descriptor.findFieldByName("optional_int32"); 219 FieldDescriptor optionalStringField = descriptor.findFieldByName("optional_string"); 220 FieldDescriptor optionalBytesField = descriptor.findFieldByName("optional_bytes"); 221 FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum"); 222 EnumDescriptor enumDescriptor = optionalNestedEnumField.getEnumType(); 223 EnumValueDescriptor defaultEnumValueDescriptor = enumDescriptor.getValues().get(0); 224 EnumValueDescriptor nonDefaultEnumValueDescriptor = enumDescriptor.getValues().get(1); 225 226 DynamicMessage defaultInstance = DynamicMessage.getDefaultInstance(descriptor); 227 // Field not present. 228 DynamicMessage message = defaultInstance.newBuilderForType().build(); 229 assertFalse(message.hasField(optionalInt32Field)); 230 assertFalse(message.hasField(optionalStringField)); 231 assertFalse(message.hasField(optionalBytesField)); 232 assertFalse(message.hasField(optionalNestedEnumField)); 233 assertEquals(0, message.getAllFields().size()); 234 235 // Field set to non-default value is seen as present. 236 message = 237 defaultInstance 238 .newBuilderForType() 239 .setField(optionalInt32Field, 1) 240 .setField(optionalStringField, "x") 241 .setField(optionalBytesField, ByteString.copyFromUtf8("y")) 242 .setField(optionalNestedEnumField, nonDefaultEnumValueDescriptor) 243 .build(); 244 assertTrue(message.hasField(optionalInt32Field)); 245 assertTrue(message.hasField(optionalStringField)); 246 assertTrue(message.hasField(optionalBytesField)); 247 assertTrue(message.hasField(optionalNestedEnumField)); 248 assertEquals(4, message.getAllFields().size()); 249 250 // Field set to default value is seen as not present. 251 message = 252 message 253 .toBuilder() 254 .setField(optionalInt32Field, 0) 255 .setField(optionalStringField, "") 256 .setField(optionalBytesField, ByteString.EMPTY) 257 .setField(optionalNestedEnumField, defaultEnumValueDescriptor) 258 .build(); 259 assertFalse(message.hasField(optionalInt32Field)); 260 assertFalse(message.hasField(optionalStringField)); 261 assertFalse(message.hasField(optionalBytesField)); 262 assertFalse(message.hasField(optionalNestedEnumField)); 263 assertEquals(0, message.getAllFields().size()); 264 } 265 testMessageField()266 public void testMessageField() { 267 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 268 assertFalse(builder.hasOptionalNestedMessage()); 269 assertFalse(builder.build().hasOptionalNestedMessage()); 270 271 TestAllTypes.NestedMessage.Builder nestedBuilder = builder.getOptionalNestedMessageBuilder(); 272 assertTrue(builder.hasOptionalNestedMessage()); 273 assertTrue(builder.build().hasOptionalNestedMessage()); 274 275 nestedBuilder.setValue(1); 276 assertEquals(1, builder.build().getOptionalNestedMessage().getValue()); 277 278 builder.clearOptionalNestedMessage(); 279 assertFalse(builder.hasOptionalNestedMessage()); 280 assertFalse(builder.build().hasOptionalNestedMessage()); 281 282 // Unlike non-message fields, if we set a message field to its default value (i.e., 283 // default instance), the field should be seen as present. 284 builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); 285 assertTrue(builder.hasOptionalNestedMessage()); 286 assertTrue(builder.build().hasOptionalNestedMessage()); 287 } 288 testSerializeAndParse()289 public void testSerializeAndParse() throws Exception { 290 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 291 builder.setOptionalInt32(1234); 292 builder.setOptionalString("hello"); 293 builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); 294 // Set an oneof field to its default value and expect it to be serialized (i.e., 295 // an oneof field set to the default value should be treated as present). 296 builder.setOneofInt32(0); 297 ByteString data = builder.build().toByteString(); 298 299 TestAllTypes message = TestAllTypes.parseFrom(data); 300 assertEquals(1234, message.getOptionalInt32()); 301 assertEquals("hello", message.getOptionalString()); 302 // Fields not set will have the default value. 303 assertEquals(ByteString.EMPTY, message.getOptionalBytes()); 304 assertEquals(TestAllTypes.NestedEnum.FOO, message.getOptionalNestedEnum()); 305 // The message field is set despite that it's set with a default instance. 306 assertTrue(message.hasOptionalNestedMessage()); 307 assertEquals(0, message.getOptionalNestedMessage().getValue()); 308 // The oneof field set to its default value is also present. 309 assertEquals(TestAllTypes.OneofFieldCase.ONEOF_INT32, message.getOneofFieldCase()); 310 } 311 312 // Regression test for b/16173397 313 // Make sure we haven't screwed up the code generation for repeated fields. testRepeatedFields()314 public void testRepeatedFields() throws Exception { 315 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 316 builder.setOptionalInt32(1234); 317 builder.setOptionalString("hello"); 318 builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); 319 builder.addRepeatedInt32(4321); 320 builder.addRepeatedString("world"); 321 builder.addRepeatedNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance()); 322 ByteString data = builder.build().toByteString(); 323 324 TestOptionalFieldsOnly optionalOnlyMessage = TestOptionalFieldsOnly.parseFrom(data); 325 assertEquals(1234, optionalOnlyMessage.getOptionalInt32()); 326 assertEquals("hello", optionalOnlyMessage.getOptionalString()); 327 assertTrue(optionalOnlyMessage.hasOptionalNestedMessage()); 328 assertEquals(0, optionalOnlyMessage.getOptionalNestedMessage().getValue()); 329 330 TestRepeatedFieldsOnly repeatedOnlyMessage = TestRepeatedFieldsOnly.parseFrom(data); 331 assertEquals(1, repeatedOnlyMessage.getRepeatedInt32Count()); 332 assertEquals(4321, repeatedOnlyMessage.getRepeatedInt32(0)); 333 assertEquals(1, repeatedOnlyMessage.getRepeatedStringCount()); 334 assertEquals("world", repeatedOnlyMessage.getRepeatedString(0)); 335 assertEquals(1, repeatedOnlyMessage.getRepeatedNestedMessageCount()); 336 assertEquals(0, repeatedOnlyMessage.getRepeatedNestedMessage(0).getValue()); 337 } 338 testIsInitialized()339 public void testIsInitialized() throws Exception { 340 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 341 342 // Test optional proto2 message fields. 343 UnittestProto.TestRequired.Builder proto2Builder = builder.getOptionalProto2MessageBuilder(); 344 assertFalse(builder.isInitialized()); 345 assertFalse(builder.buildPartial().isInitialized()); 346 347 proto2Builder.setA(1).setB(2).setC(3); 348 assertTrue(builder.isInitialized()); 349 assertTrue(builder.buildPartial().isInitialized()); 350 351 // Test oneof proto2 message fields. 352 proto2Builder = builder.getOneofProto2MessageBuilder(); 353 assertFalse(builder.isInitialized()); 354 assertFalse(builder.buildPartial().isInitialized()); 355 356 proto2Builder.setA(1).setB(2).setC(3); 357 assertTrue(builder.isInitialized()); 358 assertTrue(builder.buildPartial().isInitialized()); 359 360 // Test repeated proto2 message fields. 361 proto2Builder = builder.addRepeatedProto2MessageBuilder(); 362 assertFalse(builder.isInitialized()); 363 assertFalse(builder.buildPartial().isInitialized()); 364 365 proto2Builder.setA(1).setB(2).setC(3); 366 assertTrue(builder.isInitialized()); 367 assertTrue(builder.buildPartial().isInitialized()); 368 } 369 370 } 371