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
33using System;
34using System.IO;
35using Google.Protobuf.TestProtos;
36using Proto2 = Google.Protobuf.TestProtos.Proto2;
37using NUnit.Framework;
38using System.Collections;
39using System.Collections.Generic;
40using System.Linq;
41using Google.Protobuf.WellKnownTypes;
42
43namespace Google.Protobuf
44{
45    /// <summary>
46    /// Tests around the generated TestAllTypes message.
47    /// </summary>
48    public partial class GeneratedMessageTest
49    {
50        [Test]
51        public void EmptyMessageFieldDistinctFromMissingMessageField()
52        {
53            // This demonstrates what we're really interested in...
54            var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() };
55            var message2 = new TestAllTypes(); // SingleForeignMessage is null
56            EqualityTester.AssertInequality(message1, message2);
57        }
58
59        [Test]
60        public void DefaultValues()
61        {
62            // Single fields
63            var message = new TestAllTypes();
64            Assert.AreEqual(false, message.SingleBool);
65            Assert.AreEqual(ByteString.Empty, message.SingleBytes);
66            Assert.AreEqual(0.0, message.SingleDouble);
67            Assert.AreEqual(0, message.SingleFixed32);
68            Assert.AreEqual(0L, message.SingleFixed64);
69            Assert.AreEqual(0.0f, message.SingleFloat);
70            Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum);
71            Assert.IsNull(message.SingleForeignMessage);
72            Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum);
73            Assert.IsNull(message.SingleImportMessage);
74            Assert.AreEqual(0, message.SingleInt32);
75            Assert.AreEqual(0L, message.SingleInt64);
76            Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum);
77            Assert.IsNull(message.SingleNestedMessage);
78            Assert.IsNull(message.SinglePublicImportMessage);
79            Assert.AreEqual(0, message.SingleSfixed32);
80            Assert.AreEqual(0L, message.SingleSfixed64);
81            Assert.AreEqual(0, message.SingleSint32);
82            Assert.AreEqual(0L, message.SingleSint64);
83            Assert.AreEqual("", message.SingleString);
84            Assert.AreEqual(0U, message.SingleUint32);
85            Assert.AreEqual(0UL, message.SingleUint64);
86
87            // Repeated fields
88            Assert.AreEqual(0, message.RepeatedBool.Count);
89            Assert.AreEqual(0, message.RepeatedBytes.Count);
90            Assert.AreEqual(0, message.RepeatedDouble.Count);
91            Assert.AreEqual(0, message.RepeatedFixed32.Count);
92            Assert.AreEqual(0, message.RepeatedFixed64.Count);
93            Assert.AreEqual(0, message.RepeatedFloat.Count);
94            Assert.AreEqual(0, message.RepeatedForeignEnum.Count);
95            Assert.AreEqual(0, message.RepeatedForeignMessage.Count);
96            Assert.AreEqual(0, message.RepeatedImportEnum.Count);
97            Assert.AreEqual(0, message.RepeatedImportMessage.Count);
98            Assert.AreEqual(0, message.RepeatedNestedEnum.Count);
99            Assert.AreEqual(0, message.RepeatedNestedMessage.Count);
100            Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count);
101            Assert.AreEqual(0, message.RepeatedSfixed32.Count);
102            Assert.AreEqual(0, message.RepeatedSfixed64.Count);
103            Assert.AreEqual(0, message.RepeatedSint32.Count);
104            Assert.AreEqual(0, message.RepeatedSint64.Count);
105            Assert.AreEqual(0, message.RepeatedString.Count);
106            Assert.AreEqual(0, message.RepeatedUint32.Count);
107            Assert.AreEqual(0, message.RepeatedUint64.Count);
108
109            // Oneof fields
110            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
111            Assert.AreEqual(0, message.OneofUint32);
112            Assert.AreEqual("", message.OneofString);
113            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
114            Assert.IsNull(message.OneofNestedMessage);
115        }
116
117        [Test]
118        public void NullStringAndBytesRejected()
119        {
120            var message = new TestAllTypes();
121            Assert.Throws<ArgumentNullException>(() => message.SingleString = null);
122            Assert.Throws<ArgumentNullException>(() => message.OneofString = null);
123            Assert.Throws<ArgumentNullException>(() => message.SingleBytes = null);
124            Assert.Throws<ArgumentNullException>(() => message.OneofBytes = null);
125        }
126
127        [Test]
128        public void RoundTrip_Empty()
129        {
130            var message = new TestAllTypes();
131            // Without setting any values, there's nothing to write.
132            byte[] bytes = message.ToByteArray();
133            Assert.AreEqual(0, bytes.Length);
134
135            MessageParsingHelpers.AssertWritingMessage(message);
136
137            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
138        }
139
140        [Test]
141        public void RoundTrip_SingleValues()
142        {
143            var message = new TestAllTypes
144            {
145                SingleBool = true,
146                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
147                SingleDouble = 23.5,
148                SingleFixed32 = 23,
149                SingleFixed64 = 1234567890123,
150                SingleFloat = 12.25f,
151                SingleForeignEnum = ForeignEnum.ForeignBar,
152                SingleForeignMessage = new ForeignMessage { C = 10 },
153                SingleImportEnum = ImportEnum.ImportBaz,
154                SingleImportMessage = new ImportMessage { D = 20 },
155                SingleInt32 = 100,
156                SingleInt64 = 3210987654321,
157                SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
158                SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
159                SinglePublicImportMessage = new PublicImportMessage { E = 54 },
160                SingleSfixed32 = -123,
161                SingleSfixed64 = -12345678901234,
162                SingleSint32 = -456,
163                SingleSint64 = -12345678901235,
164                SingleString = "test",
165                SingleUint32 = uint.MaxValue,
166                SingleUint64 = ulong.MaxValue
167            };
168
169            MessageParsingHelpers.AssertWritingMessage(message);
170
171            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
172        }
173
174        [Test]
175        public void RoundTrip_RepeatedValues()
176        {
177            var message = new TestAllTypes
178            {
179                RepeatedBool = { true, false },
180                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
181                RepeatedDouble = { -12.25, 23.5 },
182                RepeatedFixed32 = { uint.MaxValue, 23 },
183                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
184                RepeatedFloat = { 100f, 12.25f },
185                RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar },
186                RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } },
187                RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.Unspecified },
188                RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } },
189                RepeatedInt32 = { 100, 200 },
190                RepeatedInt64 = { 3210987654321, long.MaxValue },
191                RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
192                RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 35 }, new TestAllTypes.Types.NestedMessage { Bb = 10 } },
193                RepeatedPublicImportMessage = { new PublicImportMessage { E = 54 }, new PublicImportMessage { E = -1 } },
194                RepeatedSfixed32 = { -123, 123 },
195                RepeatedSfixed64 = { -12345678901234, 12345678901234 },
196                RepeatedSint32 = { -456, 100 },
197                RepeatedSint64 = { -12345678901235, 123 },
198                RepeatedString = { "foo", "bar" },
199                RepeatedUint32 = { uint.MaxValue, uint.MinValue },
200                RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
201            };
202
203            MessageParsingHelpers.AssertWritingMessage(message);
204
205            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
206        }
207
208        // Note that not every map within map_unittest_proto3 is used. They all go through very
209        // similar code paths. The fact that all maps are present is validation that we have codecs
210        // for every type.
211        [Test]
212        public void RoundTrip_Maps()
213        {
214            var message = new TestMap
215            {
216                MapBoolBool = {
217                    { false, true },
218                    { true, false }
219                },
220                MapInt32Bytes = {
221                    { 5, ByteString.CopyFrom(6, 7, 8) },
222                    { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
223                    { 10, ByteString.Empty }
224                },
225                MapInt32ForeignMessage = {
226                    { 0, new ForeignMessage { C = 10 } },
227                    { 5, new ForeignMessage() },
228                },
229                MapInt32Enum = {
230                    { 1, MapEnum.Bar },
231                    { 2000, MapEnum.Foo }
232                }
233            };
234
235            MessageParsingHelpers.AssertWritingMessage(message);
236
237            MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);
238        }
239
240        [Test]
241        public void MapWithEmptyEntry()
242        {
243            var message = new TestMap
244            {
245                MapInt32Bytes = { { 0, ByteString.Empty } }
246            };
247
248            byte[] bytes = message.ToByteArray();
249            Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
250
251            MessageParsingHelpers.AssertWritingMessage(message);
252
253            MessageParsingHelpers.AssertReadingMessage(
254                TestMap.Parser,
255                bytes,
256                parsed=>
257                {
258                    Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
259                    Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
260                });
261        }
262
263        [Test]
264        public void MapWithOnlyValue()
265        {
266            // Hand-craft the stream to contain a single entry with just a value.
267            var memoryStream = new MemoryStream();
268            var output = new CodedOutputStream(memoryStream);
269            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
270            var nestedMessage = new ForeignMessage { C = 20 };
271            // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
272            output.WriteLength(2 + nestedMessage.CalculateSize());
273            output.WriteTag(2, WireFormat.WireType.LengthDelimited);
274            output.WriteMessage(nestedMessage);
275            output.Flush();
276
277            MessageParsingHelpers.AssertReadingMessage(
278                TestMap.Parser,
279                memoryStream.ToArray(),
280                parsed =>
281                {
282                    Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
283                });
284        }
285
286        [Test]
287        public void MapWithOnlyKey_PrimitiveValue()
288        {
289            // Hand-craft the stream to contain a single entry with just a key.
290            var memoryStream = new MemoryStream();
291            var output = new CodedOutputStream(memoryStream);
292            output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
293            int key = 10;
294            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
295            output.WriteTag(1, WireFormat.WireType.Varint);
296            output.WriteInt32(key);
297            output.Flush();
298
299            MessageParsingHelpers.AssertReadingMessage(
300                TestMap.Parser,
301                memoryStream.ToArray(),
302                parsed =>
303                {
304                    Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
305                });
306        }
307
308        [Test]
309        public void MapWithOnlyKey_MessageValue()
310        {
311            // Hand-craft the stream to contain a single entry with just a key.
312            var memoryStream = new MemoryStream();
313            var output = new CodedOutputStream(memoryStream);
314            output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
315            int key = 10;
316            output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
317            output.WriteTag(1, WireFormat.WireType.Varint);
318            output.WriteInt32(key);
319            output.Flush();
320
321            MessageParsingHelpers.AssertReadingMessage(
322                TestMap.Parser,
323                memoryStream.ToArray(),
324                parsed =>
325                {
326                    Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
327                });
328        }
329
330        [Test]
331        public void MapIgnoresExtraFieldsWithinEntryMessages()
332        {
333            // Hand-craft the stream to contain a single entry with three fields
334            var memoryStream = new MemoryStream();
335            var output = new CodedOutputStream(memoryStream);
336
337            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
338
339            var key = 10; // Field 1
340            var value = 20; // Field 2
341            var extra = 30; // Field 3
342
343            // Each field can be represented in a single byte, with a single byte tag.
344            // Total message size: 6 bytes.
345            output.WriteLength(6);
346            output.WriteTag(1, WireFormat.WireType.Varint);
347            output.WriteInt32(key);
348            output.WriteTag(2, WireFormat.WireType.Varint);
349            output.WriteInt32(value);
350            output.WriteTag(3, WireFormat.WireType.Varint);
351            output.WriteInt32(extra);
352            output.Flush();
353
354            MessageParsingHelpers.AssertReadingMessage(
355                TestMap.Parser,
356                memoryStream.ToArray(),
357                parsed =>
358                {
359                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);
360                });
361        }
362
363        [Test]
364        public void MapFieldOrderIsIrrelevant()
365        {
366            var memoryStream = new MemoryStream();
367            var output = new CodedOutputStream(memoryStream);
368
369            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
370
371            var key = 10;
372            var value = 20;
373
374            // Each field can be represented in a single byte, with a single byte tag.
375            // Total message size: 4 bytes.
376            output.WriteLength(4);
377            output.WriteTag(2, WireFormat.WireType.Varint);
378            output.WriteInt32(value);
379            output.WriteTag(1, WireFormat.WireType.Varint);
380            output.WriteInt32(key);
381            output.Flush();
382
383            MessageParsingHelpers.AssertReadingMessage(
384                TestMap.Parser,
385                memoryStream.ToArray(),
386                parsed =>
387                {
388                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);
389                });
390        }
391
392        [Test]
393        public void MapNonContiguousEntries()
394        {
395            var memoryStream = new MemoryStream();
396            var output = new CodedOutputStream(memoryStream);
397
398            // Message structure:
399            // Entry for MapInt32Int32
400            // Entry for MapStringString
401            // Entry for MapInt32Int32
402
403            // First entry
404            var key1 = 10;
405            var value1 = 20;
406            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
407            output.WriteLength(4);
408            output.WriteTag(1, WireFormat.WireType.Varint);
409            output.WriteInt32(key1);
410            output.WriteTag(2, WireFormat.WireType.Varint);
411            output.WriteInt32(value1);
412
413            // Second entry
414            var key2 = "a";
415            var value2 = "b";
416            output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited);
417            output.WriteLength(6); // 3 bytes per entry: tag, size, character
418            output.WriteTag(1, WireFormat.WireType.LengthDelimited);
419            output.WriteString(key2);
420            output.WriteTag(2, WireFormat.WireType.LengthDelimited);
421            output.WriteString(value2);
422
423            // Third entry
424            var key3 = 15;
425            var value3 = 25;
426            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
427            output.WriteLength(4);
428            output.WriteTag(1, WireFormat.WireType.Varint);
429            output.WriteInt32(key3);
430            output.WriteTag(2, WireFormat.WireType.Varint);
431            output.WriteInt32(value3);
432
433            output.Flush();
434
435            MessageParsingHelpers.AssertReadingMessage(
436                TestMap.Parser,
437                memoryStream.ToArray(),
438                parsed =>
439                {
440                    var expected = new TestMap
441                    {
442                        MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
443                        MapStringString = { { key2, value2 } }
444                    };
445                    Assert.AreEqual(expected, parsed);
446                });
447        }
448
449        [Test]
450        public void DuplicateKeys_LastEntryWins()
451        {
452            var memoryStream = new MemoryStream();
453            var output = new CodedOutputStream(memoryStream);
454
455            var key = 10;
456            var value1 = 20;
457            var value2 = 30;
458
459            // First entry
460            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
461            output.WriteLength(4);
462            output.WriteTag(1, WireFormat.WireType.Varint);
463            output.WriteInt32(key);
464            output.WriteTag(2, WireFormat.WireType.Varint);
465            output.WriteInt32(value1);
466
467            // Second entry - same key, different value
468            output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited);
469            output.WriteLength(4);
470            output.WriteTag(1, WireFormat.WireType.Varint);
471            output.WriteInt32(key);
472            output.WriteTag(2, WireFormat.WireType.Varint);
473            output.WriteInt32(value2);
474            output.Flush();
475
476            MessageParsingHelpers.AssertReadingMessage(
477                TestMap.Parser,
478                memoryStream.ToArray(),
479                parsed =>
480                {
481                    Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
482                });
483        }
484
485        [Test]
486        public void CloneSingleNonMessageValues()
487        {
488            var original = new TestAllTypes
489            {
490                SingleBool = true,
491                SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
492                SingleDouble = 23.5,
493                SingleFixed32 = 23,
494                SingleFixed64 = 1234567890123,
495                SingleFloat = 12.25f,
496                SingleInt32 = 100,
497                SingleInt64 = 3210987654321,
498                SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
499                SingleSfixed32 = -123,
500                SingleSfixed64 = -12345678901234,
501                SingleSint32 = -456,
502                SingleSint64 = -12345678901235,
503                SingleString = "test",
504                SingleUint32 = uint.MaxValue,
505                SingleUint64 = ulong.MaxValue
506            };
507            var clone = original.Clone();
508            Assert.AreNotSame(original, clone);
509            Assert.AreEqual(original, clone);
510            // Just as a single example
511            clone.SingleInt32 = 150;
512            Assert.AreNotEqual(original, clone);
513        }
514
515        [Test]
516        public void CloneRepeatedNonMessageValues()
517        {
518            var original = new TestAllTypes
519            {
520                RepeatedBool = { true, false },
521                RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) },
522                RepeatedDouble = { -12.25, 23.5 },
523                RepeatedFixed32 = { uint.MaxValue, 23 },
524                RepeatedFixed64 = { ulong.MaxValue, 1234567890123 },
525                RepeatedFloat = { 100f, 12.25f },
526                RepeatedInt32 = { 100, 200 },
527                RepeatedInt64 = { 3210987654321, long.MaxValue },
528                RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg },
529                RepeatedSfixed32 = { -123, 123 },
530                RepeatedSfixed64 = { -12345678901234, 12345678901234 },
531                RepeatedSint32 = { -456, 100 },
532                RepeatedSint64 = { -12345678901235, 123 },
533                RepeatedString = { "foo", "bar" },
534                RepeatedUint32 = { uint.MaxValue, uint.MinValue },
535                RepeatedUint64 = { ulong.MaxValue, uint.MinValue }
536            };
537
538            var clone = original.Clone();
539            Assert.AreNotSame(original, clone);
540            Assert.AreEqual(original, clone);
541            // Just as a single example
542            clone.RepeatedDouble.Add(25.5);
543            Assert.AreNotEqual(original, clone);
544        }
545
546        [Test]
547        public void CloneSingleMessageField()
548        {
549            var original = new TestAllTypes
550            {
551                SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
552            };
553
554            var clone = original.Clone();
555            Assert.AreNotSame(original, clone);
556            Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage);
557            Assert.AreEqual(original, clone);
558
559            clone.SingleNestedMessage.Bb = 30;
560            Assert.AreNotEqual(original, clone);
561        }
562
563        [Test]
564        public void CloneRepeatedMessageField()
565        {
566            var original = new TestAllTypes
567            {
568                RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } }
569            };
570
571            var clone = original.Clone();
572            Assert.AreNotSame(original, clone);
573            Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage);
574            Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]);
575            Assert.AreEqual(original, clone);
576
577            clone.RepeatedNestedMessage[0].Bb = 30;
578            Assert.AreNotEqual(original, clone);
579        }
580
581        [Test]
582        public void CloneOneofField()
583        {
584            var original = new TestAllTypes
585            {
586                OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 }
587            };
588
589            var clone = original.Clone();
590            Assert.AreNotSame(original, clone);
591            Assert.AreEqual(original, clone);
592
593            // We should have cloned the message
594            original.OneofNestedMessage.Bb = 30;
595            Assert.AreNotEqual(original, clone);
596        }
597
598        [Test]
599        public void OneofProperties()
600        {
601            // Switch the oneof case between each of the different options, and check everything behaves
602            // as expected in each case.
603            var message = new TestAllTypes();
604            Assert.AreEqual("", message.OneofString);
605            Assert.AreEqual(0, message.OneofUint32);
606            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
607            Assert.IsNull(message.OneofNestedMessage);
608            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
609
610            message.OneofString = "sample";
611            Assert.AreEqual("sample", message.OneofString);
612            Assert.AreEqual(0, message.OneofUint32);
613            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
614            Assert.IsNull(message.OneofNestedMessage);
615            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase);
616
617            var bytes = ByteString.CopyFrom(1, 2, 3);
618            message.OneofBytes = bytes;
619            Assert.AreEqual("", message.OneofString);
620            Assert.AreEqual(0, message.OneofUint32);
621            Assert.AreEqual(bytes, message.OneofBytes);
622            Assert.IsNull(message.OneofNestedMessage);
623            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofBytes, message.OneofFieldCase);
624
625            message.OneofUint32 = 20;
626            Assert.AreEqual("", message.OneofString);
627            Assert.AreEqual(20, message.OneofUint32);
628            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
629            Assert.IsNull(message.OneofNestedMessage);
630            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase);
631
632            var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 25 };
633            message.OneofNestedMessage = nestedMessage;
634            Assert.AreEqual("", message.OneofString);
635            Assert.AreEqual(0, message.OneofUint32);
636            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
637            Assert.AreEqual(nestedMessage, message.OneofNestedMessage);
638            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofNestedMessage, message.OneofFieldCase);
639
640            message.ClearOneofField();
641            Assert.AreEqual("", message.OneofString);
642            Assert.AreEqual(0, message.OneofUint32);
643            Assert.AreEqual(ByteString.Empty, message.OneofBytes);
644            Assert.IsNull(message.OneofNestedMessage);
645            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
646        }
647
648        [Test]
649        public void Oneof_DefaultValuesNotEqual()
650        {
651            var message1 = new TestAllTypes { OneofString = "" };
652            var message2 = new TestAllTypes { OneofUint32 = 0 };
653            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase);
654            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
655            Assert.AreNotEqual(message1, message2);
656        }
657
658        [Test]
659        public void OneofSerialization_NonDefaultValue()
660        {
661            var message = new TestAllTypes();
662            message.OneofString = "this would take a bit of space";
663            message.OneofUint32 = 10;
664            var bytes = message.ToByteArray();
665            Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
666
667            MessageParsingHelpers.AssertWritingMessage(message);
668
669            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
670            {
671                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
672            });
673        }
674
675        [Test]
676        public void OneofSerialization_DefaultValue()
677        {
678            var message = new TestAllTypes();
679            message.OneofString = "this would take a bit of space";
680            message.OneofUint32 = 0; // This is the default value for UInt32; normally wouldn't be serialized
681            var bytes = message.ToByteArray();
682            Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
683
684            MessageParsingHelpers.AssertWritingMessage(message);
685
686            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
687            {
688                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
689            });
690        }
691
692        [Test]
693        public void DiscardUnknownFields_RealDataStillRead()
694        {
695            var message = SampleMessages.CreateFullTestAllTypes();
696            var stream = new MemoryStream();
697            var output = new CodedOutputStream(stream);
698            var unusedFieldNumber = 23456;
699            Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber));
700            output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited);
701            output.WriteString("ignore me");
702            message.WriteTo(output);
703            output.Flush();
704
705            MessageParsingHelpers.AssertReadingMessage(
706                TestAllTypes.Parser,
707                stream.ToArray(),
708                parsed =>
709                {
710                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
711                    // Assert.AreEqual(message, parsed);
712                });
713        }
714
715        [Test]
716        public void DiscardUnknownFields_AllTypes()
717        {
718            // Simple way of ensuring we can skip all kinds of fields.
719            var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
720            var empty = Empty.Parser.ParseFrom(data);
721
722            MessageParsingHelpers.AssertReadingMessage(
723                Empty.Parser,
724                data,
725                parsed =>
726                {
727                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
728                    // Assert.AreNotEqual(new Empty(), empty);
729                });
730        }
731
732        // This was originally seen as a conformance test failure.
733        [Test]
734        public void TruncatedMessageFieldThrows()
735        {
736            // 130, 3 is the message tag
737            // 1 is the data length - but there's no data.
738            var data = new byte[] { 130, 3, 1 };
739            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, data);
740        }
741
742        /// <summary>
743        /// Demonstrates current behaviour with an extraneous end group tag - see issue 688
744        /// for details; we may want to change this.
745        /// </summary>
746        [Test]
747        public void ExtraEndGroupThrows()
748        {
749            var message = SampleMessages.CreateFullTestAllTypes();
750            var stream = new MemoryStream();
751            var output = new CodedOutputStream(stream);
752
753            output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32);
754            output.WriteFixed32(123);
755            output.WriteTag(100, WireFormat.WireType.EndGroup);
756
757            output.Flush();
758
759            stream.Position = 0;
760            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, stream.ToArray());
761        }
762
763        [Test]
764        public void CustomDiagnosticMessage_DirectToStringCall()
765        {
766            var message = new ForeignMessage { C = 31 };
767            Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString());
768            Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message));
769        }
770
771        [Test]
772        public void CustomDiagnosticMessage_Nested()
773        {
774            var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } };
775            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString());
776            Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message));
777        }
778
779        [Test]
780        public void CustomDiagnosticMessage_DirectToTextWriterCall()
781        {
782            var message = new ForeignMessage { C = 31 };
783            var writer = new StringWriter();
784            JsonFormatter.Default.Format(message, writer);
785            Assert.AreEqual("{ \"c\": 31 }", writer.ToString());
786        }
787
788        [Test]
789        public void NaNComparisons()
790        {
791            var message1 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };
792            var message2 = new TestAllTypes { SingleDouble = SampleNaNs.PayloadFlipped };
793            var message3 = new TestAllTypes { SingleDouble = SampleNaNs.Regular };
794
795            EqualityTester.AssertInequality(message1, message2);
796            EqualityTester.AssertEquality(message1, message3);
797        }
798    }
799}