gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/safemem/seq_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 safemem
    16  
    17  import (
    18  	"bytes"
    19  	"reflect"
    20  	"testing"
    21  )
    22  
    23  func TestBlockSeqOfEmptyBlock(t *testing.T) {
    24  	bs := BlockSeqOf(Block{})
    25  	if !bs.IsEmpty() {
    26  		t.Errorf("BlockSeqOf(Block{}).IsEmpty(): got false, wanted true; BlockSeq is %v", bs)
    27  	}
    28  }
    29  
    30  func TestBlockSeqOfNonemptyBlock(t *testing.T) {
    31  	b := BlockFromSafeSlice(make([]byte, 1))
    32  	bs := BlockSeqOf(b)
    33  	if bs.IsEmpty() {
    34  		t.Fatalf("BlockSeqOf(non-empty Block).IsEmpty(): got true, wanted false; BlockSeq is %v", bs)
    35  	}
    36  	if head := bs.Head(); head != b {
    37  		t.Fatalf("BlockSeqOf(non-empty Block).Head(): got %v, wanted %v", head, b)
    38  	}
    39  	if tail := bs.Tail(); !tail.IsEmpty() {
    40  		t.Fatalf("BlockSeqOf(non-empty Block).Tail().IsEmpty(): got false, wanted true: tail is %v", tail)
    41  	}
    42  }
    43  
    44  type blockSeqTest struct {
    45  	desc string
    46  
    47  	pieces     []string
    48  	haveOffset bool
    49  	offset     uint64
    50  	haveLimit  bool
    51  	limit      uint64
    52  
    53  	want string
    54  }
    55  
    56  func (t blockSeqTest) NonEmptyByteSlices() [][]byte {
    57  	// t is a value, so we can mutate it freely.
    58  	slices := make([][]byte, 0, len(t.pieces))
    59  	for _, str := range t.pieces {
    60  		if t.haveOffset {
    61  			strOff := t.offset
    62  			if strOff > uint64(len(str)) {
    63  				strOff = uint64(len(str))
    64  			}
    65  			str = str[strOff:]
    66  			t.offset -= strOff
    67  		}
    68  		if t.haveLimit {
    69  			strLim := t.limit
    70  			if strLim > uint64(len(str)) {
    71  				strLim = uint64(len(str))
    72  			}
    73  			str = str[:strLim]
    74  			t.limit -= strLim
    75  		}
    76  		if len(str) != 0 {
    77  			slices = append(slices, []byte(str))
    78  		}
    79  	}
    80  	return slices
    81  }
    82  
    83  func (t blockSeqTest) BlockSeq() BlockSeq {
    84  	blocks := make([]Block, 0, len(t.pieces))
    85  	for _, str := range t.pieces {
    86  		blocks = append(blocks, BlockFromSafeSlice([]byte(str)))
    87  	}
    88  	bs := BlockSeqFromSlice(blocks)
    89  	if t.haveOffset {
    90  		bs = bs.DropFirst64(t.offset)
    91  	}
    92  	if t.haveLimit {
    93  		bs = bs.TakeFirst64(t.limit)
    94  	}
    95  	return bs
    96  }
    97  
    98  var blockSeqTests = []blockSeqTest{
    99  	{
   100  		desc: "Empty sequence",
   101  	},
   102  	{
   103  		desc:   "Sequence of length 1",
   104  		pieces: []string{"foobar"},
   105  		want:   "foobar",
   106  	},
   107  	{
   108  		desc:   "Sequence of length 2",
   109  		pieces: []string{"foo", "bar"},
   110  		want:   "foobar",
   111  	},
   112  	{
   113  		desc:   "Empty Blocks",
   114  		pieces: []string{"", "foo", "", "", "bar", ""},
   115  		want:   "foobar",
   116  	},
   117  	{
   118  		desc:       "Sequence with non-zero offset",
   119  		pieces:     []string{"foo", "bar"},
   120  		haveOffset: true,
   121  		offset:     2,
   122  		want:       "obar",
   123  	},
   124  	{
   125  		desc:      "Sequence with non-maximal limit",
   126  		pieces:    []string{"foo", "bar"},
   127  		haveLimit: true,
   128  		limit:     5,
   129  		want:      "fooba",
   130  	},
   131  	{
   132  		desc:       "Sequence with offset and limit",
   133  		pieces:     []string{"foo", "bar"},
   134  		haveOffset: true,
   135  		offset:     2,
   136  		haveLimit:  true,
   137  		limit:      3,
   138  		want:       "oba",
   139  	},
   140  }
   141  
   142  func TestBlockSeqNumBytes(t *testing.T) {
   143  	for _, test := range blockSeqTests {
   144  		t.Run(test.desc, func(t *testing.T) {
   145  			if got, want := test.BlockSeq().NumBytes(), uint64(len(test.want)); got != want {
   146  				t.Errorf("NumBytes: got %d, wanted %d", got, want)
   147  			}
   148  		})
   149  	}
   150  }
   151  
   152  func TestBlockSeqIterBlocks(t *testing.T) {
   153  	// Tests BlockSeq iteration using Head/Tail.
   154  	for _, test := range blockSeqTests {
   155  		t.Run(test.desc, func(t *testing.T) {
   156  			srcs := test.BlockSeq()
   157  			// "Note that a non-nil empty slice and a nil slice ... are not
   158  			// deeply equal." - reflect
   159  			slices := make([][]byte, 0, 0)
   160  			for !srcs.IsEmpty() {
   161  				src := srcs.Head()
   162  				slices = append(slices, src.ToSlice())
   163  				nextSrcs := srcs.Tail()
   164  				if got, want := nextSrcs.NumBytes(), srcs.NumBytes()-uint64(src.Len()); got != want {
   165  					t.Fatalf("%v.Tail(): got %v (%d bytes), wanted %d bytes", srcs, nextSrcs, got, want)
   166  				}
   167  				srcs = nextSrcs
   168  			}
   169  			if wantSlices := test.NonEmptyByteSlices(); !reflect.DeepEqual(slices, wantSlices) {
   170  				t.Errorf("Accumulated slices: got %v, wanted %v", slices, wantSlices)
   171  			}
   172  		})
   173  	}
   174  }
   175  
   176  func TestBlockSeqIterBytes(t *testing.T) {
   177  	// Tests BlockSeq iteration using Head/DropFirst.
   178  	for _, test := range blockSeqTests {
   179  		t.Run(test.desc, func(t *testing.T) {
   180  			srcs := test.BlockSeq()
   181  			var dst bytes.Buffer
   182  			for !srcs.IsEmpty() {
   183  				src := srcs.Head()
   184  				var b [1]byte
   185  				n, err := Copy(BlockFromSafeSlice(b[:]), src)
   186  				if n != 1 || err != nil {
   187  					t.Fatalf("Copy: got (%v, %v), wanted (1, nil)", n, err)
   188  				}
   189  				dst.WriteByte(b[0])
   190  				nextSrcs := srcs.DropFirst(1)
   191  				if got, want := nextSrcs.NumBytes(), srcs.NumBytes()-1; got != want {
   192  					t.Fatalf("%v.DropFirst(1): got %v (%d bytes), wanted %d bytes", srcs, nextSrcs, got, want)
   193  				}
   194  				srcs = nextSrcs
   195  			}
   196  			if got := string(dst.Bytes()); got != test.want {
   197  				t.Errorf("Copied string: got %q, wanted %q", got, test.want)
   198  			}
   199  		})
   200  	}
   201  }
   202  
   203  func TestBlockSeqDropBeyondLimit(t *testing.T) {
   204  	blocks := []Block{BlockFromSafeSlice([]byte("123")), BlockFromSafeSlice([]byte("4"))}
   205  	bs := BlockSeqFromSlice(blocks)
   206  	if got, want := bs.NumBytes(), uint64(4); got != want {
   207  		t.Errorf("%v.NumBytes(): got %d, wanted %d", bs, got, want)
   208  	}
   209  	bs = bs.TakeFirst(1)
   210  	if got, want := bs.NumBytes(), uint64(1); got != want {
   211  		t.Errorf("%v.NumBytes(): got %d, wanted %d", bs, got, want)
   212  	}
   213  	bs = bs.DropFirst(2)
   214  	if got, want := bs.NumBytes(), uint64(0); got != want {
   215  		t.Errorf("%v.NumBytes(): got %d, wanted %d", bs, got, want)
   216  	}
   217  }