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 protobuf_unittest.UnittestProto.NestedTestAllTypes; 34 import protobuf_unittest.UnittestProto.TestAllTypes; 35 import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage; 36 37 import junit.framework.TestCase; 38 39 public class FieldMaskTreeTest extends TestCase { testAddFieldPath()40 public void testAddFieldPath() throws Exception { 41 FieldMaskTree tree = new FieldMaskTree(); 42 assertEquals("", tree.toString()); 43 tree.addFieldPath(""); 44 assertEquals("", tree.toString()); 45 // New branch. 46 tree.addFieldPath("foo"); 47 assertEquals("foo", tree.toString()); 48 // Redundant path. 49 tree.addFieldPath("foo"); 50 assertEquals("foo", tree.toString()); 51 // New branch. 52 tree.addFieldPath("bar.baz"); 53 assertEquals("bar.baz,foo", tree.toString()); 54 // Redundant sub-path. 55 tree.addFieldPath("foo.bar"); 56 assertEquals("bar.baz,foo", tree.toString()); 57 // New branch from a non-root node. 58 tree.addFieldPath("bar.quz"); 59 assertEquals("bar.baz,bar.quz,foo", tree.toString()); 60 // A path that matches several existing sub-paths. 61 tree.addFieldPath("bar"); 62 assertEquals("bar,foo", tree.toString()); 63 } 64 testMergeFromFieldMask()65 public void testMergeFromFieldMask() throws Exception { 66 FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz")); 67 assertEquals("bar.baz,bar.quz,foo", tree.toString()); 68 tree.mergeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar")); 69 assertEquals("bar,foo", tree.toString()); 70 } 71 testIntersectFieldPath()72 public void testIntersectFieldPath() throws Exception { 73 FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz")); 74 FieldMaskTree result = new FieldMaskTree(); 75 // Empty path. 76 tree.intersectFieldPath("", result); 77 assertEquals("", result.toString()); 78 // Non-exist path. 79 tree.intersectFieldPath("quz", result); 80 assertEquals("", result.toString()); 81 // Sub-path of an existing leaf. 82 tree.intersectFieldPath("foo.bar", result); 83 assertEquals("foo.bar", result.toString()); 84 // Match an existing leaf node. 85 tree.intersectFieldPath("foo", result); 86 assertEquals("foo", result.toString()); 87 // Non-exist path. 88 tree.intersectFieldPath("bar.foo", result); 89 assertEquals("foo", result.toString()); 90 // Match a non-leaf node. 91 tree.intersectFieldPath("bar", result); 92 assertEquals("bar.baz,bar.quz,foo", result.toString()); 93 } 94 testMerge()95 public void testMerge() throws Exception { 96 TestAllTypes value = 97 TestAllTypes.newBuilder() 98 .setOptionalInt32(1234) 99 .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678)) 100 .addRepeatedInt32(4321) 101 .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765)) 102 .build(); 103 NestedTestAllTypes source = 104 NestedTestAllTypes.newBuilder() 105 .setPayload(value) 106 .setChild(NestedTestAllTypes.newBuilder().setPayload(value)) 107 .build(); 108 // Now we have a message source with the following structure: 109 // [root] -+- payload -+- optional_int32 110 // | +- optional_nested_message 111 // | +- repeated_int32 112 // | +- repeated_nested_message 113 // | 114 // +- child --- payload -+- optional_int32 115 // +- optional_nested_message 116 // +- repeated_int32 117 // +- repeated_nested_message 118 119 FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions(); 120 121 // Test merging each individual field. 122 NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder(); 123 new FieldMaskTree().addFieldPath("payload.optional_int32").merge(source, builder, options); 124 NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder(); 125 expected.getPayloadBuilder().setOptionalInt32(1234); 126 assertEquals(expected.build(), builder.build()); 127 128 builder = NestedTestAllTypes.newBuilder(); 129 new FieldMaskTree() 130 .addFieldPath("payload.optional_nested_message") 131 .merge(source, builder, options); 132 expected = NestedTestAllTypes.newBuilder(); 133 expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678)); 134 assertEquals(expected.build(), builder.build()); 135 136 builder = NestedTestAllTypes.newBuilder(); 137 new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options); 138 expected = NestedTestAllTypes.newBuilder(); 139 expected.getPayloadBuilder().addRepeatedInt32(4321); 140 assertEquals(expected.build(), builder.build()); 141 142 builder = NestedTestAllTypes.newBuilder(); 143 new FieldMaskTree() 144 .addFieldPath("payload.repeated_nested_message") 145 .merge(source, builder, options); 146 expected = NestedTestAllTypes.newBuilder(); 147 expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765)); 148 assertEquals(expected.build(), builder.build()); 149 150 builder = NestedTestAllTypes.newBuilder(); 151 new FieldMaskTree() 152 .addFieldPath("child.payload.optional_int32") 153 .merge(source, builder, options); 154 expected = NestedTestAllTypes.newBuilder(); 155 expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234); 156 assertEquals(expected.build(), builder.build()); 157 158 builder = NestedTestAllTypes.newBuilder(); 159 new FieldMaskTree() 160 .addFieldPath("child.payload.optional_nested_message") 161 .merge(source, builder, options); 162 expected = NestedTestAllTypes.newBuilder(); 163 expected 164 .getChildBuilder() 165 .getPayloadBuilder() 166 .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678)); 167 assertEquals(expected.build(), builder.build()); 168 169 builder = NestedTestAllTypes.newBuilder(); 170 new FieldMaskTree() 171 .addFieldPath("child.payload.repeated_int32") 172 .merge(source, builder, options); 173 expected = NestedTestAllTypes.newBuilder(); 174 expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321); 175 assertEquals(expected.build(), builder.build()); 176 177 builder = NestedTestAllTypes.newBuilder(); 178 new FieldMaskTree() 179 .addFieldPath("child.payload.repeated_nested_message") 180 .merge(source, builder, options); 181 expected = NestedTestAllTypes.newBuilder(); 182 expected 183 .getChildBuilder() 184 .getPayloadBuilder() 185 .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765)); 186 assertEquals(expected.build(), builder.build()); 187 188 // Test merging all fields. 189 builder = NestedTestAllTypes.newBuilder(); 190 new FieldMaskTree() 191 .addFieldPath("child") 192 .addFieldPath("payload") 193 .merge(source, builder, options); 194 assertEquals(source, builder.build()); 195 196 // Test repeated options. 197 builder = NestedTestAllTypes.newBuilder(); 198 builder.getPayloadBuilder().addRepeatedInt32(1000); 199 new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options); 200 // Default behavior is to append repeated fields. 201 assertEquals(2, builder.getPayload().getRepeatedInt32Count()); 202 assertEquals(1000, builder.getPayload().getRepeatedInt32(0)); 203 assertEquals(4321, builder.getPayload().getRepeatedInt32(1)); 204 // Change to replace repeated fields. 205 options.setReplaceRepeatedFields(true); 206 new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options); 207 assertEquals(1, builder.getPayload().getRepeatedInt32Count()); 208 assertEquals(4321, builder.getPayload().getRepeatedInt32(0)); 209 210 // Test message options. 211 builder = NestedTestAllTypes.newBuilder(); 212 builder.getPayloadBuilder().setOptionalInt32(1000); 213 builder.getPayloadBuilder().setOptionalUint32(2000); 214 new FieldMaskTree().addFieldPath("payload").merge(source, builder, options); 215 // Default behavior is to merge message fields. 216 assertEquals(1234, builder.getPayload().getOptionalInt32()); 217 assertEquals(2000, builder.getPayload().getOptionalUint32()); 218 219 // Test merging unset message fields. 220 NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build(); 221 builder = NestedTestAllTypes.newBuilder(); 222 new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options); 223 assertEquals(false, builder.hasPayload()); 224 225 // Change to replace message fields. 226 options.setReplaceMessageFields(true); 227 builder = NestedTestAllTypes.newBuilder(); 228 builder.getPayloadBuilder().setOptionalInt32(1000); 229 builder.getPayloadBuilder().setOptionalUint32(2000); 230 new FieldMaskTree().addFieldPath("payload").merge(source, builder, options); 231 assertEquals(1234, builder.getPayload().getOptionalInt32()); 232 assertEquals(0, builder.getPayload().getOptionalUint32()); 233 234 // Test merging unset message fields. 235 builder = NestedTestAllTypes.newBuilder(); 236 builder.getPayloadBuilder().setOptionalInt32(1000); 237 builder.getPayloadBuilder().setOptionalUint32(2000); 238 new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options); 239 assertEquals(false, builder.hasPayload()); 240 241 // Test merging unset primitive fields. 242 builder = source.toBuilder(); 243 builder.getPayloadBuilder().clearOptionalInt32(); 244 NestedTestAllTypes sourceWithPayloadInt32Unset = builder.build(); 245 builder = source.toBuilder(); 246 new FieldMaskTree() 247 .addFieldPath("payload.optional_int32") 248 .merge(sourceWithPayloadInt32Unset, builder, options); 249 assertEquals(true, builder.getPayload().hasOptionalInt32()); 250 assertEquals(0, builder.getPayload().getOptionalInt32()); 251 252 // Change to clear unset primitive fields. 253 options.setReplacePrimitiveFields(true); 254 builder = source.toBuilder(); 255 new FieldMaskTree() 256 .addFieldPath("payload.optional_int32") 257 .merge(sourceWithPayloadInt32Unset, builder, options); 258 assertEquals(true, builder.hasPayload()); 259 assertEquals(false, builder.getPayload().hasOptionalInt32()); 260 } 261 } 262