1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2015 Google Inc. All rights reserved. 4 // 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file or at 7 // https://developers.google.com/open-source/licenses/bsd 8 #endregion 9 10 using NUnit.Framework; 11 using System; 12 using System.Buffers; 13 using Google.Protobuf.Buffers; 14 15 namespace Google.Protobuf 16 { 17 public static class MessageParsingHelpers 18 { 19 public static void AssertReadingMessage<T>(MessageParser<T> parser, byte[] bytes, Action<T> assert) where T : IMessage<T> 20 { 21 var parsedMsg = parser.ParseFrom(bytes); 22 assert(parsedMsg); 23 24 // Load content as single segment 25 parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes)); 26 assert(parsedMsg); 27 28 // Load content as multiple segments 29 parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes)); 30 assert(parsedMsg); 31 32 // Load content as ReadOnlySpan 33 parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes)); 34 assert(parsedMsg); 35 } 36 AssertReadingMessage(MessageParser parser, byte[] bytes, Action<IMessage> assert)37 public static void AssertReadingMessage(MessageParser parser, byte[] bytes, Action<IMessage> assert) 38 { 39 var parsedMsg = parser.ParseFrom(bytes); 40 assert(parsedMsg); 41 42 // Load content as single segment 43 parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes)); 44 assert(parsedMsg); 45 46 // Load content as multiple segments 47 parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes)); 48 assert(parsedMsg); 49 50 // Load content as ReadOnlySpan 51 parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes)); 52 assert(parsedMsg); 53 } 54 55 public static void AssertReadingMessageThrows<TMessage, TException>(MessageParser<TMessage> parser, byte[] bytes) 56 where TMessage : IMessage<TMessage> 57 where TException : Exception 58 { 59 Assert.Throws<TException>(() => parser.ParseFrom(bytes)); 60 61 Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySequence<byte>(bytes))); 62 63 Assert.Throws<TException>(() => parser.ParseFrom(new ReadOnlySpan<byte>(bytes))); 64 } 65 66 public static void AssertRoundtrip<T>(MessageParser<T> parser, T message, Action<T> additionalAssert = null) where T : IMessage<T> 67 { 68 var bytes = message.ToByteArray(); 69 70 // also serialize using IBufferWriter and check it leads to the same data 71 var bufferWriter = new TestArrayBufferWriter<byte>(); 72 message.WriteTo(bufferWriter); 73 Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray(), "Both serialization approaches need to result in the same data."); 74 75 var parsedMsg = parser.ParseFrom(bytes); 76 Assert.AreEqual(message, parsedMsg); 77 additionalAssert?.Invoke(parsedMsg); 78 79 // Load content as single segment 80 parsedMsg = parser.ParseFrom(new ReadOnlySequence<byte>(bytes)); 81 Assert.AreEqual(message, parsedMsg); 82 additionalAssert?.Invoke(parsedMsg); 83 84 // Load content as multiple segments 85 parsedMsg = parser.ParseFrom(ReadOnlySequenceFactory.CreateWithContent(bytes)); 86 Assert.AreEqual(message, parsedMsg); 87 additionalAssert?.Invoke(parsedMsg); 88 89 // Load content as ReadOnlySpan 90 parsedMsg = parser.ParseFrom(new ReadOnlySpan<byte>(bytes)); 91 Assert.AreEqual(message, parsedMsg); 92 additionalAssert?.Invoke(parsedMsg); 93 } 94 AssertWritingMessage(IMessage message)95 public static void AssertWritingMessage(IMessage message) 96 { 97 // serialize using CodedOutputStream 98 var bytes = message.ToByteArray(); 99 100 int messageSize = message.CalculateSize(); 101 Assert.AreEqual(message.CalculateSize(), bytes.Length); 102 103 // serialize using IBufferWriter and check it leads to the same output 104 var bufferWriter = new TestArrayBufferWriter<byte>(); 105 message.WriteTo(bufferWriter); 106 Assert.AreEqual(bytes, bufferWriter.WrittenSpan.ToArray()); 107 108 // serialize into a single span and check it leads to the same output 109 var singleSpan = new Span<byte>(new byte[messageSize]); 110 message.WriteTo(singleSpan); 111 Assert.AreEqual(bytes, singleSpan.ToArray()); 112 113 // test for different IBufferWriter.GetSpan() segment sizes 114 for (int blockSize = 1; blockSize < 256; blockSize *= 2) 115 { 116 var segmentedBufferWriter = new TestArrayBufferWriter<byte> { MaxGrowBy = blockSize }; 117 message.WriteTo(segmentedBufferWriter); 118 Assert.AreEqual(bytes, segmentedBufferWriter.WrittenSpan.ToArray()); 119 } 120 121 // if the full message is small enough, try serializing directly into stack-allocated buffer 122 if (bytes.Length <= 256) 123 { 124 Span<byte> stackAllocBuffer = stackalloc byte[bytes.Length]; 125 message.WriteTo(stackAllocBuffer); 126 Assert.AreEqual(bytes, stackAllocBuffer.ToArray()); 127 } 128 } 129 } 130 }