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  }