1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2015 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 using System.Collections; 37 using System.IO; 38 39 namespace Google.Protobuf.WellKnownTypes 40 { 41 public class WrappersTest 42 { 43 [Test] NullIsDefault()44 public void NullIsDefault() 45 { 46 var message = new TestWellKnownTypes(); 47 Assert.IsNull(message.StringField); 48 Assert.IsNull(message.BytesField); 49 Assert.IsNull(message.BoolField); 50 Assert.IsNull(message.FloatField); 51 Assert.IsNull(message.DoubleField); 52 Assert.IsNull(message.Int32Field); 53 Assert.IsNull(message.Int64Field); 54 Assert.IsNull(message.Uint32Field); 55 Assert.IsNull(message.Uint64Field); 56 } 57 58 [Test] NonDefaultSingleValues()59 public void NonDefaultSingleValues() 60 { 61 var message = new TestWellKnownTypes 62 { 63 StringField = "x", 64 BytesField = ByteString.CopyFrom(1, 2, 3), 65 BoolField = true, 66 FloatField = 12.5f, 67 DoubleField = 12.25d, 68 Int32Field = 1, 69 Int64Field = 2, 70 Uint32Field = 3, 71 Uint64Field = 4 72 }; 73 74 var bytes = message.ToByteArray(); 75 var parsed = TestWellKnownTypes.Parser.ParseFrom(bytes); 76 77 Assert.AreEqual("x", parsed.StringField); 78 Assert.AreEqual(ByteString.CopyFrom(1, 2, 3), parsed.BytesField); 79 Assert.AreEqual(true, parsed.BoolField); 80 Assert.AreEqual(12.5f, parsed.FloatField); 81 Assert.AreEqual(12.25d, parsed.DoubleField); 82 Assert.AreEqual(1, parsed.Int32Field); 83 Assert.AreEqual(2L, parsed.Int64Field); 84 Assert.AreEqual(3U, parsed.Uint32Field); 85 Assert.AreEqual(4UL, parsed.Uint64Field); 86 } 87 88 [Test] NonNullDefaultIsPreservedThroughSerialization()89 public void NonNullDefaultIsPreservedThroughSerialization() 90 { 91 var message = new TestWellKnownTypes 92 { 93 StringField = "", 94 BytesField = ByteString.Empty, 95 BoolField = false, 96 FloatField = 0f, 97 DoubleField = 0d, 98 Int32Field = 0, 99 Int64Field = 0, 100 Uint32Field = 0, 101 Uint64Field = 0 102 }; 103 104 var bytes = message.ToByteArray(); 105 var parsed = TestWellKnownTypes.Parser.ParseFrom(bytes); 106 107 Assert.AreEqual("", parsed.StringField); 108 Assert.AreEqual(ByteString.Empty, parsed.BytesField); 109 Assert.AreEqual(false, parsed.BoolField); 110 Assert.AreEqual(0f, parsed.FloatField); 111 Assert.AreEqual(0d, parsed.DoubleField); 112 Assert.AreEqual(0, parsed.Int32Field); 113 Assert.AreEqual(0L, parsed.Int64Field); 114 Assert.AreEqual(0U, parsed.Uint32Field); 115 Assert.AreEqual(0UL, parsed.Uint64Field); 116 } 117 118 [Test] RepeatedWrappersProhibitNullItems()119 public void RepeatedWrappersProhibitNullItems() 120 { 121 var message = new RepeatedWellKnownTypes(); 122 Assert.Throws<ArgumentNullException>(() => message.BoolField.Add((bool?) null)); 123 Assert.Throws<ArgumentNullException>(() => message.Int32Field.Add((int?) null)); 124 Assert.Throws<ArgumentNullException>(() => message.StringField.Add((string) null)); 125 Assert.Throws<ArgumentNullException>(() => message.BytesField.Add((ByteString) null)); 126 } 127 128 [Test] RepeatedWrappersSerializeDeserialize()129 public void RepeatedWrappersSerializeDeserialize() 130 { 131 var message = new RepeatedWellKnownTypes 132 { 133 BoolField = { true, false }, 134 BytesField = { ByteString.CopyFrom(1, 2, 3), ByteString.CopyFrom(4, 5, 6), ByteString.Empty }, 135 DoubleField = { 12.5, -1.5, 0d }, 136 FloatField = { 123.25f, -20f, 0f }, 137 Int32Field = { int.MaxValue, int.MinValue, 0 }, 138 Int64Field = { long.MaxValue, long.MinValue, 0L }, 139 StringField = { "First", "Second", "" }, 140 Uint32Field = { uint.MaxValue, uint.MinValue, 0U }, 141 Uint64Field = { ulong.MaxValue, ulong.MinValue, 0UL }, 142 }; 143 var bytes = message.ToByteArray(); 144 var parsed = RepeatedWellKnownTypes.Parser.ParseFrom(bytes); 145 146 Assert.AreEqual(message, parsed); 147 // Just to test a single value for sanity... 148 Assert.AreEqual("Second", message.StringField[1]); 149 } 150 151 [Test] RepeatedWrappersBinaryFormat()152 public void RepeatedWrappersBinaryFormat() 153 { 154 // At one point we accidentally used a packed format for repeated wrappers, which is wrong (and weird). 155 // This test is just to prove that we use the right format. 156 157 var rawOutput = new MemoryStream(); 158 var output = new CodedOutputStream(rawOutput); 159 // Write a value of 5 160 output.WriteTag(RepeatedWellKnownTypes.Int32FieldFieldNumber, WireFormat.WireType.LengthDelimited); 161 output.WriteLength(2); 162 output.WriteTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint); 163 output.WriteInt32(5); 164 // Write a value of 0 (empty message) 165 output.WriteTag(RepeatedWellKnownTypes.Int32FieldFieldNumber, WireFormat.WireType.LengthDelimited); 166 output.WriteLength(0); 167 output.Flush(); 168 var expectedBytes = rawOutput.ToArray(); 169 170 var message = new RepeatedWellKnownTypes { Int32Field = { 5, 0 } }; 171 var actualBytes = message.ToByteArray(); 172 Assert.AreEqual(expectedBytes, actualBytes); 173 } 174 175 [Test] MapWrappersSerializeDeserialize()176 public void MapWrappersSerializeDeserialize() 177 { 178 // Note: no null values here, as they are prohibited in map fields 179 // (despite being representable). 180 var message = new MapWellKnownTypes 181 { 182 BoolField = { { 10, false }, { 20, true } }, 183 BytesField = { 184 { -1, ByteString.CopyFrom(1, 2, 3) }, 185 { 10, ByteString.CopyFrom(4, 5, 6) }, 186 { 1000, ByteString.Empty }, 187 }, 188 DoubleField = { { 1, 12.5 }, { 10, -1.5 }, { 20, 0d } }, 189 FloatField = { { 2, 123.25f }, { 3, -20f }, { 4, 0f } }, 190 Int32Field = { { 5, int.MaxValue }, { 6, int.MinValue }, { 7, 0 } }, 191 Int64Field = { { 8, long.MaxValue }, { 9, long.MinValue }, { 10, 0L } }, 192 StringField = { { 11, "First" }, { 12, "Second" }, { 13, "" } }, 193 Uint32Field = { { 15, uint.MaxValue }, { 16, uint.MinValue }, { 17, 0U } }, 194 Uint64Field = { { 18, ulong.MaxValue }, { 19, ulong.MinValue }, { 20, 0UL } }, 195 }; 196 197 var bytes = message.ToByteArray(); 198 var parsed = MapWellKnownTypes.Parser.ParseFrom(bytes); 199 200 Assert.AreEqual(message, parsed); 201 // Just to test a single value for sanity... 202 Assert.AreEqual("Second", message.StringField[12]); 203 } 204 205 [Test] Reflection_SingleValues()206 public void Reflection_SingleValues() 207 { 208 var message = new TestWellKnownTypes 209 { 210 StringField = "x", 211 BytesField = ByteString.CopyFrom(1, 2, 3), 212 BoolField = true, 213 FloatField = 12.5f, 214 DoubleField = 12.25d, 215 Int32Field = 1, 216 Int64Field = 2, 217 Uint32Field = 3, 218 Uint64Field = 4 219 }; 220 var fields = TestWellKnownTypes.Descriptor.Fields; 221 222 Assert.AreEqual("x", fields[TestWellKnownTypes.StringFieldFieldNumber].Accessor.GetValue(message)); 223 Assert.AreEqual(ByteString.CopyFrom(1, 2, 3), fields[TestWellKnownTypes.BytesFieldFieldNumber].Accessor.GetValue(message)); 224 Assert.AreEqual(true, fields[TestWellKnownTypes.BoolFieldFieldNumber].Accessor.GetValue(message)); 225 Assert.AreEqual(12.5f, fields[TestWellKnownTypes.FloatFieldFieldNumber].Accessor.GetValue(message)); 226 Assert.AreEqual(12.25d, fields[TestWellKnownTypes.DoubleFieldFieldNumber].Accessor.GetValue(message)); 227 Assert.AreEqual(1, fields[TestWellKnownTypes.Int32FieldFieldNumber].Accessor.GetValue(message)); 228 Assert.AreEqual(2L, fields[TestWellKnownTypes.Int64FieldFieldNumber].Accessor.GetValue(message)); 229 Assert.AreEqual(3U, fields[TestWellKnownTypes.Uint32FieldFieldNumber].Accessor.GetValue(message)); 230 Assert.AreEqual(4UL, fields[TestWellKnownTypes.Uint64FieldFieldNumber].Accessor.GetValue(message)); 231 232 // And a couple of null fields... 233 message.StringField = null; 234 message.FloatField = null; 235 Assert.IsNull(fields[TestWellKnownTypes.StringFieldFieldNumber].Accessor.GetValue(message)); 236 Assert.IsNull(fields[TestWellKnownTypes.FloatFieldFieldNumber].Accessor.GetValue(message)); 237 } 238 239 [Test] Reflection_RepeatedFields()240 public void Reflection_RepeatedFields() 241 { 242 // Just a single example... note that we can't have a null value here 243 var message = new RepeatedWellKnownTypes { Int32Field = { 1, 2 } }; 244 var fields = RepeatedWellKnownTypes.Descriptor.Fields; 245 var list = (IList) fields[RepeatedWellKnownTypes.Int32FieldFieldNumber].Accessor.GetValue(message); 246 CollectionAssert.AreEqual(new[] { 1, 2 }, list); 247 } 248 249 [Test] Reflection_MapFields()250 public void Reflection_MapFields() 251 { 252 // Just a single example... note that we can't have a null value here despite the value type being int? 253 var message = new MapWellKnownTypes { Int32Field = { { 1, 2 } } }; 254 var fields = MapWellKnownTypes.Descriptor.Fields; 255 var dictionary = (IDictionary) fields[MapWellKnownTypes.Int32FieldFieldNumber].Accessor.GetValue(message); 256 Assert.AreEqual(2, dictionary[1]); 257 } 258 259 [Test] Oneof()260 public void Oneof() 261 { 262 var message = new OneofWellKnownTypes { EmptyField = new Empty() }; 263 // Start off with a non-wrapper 264 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.EmptyField, message.OneofFieldCase); 265 AssertOneofRoundTrip(message); 266 267 message.StringField = "foo"; 268 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.StringField, message.OneofFieldCase); 269 AssertOneofRoundTrip(message); 270 271 message.StringField = "foo"; 272 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.StringField, message.OneofFieldCase); 273 AssertOneofRoundTrip(message); 274 275 message.DoubleField = 0.0f; 276 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.DoubleField, message.OneofFieldCase); 277 AssertOneofRoundTrip(message); 278 279 message.DoubleField = 1.0f; 280 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.DoubleField, message.OneofFieldCase); 281 AssertOneofRoundTrip(message); 282 283 message.ClearOneofField(); 284 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.None, message.OneofFieldCase); 285 AssertOneofRoundTrip(message); 286 } 287 AssertOneofRoundTrip(OneofWellKnownTypes message)288 private void AssertOneofRoundTrip(OneofWellKnownTypes message) 289 { 290 // Normal roundtrip, but explicitly checking the case... 291 var bytes = message.ToByteArray(); 292 var parsed = OneofWellKnownTypes.Parser.ParseFrom(bytes); 293 Assert.AreEqual(message, parsed); 294 Assert.AreEqual(message.OneofFieldCase, parsed.OneofFieldCase); 295 } 296 297 [Test] 298 [TestCase("x", "y", "y")] 299 [TestCase("x", "", "x")] 300 [TestCase("x", null, "x")] 301 [TestCase("", "y", "y")] 302 [TestCase("", "", "")] 303 [TestCase("", null, "")] 304 [TestCase(null, "y", "y")] 305 [TestCase(null, "", "")] 306 [TestCase(null, null, null)] Merging(string original, string merged, string expected)307 public void Merging(string original, string merged, string expected) 308 { 309 var originalMessage = new TestWellKnownTypes { StringField = original }; 310 var mergingMessage = new TestWellKnownTypes { StringField = merged }; 311 originalMessage.MergeFrom(mergingMessage); 312 Assert.AreEqual(expected, originalMessage.StringField); 313 314 // Try it using MergeFrom(CodedInputStream) too... 315 originalMessage = new TestWellKnownTypes { StringField = original }; 316 originalMessage.MergeFrom(mergingMessage.ToByteArray()); 317 Assert.AreEqual(expected, originalMessage.StringField); 318 } 319 320 // Merging is odd with wrapper types, due to the way that default values aren't emitted in 321 // the binary stream. In fact we cheat a little bit - a message with an explicitly present default 322 // value will have that default value ignored. See issue 615. Fixing this would require significant upheaval to 323 // the FieldCodec side of things. 324 [Test] MergingStreamExplicitValue()325 public void MergingStreamExplicitValue() 326 { 327 var message = new TestWellKnownTypes { Int32Field = 5 }; 328 329 // Create a byte array which has the data of an Int32Value explicitly containing a value of 0. 330 // This wouldn't normally happen. 331 byte[] bytes; 332 var wrapperTag = WireFormat.MakeTag(TestWellKnownTypes.Int32FieldFieldNumber, WireFormat.WireType.LengthDelimited); 333 var valueTag = WireFormat.MakeTag(Int32Value.ValueFieldNumber, WireFormat.WireType.Varint); 334 using (var stream = new MemoryStream()) 335 { 336 var coded = new CodedOutputStream(stream); 337 coded.WriteTag(wrapperTag); 338 coded.WriteLength(2); // valueTag + a value 0, each one byte 339 coded.WriteTag(valueTag); 340 coded.WriteInt32(0); 341 coded.Flush(); 342 bytes = stream.ToArray(); 343 } 344 345 message.MergeFrom(bytes); 346 // A normal implementation would have 0 now, as the explicit default would have been overwritten the 5. 347 // With the FieldCodec for Nullable<int>, we can't tell the difference between an implicit 0 and an explicit 0. 348 Assert.AreEqual(5, message.Int32Field); 349 } 350 351 [Test] MergingStreamNoValue()352 public void MergingStreamNoValue() 353 { 354 var message = new TestWellKnownTypes { Int32Field = 5 }; 355 356 // Create a byte array which an Int32 field, but with no value. 357 var bytes = new TestWellKnownTypes { Int32Field = 0 }.ToByteArray(); 358 Assert.AreEqual(2, bytes.Length); // The tag for Int32Field is a single byte, then a byte indicating a 0-length message. 359 message.MergeFrom(bytes); 360 361 // The "implicit" 0 did *not* overwrite the value. 362 // (This is the correct behaviour.) 363 Assert.AreEqual(5, message.Int32Field); 364 } 365 366 // All permutations of origin/merging value being null, zero (default) or non-default. 367 // As this is the in-memory version, we don't need to worry about the difference between implicit and explicit 0. 368 [Test] 369 [TestCase(null, null, null)] 370 [TestCase(null, 0, 0)] 371 [TestCase(null, 5, 5)] 372 [TestCase(0, null, 0)] 373 [TestCase(0, 0, 0)] 374 [TestCase(0, 5, 5)] 375 [TestCase(5, null, 5)] 376 [TestCase(5, 0, 5)] 377 [TestCase(5, 10, 10)] MergingMessageWithZero(int? originValue, int? mergingValue, int? expectedResult)378 public void MergingMessageWithZero(int? originValue, int? mergingValue, int? expectedResult) 379 { 380 // This differs from the MergingStreamCornerCase because when we merge message *objects*, 381 // we ignore default values from the "source". 382 var message1 = new TestWellKnownTypes { Int32Field = originValue }; 383 var message2 = new TestWellKnownTypes { Int32Field = mergingValue }; 384 message1.MergeFrom(message2); 385 Assert.AreEqual(expectedResult, message1.Int32Field); 386 } 387 388 [Test] UnknownFieldInWrapper()389 public void UnknownFieldInWrapper() 390 { 391 var stream = new MemoryStream(); 392 var output = new CodedOutputStream(stream); 393 var wrapperTag = WireFormat.MakeTag(TestWellKnownTypes.Int32FieldFieldNumber, WireFormat.WireType.LengthDelimited); 394 var unknownTag = WireFormat.MakeTag(15, WireFormat.WireType.Varint); 395 var valueTag = WireFormat.MakeTag(Int32Value.ValueFieldNumber, WireFormat.WireType.Varint); 396 397 output.WriteTag(wrapperTag); 398 output.WriteLength(4); // unknownTag + value 5 + valueType + value 6, each 1 byte 399 output.WriteTag(unknownTag); 400 output.WriteInt32((int) valueTag); // Sneakily "pretend" it's a tag when it's really a value 401 output.WriteTag(valueTag); 402 output.WriteInt32(6); 403 404 output.Flush(); 405 stream.Position = 0; 406 407 var message = TestWellKnownTypes.Parser.ParseFrom(stream); 408 Assert.AreEqual(6, message.Int32Field); 409 } 410 411 [Test] ClearWithReflection()412 public void ClearWithReflection() 413 { 414 // String and Bytes are the tricky ones here, as the CLR type of the property 415 // is the same between the wrapper and non-wrapper types. 416 var message = new TestWellKnownTypes { StringField = "foo" }; 417 TestWellKnownTypes.Descriptor.Fields[TestWellKnownTypes.StringFieldFieldNumber].Accessor.Clear(message); 418 Assert.IsNull(message.StringField); 419 } 420 } 421 } 422