github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/buffer/view_test.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package buffer_test contains tests for the buffer.VectorisedView type.
    16  package buffer_test
    17  
    18  import (
    19  	"bytes"
    20  	"io"
    21  	"reflect"
    22  	"testing"
    23  	"unsafe"
    24  
    25  	"github.com/SagerNet/gvisor/pkg/tcpip"
    26  	"github.com/SagerNet/gvisor/pkg/tcpip/buffer"
    27  )
    28  
    29  // copy returns a deep-copy of the vectorised view.
    30  func copyVV(vv buffer.VectorisedView) buffer.VectorisedView {
    31  	views := make([]buffer.View, 0, len(vv.Views()))
    32  	for _, v := range vv.Views() {
    33  		views = append(views, append(buffer.View(nil), v...))
    34  	}
    35  	return buffer.NewVectorisedView(vv.Size(), views)
    36  }
    37  
    38  // vv is an helper to build buffer.VectorisedView from different strings.
    39  func vv(size int, pieces ...string) buffer.VectorisedView {
    40  	views := make([]buffer.View, len(pieces))
    41  	for i, p := range pieces {
    42  		views[i] = []byte(p)
    43  	}
    44  
    45  	return buffer.NewVectorisedView(size, views)
    46  }
    47  
    48  // v returns a buffer.View containing piece.
    49  func v(piece string) buffer.View {
    50  	return buffer.View(piece)
    51  }
    52  
    53  var capLengthTestCases = []struct {
    54  	comment string
    55  	in      buffer.VectorisedView
    56  	length  int
    57  	want    buffer.VectorisedView
    58  }{
    59  	{
    60  		comment: "Simple case",
    61  		in:      vv(2, "12"),
    62  		length:  1,
    63  		want:    vv(1, "1"),
    64  	},
    65  	{
    66  		comment: "Case spanning across two Views",
    67  		in:      vv(4, "123", "4"),
    68  		length:  2,
    69  		want:    vv(2, "12"),
    70  	},
    71  	{
    72  		comment: "Corner case with negative length",
    73  		in:      vv(1, "1"),
    74  		length:  -1,
    75  		want:    vv(0),
    76  	},
    77  	{
    78  		comment: "Corner case with length = 0",
    79  		in:      vv(3, "12", "3"),
    80  		length:  0,
    81  		want:    vv(0),
    82  	},
    83  	{
    84  		comment: "Corner case with length = size",
    85  		in:      vv(1, "1"),
    86  		length:  1,
    87  		want:    vv(1, "1"),
    88  	},
    89  	{
    90  		comment: "Corner case with length > size",
    91  		in:      vv(1, "1"),
    92  		length:  2,
    93  		want:    vv(1, "1"),
    94  	},
    95  }
    96  
    97  func TestCapLength(t *testing.T) {
    98  	for _, c := range capLengthTestCases {
    99  		orig := copyVV(c.in)
   100  		c.in.CapLength(c.length)
   101  		if !reflect.DeepEqual(c.in, c.want) {
   102  			t.Errorf("Test \"%s\" failed when calling CapLength(%d) on %v. Got %v. Want %v",
   103  				c.comment, c.length, orig, c.in, c.want)
   104  		}
   105  	}
   106  }
   107  
   108  var trimFrontTestCases = []struct {
   109  	comment string
   110  	in      buffer.VectorisedView
   111  	count   int
   112  	want    buffer.VectorisedView
   113  }{
   114  	{
   115  		comment: "Simple case",
   116  		in:      vv(2, "12"),
   117  		count:   1,
   118  		want:    vv(1, "2"),
   119  	},
   120  	{
   121  		comment: "Case where we trim an entire View",
   122  		in:      vv(2, "1", "2"),
   123  		count:   1,
   124  		want:    vv(1, "2"),
   125  	},
   126  	{
   127  		comment: "Case spanning across two Views",
   128  		in:      vv(3, "1", "23"),
   129  		count:   2,
   130  		want:    vv(1, "3"),
   131  	},
   132  	{
   133  		comment: "Case with one empty Views",
   134  		in:      vv(3, "1", "", "23"),
   135  		count:   2,
   136  		want:    vv(1, "3"),
   137  	},
   138  	{
   139  		comment: "Corner case with negative count",
   140  		in:      vv(1, "1"),
   141  		count:   -1,
   142  		want:    vv(1, "1"),
   143  	},
   144  	{
   145  		comment: " Corner case with count = 0",
   146  		in:      vv(1, "1"),
   147  		count:   0,
   148  		want:    vv(1, "1"),
   149  	},
   150  	{
   151  		comment: "Corner case with count = size",
   152  		in:      vv(1, "1"),
   153  		count:   1,
   154  		want:    vv(0),
   155  	},
   156  	{
   157  		comment: "Corner case with count > size",
   158  		in:      vv(1, "1"),
   159  		count:   2,
   160  		want:    vv(0),
   161  	},
   162  }
   163  
   164  func TestTrimFront(t *testing.T) {
   165  	for _, c := range trimFrontTestCases {
   166  		orig := copyVV(c.in)
   167  		c.in.TrimFront(c.count)
   168  		if !reflect.DeepEqual(c.in, c.want) {
   169  			t.Errorf("Test \"%s\" failed when calling TrimFront(%d) on %v. Got %v. Want %v",
   170  				c.comment, c.count, orig, c.in, c.want)
   171  		}
   172  	}
   173  }
   174  
   175  var toViewCases = []struct {
   176  	comment string
   177  	in      buffer.VectorisedView
   178  	want    buffer.View
   179  }{
   180  	{
   181  		comment: "Simple case",
   182  		in:      vv(2, "12"),
   183  		want:    []byte("12"),
   184  	},
   185  	{
   186  		comment: "Case with multiple views",
   187  		in:      vv(2, "1", "2"),
   188  		want:    []byte("12"),
   189  	},
   190  	{
   191  		comment: "Empty case",
   192  		in:      vv(0),
   193  		want:    []byte(""),
   194  	},
   195  }
   196  
   197  func TestToView(t *testing.T) {
   198  	for _, c := range toViewCases {
   199  		got := c.in.ToView()
   200  		if !reflect.DeepEqual(got, c.want) {
   201  			t.Errorf("Test \"%s\" failed when calling ToView() on %v. Got %v. Want %v",
   202  				c.comment, c.in, got, c.want)
   203  		}
   204  	}
   205  }
   206  
   207  var toCloneCases = []struct {
   208  	comment  string
   209  	inView   buffer.VectorisedView
   210  	inBuffer []buffer.View
   211  }{
   212  	{
   213  		comment:  "Simple case",
   214  		inView:   vv(1, "1"),
   215  		inBuffer: make([]buffer.View, 1),
   216  	},
   217  	{
   218  		comment:  "Case with multiple views",
   219  		inView:   vv(2, "1", "2"),
   220  		inBuffer: make([]buffer.View, 2),
   221  	},
   222  	{
   223  		comment:  "Case with buffer too small",
   224  		inView:   vv(2, "1", "2"),
   225  		inBuffer: make([]buffer.View, 1),
   226  	},
   227  	{
   228  		comment:  "Case with buffer larger than needed",
   229  		inView:   vv(1, "1"),
   230  		inBuffer: make([]buffer.View, 2),
   231  	},
   232  	{
   233  		comment:  "Case with nil buffer",
   234  		inView:   vv(1, "1"),
   235  		inBuffer: nil,
   236  	},
   237  }
   238  
   239  func TestToClone(t *testing.T) {
   240  	for _, c := range toCloneCases {
   241  		t.Run(c.comment, func(t *testing.T) {
   242  			got := c.inView.Clone(c.inBuffer)
   243  			if !reflect.DeepEqual(got, c.inView) {
   244  				t.Fatalf("got (%+v).Clone(%+v) = %+v, want = %+v",
   245  					c.inView, c.inBuffer, got, c.inView)
   246  			}
   247  		})
   248  	}
   249  }
   250  
   251  type readToTestCases struct {
   252  	comment     string
   253  	vv          buffer.VectorisedView
   254  	bytesToRead int
   255  	wantBytes   string
   256  	leftVV      buffer.VectorisedView
   257  }
   258  
   259  func createReadToTestCases() []readToTestCases {
   260  	return []readToTestCases{
   261  		{
   262  			comment:     "large VV, short read",
   263  			vv:          vv(30, "012345678901234567890123456789"),
   264  			bytesToRead: 10,
   265  			wantBytes:   "0123456789",
   266  			leftVV:      vv(20, "01234567890123456789"),
   267  		},
   268  		{
   269  			comment:     "largeVV, multiple views, short read",
   270  			vv:          vv(13, "123", "345", "567", "8910"),
   271  			bytesToRead: 6,
   272  			wantBytes:   "123345",
   273  			leftVV:      vv(7, "567", "8910"),
   274  		},
   275  		{
   276  			comment:     "smallVV (multiple views), large read",
   277  			vv:          vv(3, "1", "2", "3"),
   278  			bytesToRead: 10,
   279  			wantBytes:   "123",
   280  			leftVV:      vv(0, ""),
   281  		},
   282  		{
   283  			comment:     "smallVV (single view), large read",
   284  			vv:          vv(1, "1"),
   285  			bytesToRead: 10,
   286  			wantBytes:   "1",
   287  			leftVV:      vv(0, ""),
   288  		},
   289  		{
   290  			comment:     "emptyVV, large read",
   291  			vv:          vv(0, ""),
   292  			bytesToRead: 10,
   293  			wantBytes:   "",
   294  			leftVV:      vv(0, ""),
   295  		},
   296  	}
   297  }
   298  
   299  func TestVVReadToVV(t *testing.T) {
   300  	for _, tc := range createReadToTestCases() {
   301  		t.Run(tc.comment, func(t *testing.T) {
   302  			var readTo buffer.VectorisedView
   303  			inSize := tc.vv.Size()
   304  			copied := tc.vv.ReadToVV(&readTo, tc.bytesToRead)
   305  			if got, want := copied, len(tc.wantBytes); got != want {
   306  				t.Errorf("incorrect number of bytes copied returned in ReadToVV got: %d, want: %d, tc: %+v", got, want, tc)
   307  			}
   308  			if got, want := string(readTo.ToView()), tc.wantBytes; got != want {
   309  				t.Errorf("unexpected content in readTo got: %s, want: %s", got, want)
   310  			}
   311  			if got, want := tc.vv.Size(), inSize-copied; got != want {
   312  				t.Errorf("test VV has incorrect size after reading got: %d, want: %d, tc.vv: %+v", got, want, tc.vv)
   313  			}
   314  			if got, want := string(tc.vv.ToView()), string(tc.leftVV.ToView()); got != want {
   315  				t.Errorf("unexpected data left in vv after read got: %+v, want: %+v", got, want)
   316  			}
   317  		})
   318  	}
   319  }
   320  
   321  func TestVVReadTo(t *testing.T) {
   322  	for _, tc := range createReadToTestCases() {
   323  		t.Run(tc.comment, func(t *testing.T) {
   324  			b := make([]byte, tc.bytesToRead)
   325  			dst := tcpip.SliceWriter(b)
   326  			origSize := tc.vv.Size()
   327  			copied, err := tc.vv.ReadTo(&dst, false /* peek */)
   328  			if err != nil && err != io.ErrShortWrite {
   329  				t.Errorf("got ReadTo(&dst, false) = (_, %s); want nil or io.ErrShortWrite", err)
   330  			}
   331  			if got, want := copied, len(tc.wantBytes); got != want {
   332  				t.Errorf("got ReadTo(&dst, false) = (%d, _); want %d", got, want)
   333  			}
   334  			if got, want := string(b[:copied]), tc.wantBytes; got != want {
   335  				t.Errorf("got dst = %q, want %q", got, want)
   336  			}
   337  			if got, want := tc.vv.Size(), origSize-copied; got != want {
   338  				t.Errorf("got after-read tc.vv.Size() = %d, want %d", got, want)
   339  			}
   340  			if got, want := string(tc.vv.ToView()), string(tc.leftVV.ToView()); got != want {
   341  				t.Errorf("got after-read data in tc.vv = %q, want %q", got, want)
   342  			}
   343  		})
   344  	}
   345  }
   346  
   347  func TestVVReadToPeek(t *testing.T) {
   348  	for _, tc := range createReadToTestCases() {
   349  		t.Run(tc.comment, func(t *testing.T) {
   350  			b := make([]byte, tc.bytesToRead)
   351  			dst := tcpip.SliceWriter(b)
   352  			origSize := tc.vv.Size()
   353  			origData := string(tc.vv.ToView())
   354  			copied, err := tc.vv.ReadTo(&dst, true /* peek */)
   355  			if err != nil && err != io.ErrShortWrite {
   356  				t.Errorf("got ReadTo(&dst, true) = (_, %s); want nil or io.ErrShortWrite", err)
   357  			}
   358  			if got, want := copied, len(tc.wantBytes); got != want {
   359  				t.Errorf("got ReadTo(&dst, true) = (%d, _); want %d", got, want)
   360  			}
   361  			if got, want := string(b[:copied]), tc.wantBytes; got != want {
   362  				t.Errorf("got dst = %q, want %q", got, want)
   363  			}
   364  			// Expect tc.vv is unchanged.
   365  			if got, want := tc.vv.Size(), origSize; got != want {
   366  				t.Errorf("got after-read tc.vv.Size() = %d, want %d", got, want)
   367  			}
   368  			if got, want := string(tc.vv.ToView()), origData; got != want {
   369  				t.Errorf("got after-read data in tc.vv = %q, want %q", got, want)
   370  			}
   371  		})
   372  	}
   373  }
   374  
   375  func TestVVRead(t *testing.T) {
   376  	testCases := []struct {
   377  		comment     string
   378  		vv          buffer.VectorisedView
   379  		bytesToRead int
   380  		readBytes   string
   381  		leftBytes   string
   382  		wantError   bool
   383  	}{
   384  		{
   385  			comment:     "large VV, short read",
   386  			vv:          vv(30, "012345678901234567890123456789"),
   387  			bytesToRead: 10,
   388  			readBytes:   "0123456789",
   389  			leftBytes:   "01234567890123456789",
   390  		},
   391  		{
   392  			comment:     "largeVV, multiple buffers, short read",
   393  			vv:          vv(13, "123", "345", "567", "8910"),
   394  			bytesToRead: 6,
   395  			readBytes:   "123345",
   396  			leftBytes:   "5678910",
   397  		},
   398  		{
   399  			comment:     "smallVV, large read",
   400  			vv:          vv(3, "1", "2", "3"),
   401  			bytesToRead: 10,
   402  			readBytes:   "123",
   403  			leftBytes:   "",
   404  		},
   405  		{
   406  			comment:     "smallVV, large read",
   407  			vv:          vv(1, "1"),
   408  			bytesToRead: 10,
   409  			readBytes:   "1",
   410  			leftBytes:   "",
   411  		},
   412  		{
   413  			comment:     "emptyVV, large read",
   414  			vv:          vv(0, ""),
   415  			bytesToRead: 10,
   416  			readBytes:   "",
   417  			wantError:   true,
   418  		},
   419  	}
   420  
   421  	for _, tc := range testCases {
   422  		t.Run(tc.comment, func(t *testing.T) {
   423  			readTo := buffer.NewView(tc.bytesToRead)
   424  			inSize := tc.vv.Size()
   425  			copied, err := tc.vv.Read(readTo)
   426  			if !tc.wantError && err != nil {
   427  				t.Fatalf("unexpected error in tc.vv.Read(..) = %s", err)
   428  			}
   429  			readTo = readTo[:copied]
   430  			if got, want := copied, len(tc.readBytes); got != want {
   431  				t.Errorf("incorrect number of bytes copied returned in ReadToVV got: %d, want: %d, tc.vv: %+v", got, want, tc.vv)
   432  			}
   433  			if got, want := string(readTo), tc.readBytes; got != want {
   434  				t.Errorf("unexpected data in readTo got: %s, want: %s", got, want)
   435  			}
   436  			if got, want := tc.vv.Size(), inSize-copied; got != want {
   437  				t.Errorf("test VV has incorrect size after reading got: %d, want: %d, tc.vv: %+v", got, want, tc.vv)
   438  			}
   439  			if got, want := string(tc.vv.ToView()), tc.leftBytes; got != want {
   440  				t.Errorf("vv has incorrect data after Read got: %s, want: %s", got, want)
   441  			}
   442  		})
   443  	}
   444  }
   445  
   446  var pullUpTestCases = []struct {
   447  	comment string
   448  	in      buffer.VectorisedView
   449  	count   int
   450  	want    []byte
   451  	result  buffer.VectorisedView
   452  	ok      bool
   453  }{
   454  	{
   455  		comment: "simple case",
   456  		in:      vv(2, "12"),
   457  		count:   1,
   458  		want:    []byte("1"),
   459  		result:  vv(2, "12"),
   460  		ok:      true,
   461  	},
   462  	{
   463  		comment: "entire View",
   464  		in:      vv(2, "1", "2"),
   465  		count:   1,
   466  		want:    []byte("1"),
   467  		result:  vv(2, "1", "2"),
   468  		ok:      true,
   469  	},
   470  	{
   471  		comment: "spanning across two Views",
   472  		in:      vv(3, "1", "23"),
   473  		count:   2,
   474  		want:    []byte("12"),
   475  		result:  vv(3, "12", "3"),
   476  		ok:      true,
   477  	},
   478  	{
   479  		comment: "spanning across all Views",
   480  		in:      vv(5, "1", "23", "45"),
   481  		count:   5,
   482  		want:    []byte("12345"),
   483  		result:  vv(5, "12345"),
   484  		ok:      true,
   485  	},
   486  	{
   487  		comment: "count = 0",
   488  		in:      vv(1, "1"),
   489  		count:   0,
   490  		want:    []byte{},
   491  		result:  vv(1, "1"),
   492  		ok:      true,
   493  	},
   494  	{
   495  		comment: "count = size",
   496  		in:      vv(1, "1"),
   497  		count:   1,
   498  		want:    []byte("1"),
   499  		result:  vv(1, "1"),
   500  		ok:      true,
   501  	},
   502  	{
   503  		comment: "count too large",
   504  		in:      vv(3, "1", "23"),
   505  		count:   4,
   506  		want:    nil,
   507  		result:  vv(3, "1", "23"),
   508  		ok:      false,
   509  	},
   510  	{
   511  		comment: "empty vv",
   512  		in:      vv(0, ""),
   513  		count:   1,
   514  		want:    nil,
   515  		result:  vv(0, ""),
   516  		ok:      false,
   517  	},
   518  	{
   519  		comment: "empty vv, count = 0",
   520  		in:      vv(0, ""),
   521  		count:   0,
   522  		want:    nil,
   523  		result:  vv(0, ""),
   524  		ok:      true,
   525  	},
   526  	{
   527  		comment: "empty views",
   528  		in:      vv(3, "", "1", "", "23"),
   529  		count:   2,
   530  		want:    []byte("12"),
   531  		result:  vv(3, "12", "3"),
   532  		ok:      true,
   533  	},
   534  }
   535  
   536  func TestPullUp(t *testing.T) {
   537  	for _, c := range pullUpTestCases {
   538  		got, ok := c.in.PullUp(c.count)
   539  
   540  		// Is the return value right?
   541  		if ok != c.ok {
   542  			t.Errorf("Test %q failed when calling PullUp(%d) on %v. Got an ok of %t. Want %t",
   543  				c.comment, c.count, c.in, ok, c.ok)
   544  		}
   545  		if bytes.Compare(got, buffer.View(c.want)) != 0 {
   546  			t.Errorf("Test %q failed when calling PullUp(%d) on %v. Got %v. Want %v",
   547  				c.comment, c.count, c.in, got, c.want)
   548  		}
   549  
   550  		// Is the underlying structure right?
   551  		if !reflect.DeepEqual(c.in, c.result) {
   552  			t.Errorf("Test %q failed when calling PullUp(%d). Got vv with structure %v. Wanted %v",
   553  				c.comment, c.count, c.in, c.result)
   554  		}
   555  	}
   556  }
   557  
   558  func TestToVectorisedView(t *testing.T) {
   559  	testCases := []struct {
   560  		in   buffer.View
   561  		want buffer.VectorisedView
   562  	}{
   563  		{nil, buffer.VectorisedView{}},
   564  		{buffer.View{}, buffer.VectorisedView{}},
   565  		{buffer.View{'a'}, buffer.NewVectorisedView(1, []buffer.View{{'a'}})},
   566  	}
   567  	for _, tc := range testCases {
   568  		if got, want := tc.in.ToVectorisedView(), tc.want; !reflect.DeepEqual(got, want) {
   569  			t.Errorf("(%v).ToVectorisedView failed got: %+v, want: %+v", tc.in, got, want)
   570  		}
   571  	}
   572  }
   573  
   574  func TestAppendView(t *testing.T) {
   575  	testCases := []struct {
   576  		vv   buffer.VectorisedView
   577  		in   buffer.View
   578  		want buffer.VectorisedView
   579  	}{
   580  		{vv(0), nil, vv(0)},
   581  		{vv(0), v(""), vv(0)},
   582  		{vv(4, "abcd"), nil, vv(4, "abcd")},
   583  		{vv(4, "abcd"), v(""), vv(4, "abcd")},
   584  		{vv(4, "abcd"), v("e"), vv(5, "abcd", "e")},
   585  	}
   586  	for _, tc := range testCases {
   587  		tc.vv.AppendView(tc.in)
   588  		if got, want := tc.vv, tc.want; !reflect.DeepEqual(got, want) {
   589  			t.Errorf("(%v).ToVectorisedView failed got: %+v, want: %+v", tc.in, got, want)
   590  		}
   591  	}
   592  }
   593  
   594  func TestAppendViews(t *testing.T) {
   595  	testCases := []struct {
   596  		vv   buffer.VectorisedView
   597  		in   []buffer.View
   598  		want buffer.VectorisedView
   599  	}{
   600  		{vv(0), nil, vv(0)},
   601  		{vv(0), []buffer.View{}, vv(0)},
   602  		{vv(0), []buffer.View{v("")}, vv(0, "")},
   603  		{vv(4, "abcd"), nil, vv(4, "abcd")},
   604  		{vv(4, "abcd"), []buffer.View{}, vv(4, "abcd")},
   605  		{vv(4, "abcd"), []buffer.View{v("")}, vv(4, "abcd", "")},
   606  		{vv(4, "abcd"), []buffer.View{v("")}, vv(4, "abcd", "")},
   607  		{vv(4, "abcd"), []buffer.View{v("e")}, vv(5, "abcd", "e")},
   608  		{vv(4, "abcd"), []buffer.View{v("e"), v("fg")}, vv(7, "abcd", "e", "fg")},
   609  		{vv(4, "abcd"), []buffer.View{v(""), v("fg")}, vv(6, "abcd", "", "fg")},
   610  	}
   611  	for _, tc := range testCases {
   612  		tc.vv.AppendViews(tc.in)
   613  		if got, want := tc.vv, tc.want; !reflect.DeepEqual(got, want) {
   614  			t.Errorf("(%v).ToVectorisedView failed got: %+v, want: %+v", tc.in, got, want)
   615  		}
   616  	}
   617  }
   618  
   619  func TestMemSize(t *testing.T) {
   620  	const perViewCap = 128
   621  	views := make([]buffer.View, 2, 32)
   622  	views[0] = make(buffer.View, 10, perViewCap)
   623  	views[1] = make(buffer.View, 20, perViewCap)
   624  	vv := buffer.NewVectorisedView(30, views)
   625  	want := int(unsafe.Sizeof(vv)) + cap(views)*int(unsafe.Sizeof(views)) + 2*perViewCap
   626  	if got := vv.MemSize(); got != want {
   627  		t.Errorf("vv.MemSize() = %d, want %d", got, want)
   628  	}
   629  }