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 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 AssertReadingMessage(MessageParser parser, byte[] bytes, Action<IMessage> assert)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 AssertWritingMessage(IMessage message)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 }