1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2016 Google Inc. All rights reserved. 4 // https://developers.google.com/protocol-buffers/ 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 #endregion 32 33 using System; 34 using Google.Protobuf.TestProtos; 35 using NUnit.Framework; 36 37 namespace Google.Protobuf.WellKnownTypes 38 { 39 public class FieldMaskTest 40 { 41 [Test] 42 [TestCase("foo__bar")] 43 [TestCase("foo_3_ar")] 44 [TestCase("fooBar")] ToString_Invalid(string input)45 public void ToString_Invalid(string input) 46 { 47 var mask = new FieldMask { Paths = { input } }; 48 var text = mask.ToString(); 49 // More specific test below 50 Assert.That(text, Does.Contain("@warning")); 51 Assert.That(text, Does.Contain(input)); 52 } 53 54 [Test] ToString_Invalid_Precise()55 public void ToString_Invalid_Precise() 56 { 57 var mask = new FieldMask { Paths = { "x", "foo__bar", @"x\y" } }; 58 Assert.AreEqual( 59 "{ \"@warning\": \"Invalid FieldMask\", \"paths\": [ \"x\", \"foo__bar\", \"x\\\\y\" ] }", 60 mask.ToString()); 61 } 62 63 [Test] IsValid()64 public void IsValid() 65 { 66 Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload")); 67 Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>("nonexist")); 68 Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.single_int32")); 69 Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.repeated_int32")); 70 Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.single_nested_message")); 71 Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.repeated_nested_message")); 72 Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>("payload.nonexist")); 73 74 Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>(FieldMask.FromString("payload"))); 75 Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>(FieldMask.FromString("nonexist"))); 76 Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>(FieldMask.FromString("payload,nonexist"))); 77 78 Assert.IsTrue(FieldMask.IsValid(NestedTestAllTypes.Descriptor, "payload")); 79 Assert.IsFalse(FieldMask.IsValid(NestedTestAllTypes.Descriptor, "nonexist")); 80 81 Assert.IsTrue(FieldMask.IsValid(NestedTestAllTypes.Descriptor, FieldMask.FromString("payload"))); 82 Assert.IsFalse(FieldMask.IsValid(NestedTestAllTypes.Descriptor, FieldMask.FromString("nonexist"))); 83 84 Assert.IsTrue(FieldMask.IsValid<NestedTestAllTypes>("payload.single_nested_message.bb")); 85 86 // Repeated fields cannot have sub-paths. 87 Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>("payload.repeated_nested_message.bb")); 88 89 // Non-message fields cannot have sub-paths. 90 Assert.IsFalse(FieldMask.IsValid<NestedTestAllTypes>("payload.single_int32.bb")); 91 } 92 93 [Test] 94 [TestCase(new string[] { }, "\"\"")] 95 [TestCase(new string[] { "foo" }, "\"foo\"")] 96 [TestCase(new string[] { "foo", "bar" }, "\"foo,bar\"")] 97 [TestCase(new string[] { "", "foo", "", "bar", "" }, "\",foo,,bar,\"")] ToString(string[] input, string expectedOutput)98 public void ToString(string[] input, string expectedOutput) 99 { 100 FieldMask mask = new FieldMask(); 101 mask.Paths.AddRange(input); 102 Assert.AreEqual(expectedOutput, mask.ToString()); 103 } 104 105 [Test] 106 [TestCase("", new string[] { })] 107 [TestCase("foo", new string[] { "foo" })] 108 [TestCase("foo,bar.baz", new string[] { "foo", "bar.baz" })] 109 [TestCase(",foo,,bar,", new string[] { "foo", "bar" })] FromString(string input, string[] expectedOutput)110 public void FromString(string input, string[] expectedOutput) 111 { 112 FieldMask mask = FieldMask.FromString(input); 113 Assert.AreEqual(expectedOutput.Length, mask.Paths.Count); 114 for (int i = 0; i < expectedOutput.Length; i++) 115 { 116 Assert.AreEqual(expectedOutput[i], mask.Paths[i]); 117 } 118 } 119 120 [Test] FromString_Validated()121 public void FromString_Validated() 122 { 123 // Check whether the field paths are valid if a class parameter is provided. 124 Assert.DoesNotThrow(() => FieldMask.FromString<NestedTestAllTypes>(",payload")); 125 Assert.Throws<InvalidProtocolBufferException>(() => FieldMask.FromString<NestedTestAllTypes>("payload,nonexist")); 126 } 127 128 [Test] 129 [TestCase(new int[] { }, new string[] { })] 130 [TestCase(new int[] { TestAllTypes.SingleInt32FieldNumber }, new string[] { "single_int32" })] 131 [TestCase(new int[] { TestAllTypes.SingleInt32FieldNumber, TestAllTypes.SingleInt64FieldNumber }, new string[] { "single_int32", "single_int64" })] FromFieldNumbers(int[] input, string[] expectedOutput)132 public void FromFieldNumbers(int[] input, string[] expectedOutput) 133 { 134 FieldMask mask = FieldMask.FromFieldNumbers<TestAllTypes>(input); 135 Assert.AreEqual(expectedOutput.Length, mask.Paths.Count); 136 for (int i = 0; i < expectedOutput.Length; i++) 137 { 138 Assert.AreEqual(expectedOutput[i], mask.Paths[i]); 139 } 140 } 141 142 [Test] FromFieldNumbers_Invalid()143 public void FromFieldNumbers_Invalid() 144 { 145 Assert.Throws<ArgumentNullException>(() => 146 { 147 int invalidFieldNumber = 1000; 148 FieldMask.FromFieldNumbers<TestAllTypes>(invalidFieldNumber); 149 }); 150 } 151 152 [Test] 153 [TestCase(new string[] { }, "\"\"")] 154 [TestCase(new string[] { "foo" }, "\"foo\"")] 155 [TestCase(new string[] { "foo", "bar" }, "\"foo,bar\"")] 156 [TestCase(new string[] { "", "foo", "", "bar", "" }, "\",foo,bar\"")] Normalize(string[] input, string expectedOutput)157 public void Normalize(string[] input, string expectedOutput) 158 { 159 FieldMask mask = new FieldMask(); 160 mask.Paths.AddRange(input); 161 FieldMask result = mask.Normalize(); 162 Assert.AreEqual(expectedOutput, result.ToString()); 163 } 164 165 [Test] Union()166 public void Union() 167 { 168 // Only test a simple case here and expect 169 // {@link FieldMaskTreeTest#AddFieldPath} to cover all scenarios. 170 FieldMask mask1 = FieldMask.FromString("foo,bar.baz,bar.quz"); 171 FieldMask mask2 = FieldMask.FromString("foo.bar,bar"); 172 FieldMask result = mask1.Union(mask2); 173 Assert.AreEqual(2, result.Paths.Count); 174 Assert.Contains("bar", result.Paths); 175 Assert.Contains("foo", result.Paths); 176 Assert.That(result.Paths, Has.No.Member("bar.baz")); 177 Assert.That(result.Paths, Has.No.Member("bar.quz")); 178 Assert.That(result.Paths, Has.No.Member("foo.bar")); 179 } 180 181 [Test] Union_UsingVarArgs()182 public void Union_UsingVarArgs() 183 { 184 FieldMask mask1 = FieldMask.FromString("foo"); 185 FieldMask mask2 = FieldMask.FromString("foo.bar,bar.quz"); 186 FieldMask mask3 = FieldMask.FromString("bar.quz"); 187 FieldMask mask4 = FieldMask.FromString("bar"); 188 FieldMask result = mask1.Union(mask2, mask3, mask4); 189 Assert.AreEqual(2, result.Paths.Count); 190 Assert.Contains("bar", result.Paths); 191 Assert.Contains("foo", result.Paths); 192 Assert.That(result.Paths, Has.No.Member("foo.bar")); 193 Assert.That(result.Paths, Has.No.Member("bar.quz")); 194 } 195 196 [Test] Intersection()197 public void Intersection() 198 { 199 // Only test a simple case here and expect 200 // {@link FieldMaskTreeTest#IntersectFieldPath} to cover all scenarios. 201 FieldMask mask1 = FieldMask.FromString("foo,bar.baz,bar.quz"); 202 FieldMask mask2 = FieldMask.FromString("foo.bar,bar"); 203 FieldMask result = mask1.Intersection(mask2); 204 Assert.AreEqual(3, result.Paths.Count); 205 Assert.Contains("foo.bar", result.Paths); 206 Assert.Contains("bar.baz", result.Paths); 207 Assert.Contains("bar.quz", result.Paths); 208 Assert.That(result.Paths, Has.No.Member("foo")); 209 Assert.That(result.Paths, Has.No.Member("bar")); 210 } 211 212 [Test] Merge()213 public void Merge() 214 { 215 // Only test a simple case here and expect 216 // {@link FieldMaskTreeTest#Merge} to cover all scenarios. 217 FieldMask fieldMask = FieldMask.FromString("payload"); 218 NestedTestAllTypes source = new NestedTestAllTypes 219 { 220 Payload = new TestAllTypes 221 { 222 SingleInt32 = 1234, 223 SingleFixed64 = 4321 224 } 225 }; 226 NestedTestAllTypes destination = new NestedTestAllTypes(); 227 fieldMask.Merge(source, destination); 228 Assert.AreEqual(1234, destination.Payload.SingleInt32); 229 Assert.AreEqual(4321, destination.Payload.SingleFixed64); 230 231 destination = new NestedTestAllTypes 232 { 233 Payload = new TestAllTypes 234 { 235 SingleInt32 = 4321, 236 SingleInt64 = 5678 237 } 238 }; 239 fieldMask.Merge(source, destination); 240 Assert.AreEqual(1234, destination.Payload.SingleInt32); 241 Assert.AreEqual(5678, destination.Payload.SingleInt64); 242 Assert.AreEqual(4321, destination.Payload.SingleFixed64); 243 } 244 } 245 } 246