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