| #region Copyright notice and license |
| // Protocol Buffers - Google's data interchange format |
| // Copyright 2015 Google Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| #endregion |
| |
| using System; |
| using System.IO; |
| using Google.Protobuf.TestProtos; |
| using NUnit.Framework; |
| using System.Linq; |
| using Google.Protobuf.WellKnownTypes; |
| using Google.Protobuf.Collections; |
| |
| namespace Google.Protobuf |
| { |
| /// <summary> |
| /// Tests around the generated TestAllTypes message. |
| /// </summary> |
| public partial class GeneratedMessageTest |
| { |
| [Test] |
| public void EmptyMessageFieldDistinctFromMissingMessageField() |
| { |
| // This demonstrates what we're really interested in... |
| var message1 = new TestAllTypes { SingleForeignMessage = new ForeignMessage() }; |
| var message2 = new TestAllTypes(); // SingleForeignMessage is null |
| EqualityTester.AssertInequality(message1, message2); |
| } |
| |
| [Test] |
| public void DefaultValues() |
| { |
| // Single fields |
| var message = new TestAllTypes(); |
| Assert.AreEqual(false, message.SingleBool); |
| Assert.AreEqual(ByteString.Empty, message.SingleBytes); |
| Assert.AreEqual(0.0, message.SingleDouble); |
| Assert.AreEqual(0, message.SingleFixed32); |
| Assert.AreEqual(0L, message.SingleFixed64); |
| Assert.AreEqual(0.0f, message.SingleFloat); |
| Assert.AreEqual(ForeignEnum.ForeignUnspecified, message.SingleForeignEnum); |
| Assert.IsNull(message.SingleForeignMessage); |
| Assert.AreEqual(ImportEnum.Unspecified, message.SingleImportEnum); |
| Assert.IsNull(message.SingleImportMessage); |
| Assert.AreEqual(0, message.SingleInt32); |
| Assert.AreEqual(0L, message.SingleInt64); |
| Assert.AreEqual(TestAllTypes.Types.NestedEnum.Unspecified, message.SingleNestedEnum); |
| Assert.IsNull(message.SingleNestedMessage); |
| Assert.IsNull(message.SinglePublicImportMessage); |
| Assert.AreEqual(0, message.SingleSfixed32); |
| Assert.AreEqual(0L, message.SingleSfixed64); |
| Assert.AreEqual(0, message.SingleSint32); |
| Assert.AreEqual(0L, message.SingleSint64); |
| Assert.AreEqual("", message.SingleString); |
| Assert.AreEqual(0U, message.SingleUint32); |
| Assert.AreEqual(0UL, message.SingleUint64); |
| |
| // Repeated fields |
| Assert.AreEqual(0, message.RepeatedBool.Count); |
| Assert.AreEqual(0, message.RepeatedBytes.Count); |
| Assert.AreEqual(0, message.RepeatedDouble.Count); |
| Assert.AreEqual(0, message.RepeatedFixed32.Count); |
| Assert.AreEqual(0, message.RepeatedFixed64.Count); |
| Assert.AreEqual(0, message.RepeatedFloat.Count); |
| Assert.AreEqual(0, message.RepeatedForeignEnum.Count); |
| Assert.AreEqual(0, message.RepeatedForeignMessage.Count); |
| Assert.AreEqual(0, message.RepeatedImportEnum.Count); |
| Assert.AreEqual(0, message.RepeatedImportMessage.Count); |
| Assert.AreEqual(0, message.RepeatedNestedEnum.Count); |
| Assert.AreEqual(0, message.RepeatedNestedMessage.Count); |
| Assert.AreEqual(0, message.RepeatedPublicImportMessage.Count); |
| Assert.AreEqual(0, message.RepeatedSfixed32.Count); |
| Assert.AreEqual(0, message.RepeatedSfixed64.Count); |
| Assert.AreEqual(0, message.RepeatedSint32.Count); |
| Assert.AreEqual(0, message.RepeatedSint64.Count); |
| Assert.AreEqual(0, message.RepeatedString.Count); |
| Assert.AreEqual(0, message.RepeatedUint32.Count); |
| Assert.AreEqual(0, message.RepeatedUint64.Count); |
| |
| // Oneof fields |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase); |
| Assert.AreEqual(0, message.OneofUint32); |
| Assert.AreEqual("", message.OneofString); |
| Assert.AreEqual(ByteString.Empty, message.OneofBytes); |
| Assert.IsNull(message.OneofNestedMessage); |
| } |
| |
| [Test] |
| public void NullStringAndBytesRejected() |
| { |
| var message = new TestAllTypes(); |
| Assert.Throws<ArgumentNullException>(() => message.SingleString = null); |
| Assert.Throws<ArgumentNullException>(() => message.OneofString = null); |
| Assert.Throws<ArgumentNullException>(() => message.SingleBytes = null); |
| Assert.Throws<ArgumentNullException>(() => message.OneofBytes = null); |
| } |
| |
| [Test] |
| public void Roundtrip_UnpairedSurrogate() |
| { |
| var message = new TestAllTypes { SingleString = "\ud83d" }; |
| |
| Assert.AreEqual("\ud83d", message.SingleString); |
| |
| // The serialized bytes contain the replacement character. |
| var bytes = message.ToByteArray(); |
| CollectionAssert.AreEqual(bytes, new byte[] { 0x72, 3, 0xEF, 0xBF, 0xBD }); |
| } |
| |
| [Test] |
| public void InvalidUtf8ParsesAsReplacementChars() |
| { |
| var payload = new byte[] { 0x72, 1, 0x80 }; |
| |
| // We would prefer to have this parse operation fail, but at the moment it substitutes |
| // the replacement character. |
| var message = TestAllTypes.Parser.ParseFrom(payload); |
| Assert.AreEqual("\ufffd", message.SingleString); |
| } |
| |
| [Test] |
| public void RoundTrip_Empty() |
| { |
| var message = new TestAllTypes(); |
| // Without setting any values, there's nothing to write. |
| byte[] bytes = message.ToByteArray(); |
| Assert.AreEqual(0, bytes.Length); |
| |
| MessageParsingHelpers.AssertWritingMessage(message); |
| |
| MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message); |
| } |
| |
| [Test] |
| public void RoundTrip_SingleValues() |
| { |
| var message = new TestAllTypes |
| { |
| SingleBool = true, |
| SingleBytes = ByteString.CopyFrom(1, 2, 3, 4), |
| SingleDouble = 23.5, |
| SingleFixed32 = 23, |
| SingleFixed64 = 1234567890123, |
| SingleFloat = 12.25f, |
| SingleForeignEnum = ForeignEnum.ForeignBar, |
| SingleForeignMessage = new ForeignMessage { C = 10 }, |
| SingleImportEnum = ImportEnum.ImportBaz, |
| SingleImportMessage = new ImportMessage { D = 20 }, |
| SingleInt32 = 100, |
| SingleInt64 = 3210987654321, |
| SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo, |
| SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 }, |
| SinglePublicImportMessage = new PublicImportMessage { E = 54 }, |
| SingleSfixed32 = -123, |
| SingleSfixed64 = -12345678901234, |
| SingleSint32 = -456, |
| SingleSint64 = -12345678901235, |
| SingleString = "test", |
| SingleUint32 = uint.MaxValue, |
| SingleUint64 = ulong.MaxValue |
| }; |
| |
| MessageParsingHelpers.AssertWritingMessage(message); |
| |
| MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message); |
| } |
| |
| [Test] |
| public void RoundTrip_RepeatedValues() |
| { |
| var message = new TestAllTypes |
| { |
| RepeatedBool = { true, false }, |
| RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) }, |
| RepeatedDouble = { -12.25, 23.5 }, |
| RepeatedFixed32 = { uint.MaxValue, 23 }, |
| RepeatedFixed64 = { ulong.MaxValue, 1234567890123 }, |
| RepeatedFloat = { 100f, 12.25f }, |
| RepeatedForeignEnum = { ForeignEnum.ForeignFoo, ForeignEnum.ForeignBar }, |
| RepeatedForeignMessage = { new ForeignMessage(), new ForeignMessage { C = 10 } }, |
| RepeatedImportEnum = { ImportEnum.ImportBaz, ImportEnum.Unspecified }, |
| RepeatedImportMessage = { new ImportMessage { D = 20 }, new ImportMessage { D = 25 } }, |
| RepeatedInt32 = { 100, 200 }, |
| RepeatedInt64 = { 3210987654321, long.MaxValue }, |
| RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg }, |
| RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 35 }, new TestAllTypes.Types.NestedMessage { Bb = 10 } }, |
| RepeatedPublicImportMessage = { new PublicImportMessage { E = 54 }, new PublicImportMessage { E = -1 } }, |
| RepeatedSfixed32 = { -123, 123 }, |
| RepeatedSfixed64 = { -12345678901234, 12345678901234 }, |
| RepeatedSint32 = { -456, 100 }, |
| RepeatedSint64 = { -12345678901235, 123 }, |
| RepeatedString = { "foo", "bar" }, |
| RepeatedUint32 = { uint.MaxValue, uint.MinValue }, |
| RepeatedUint64 = { ulong.MaxValue, uint.MinValue } |
| }; |
| |
| MessageParsingHelpers.AssertWritingMessage(message); |
| |
| MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message); |
| } |
| |
| // Note that not every map within map_unittest_proto3 is used. They all go through very |
| // similar code paths. The fact that all maps are present is validation that we have codecs |
| // for every type. |
| [Test] |
| public void RoundTrip_Maps() |
| { |
| var message = new TestMap |
| { |
| MapBoolBool = { |
| { false, true }, |
| { true, false } |
| }, |
| MapInt32Bytes = { |
| { 5, ByteString.CopyFrom(6, 7, 8) }, |
| { 25, ByteString.CopyFrom(1, 2, 3, 4, 5) }, |
| { 10, ByteString.Empty } |
| }, |
| MapInt32ForeignMessage = { |
| { 0, new ForeignMessage { C = 10 } }, |
| { 5, new ForeignMessage() }, |
| }, |
| MapInt32Enum = { |
| { 1, MapEnum.Bar }, |
| { 2000, MapEnum.Foo } |
| } |
| }; |
| |
| MessageParsingHelpers.AssertWritingMessage(message); |
| |
| MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message); |
| } |
| |
| [Test] |
| public void MapWithEmptyEntry() |
| { |
| var message = new TestMap |
| { |
| MapInt32Bytes = { { 0, ByteString.Empty } } |
| }; |
| |
| byte[] bytes = message.ToByteArray(); |
| Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte) |
| |
| MessageParsingHelpers.AssertWritingMessage(message); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestMap.Parser, |
| bytes, |
| parsed=> |
| { |
| Assert.AreEqual(1, parsed.MapInt32Bytes.Count); |
| Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]); |
| }); |
| } |
| |
| [Test] |
| public void MapWithOnlyValue() |
| { |
| // Hand-craft the stream to contain a single entry with just a value. |
| var memoryStream = new MemoryStream(); |
| var output = new CodedOutputStream(memoryStream); |
| output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited); |
| var nestedMessage = new ForeignMessage { C = 20 }; |
| // Size of the entry (tag, size written by WriteMessage, data written by WriteMessage) |
| output.WriteLength(2 + nestedMessage.CalculateSize()); |
| output.WriteTag(2, WireFormat.WireType.LengthDelimited); |
| output.WriteMessage(nestedMessage); |
| output.Flush(); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestMap.Parser, |
| memoryStream.ToArray(), |
| parsed => |
| { |
| Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]); |
| }); |
| } |
| |
| [Test] |
| public void MapWithOnlyKey_PrimitiveValue() |
| { |
| // Hand-craft the stream to contain a single entry with just a key. |
| var memoryStream = new MemoryStream(); |
| var output = new CodedOutputStream(memoryStream); |
| output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited); |
| int key = 10; |
| output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key)); |
| output.WriteTag(1, WireFormat.WireType.Varint); |
| output.WriteInt32(key); |
| output.Flush(); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestMap.Parser, |
| memoryStream.ToArray(), |
| parsed => |
| { |
| Assert.AreEqual(0.0, parsed.MapInt32Double[key]); |
| }); |
| } |
| |
| [Test] |
| public void MapWithOnlyKey_MessageValue() |
| { |
| // Hand-craft the stream to contain a single entry with just a key. |
| var memoryStream = new MemoryStream(); |
| var output = new CodedOutputStream(memoryStream); |
| output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited); |
| int key = 10; |
| output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key)); |
| output.WriteTag(1, WireFormat.WireType.Varint); |
| output.WriteInt32(key); |
| output.Flush(); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestMap.Parser, |
| memoryStream.ToArray(), |
| parsed => |
| { |
| Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]); |
| }); |
| } |
| |
| [Test] |
| public void MapIgnoresExtraFieldsWithinEntryMessages() |
| { |
| // Hand-craft the stream to contain a single entry with three fields |
| var memoryStream = new MemoryStream(); |
| var output = new CodedOutputStream(memoryStream); |
| |
| output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); |
| |
| var key = 10; // Field 1 |
| var value = 20; // Field 2 |
| var extra = 30; // Field 3 |
| |
| // Each field can be represented in a single byte, with a single byte tag. |
| // Total message size: 6 bytes. |
| output.WriteLength(6); |
| output.WriteTag(1, WireFormat.WireType.Varint); |
| output.WriteInt32(key); |
| output.WriteTag(2, WireFormat.WireType.Varint); |
| output.WriteInt32(value); |
| output.WriteTag(3, WireFormat.WireType.Varint); |
| output.WriteInt32(extra); |
| output.Flush(); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestMap.Parser, |
| memoryStream.ToArray(), |
| parsed => |
| { |
| Assert.AreEqual(value, parsed.MapInt32Int32[key]); |
| }); |
| } |
| |
| [Test] |
| public void MapFieldOrderIsIrrelevant() |
| { |
| var memoryStream = new MemoryStream(); |
| var output = new CodedOutputStream(memoryStream); |
| |
| output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); |
| |
| var key = 10; |
| var value = 20; |
| |
| // Each field can be represented in a single byte, with a single byte tag. |
| // Total message size: 4 bytes. |
| output.WriteLength(4); |
| output.WriteTag(2, WireFormat.WireType.Varint); |
| output.WriteInt32(value); |
| output.WriteTag(1, WireFormat.WireType.Varint); |
| output.WriteInt32(key); |
| output.Flush(); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestMap.Parser, |
| memoryStream.ToArray(), |
| parsed => |
| { |
| Assert.AreEqual(value, parsed.MapInt32Int32[key]); |
| }); |
| } |
| |
| [Test] |
| public void MapNonContiguousEntries() |
| { |
| var memoryStream = new MemoryStream(); |
| var output = new CodedOutputStream(memoryStream); |
| |
| // Message structure: |
| // Entry for MapInt32Int32 |
| // Entry for MapStringString |
| // Entry for MapInt32Int32 |
| |
| // First entry |
| var key1 = 10; |
| var value1 = 20; |
| output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); |
| output.WriteLength(4); |
| output.WriteTag(1, WireFormat.WireType.Varint); |
| output.WriteInt32(key1); |
| output.WriteTag(2, WireFormat.WireType.Varint); |
| output.WriteInt32(value1); |
| |
| // Second entry |
| var key2 = "a"; |
| var value2 = "b"; |
| output.WriteTag(TestMap.MapStringStringFieldNumber, WireFormat.WireType.LengthDelimited); |
| output.WriteLength(6); // 3 bytes per entry: tag, size, character |
| output.WriteTag(1, WireFormat.WireType.LengthDelimited); |
| output.WriteString(key2); |
| output.WriteTag(2, WireFormat.WireType.LengthDelimited); |
| output.WriteString(value2); |
| |
| // Third entry |
| var key3 = 15; |
| var value3 = 25; |
| output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); |
| output.WriteLength(4); |
| output.WriteTag(1, WireFormat.WireType.Varint); |
| output.WriteInt32(key3); |
| output.WriteTag(2, WireFormat.WireType.Varint); |
| output.WriteInt32(value3); |
| |
| output.Flush(); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestMap.Parser, |
| memoryStream.ToArray(), |
| parsed => |
| { |
| var expected = new TestMap |
| { |
| MapInt32Int32 = { { key1, value1 }, { key3, value3 } }, |
| MapStringString = { { key2, value2 } } |
| }; |
| Assert.AreEqual(expected, parsed); |
| }); |
| } |
| |
| [Test] |
| public void DuplicateKeys_LastEntryWins() |
| { |
| var memoryStream = new MemoryStream(); |
| var output = new CodedOutputStream(memoryStream); |
| |
| var key = 10; |
| var value1 = 20; |
| var value2 = 30; |
| |
| // First entry |
| output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); |
| output.WriteLength(4); |
| output.WriteTag(1, WireFormat.WireType.Varint); |
| output.WriteInt32(key); |
| output.WriteTag(2, WireFormat.WireType.Varint); |
| output.WriteInt32(value1); |
| |
| // Second entry - same key, different value |
| output.WriteTag(TestMap.MapInt32Int32FieldNumber, WireFormat.WireType.LengthDelimited); |
| output.WriteLength(4); |
| output.WriteTag(1, WireFormat.WireType.Varint); |
| output.WriteInt32(key); |
| output.WriteTag(2, WireFormat.WireType.Varint); |
| output.WriteInt32(value2); |
| output.Flush(); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestMap.Parser, |
| memoryStream.ToArray(), |
| parsed => |
| { |
| Assert.AreEqual(value2, parsed.MapInt32Int32[key]); |
| }); |
| } |
| |
| [Test] |
| public void CloneSingleNonMessageValues() |
| { |
| var original = new TestAllTypes |
| { |
| SingleBool = true, |
| SingleBytes = ByteString.CopyFrom(1, 2, 3, 4), |
| SingleDouble = 23.5, |
| SingleFixed32 = 23, |
| SingleFixed64 = 1234567890123, |
| SingleFloat = 12.25f, |
| SingleInt32 = 100, |
| SingleInt64 = 3210987654321, |
| SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo, |
| SingleSfixed32 = -123, |
| SingleSfixed64 = -12345678901234, |
| SingleSint32 = -456, |
| SingleSint64 = -12345678901235, |
| SingleString = "test", |
| SingleUint32 = uint.MaxValue, |
| SingleUint64 = ulong.MaxValue |
| }; |
| var clone = original.Clone(); |
| Assert.AreNotSame(original, clone); |
| Assert.AreEqual(original, clone); |
| // Just as a single example |
| clone.SingleInt32 = 150; |
| Assert.AreNotEqual(original, clone); |
| } |
| |
| [Test] |
| public void CloneRepeatedNonMessageValues() |
| { |
| var original = new TestAllTypes |
| { |
| RepeatedBool = { true, false }, |
| RepeatedBytes = { ByteString.CopyFrom(1, 2, 3, 4), ByteString.CopyFrom(5, 6) }, |
| RepeatedDouble = { -12.25, 23.5 }, |
| RepeatedFixed32 = { uint.MaxValue, 23 }, |
| RepeatedFixed64 = { ulong.MaxValue, 1234567890123 }, |
| RepeatedFloat = { 100f, 12.25f }, |
| RepeatedInt32 = { 100, 200 }, |
| RepeatedInt64 = { 3210987654321, long.MaxValue }, |
| RepeatedNestedEnum = { TestAllTypes.Types.NestedEnum.Foo, TestAllTypes.Types.NestedEnum.Neg }, |
| RepeatedSfixed32 = { -123, 123 }, |
| RepeatedSfixed64 = { -12345678901234, 12345678901234 }, |
| RepeatedSint32 = { -456, 100 }, |
| RepeatedSint64 = { -12345678901235, 123 }, |
| RepeatedString = { "foo", "bar" }, |
| RepeatedUint32 = { uint.MaxValue, uint.MinValue }, |
| RepeatedUint64 = { ulong.MaxValue, uint.MinValue } |
| }; |
| |
| var clone = original.Clone(); |
| Assert.AreNotSame(original, clone); |
| Assert.AreEqual(original, clone); |
| // Just as a single example |
| clone.RepeatedDouble.Add(25.5); |
| Assert.AreNotEqual(original, clone); |
| } |
| |
| [Test] |
| public void CloneSingleMessageField() |
| { |
| var original = new TestAllTypes |
| { |
| SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } |
| }; |
| |
| var clone = original.Clone(); |
| Assert.AreNotSame(original, clone); |
| Assert.AreNotSame(original.SingleNestedMessage, clone.SingleNestedMessage); |
| Assert.AreEqual(original, clone); |
| |
| clone.SingleNestedMessage.Bb = 30; |
| Assert.AreNotEqual(original, clone); |
| } |
| |
| [Test] |
| public void CloneRepeatedMessageField() |
| { |
| var original = new TestAllTypes |
| { |
| RepeatedNestedMessage = { new TestAllTypes.Types.NestedMessage { Bb = 20 } } |
| }; |
| |
| var clone = original.Clone(); |
| Assert.AreNotSame(original, clone); |
| Assert.AreNotSame(original.RepeatedNestedMessage, clone.RepeatedNestedMessage); |
| Assert.AreNotSame(original.RepeatedNestedMessage[0], clone.RepeatedNestedMessage[0]); |
| Assert.AreEqual(original, clone); |
| |
| clone.RepeatedNestedMessage[0].Bb = 30; |
| Assert.AreNotEqual(original, clone); |
| } |
| |
| [Test] |
| public void CloneOneofField() |
| { |
| var original = new TestAllTypes |
| { |
| OneofNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } |
| }; |
| |
| var clone = original.Clone(); |
| Assert.AreNotSame(original, clone); |
| Assert.AreEqual(original, clone); |
| |
| // We should have cloned the message |
| original.OneofNestedMessage.Bb = 30; |
| Assert.AreNotEqual(original, clone); |
| } |
| |
| [Test] |
| public void OneofProperties() |
| { |
| // Switch the oneof case between each of the different options, and check everything behaves |
| // as expected in each case. |
| var message = new TestAllTypes(); |
| Assert.AreEqual("", message.OneofString); |
| Assert.AreEqual(0, message.OneofUint32); |
| Assert.AreEqual(ByteString.Empty, message.OneofBytes); |
| Assert.IsNull(message.OneofNestedMessage); |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase); |
| |
| message.OneofString = "sample"; |
| Assert.AreEqual("sample", message.OneofString); |
| Assert.AreEqual(0, message.OneofUint32); |
| Assert.AreEqual(ByteString.Empty, message.OneofBytes); |
| Assert.IsNull(message.OneofNestedMessage); |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message.OneofFieldCase); |
| |
| var bytes = ByteString.CopyFrom(1, 2, 3); |
| message.OneofBytes = bytes; |
| Assert.AreEqual("", message.OneofString); |
| Assert.AreEqual(0, message.OneofUint32); |
| Assert.AreEqual(bytes, message.OneofBytes); |
| Assert.IsNull(message.OneofNestedMessage); |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofBytes, message.OneofFieldCase); |
| |
| message.OneofUint32 = 20; |
| Assert.AreEqual("", message.OneofString); |
| Assert.AreEqual(20, message.OneofUint32); |
| Assert.AreEqual(ByteString.Empty, message.OneofBytes); |
| Assert.IsNull(message.OneofNestedMessage); |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message.OneofFieldCase); |
| |
| var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 25 }; |
| message.OneofNestedMessage = nestedMessage; |
| Assert.AreEqual("", message.OneofString); |
| Assert.AreEqual(0, message.OneofUint32); |
| Assert.AreEqual(ByteString.Empty, message.OneofBytes); |
| Assert.AreEqual(nestedMessage, message.OneofNestedMessage); |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofNestedMessage, message.OneofFieldCase); |
| |
| message.ClearOneofField(); |
| Assert.AreEqual("", message.OneofString); |
| Assert.AreEqual(0, message.OneofUint32); |
| Assert.AreEqual(ByteString.Empty, message.OneofBytes); |
| Assert.IsNull(message.OneofNestedMessage); |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase); |
| } |
| |
| [Test] |
| public void Oneof_DefaultValuesNotEqual() |
| { |
| var message1 = new TestAllTypes { OneofString = "" }; |
| var message2 = new TestAllTypes { OneofUint32 = 0 }; |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofString, message1.OneofFieldCase); |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase); |
| Assert.AreNotEqual(message1, message2); |
| } |
| |
| [Test] |
| public void OneofSerialization_NonDefaultValue() |
| { |
| var message = new TestAllTypes |
| { |
| OneofString = "this would take a bit of space", |
| OneofUint32 = 10 |
| }; |
| var bytes = message.ToByteArray(); |
| Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string! |
| |
| MessageParsingHelpers.AssertWritingMessage(message); |
| |
| MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage => |
| { |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase); |
| }); |
| } |
| |
| [Test] |
| public void OneofSerialization_DefaultValue() |
| { |
| var message = new TestAllTypes |
| { |
| OneofString = "this would take a bit of space", |
| OneofUint32 = 0 // This is the default value for UInt32; normally wouldn't be serialized |
| }; |
| var bytes = message.ToByteArray(); |
| Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized |
| |
| MessageParsingHelpers.AssertWritingMessage(message); |
| |
| MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage => |
| { |
| Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase); |
| }); |
| } |
| |
| [Test] |
| public void MapStringString_DeterministicTrue_ThenBytesIdentical() |
| { |
| // Define three strings consisting of different versions of the letter I. |
| // LATIN CAPITAL LETTER I (U+0049) |
| string capitalLetterI = "I"; |
| // LATIN SMALL LETTER I (U+0069) |
| string smallLetterI = "i"; |
| // LATIN SMALL LETTER DOTLESS I (U+0131) |
| string smallLetterDotlessI = "\u0131"; |
| var testMap1 = new TestMap(); |
| |
| testMap1.MapStringString.Add(smallLetterDotlessI, "value_"+smallLetterDotlessI); |
| testMap1.MapStringString.Add(smallLetterI, "value_"+smallLetterI); |
| testMap1.MapStringString.Add(capitalLetterI, "content_"+capitalLetterI); |
| var bytes1 = SerializeTestMap(testMap1, true); |
| |
| var testMap2 = new TestMap(); |
| testMap2.MapStringString.Add(capitalLetterI, "content_"+capitalLetterI); |
| testMap2.MapStringString.Add(smallLetterI, "value_"+smallLetterI); |
| testMap2.MapStringString.Add(smallLetterDotlessI, "value_"+smallLetterDotlessI); |
| |
| var bytes2 = SerializeTestMap(testMap2, true); |
| var parsedBytes2 = TestMap.Parser.ParseFrom(bytes2); |
| var parsedBytes1 = TestMap.Parser.ParseFrom(bytes1); |
| Assert.IsTrue(bytes1.SequenceEqual(bytes2)); |
| } |
| |
| [Test] |
| public void MapInt32Bytes_DeterministicTrue_ThenBytesIdentical() |
| { |
| var testMap1 = new TestMap(); |
| testMap1.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test1")); |
| testMap1.MapInt32Bytes.Add(2, ByteString.CopyFromUtf8("test2")); |
| var bytes1 = SerializeTestMap(testMap1, true); |
| |
| var testMap2 = new TestMap(); |
| testMap2.MapInt32Bytes.Add(2, ByteString.CopyFromUtf8("test2")); |
| testMap2.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test1")); |
| var bytes2 = SerializeTestMap(testMap2, true); |
| |
| Assert.IsTrue(bytes1.SequenceEqual(bytes2)); |
| } |
| |
| [Test] |
| public void MapInt32Bytes_DeterministicFalse_ThenBytesDifferent() |
| { |
| var testMap1 = new TestMap(); |
| testMap1.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test1")); |
| testMap1.MapInt32Bytes.Add(2, ByteString.CopyFromUtf8("test2")); |
| var bytes1 = SerializeTestMap(testMap1, false); |
| |
| var testMap2 = new TestMap(); |
| testMap2.MapInt32Bytes.Add(2, ByteString.CopyFromUtf8("test2")); |
| testMap2.MapInt32Bytes.Add(1, ByteString.CopyFromUtf8("test1")); |
| var bytes2 = SerializeTestMap(testMap2, false); |
| |
| Assert.IsFalse(bytes1.SequenceEqual(bytes2)); |
| } |
| |
| private byte[] SerializeTestMap(TestMap testMap, bool deterministic) |
| { |
| using var memoryStream = new MemoryStream(); |
| var codedOutputStream = new CodedOutputStream(memoryStream); |
| codedOutputStream.Deterministic = deterministic; |
| |
| testMap.WriteTo(codedOutputStream); |
| codedOutputStream.Flush(); |
| |
| memoryStream.Seek(0, SeekOrigin.Begin); |
| return memoryStream.ToArray(); |
| } |
| |
| [Test] |
| public void DiscardUnknownFields_RealDataStillRead() |
| { |
| var message = SampleMessages.CreateFullTestAllTypes(); |
| var stream = new MemoryStream(); |
| var output = new CodedOutputStream(stream); |
| var unusedFieldNumber = 23456; |
| Assert.IsFalse(TestAllTypes.Descriptor.Fields.InDeclarationOrder().Select(x => x.FieldNumber).Contains(unusedFieldNumber)); |
| output.WriteTag(unusedFieldNumber, WireFormat.WireType.LengthDelimited); |
| output.WriteString("ignore me"); |
| message.WriteTo(output); |
| output.Flush(); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| TestAllTypes.Parser, |
| stream.ToArray(), |
| parsed => |
| { |
| // TODO: Add test back when DiscardUnknownFields API is supported. |
| // Assert.AreEqual(message, parsed); |
| }); |
| } |
| |
| [Test] |
| public void DiscardUnknownFields_AllTypes() |
| { |
| // Simple way of ensuring we can skip all kinds of fields. |
| var data = SampleMessages.CreateFullTestAllTypes().ToByteArray(); |
| var empty = Empty.Parser.ParseFrom(data); |
| |
| MessageParsingHelpers.AssertReadingMessage( |
| Empty.Parser, |
| data, |
| parsed => |
| { |
| // TODO: Add test back when DiscardUnknownFields API is supported. |
| // Assert.AreNotEqual(new Empty(), empty); |
| }); |
| } |
| |
| // This was originally seen as a conformance test failure. |
| [Test] |
| public void TruncatedMessageFieldThrows() |
| { |
| // 130, 3 is the message tag |
| // 1 is the data length - but there's no data. |
| var data = new byte[] { 130, 3, 1 }; |
| MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, data); |
| } |
| |
| /// <summary> |
| /// Demonstrates current behaviour with an extraneous end group tag - see issue 688 |
| /// for details; we may want to change this. |
| /// </summary> |
| [Test] |
| public void ExtraEndGroupThrows() |
| { |
| var stream = new MemoryStream(); |
| var output = new CodedOutputStream(stream); |
| |
| output.WriteTag(TestAllTypes.SingleFixed32FieldNumber, WireFormat.WireType.Fixed32); |
| output.WriteFixed32(123); |
| output.WriteTag(100, WireFormat.WireType.EndGroup); |
| |
| output.Flush(); |
| |
| stream.Position = 0; |
| MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, stream.ToArray()); |
| } |
| |
| [Test] |
| public void CustomDiagnosticMessage_DirectToStringCall() |
| { |
| var message = new ForeignMessage { C = 31 }; |
| Assert.AreEqual("{ \"c\": 31, \"@cInHex\": \"1f\" }", message.ToString()); |
| Assert.AreEqual("{ \"c\": 31 }", JsonFormatter.Default.Format(message)); |
| } |
| |
| [Test] |
| public void CustomDiagnosticMessage_Nested() |
| { |
| var message = new TestAllTypes { SingleForeignMessage = new ForeignMessage { C = 16 } }; |
| Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16, \"@cInHex\": \"10\" } }", message.ToString()); |
| Assert.AreEqual("{ \"singleForeignMessage\": { \"c\": 16 } }", JsonFormatter.Default.Format(message)); |
| } |
| |
| [Test] |
| public void CustomDiagnosticMessage_DirectToTextWriterCall() |
| { |
| var message = new ForeignMessage { C = 31 }; |
| var writer = new StringWriter(); |
| JsonFormatter.Default.Format(message, writer); |
| Assert.AreEqual("{ \"c\": 31 }", writer.ToString()); |
| } |
| |
| [Test] |
| public void NaNComparisons() |
| { |
| var message1 = new TestAllTypes { SingleDouble = SampleNaNs.Regular }; |
| var message2 = new TestAllTypes { SingleDouble = SampleNaNs.PayloadFlipped }; |
| var message3 = new TestAllTypes { SingleDouble = SampleNaNs.Regular }; |
| |
| EqualityTester.AssertInequality(message1, message2); |
| EqualityTester.AssertEquality(message1, message3); |
| } |
| |
| [Test] |
| [TestCase(false)] |
| [TestCase(true)] |
| public void MapFieldMerging(bool direct) |
| { |
| var message1 = new TestMap |
| { |
| MapStringString = |
| { |
| { "x1", "y1" }, |
| { "common", "message1" } |
| } |
| }; |
| var message2 = new TestMap |
| { |
| MapStringString = |
| { |
| { "x2", "y2" }, |
| { "common", "message2" } |
| } |
| }; |
| if (direct) |
| { |
| message1.MergeFrom(message2); |
| } |
| else |
| { |
| message1.MergeFrom(message2.ToByteArray()); |
| } |
| |
| var expected = new MapField<string, string> |
| { |
| { "x1", "y1" }, |
| { "x2", "y2" }, |
| { "common", "message2" } |
| }; |
| Assert.AreEqual(expected, message1.MapStringString); |
| } |
| } |
| } |