increase coverage of GeneratedMessageTest
diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
index 103df7d..3499e66 100644
--- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
+++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
@@ -131,8 +131,8 @@
             // 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 @@
             };

 

             byte[] bytes = message.ToByteArray();

-            TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);

-            Assert.AreEqual(message, parsed);

+

+            MessageParsingHelpers.AssertRoundtrip(TestAllTypes.Parser, message);

         }

 

         [Test]

@@ -199,8 +199,8 @@
             };

 

             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 @@
             };

 

             byte[] bytes = message.ToByteArray();

-            TestMap parsed = TestMap.Parser.ParseFrom(bytes);

-            Assert.AreEqual(message, parsed);

+

+            MessageParsingHelpers.AssertRoundtrip(TestMap.Parser, message);

         }

 

         [Test]

@@ -246,9 +246,14 @@
             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 @@
             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 @@
             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 @@
             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 @@
             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 @@
             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 @@
             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 @@
             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 @@
             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 @@
             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 @@
             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 @@
             // 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 @@
             // 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 @@
             output.Flush();

 

             stream.Position = 0;

-            Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseFrom(stream));

+            MessageParsingHelpers.AssertReadingMessageThrows<TestAllTypes, InvalidProtocolBufferException>(TestAllTypes.Parser, stream.ToArray());

         }

 

         [Test]

diff --git a/csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs b/csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs
new file mode 100644
index 0000000..d629d9c
--- /dev/null
+++ b/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);
+        }
+    }
+}
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test/ReadOnlySequenceFactory.cs b/csharp/src/Google.Protobuf.Test/ReadOnlySequenceFactory.cs
new file mode 100644
index 0000000..f35844c
--- /dev/null
+++ b/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;
+            }
+        }
+    }
+}
\ No newline at end of file