xref: /aosp_15_r20/external/protobuf/csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs (revision 1b3f573f81763fcece89efc2b6a5209149e44ab8)
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 
33 using NUnit.Framework;
34 using System;
35 using System.Buffers;
36 using Google.Protobuf.Buffers;
37 
38 namespace 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 parsedMsg = parser.ParseFrom(bytes);
45             assert(parsedMsg);
46 
47             // Load content as single segment
48             parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
49             assert(parsedMsg);
50 
51             // Load content as multiple segments
52             parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
53             assert(parsedMsg);
54 
55             // Load content as ReadOnlySpan
56             parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes));
57             assert(parsedMsg);
58         }
59 
AssertReadingMessage(MessageParser parser, byte[] bytes, Action<IMessage> assert)60         public static void AssertReadingMessage(MessageParser parser, byte[] bytes, Action<IMessage> assert)
61         {
62             var parsedMsg = parser.ParseFrom(bytes);
63             assert(parsedMsg);
64 
65             // Load content as single segment
66             parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
67             assert(parsedMsg);
68 
69             // Load content as multiple segments
70             parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
71             assert(parsedMsg);
72 
73             // Load content as ReadOnlySpan
74             parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes));
75             assert(parsedMsg);
76         }
77 
78         public static void AssertReadingMessageThrows<TMessage, TException>(MessageParser<TMessage> parser, byte[] bytes)
79             where TMessage : IMessage<TMessage>
80             where TException : Exception
81         {
82             Assert.Throws<TException>(() => parser.ParseFrom(bytes));
83 
84             Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySequence<byte>(bytes)));
85 
86             Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySpan<byte>(bytes)));
87         }
88 
89         public static void AssertRoundtrip<T>(MessageParser<T> parser, T message, Action<T> additionalAssert = null) where T : IMessage<T>
90         {
91             var bytes = message.ToByteArray();
92 
93             // also serialize using IBufferWriter and check it leads to the same data
94             var bufferWriter = new TestArrayBufferWriter<byte>();
95             message.WriteTo(bufferWriter);
96             Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray(), "Both serialization approaches need to result in the same data.");
97 
98             var parsedMsg = parser.ParseFrom(bytes);
99             Assert.AreEqual(message, parsedMsg);
100             additionalAssert?.Invoke(parsedMsg);
101 
102             // Load content as single segment
103             parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes));
104             Assert.AreEqual(message, parsedMsg);
105             additionalAssert?.Invoke(parsedMsg);
106 
107             // Load content as multiple segments
108             parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes));
109             Assert.AreEqual(message, parsedMsg);
110             additionalAssert?.Invoke(parsedMsg);
111 
112             // Load content as ReadOnlySpan
113             parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes));
114             Assert.AreEqual(message, parsedMsg);
115             additionalAssert?.Invoke(parsedMsg);
116         }
117 
AssertWritingMessage(IMessage message)118         public static void AssertWritingMessage(IMessage message)
119         {
120             // serialize using CodedOutputStream
121             var bytes = message.ToByteArray();
122 
123             int messageSize = message.CalculateSize();
124             Assert.AreEqual(message.CalculateSize(), bytes.Length);
125 
126             // serialize using IBufferWriter and check it leads to the same output
127             var bufferWriter = new TestArrayBufferWriter<byte>();
128             message.WriteTo(bufferWriter);
129             Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray());
130 
131             // serialize into a single span and check it leads to the same output
132             var singleSpan = new Span<byte>(new byte[messageSize]);
133             message.WriteTo(singleSpan);
134             Assert.AreEqual(bytes, singleSpan.ToArray());
135 
136             // test for different IBufferWriter.GetSpan() segment sizes
137             for (int blockSize = 1; blockSize < 256; blockSize *= 2)
138             {
139                 var segmentedBufferWriter = new TestArrayBufferWriter<byte>();
140                 segmentedBufferWriter.MaxGrowBy = blockSize;
141                 message.WriteTo(segmentedBufferWriter);
142                 Assert.AreEqual(bytes, segmentedBufferWriter.WrittenSpan.ToArray());
143             }
144 
145             // if the full message is small enough, try serializing directly into stack-allocated buffer
146             if (bytes.Length <= 256)
147             {
148                 Span<byte> stackAllocBuffer = stackalloc byte[bytes.Length];
149                 message.WriteTo(stackAllocBuffer);
150                 Assert.AreEqual(bytes, stackAllocBuffer.ToArray());
151             }
152         }
153     }
154 }