/* * * Copyright 2023 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package mem_test import ( "bytes" "fmt" "testing" "unsafe" "google.golang.org/grpc/mem" ) func (s) TestBufferPool(t *testing.T) { var poolSizes = []int{4, 8, 16, 32} pools := []mem.BufferPool{ mem.NopBufferPool{}, mem.NewTieredBufferPool(poolSizes...), } testSizes := append([]int{1}, poolSizes...) testSizes = append(testSizes, 64) for _, p := range pools { for _, l := range testSizes { bs := p.Get(l) if len(*bs) != l { t.Fatalf("Get(%d) returned buffer of length %d, want %d", l, len(*bs), l) } p.Put(bs) } } } func (s) TestBufferPoolClears(t *testing.T) { const poolSize = 4 pool := mem.NewTieredBufferPool(poolSize) tests := []struct { name string bufferSize int }{ { name: "sized_buffer_pool", bufferSize: poolSize, }, { name: "simple_buffer_pool", bufferSize: poolSize + 1, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { for { buf1 := pool.Get(tc.bufferSize) copy(*buf1, "1234") pool.Put(buf1) buf2 := pool.Get(tc.bufferSize) if unsafe.SliceData(*buf1) != unsafe.SliceData(*buf2) { pool.Put(buf2) // This test is only relevant if a buffer is reused, otherwise try again. This // can happen if a GC pause happens between putting the buffer back in the pool // and getting a new one. continue } if !bytes.Equal(*buf1, make([]byte, tc.bufferSize)) { t.Fatalf("buffer not cleared") } break } }) } } func (s) TestBufferPoolIgnoresShortBuffers(t *testing.T) { pool := mem.NewTieredBufferPool(10, 20) buf := pool.Get(1) if cap(*buf) != 10 { t.Fatalf("Get(1) returned buffer with capacity: %d, want 10", cap(*buf)) } // Insert a short buffer into the pool, which is currently empty. short := make([]byte, 1) pool.Put(&short) // Then immediately request a buffer that would be pulled from the pool where the // short buffer would have been returned. If the short buffer is pulled from the // pool, it could cause a panic. pool.Get(10) } func TestBinaryBufferPool(t *testing.T) { poolSizes := []uint8{0, 2, 3, 4, 2, 3, 4} // duplicates will be ignored. testCases := []struct { requestSize int wantCapacity int }{ {requestSize: 0, wantCapacity: 0}, {requestSize: 1, wantCapacity: 1}, {requestSize: 2, wantCapacity: 4}, {requestSize: 3, wantCapacity: 4}, {requestSize: 4, wantCapacity: 4}, {requestSize: 5, wantCapacity: 8}, {requestSize: 6, wantCapacity: 8}, {requestSize: 7, wantCapacity: 8}, {requestSize: 8, wantCapacity: 8}, {requestSize: 9, wantCapacity: 16}, {requestSize: 15, wantCapacity: 16}, {requestSize: 16, wantCapacity: 16}, {requestSize: 17, wantCapacity: 4096}, // fallback pool returns sizes in multiples of 4096. } for _, tc := range testCases { t.Run(fmt.Sprintf("requestSize=%d", tc.requestSize), func(t *testing.T) { pool, err := mem.NewBinaryTieredBufferPool(poolSizes...) if err != nil { t.Fatalf("Failed to create buffer pool: %v", err) } buf := pool.Get(tc.requestSize) if cap(*buf) != tc.wantCapacity { t.Errorf("Get(%d) returned buffer with capacity: %d, want %d", tc.requestSize, cap(*buf), tc.wantCapacity) } if len(*buf) != tc.requestSize { t.Errorf("Get(%d) returned buffer with length: %d, want %d", tc.requestSize, len(*buf), tc.requestSize) } pool.Put(buf) }) } }