• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.util;
32 
33 import com.google.protobuf.DynamicMessage;
34 import com.google.protobuf.Message;
35 import com.google.protobuf.UninitializedMessageException;
36 import protobuf_unittest.UnittestProto.NestedTestAllTypes;
37 import protobuf_unittest.UnittestProto.TestAllTypes;
38 import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
39 import protobuf_unittest.UnittestProto.TestRequired;
40 import protobuf_unittest.UnittestProto.TestRequiredMessage;
41 import junit.framework.TestCase;
42 
43 public class FieldMaskTreeTest extends TestCase {
testAddFieldPath()44   public void testAddFieldPath() throws Exception {
45     FieldMaskTree tree = new FieldMaskTree();
46     assertEquals("", tree.toString());
47     tree.addFieldPath("");
48     assertEquals("", tree.toString());
49     // New branch.
50     tree.addFieldPath("foo");
51     assertEquals("foo", tree.toString());
52     // Redundant path.
53     tree.addFieldPath("foo");
54     assertEquals("foo", tree.toString());
55     // New branch.
56     tree.addFieldPath("bar.baz");
57     assertEquals("bar.baz,foo", tree.toString());
58     // Redundant sub-path.
59     tree.addFieldPath("foo.bar");
60     assertEquals("bar.baz,foo", tree.toString());
61     // New branch from a non-root node.
62     tree.addFieldPath("bar.quz");
63     assertEquals("bar.baz,bar.quz,foo", tree.toString());
64     // A path that matches several existing sub-paths.
65     tree.addFieldPath("bar");
66     assertEquals("bar,foo", tree.toString());
67   }
68 
testMergeFromFieldMask()69   public void testMergeFromFieldMask() throws Exception {
70     FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
71     assertEquals("bar.baz,bar.quz,foo", tree.toString());
72     tree.mergeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar"));
73     assertEquals("bar,foo", tree.toString());
74   }
75 
testIntersectFieldPath()76   public void testIntersectFieldPath() throws Exception {
77     FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
78     FieldMaskTree result = new FieldMaskTree();
79     // Empty path.
80     tree.intersectFieldPath("", result);
81     assertEquals("", result.toString());
82     // Non-exist path.
83     tree.intersectFieldPath("quz", result);
84     assertEquals("", result.toString());
85     // Sub-path of an existing leaf.
86     tree.intersectFieldPath("foo.bar", result);
87     assertEquals("foo.bar", result.toString());
88     // Match an existing leaf node.
89     tree.intersectFieldPath("foo", result);
90     assertEquals("foo", result.toString());
91     // Non-exist path.
92     tree.intersectFieldPath("bar.foo", result);
93     assertEquals("foo", result.toString());
94     // Match a non-leaf node.
95     tree.intersectFieldPath("bar", result);
96     assertEquals("bar.baz,bar.quz,foo", result.toString());
97   }
98 
testMerge()99   public void testMerge() throws Exception {
100     testMergeImpl(true);
101     testMergeImpl(false);
102     testMergeRequire(false);
103     testMergeRequire(true);
104   }
105 
merge( FieldMaskTree tree, Message source, Message.Builder builder, FieldMaskUtil.MergeOptions options, boolean useDynamicMessage)106   private void merge(
107       FieldMaskTree tree,
108       Message source,
109       Message.Builder builder,
110       FieldMaskUtil.MergeOptions options,
111       boolean useDynamicMessage)
112       throws Exception {
113     if (useDynamicMessage) {
114       Message.Builder newBuilder =
115           DynamicMessage.newBuilder(source.getDescriptorForType())
116               .mergeFrom(builder.buildPartial().toByteArray());
117       tree.merge(
118           DynamicMessage.newBuilder(source.getDescriptorForType())
119               .mergeFrom(source.toByteArray())
120               .build(),
121           newBuilder,
122           options);
123       builder.clear();
124       builder.mergeFrom(newBuilder.buildPartial());
125     } else {
126       tree.merge(source, builder, options);
127     }
128   }
129 
testMergeRequire(boolean useDynamicMessage)130   private void testMergeRequire(boolean useDynamicMessage) throws Exception {
131     TestRequired value = TestRequired.newBuilder().setA(4321).setB(8765).setC(233333).build();
132     TestRequiredMessage source = TestRequiredMessage.newBuilder().setRequiredMessage(value).build();
133 
134     FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
135     TestRequiredMessage.Builder builder = TestRequiredMessage.newBuilder();
136     merge(
137         new FieldMaskTree().addFieldPath("required_message.a"),
138         source,
139         builder,
140         options,
141         useDynamicMessage);
142     assertTrue(builder.hasRequiredMessage());
143     assertTrue(builder.getRequiredMessage().hasA());
144     assertFalse(builder.getRequiredMessage().hasB());
145     assertFalse(builder.getRequiredMessage().hasC());
146     merge(
147         new FieldMaskTree().addFieldPath("required_message.b").addFieldPath("required_message.c"),
148         source,
149         builder,
150         options,
151         useDynamicMessage);
152     try {
153       assertEquals(builder.build(), source);
154     } catch (UninitializedMessageException e) {
155       throw new AssertionError("required field isn't set", e);
156     }
157   }
158 
testMergeImpl(boolean useDynamicMessage)159   private void testMergeImpl(boolean useDynamicMessage) throws Exception {
160     TestAllTypes value =
161         TestAllTypes.newBuilder()
162             .setOptionalInt32(1234)
163             .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678))
164             .addRepeatedInt32(4321)
165             .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765))
166             .build();
167     NestedTestAllTypes source =
168         NestedTestAllTypes.newBuilder()
169             .setPayload(value)
170             .setChild(NestedTestAllTypes.newBuilder().setPayload(value))
171             .build();
172     // Now we have a message source with the following structure:
173     //   [root] -+- payload -+- optional_int32
174     //           |           +- optional_nested_message
175     //           |           +- repeated_int32
176     //           |           +- repeated_nested_message
177     //           |
178     //           +- child --- payload -+- optional_int32
179     //                                 +- optional_nested_message
180     //                                 +- repeated_int32
181     //                                 +- repeated_nested_message
182 
183     FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
184 
185     // Test merging each individual field.
186     NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
187     merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
188         source, builder, options, useDynamicMessage);
189     NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder();
190     expected.getPayloadBuilder().setOptionalInt32(1234);
191     assertEquals(expected.build(), builder.build());
192 
193     builder = NestedTestAllTypes.newBuilder();
194     merge(new FieldMaskTree().addFieldPath("payload.optional_nested_message"),
195         source, builder, options, useDynamicMessage);
196     expected = NestedTestAllTypes.newBuilder();
197     expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
198     assertEquals(expected.build(), builder.build());
199 
200     builder = NestedTestAllTypes.newBuilder();
201     merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
202         source, builder, options, useDynamicMessage);
203     expected = NestedTestAllTypes.newBuilder();
204     expected.getPayloadBuilder().addRepeatedInt32(4321);
205     assertEquals(expected.build(), builder.build());
206 
207     builder = NestedTestAllTypes.newBuilder();
208     merge(new FieldMaskTree().addFieldPath("payload.repeated_nested_message"),
209         source, builder, options, useDynamicMessage);
210     expected = NestedTestAllTypes.newBuilder();
211     expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
212     assertEquals(expected.build(), builder.build());
213 
214     builder = NestedTestAllTypes.newBuilder();
215     merge(
216         new FieldMaskTree().addFieldPath("child.payload.optional_int32"),
217         source,
218         builder,
219         options,
220         useDynamicMessage);
221     expected = NestedTestAllTypes.newBuilder();
222     expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234);
223     assertEquals(expected.build(), builder.build());
224 
225     builder = NestedTestAllTypes.newBuilder();
226     merge(
227         new FieldMaskTree().addFieldPath("child.payload.optional_nested_message"),
228         source,
229         builder,
230         options,
231         useDynamicMessage);
232     expected = NestedTestAllTypes.newBuilder();
233     expected
234         .getChildBuilder()
235         .getPayloadBuilder()
236         .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
237     assertEquals(expected.build(), builder.build());
238 
239     builder = NestedTestAllTypes.newBuilder();
240     merge(new FieldMaskTree().addFieldPath("child.payload.repeated_int32"),
241         source, builder, options, useDynamicMessage);
242     expected = NestedTestAllTypes.newBuilder();
243     expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321);
244     assertEquals(expected.build(), builder.build());
245 
246     builder = NestedTestAllTypes.newBuilder();
247     merge(new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message"),
248         source, builder, options, useDynamicMessage);
249     expected = NestedTestAllTypes.newBuilder();
250     expected
251         .getChildBuilder()
252         .getPayloadBuilder()
253         .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
254     assertEquals(expected.build(), builder.build());
255 
256     // Test merging all fields.
257     builder = NestedTestAllTypes.newBuilder();
258     merge(new FieldMaskTree().addFieldPath("child").addFieldPath("payload"),
259         source, builder, options, useDynamicMessage);
260     assertEquals(source, builder.build());
261 
262     // Test repeated options.
263     builder = NestedTestAllTypes.newBuilder();
264     builder.getPayloadBuilder().addRepeatedInt32(1000);
265     merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
266         source, builder, options, useDynamicMessage);
267     // Default behavior is to append repeated fields.
268     assertEquals(2, builder.getPayload().getRepeatedInt32Count());
269     assertEquals(1000, builder.getPayload().getRepeatedInt32(0));
270     assertEquals(4321, builder.getPayload().getRepeatedInt32(1));
271     // Change to replace repeated fields.
272     options.setReplaceRepeatedFields(true);
273     merge(new FieldMaskTree().addFieldPath("payload.repeated_int32"),
274         source, builder, options, useDynamicMessage);
275     assertEquals(1, builder.getPayload().getRepeatedInt32Count());
276     assertEquals(4321, builder.getPayload().getRepeatedInt32(0));
277 
278     // Test message options.
279     builder = NestedTestAllTypes.newBuilder();
280     builder.getPayloadBuilder().setOptionalInt32(1000);
281     builder.getPayloadBuilder().setOptionalUint32(2000);
282     merge(new FieldMaskTree().addFieldPath("payload"),
283         source, builder, options, useDynamicMessage);
284     // Default behavior is to merge message fields.
285     assertEquals(1234, builder.getPayload().getOptionalInt32());
286     assertEquals(2000, builder.getPayload().getOptionalUint32());
287 
288     // Test merging unset message fields.
289     NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build();
290     builder = NestedTestAllTypes.newBuilder();
291     merge(new FieldMaskTree().addFieldPath("payload"),
292         clearedSource, builder, options, useDynamicMessage);
293     assertEquals(false, builder.hasPayload());
294 
295     // Skip a message field if they are unset in both source and target.
296     builder = NestedTestAllTypes.newBuilder();
297     merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
298         clearedSource, builder, options, useDynamicMessage);
299     assertEquals(false, builder.hasPayload());
300 
301     // Change to replace message fields.
302     options.setReplaceMessageFields(true);
303     builder = NestedTestAllTypes.newBuilder();
304     builder.getPayloadBuilder().setOptionalInt32(1000);
305     builder.getPayloadBuilder().setOptionalUint32(2000);
306     merge(new FieldMaskTree().addFieldPath("payload"),
307         source, builder, options, useDynamicMessage);
308     assertEquals(1234, builder.getPayload().getOptionalInt32());
309     assertEquals(0, builder.getPayload().getOptionalUint32());
310 
311     // Test merging unset message fields.
312     builder = NestedTestAllTypes.newBuilder();
313     builder.getPayloadBuilder().setOptionalInt32(1000);
314     builder.getPayloadBuilder().setOptionalUint32(2000);
315     merge(new FieldMaskTree().addFieldPath("payload"),
316         clearedSource, builder, options, useDynamicMessage);
317     assertEquals(false, builder.hasPayload());
318 
319     // Test merging unset primitive fields.
320     builder = source.toBuilder();
321     builder.getPayloadBuilder().clearOptionalInt32();
322     NestedTestAllTypes sourceWithPayloadInt32Unset = builder.build();
323     builder = source.toBuilder();
324     merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
325         sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
326     assertEquals(true, builder.getPayload().hasOptionalInt32());
327     assertEquals(0, builder.getPayload().getOptionalInt32());
328 
329     // Change to clear unset primitive fields.
330     options.setReplacePrimitiveFields(true);
331     builder = source.toBuilder();
332     merge(new FieldMaskTree().addFieldPath("payload.optional_int32"),
333         sourceWithPayloadInt32Unset, builder, options, useDynamicMessage);
334     assertEquals(true, builder.hasPayload());
335     assertEquals(false, builder.getPayload().hasOptionalInt32());
336   }
337 }
338