Kaynağa Gözat

increase coverage of GeneratedMessageTest

Jan Tattermusch 5 yıl önce
ebeveyn
işleme
638a0813b4

+ 98 - 44
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs

@@ -131,8 +131,8 @@ namespace Google.Protobuf
             // Without setting any values, there's nothing to write.
             byte[] bytes = message.ToByteArray();
             Assert.AreEqual(0, bytes.Length);
-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
 
         [Test]
@@ -165,8 +165,8 @@ namespace Google.Protobuf
             };
 
             byte[] bytes = message.ToByteArray();
-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
 
         [Test]
@@ -199,8 +199,8 @@ namespace Google.Protobuf
             };
 
             byte[] bytes = message.ToByteArray();
-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);
         }
 
         // Note that not every map within map_unittest_proto3 is used. They all go through very
@@ -231,8 +231,8 @@ namespace Google.Protobuf
             };
 
             byte[] bytes = message.ToByteArray();
-            TestMap parsed = TestMap.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, parsed);
+
+            MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);
         }
 
         [Test]
@@ -246,9 +246,14 @@ namespace Google.Protobuf
             byte[] bytes = message.ToByteArray();
             Assert.AreEqual(2, bytes.Length); // Tag for field entry (1 byte), length of entry (0; 1 byte)
 
-            var parsed = TestMap.Parser.ParseFrom(bytes);
-            Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
-            Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                bytes,
+                parsed=>
+                {
+                    Assert.AreEqual(1, parsed.MapInt32Bytes.Count);
+                    Assert.AreEqual(ByteString.Empty, parsed.MapInt32Bytes[0]);
+                });
         }
 
         [Test]
@@ -265,8 +270,13 @@ namespace Google.Protobuf
             output.WriteMessage(nestedMessage);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
+                });
         }
 
         [Test]
@@ -282,8 +292,13 @@ namespace Google.Protobuf
             output.WriteInt32(key);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
+                });
         }
 
         [Test]
@@ -299,8 +314,13 @@ namespace Google.Protobuf
             output.WriteInt32(key);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
+                });
         }
 
         [Test]
@@ -327,8 +347,13 @@ namespace Google.Protobuf
             output.WriteInt32(extra);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+                });
         }
 
         [Test]
@@ -351,8 +376,13 @@ namespace Google.Protobuf
             output.WriteInt32(key);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value, parsed.MapInt32Int32[key]);
+                });
         }
 
         [Test]
@@ -397,13 +427,19 @@ namespace Google.Protobuf
             output.WriteInt32(value3);
 
             output.Flush();
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            var expected = new TestMap
-            {
-                MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
-                MapStringString = { { key2, value2 } }
-            };
-            Assert.AreEqual(expected, parsed);
+
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    var expected = new TestMap
+                    {
+                        MapInt32Int32 = { { key1, value1 }, { key3, value3 } },
+                        MapStringString = { { key2, value2 } }
+                    };
+                    Assert.AreEqual(expected, parsed);
+                });
         }
 
         [Test]
@@ -433,8 +469,13 @@ namespace Google.Protobuf
             output.WriteInt32(value2);
             output.Flush();
 
-            var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
-            Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestMap.Parser,
+                memoryStream.ToArray(),
+                parsed =>
+                {
+                    Assert.AreEqual(value2, parsed.MapInt32Int32[key]);
+                });
         }
 
         [Test]
@@ -619,9 +660,10 @@ namespace Google.Protobuf
             var bytes = message.ToByteArray();
             Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - no string!
 
-            var message2 = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, message2);
-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
+            {
+                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
+            });
         }
 
         [Test]
@@ -633,9 +675,10 @@ namespace Google.Protobuf
             var bytes = message.ToByteArray();
             Assert.AreEqual(3, bytes.Length); // 2 bytes for the tag + 1 for the value - it's still serialized
 
-            var message2 = TestAllTypes.Parser.ParseFrom(bytes);
-            Assert.AreEqual(message, message2);
-            Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, message2.OneofFieldCase);
+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message, parsedMessage =>
+            {
+                Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.OneofUint32, parsedMessage.OneofFieldCase);
+            });
         }
 
         [Test]
@@ -651,10 +694,14 @@ namespace Google.Protobuf
             message.WriteTo(output);
             output.Flush();
 
-            stream.Position = 0;
-            var parsed = TestAllTypes.Parser.ParseFrom(stream);
-            // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
-            // Assert.AreEqual(message, parsed);
+            MessageParsingHelpers.AssertReadingMessage(
+                TestAllTypes.Parser,
+                stream.ToArray(),
+                parsed =>
+                {
+                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
+                    // Assert.AreEqual(message, parsed);
+                });
         }
 
         [Test]
@@ -663,8 +710,15 @@ namespace Google.Protobuf
             // Simple way of ensuring we can skip all kinds of fields.
             var data = SampleMessages.CreateFullTestAllTypes().ToByteArray();
             var empty = Empty.Parser.ParseFrom(data);
-            // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
-            // Assert.AreNotEqual(new Empty(), empty);
+
+            MessageParsingHelpers.AssertReadingMessage(
+                Empty.Parser,
+                data,
+                parsed =>
+                {
+                    // TODO(jieluo): Add test back when DiscardUnknownFields API is supported.
+                    // Assert.AreNotEqual(new Empty(), empty);
+                });
         }
 
         // This was originally seen as a conformance test failure.
@@ -674,7 +728,7 @@ namespace Google.Protobuf
             // 130, 3 is the message tag
             // 1 is the data length - but there's no data.
             var data = new byte[] { 130, 3, 1 };
-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(data));
+            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, data);
         }
 
         /// <summary>
@@ -695,7 +749,7 @@ namespace Google.Protobuf
             output.Flush();
 
             stream.Position = 0;
-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(stream));
+            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, stream.ToArray());
         }
 
         [Test]

+ 86 - 0
csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs

@@ -0,0 +1,86 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using NUnit.Framework;
+using System;
+using Google.Protobuf.Buffers;
+using System.Buffers;
+
+namespace Google.Protobuf
+{
+    public static class MessageParsingHelpers
+    {
+        public static void AssertReadingMessage<T>(MessageParser<T> parser, byte[] bytes, Action<T> assert) where T : IMessage<T>
+        {
+            var parsedStream = parser.ParseFrom(bytes);
+
+            // Load content as single segment
+            var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
+            assert(parsedBuffer);
+
+            // Load content as multiple segments
+            parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
+            assert(parsedBuffer);
+
+            assert(parsedStream);
+        }
+
+        public static void AssertReadingMessageThrows<TMessage, TException>(MessageParser<TMessage> parser, byte[] bytes)
+            where TMessage : IMessage<TMessage>
+            where TException : Exception
+        {
+            Assert.Throws<TException>(() => parser.ParseFrom(bytes));
+
+            Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySequence<byte>(bytes)));
+        }
+
+        public static void AssertRoundtrip<T>(MessageParser<T> parser, T message, Action<T> additionalAssert = null) where T : IMessage<T>
+        {
+            var bytes = message.ToByteArray();
+
+            // Load content as single segment
+            var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
+            Assert.AreEqual(message, parsedBuffer);
+            additionalAssert?.Invoke(parsedBuffer);
+
+            // Load content as multiple segments
+            parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
+            Assert.AreEqual(message, parsedBuffer);
+            additionalAssert?.Invoke(parsedBuffer);
+
+            var parsedStream = parser.ParseFrom(bytes);
+
+            Assert.AreEqual(message, parsedStream);
+            additionalAssert?.Invoke(parsedStream);
+        }
+    }
+}

+ 128 - 0
csharp/src/Google.Protobuf.Test/ReadOnlySequenceFactory.cs

@@ -0,0 +1,128 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Google.Protobuf.Buffers
+{
+    internal static class ReadOnlySequenceFactory
+    {
+        public static ReadOnlySequence<byte> CreateWithContent(byte[] data, int segmentSize = 1)
+        {
+            var segments = new List<byte[]>();
+
+            segments.Add(new byte[0]);
+            var currentIndex = 0;
+            while (currentIndex < data.Length)
+            {
+                var segment = new List<byte>();
+                for (; currentIndex < Math.Min(currentIndex + segmentSize, data.Length); currentIndex++)
+                {
+                    segment.Add(data[currentIndex]);
+                }
+                segments.Add(segment.ToArray());
+                segments.Add(new byte[0]);
+            }
+
+            return CreateSegments(segments.ToArray());
+        }
+
+        /// <summary>
+        /// Originally from corefx, and has been contributed to Protobuf
+        /// https://github.com/dotnet/corefx/blob/e99ec129cfd594d53f4390bf97d1d736cff6f860/src/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceFactory.byte.cs
+        /// </summary>
+        private static ReadOnlySequence<byte> CreateSegments(params byte[][] inputs)
+        {
+            if (inputs == null || inputs.Length == 0)
+            {
+                throw new InvalidOperationException();
+            }
+
+            int i = 0;
+
+            BufferSegment last = null;
+            BufferSegment first = null;
+
+            do
+            {
+                byte[] s = inputs[i];
+                int length = s.Length;
+                int dataOffset = length;
+                var chars = new byte[length * 2];
+
+                for (int j = 0; j < length; j++)
+                {
+                    chars[dataOffset + j] = s[j];
+                }
+
+                // Create a segment that has offset relative to the OwnedMemory and OwnedMemory itself has offset relative to array
+                var memory = new Memory<byte>(chars).Slice(length, length);
+
+                if (first == null)
+                {
+                    first = new BufferSegment(memory);
+                    last = first;
+                }
+                else
+                {
+                    last = last.Append(memory);
+                }
+                i++;
+            } while (i < inputs.Length);
+
+            return new ReadOnlySequence<byte>(first, 0, last, last.Memory.Length);
+        }
+
+        private class BufferSegment : ReadOnlySequenceSegment<byte>
+        {
+            public BufferSegment(Memory<byte> memory)
+            {
+                Memory = memory;
+            }
+
+            public BufferSegment Append(Memory<byte> memory)
+            {
+                var segment = new BufferSegment(memory)
+                {
+                    RunningIndex = RunningIndex + Memory.Length
+                };
+                Next = segment;
+                return segment;
+            }
+        }
+    }
+}