1#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2015 Google Inc.  All rights reserved.
4// https://developers.google.com/protocol-buffers/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10//     * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12//     * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16//     * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#endregion
32
33using NUnit.Framework;
34using System;
35using System.Buffers;
36using Google.Protobuf.Buffers;
37
38namespace Google.Protobuf
39{
40    public static class MessageParsingHelpers
41    {
42        public static void AssertReadingMessage<T>(MessageParser<T> parser, byte[] bytes, Action<T> assert) where T : IMessage<T>
43        {
44            var parsedStream = parser.ParseFrom(bytes);
45
46            // Load content as single segment
47            var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
48            assert(parsedBuffer);
49
50            // Load content as multiple segments
51            parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
52            assert(parsedBuffer);
53
54            assert(parsedStream);
55        }
56
57        public static void AssertReadingMessage(MessageParser parser, byte[] bytes, Action<IMessage> assert)
58        {
59            var parsedStream = parser.ParseFrom(bytes);
60
61            // Load content as single segment
62            var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
63            assert(parsedBuffer);
64
65            // Load content as multiple segments
66            parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
67            assert(parsedBuffer);
68
69            assert(parsedStream);
70        }
71
72        public static void AssertReadingMessageThrows<TMessage, TException>(MessageParser<TMessage> parser, byte[] bytes)
73            where TMessage : IMessage<TMessage>
74            where TException : Exception
75        {
76            Assert.Throws<TException>(() => parser.ParseFrom(bytes));
77
78            Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySequence<byte>(bytes)));
79        }
80
81        public static void AssertRoundtrip<T>(MessageParser<T> parser, T message, Action<T> additionalAssert = null) where T : IMessage<T>
82        {
83            var bytes = message.ToByteArray();
84
85            // also serialize using IBufferWriter and check it leads to the same data
86            var bufferWriter = new ArrayBufferWriter<byte>();
87            message.WriteTo(bufferWriter);
88            Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray(), "Both serialization approaches need to result in the same data.");
89
90            // Load content as single segment
91            var parsedBuffer = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
92            Assert.AreEqual(message, parsedBuffer);
93            additionalAssert?.Invoke(parsedBuffer);
94
95            // Load content as multiple segments
96            parsedBuffer = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
97            Assert.AreEqual(message, parsedBuffer);
98            additionalAssert?.Invoke(parsedBuffer);
99
100            var parsedStream = parser.ParseFrom(bytes);
101
102            Assert.AreEqual(message, parsedStream);
103            additionalAssert?.Invoke(parsedStream);
104        }
105
106        public static void AssertWritingMessage(IMessage message)
107        {
108            // serialize using CodedOutputStream
109            var bytes = message.ToByteArray();
110
111            int messageSize = message.CalculateSize();
112            Assert.AreEqual(message.CalculateSize(), bytes.Length);
113
114            // serialize using IBufferWriter and check it leads to the same output
115            var bufferWriter = new ArrayBufferWriter<byte>();
116            message.WriteTo(bufferWriter);
117            Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray());
118
119            // serialize into a single span and check it leads to the same output
120            var singleSpan = new Span<byte>(new byte[messageSize]);
121            message.WriteTo(singleSpan);
122            Assert.AreEqual(bytes, singleSpan.ToArray());
123
124            // test for different IBufferWriter.GetSpan() segment sizes
125            for (int blockSize = 1; blockSize < 256; blockSize *= 2)
126            {
127                var segmentedBufferWriter = new ArrayBufferWriter<byte>();
128                segmentedBufferWriter.MaxGrowBy = blockSize;
129                message.WriteTo(segmentedBufferWriter);
130                Assert.AreEqual(bytes, segmentedBufferWriter.WrittenSpan.ToArray());
131            }
132
133            // if the full message is small enough, try serializing directly into stack-allocated buffer
134            if (bytes.Length <= 256)
135            {
136                Span<byte> stackAllocBuffer = stackalloc byte[bytes.Length];
137                message.WriteTo(stackAllocBuffer);
138                Assert.AreEqual(bytes, stackAllocBuffer.ToArray());
139            }
140        }
141    }
142}