google.golang.org/grpc@v1.72.2/mem/buffer_slice_test.go (about) 1 /* 2 * 3 * Copyright 2024 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 */ 18 19 package mem_test 20 21 import ( 22 "bytes" 23 "crypto/rand" 24 "errors" 25 "fmt" 26 "io" 27 "testing" 28 29 "google.golang.org/grpc/mem" 30 ) 31 32 const ( 33 minReadSize = 1 34 // Should match the constant in buffer_slice.go (another package) 35 readAllBufSize = 32 * 1024 // 32 KiB 36 ) 37 38 func newBuffer(data []byte, pool mem.BufferPool) mem.Buffer { 39 return mem.NewBuffer(&data, pool) 40 } 41 42 func (s) TestBufferSlice_Len(t *testing.T) { 43 tests := []struct { 44 name string 45 in mem.BufferSlice 46 want int 47 }{ 48 { 49 name: "empty", 50 in: nil, 51 want: 0, 52 }, 53 { 54 name: "single", 55 in: mem.BufferSlice{newBuffer([]byte("abcd"), nil)}, 56 want: 4, 57 }, 58 { 59 name: "multiple", 60 in: mem.BufferSlice{ 61 newBuffer([]byte("abcd"), nil), 62 newBuffer([]byte("abcd"), nil), 63 newBuffer([]byte("abcd"), nil), 64 }, 65 want: 12, 66 }, 67 } 68 for _, tt := range tests { 69 t.Run(tt.name, func(t *testing.T) { 70 if got := tt.in.Len(); got != tt.want { 71 t.Errorf("BufferSlice.Len() = %v, want %v", got, tt.want) 72 } 73 }) 74 } 75 } 76 77 func (s) TestBufferSlice_Ref(t *testing.T) { 78 // Create a new buffer slice and a reference to it. 79 bs := mem.BufferSlice{ 80 newBuffer([]byte("abcd"), nil), 81 newBuffer([]byte("abcd"), nil), 82 } 83 bs.Ref() 84 85 // Free the original buffer slice and verify that the reference can still 86 // read data from it. 87 bs.Free() 88 got := bs.Materialize() 89 want := []byte("abcdabcd") 90 if !bytes.Equal(got, want) { 91 t.Errorf("BufferSlice.Materialize() = %s, want %s", string(got), string(want)) 92 } 93 } 94 95 func (s) TestBufferSlice_MaterializeToBuffer(t *testing.T) { 96 tests := []struct { 97 name string 98 in mem.BufferSlice 99 pool mem.BufferPool 100 wantData []byte 101 }{ 102 { 103 name: "single", 104 in: mem.BufferSlice{newBuffer([]byte("abcd"), nil)}, 105 pool: nil, // MaterializeToBuffer should not use the pool in this case. 106 wantData: []byte("abcd"), 107 }, 108 { 109 name: "multiple", 110 in: mem.BufferSlice{ 111 newBuffer([]byte("abcd"), nil), 112 newBuffer([]byte("abcd"), nil), 113 newBuffer([]byte("abcd"), nil), 114 }, 115 pool: mem.DefaultBufferPool(), 116 wantData: []byte("abcdabcdabcd"), 117 }, 118 } 119 for _, tt := range tests { 120 t.Run(tt.name, func(t *testing.T) { 121 defer tt.in.Free() 122 got := tt.in.MaterializeToBuffer(tt.pool) 123 defer got.Free() 124 if !bytes.Equal(got.ReadOnlyData(), tt.wantData) { 125 t.Errorf("BufferSlice.MaterializeToBuffer() = %s, want %s", string(got.ReadOnlyData()), string(tt.wantData)) 126 } 127 }) 128 } 129 } 130 131 func (s) TestBufferSlice_Reader(t *testing.T) { 132 bs := mem.BufferSlice{ 133 newBuffer([]byte("abcd"), nil), 134 newBuffer([]byte("abcd"), nil), 135 newBuffer([]byte("abcd"), nil), 136 } 137 wantData := []byte("abcdabcdabcd") 138 139 reader := bs.Reader() 140 var gotData []byte 141 // Read into a buffer of size 1 until EOF, and verify that the data matches. 142 for { 143 buf := make([]byte, 1) 144 n, err := reader.Read(buf) 145 if n > 0 { 146 gotData = append(gotData, buf[:n]...) 147 } 148 if err == io.EOF { 149 break 150 } 151 if err != nil { 152 t.Fatalf("BufferSlice.Reader() failed unexpectedly: %v", err) 153 } 154 } 155 if !bytes.Equal(gotData, wantData) { 156 t.Errorf("BufferSlice.Reader() returned data %v, want %v", string(gotData), string(wantData)) 157 } 158 159 // Reader should have released its references to the underlying buffers, but 160 // bs still holds its reference and it should be able to read data from it. 161 gotData = bs.Materialize() 162 if !bytes.Equal(gotData, wantData) { 163 t.Errorf("BufferSlice.Materialize() = %s, want %s", string(gotData), string(wantData)) 164 } 165 } 166 167 // TestBufferSlice_ReadAll_Reads exercises ReadAll by allowing it to read 168 // various combinations of data, empty data, EOF. 169 func (s) TestBufferSlice_ReadAll_Reads(t *testing.T) { 170 testcases := []struct { 171 name string 172 reads []readStep 173 wantErr string 174 wantBufs int 175 }{ 176 { 177 name: "EOF", 178 reads: []readStep{ 179 { 180 err: io.EOF, 181 }, 182 }, 183 }, 184 { 185 name: "data,EOF", 186 reads: []readStep{ 187 { 188 n: minReadSize, 189 }, 190 { 191 err: io.EOF, 192 }, 193 }, 194 wantBufs: 1, 195 }, 196 { 197 name: "data+EOF", 198 reads: []readStep{ 199 { 200 n: minReadSize, 201 err: io.EOF, 202 }, 203 }, 204 wantBufs: 1, 205 }, 206 { 207 name: "0,data+EOF", 208 reads: []readStep{ 209 {}, 210 { 211 n: minReadSize, 212 err: io.EOF, 213 }, 214 }, 215 wantBufs: 1, 216 }, 217 { 218 name: "0,data,EOF", 219 reads: []readStep{ 220 {}, 221 { 222 n: minReadSize, 223 }, 224 { 225 err: io.EOF, 226 }, 227 }, 228 wantBufs: 1, 229 }, 230 { 231 name: "data,data+EOF", 232 reads: []readStep{ 233 { 234 n: minReadSize, 235 }, 236 { 237 n: minReadSize, 238 err: io.EOF, 239 }, 240 }, 241 wantBufs: 1, 242 }, 243 { 244 name: "error", 245 reads: []readStep{ 246 { 247 err: errors.New("boom"), 248 }, 249 }, 250 wantErr: "boom", 251 }, 252 { 253 name: "data+error", 254 reads: []readStep{ 255 { 256 n: minReadSize, 257 err: errors.New("boom"), 258 }, 259 }, 260 wantErr: "boom", 261 wantBufs: 1, 262 }, 263 { 264 name: "data,data+error", 265 reads: []readStep{ 266 { 267 n: minReadSize, 268 }, 269 { 270 n: minReadSize, 271 err: errors.New("boom"), 272 }, 273 }, 274 wantErr: "boom", 275 wantBufs: 1, 276 }, 277 { 278 name: "data,data+EOF - whole buf", 279 reads: []readStep{ 280 { 281 n: minReadSize, 282 }, 283 { 284 n: readAllBufSize - minReadSize, 285 err: io.EOF, 286 }, 287 }, 288 wantBufs: 1, 289 }, 290 { 291 name: "data,data,EOF - whole buf", 292 reads: []readStep{ 293 { 294 n: minReadSize, 295 }, 296 { 297 n: readAllBufSize - minReadSize, 298 }, 299 { 300 err: io.EOF, 301 }, 302 }, 303 wantBufs: 1, 304 }, 305 { 306 name: "data,data,EOF - 2 bufs", 307 reads: []readStep{ 308 { 309 n: readAllBufSize, 310 }, 311 { 312 n: minReadSize, 313 }, 314 { 315 n: readAllBufSize - minReadSize, 316 }, 317 { 318 n: minReadSize, 319 }, 320 { 321 err: io.EOF, 322 }, 323 }, 324 wantBufs: 3, 325 }, 326 } 327 328 for _, tc := range testcases { 329 t.Run(tc.name, func(t *testing.T) { 330 pool := &testPool{ 331 allocated: make(map[*[]byte]struct{}), 332 } 333 r := &stepReader{ 334 reads: tc.reads, 335 } 336 data, err := mem.ReadAll(r, pool) 337 if tc.wantErr != "" { 338 if err == nil || err.Error() != tc.wantErr { 339 t.Fatalf("ReadAll() returned err %v, wanted %q", err, tc.wantErr) 340 } 341 } else { 342 if err != nil { 343 t.Fatal(err) 344 } 345 } 346 gotData := data.Materialize() 347 if !bytes.Equal(r.read, gotData) { 348 t.Fatalf("ReadAll() returned data %q, wanted %q", gotData, r.read) 349 } 350 if len(data) != tc.wantBufs { 351 t.Fatalf("ReadAll() returned %d bufs, wanted %d bufs", len(data), tc.wantBufs) 352 } 353 // all but last should be full buffers 354 for i := 0; i < len(data)-1; i++ { 355 if data[i].Len() != readAllBufSize { 356 t.Fatalf("ReadAll() returned data length %d, wanted %d", data[i].Len(), readAllBufSize) 357 } 358 } 359 data.Free() 360 if len(pool.allocated) > 0 { 361 t.Fatalf("got %d allocated buffers, wanted none", len(pool.allocated)) 362 } 363 }) 364 } 365 } 366 367 func (s) TestBufferSlice_ReadAll_WriteTo(t *testing.T) { 368 testcases := []struct { 369 name string 370 size int 371 }{ 372 { 373 name: "small", 374 size: minReadSize, 375 }, 376 { 377 name: "exact size", 378 size: readAllBufSize, 379 }, 380 { 381 name: "big", 382 size: readAllBufSize * 3, 383 }, 384 } 385 for _, tc := range testcases { 386 t.Run(tc.name, func(t *testing.T) { 387 pool := &testPool{ 388 allocated: make(map[*[]byte]struct{}), 389 } 390 buf := make([]byte, tc.size) 391 _, err := rand.Read(buf) 392 if err != nil { 393 t.Fatal(err) 394 } 395 r := bytes.NewBuffer(buf) 396 data, err := mem.ReadAll(r, pool) 397 if err != nil { 398 t.Fatal(err) 399 } 400 401 gotData := data.Materialize() 402 if !bytes.Equal(buf, gotData) { 403 t.Fatalf("ReadAll() = %q, wanted %q", gotData, buf) 404 } 405 data.Free() 406 if len(pool.allocated) > 0 { 407 t.Fatalf("wanted no allocated buffers, got %d", len(pool.allocated)) 408 } 409 }) 410 } 411 } 412 413 func ExampleNewWriter() { 414 var bs mem.BufferSlice 415 pool := mem.DefaultBufferPool() 416 writer := mem.NewWriter(&bs, pool) 417 418 for _, data := range [][]byte{ 419 []byte("abcd"), 420 []byte("abcd"), 421 []byte("abcd"), 422 } { 423 n, err := writer.Write(data) 424 fmt.Printf("Wrote %d bytes, err: %v\n", n, err) 425 } 426 fmt.Println(string(bs.Materialize())) 427 // Output: 428 // Wrote 4 bytes, err: <nil> 429 // Wrote 4 bytes, err: <nil> 430 // Wrote 4 bytes, err: <nil> 431 // abcdabcdabcd 432 } 433 434 var ( 435 _ io.Reader = (*stepReader)(nil) 436 _ mem.BufferPool = (*testPool)(nil) 437 ) 438 439 // readStep describes what a single stepReader.Read should do - how much data 440 // to return and what error to return. 441 type readStep struct { 442 n int 443 err error 444 } 445 446 // stepReader implements io.Reader that reads specified amount of data and/or 447 // returns the specified error in specified steps. 448 // The read data is accumulated in the read field. 449 type stepReader struct { 450 reads []readStep 451 read []byte 452 } 453 454 func (s *stepReader) Read(buf []byte) (int, error) { 455 if len(s.reads) == 0 { 456 panic("unexpected Read() call") 457 } 458 read := s.reads[0] 459 s.reads = s.reads[1:] 460 _, err := rand.Read(buf[:read.n]) 461 if err != nil { 462 panic(err) 463 } 464 s.read = append(s.read, buf[:read.n]...) 465 return read.n, read.err 466 } 467 468 // testPool is an implementation of BufferPool that allows to ensure that: 469 // - there are matching Put calls for all Get calls. 470 // - there are no unexpected Put calls. 471 type testPool struct { 472 allocated map[*[]byte]struct{} 473 } 474 475 func (t *testPool) Get(length int) *[]byte { 476 buf := make([]byte, length) 477 t.allocated[&buf] = struct{}{} 478 return &buf 479 } 480 481 func (t *testPool) Put(buf *[]byte) { 482 if _, ok := t.allocated[buf]; !ok { 483 panic("unexpected put") 484 } 485 delete(t.allocated, buf) 486 }