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