1 #region Copyright notice and license 2 3 // Copyright 2018 The gRPC Authors 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #endregion 18 19 using System; 20 using System.Buffers; 21 using System.Collections.Generic; 22 using System.Linq; 23 using System.Runtime.InteropServices; 24 using Grpc.Core; 25 using Grpc.Core.Internal; 26 using Grpc.Core.Utils; 27 using NUnit.Framework; 28 29 namespace Grpc.Core.Internal.Tests 30 { 31 // Converts IBufferReader into instances of ReadOnlySequence<byte> 32 // Objects representing the sequence segments are cached to decrease GC load. 33 public class ReusableSliceBufferTest 34 { 35 FakeBufferReaderManager fakeBufferReaderManager; 36 37 [SetUp] Init()38 public void Init() 39 { 40 fakeBufferReaderManager = new FakeBufferReaderManager(); 41 } 42 43 [TearDown] Cleanup()44 public void Cleanup() 45 { 46 fakeBufferReaderManager.Dispose(); 47 } 48 49 [TestCase] NullPayload()50 public void NullPayload() 51 { 52 var sliceBuffer = new ReusableSliceBuffer(); 53 Assert.Throws(typeof(ArgumentNullException), () => sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateNullPayloadBufferReader())); 54 } 55 56 [TestCase] ZeroSegmentPayload()57 public void ZeroSegmentPayload() 58 { 59 var sliceBuffer = new ReusableSliceBuffer(); 60 var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> {})); 61 62 Assert.AreEqual(ReadOnlySequence<byte>.Empty, sequence); 63 Assert.IsTrue(sequence.IsEmpty); 64 Assert.IsTrue(sequence.IsSingleSegment); 65 } 66 67 [TestCase] SegmentsAreCached()68 public void SegmentsAreCached() 69 { 70 var bufferSegments1 = Enumerable.Range(0, 100).Select((_) => GetTestBuffer(50)).ToList(); 71 var bufferSegments2 = Enumerable.Range(0, 100).Select((_) => GetTestBuffer(50)).ToList(); 72 73 var sliceBuffer = new ReusableSliceBuffer(); 74 75 var sequence1 = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments1)); 76 var memoryManagers1 = GetMemoryManagersForSequenceSegments(sequence1); 77 78 sliceBuffer.Invalidate(); 79 80 var sequence2 = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments2)); 81 var memoryManagers2 = GetMemoryManagersForSequenceSegments(sequence2); 82 83 // check memory managers are identical objects (i.e. they've been reused) 84 CollectionAssert.AreEquivalent(memoryManagers1, memoryManagers2); 85 } 86 87 [TestCase] MultiSegmentPayload_LotsOfSegments()88 public void MultiSegmentPayload_LotsOfSegments() 89 { 90 var bufferSegments = Enumerable.Range(0, ReusableSliceBuffer.MaxCachedSegments + 100).Select((_) => GetTestBuffer(10)).ToList(); 91 92 var sliceBuffer = new ReusableSliceBuffer(); 93 var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(bufferSegments)); 94 95 int index = 0; 96 foreach (var memory in sequence) 97 { 98 CollectionAssert.AreEqual(bufferSegments[index], memory.ToArray()); 99 index ++; 100 } 101 } 102 103 [TestCase] InvalidateMakesSequenceUnusable()104 public void InvalidateMakesSequenceUnusable() 105 { 106 var origBuffer = GetTestBuffer(100); 107 108 var sliceBuffer = new ReusableSliceBuffer(); 109 var sequence = sliceBuffer.PopulateFrom(fakeBufferReaderManager.CreateMultiSegmentBufferReader(new List<byte[]> { origBuffer })); 110 111 Assert.AreEqual(origBuffer.Length, sequence.Length); 112 113 sliceBuffer.Invalidate(); 114 115 // Invalidate with make the returned sequence completely unusable and broken, users must not use it beyond the deserializer functions. 116 Assert.Throws(typeof(ArgumentOutOfRangeException), () => { var first = sequence.First; }); 117 } 118 GetMemoryManagersForSequenceSegments(ReadOnlySequence<byte> sequence)119 private List<MemoryManager<byte>> GetMemoryManagersForSequenceSegments(ReadOnlySequence<byte> sequence) 120 { 121 var result = new List<MemoryManager<byte>>(); 122 foreach (var memory in sequence) 123 { 124 Assert.IsTrue(MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<byte> memoryManager)); 125 result.Add(memoryManager); 126 } 127 return result; 128 } 129 GetTestBuffer(int length)130 private byte[] GetTestBuffer(int length) 131 { 132 var testBuffer = new byte[length]; 133 for (int i = 0; i < testBuffer.Length; i++) 134 { 135 testBuffer[i] = (byte) i; 136 } 137 return testBuffer; 138 } 139 } 140 } 141