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/
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
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
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.
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.
34 using Google.Protobuf.TestProtos;
35 using NUnit.Framework;
36 using System.Collections;
39 namespace Google.Protobuf.WellKnownTypes
41 public class WrappersTest
44 public void NullIsDefault()
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);
59 public void NonDefaultSingleValues()
61 var message = new TestWellKnownTypes
64 BytesField = ByteString.CopyFrom(1, 2, 3),
74 var bytes = message.ToByteArray();
75 var parsed = TestWellKnownTypes.Parser.ParseFrom(bytes);
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);
89 public void NonNullDefaultIsPreservedThroughSerialization()
91 var message = new TestWellKnownTypes
94 BytesField = ByteString.Empty,
104 var bytes = message.ToByteArray();
105 var parsed = TestWellKnownTypes.Parser.ParseFrom(bytes);
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);
119 public void RepeatedWrappersProhibitNullItems()
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));
129 public void RepeatedWrappersSerializeDeserialize()
131 var message = new RepeatedWellKnownTypes
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 },
143 var bytes = message.ToByteArray();
144 var parsed = RepeatedWellKnownTypes.Parser.ParseFrom(bytes);
146 Assert.AreEqual(message, parsed);
147 // Just to test a single value for sanity...
148 Assert.AreEqual("Second", message.StringField[1]);
152 public void RepeatedWrappersBinaryFormat()
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.
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);
168 var expectedBytes = rawOutput.ToArray();
170 var message = new RepeatedWellKnownTypes { Int32Field = { 5, 0 } };
171 var actualBytes = message.ToByteArray();
172 Assert.AreEqual(expectedBytes, actualBytes);
176 public void MapWrappersSerializeDeserialize()
178 // Note: no null values here, as they are prohibited in map fields
179 // (despite being representable).
180 var message = new MapWellKnownTypes
182 BoolField = { { 10, false }, { 20, true } },
184 { -1, ByteString.CopyFrom(1, 2, 3) },
185 { 10, ByteString.CopyFrom(4, 5, 6) },
186 { 1000, ByteString.Empty },
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 } },
197 var bytes = message.ToByteArray();
198 var parsed = MapWellKnownTypes.Parser.ParseFrom(bytes);
200 Assert.AreEqual(message, parsed);
201 // Just to test a single value for sanity...
202 Assert.AreEqual("Second", message.StringField[12]);
206 public void Reflection_SingleValues()
208 var message = new TestWellKnownTypes
211 BytesField = ByteString.CopyFrom(1, 2, 3),
214 DoubleField = 12.25d,
220 var fields = TestWellKnownTypes.Descriptor.Fields;
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));
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));
240 public void Reflection_RepeatedFields()
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);
250 public void Reflection_MapFields()
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]);
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);
267 message.StringField = "foo";
268 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.StringField, message.OneofFieldCase);
269 AssertOneofRoundTrip(message);
271 message.StringField = "foo";
272 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.StringField, message.OneofFieldCase);
273 AssertOneofRoundTrip(message);
275 message.DoubleField = 0.0f;
276 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.DoubleField, message.OneofFieldCase);
277 AssertOneofRoundTrip(message);
279 message.DoubleField = 1.0f;
280 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.DoubleField, message.OneofFieldCase);
281 AssertOneofRoundTrip(message);
283 message.ClearOneofField();
284 Assert.AreEqual(OneofWellKnownTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
285 AssertOneofRoundTrip(message);
288 private void AssertOneofRoundTrip(OneofWellKnownTypes message)
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);
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)]
307 public void Merging(string original, string merged, string expected)
309 var originalMessage = new TestWellKnownTypes { StringField = original };
310 var mergingMessage = new TestWellKnownTypes { StringField = merged };
311 originalMessage.MergeFrom(mergingMessage);
312 Assert.AreEqual(expected, originalMessage.StringField);
314 // Try it using MergeFrom(CodedInputStream) too...
315 originalMessage = new TestWellKnownTypes { StringField = original };
316 originalMessage.MergeFrom(mergingMessage.ToByteArray());
317 Assert.AreEqual(expected, originalMessage.StringField);
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.
325 public void MergingStreamExplicitValue()
327 var message = new TestWellKnownTypes { Int32Field = 5 };
329 // Create a byte array which has the data of an Int32Value explicitly containing a value of 0.
330 // This wouldn't normally happen.
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())
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);
342 bytes = stream.ToArray();
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);
352 public void MergingStreamNoValue()
354 var message = new TestWellKnownTypes { Int32Field = 5 };
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);
361 // The "implicit" 0 did *not* overwrite the value.
362 // (This is the correct behaviour.)
363 Assert.AreEqual(5, message.Int32Field);
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.
369 [TestCase(null, null, null)]
370 [TestCase(null, 0, 0)]
371 [TestCase(null, 5, 5)]
372 [TestCase(0, null, 0)]
375 [TestCase(5, null, 5)]
377 [TestCase(5, 10, 10)]
378 public void MergingMessageWithZero(int? originValue, int? mergingValue, int? expectedResult)
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);
389 public void UnknownFieldInWrapper()
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);
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);
407 var message = TestWellKnownTypes.Parser.ParseFrom(stream);
408 Assert.AreEqual(6, message.Int32Field);
412 public void ClearWithReflection()
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);