gvisor/pkg/safemem/seq_test.go

218 lines
5.6 KiB
Go

// Copyright 2018 The gVisor 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 safemem
import (
"bytes"
"reflect"
"testing"
)
func TestBlockSeqOfEmptyBlock(t *testing.T) {
bs := BlockSeqOf(Block{})
if !bs.IsEmpty() {
t.Errorf("BlockSeqOf(Block{}).IsEmpty(): got false, wanted true; BlockSeq is %v", bs)
}
}
func TestBlockSeqOfNonemptyBlock(t *testing.T) {
b := BlockFromSafeSlice(make([]byte, 1))
bs := BlockSeqOf(b)
if bs.IsEmpty() {
t.Fatalf("BlockSeqOf(non-empty Block).IsEmpty(): got true, wanted false; BlockSeq is %v", bs)
}
if head := bs.Head(); head != b {
t.Fatalf("BlockSeqOf(non-empty Block).Head(): got %v, wanted %v", head, b)
}
if tail := bs.Tail(); !tail.IsEmpty() {
t.Fatalf("BlockSeqOf(non-empty Block).Tail().IsEmpty(): got false, wanted true: tail is %v", tail)
}
}
type blockSeqTest struct {
desc string
pieces []string
haveOffset bool
offset uint64
haveLimit bool
limit uint64
want string
}
func (t blockSeqTest) NonEmptyByteSlices() [][]byte {
// t is a value, so we can mutate it freely.
slices := make([][]byte, 0, len(t.pieces))
for _, str := range t.pieces {
if t.haveOffset {
strOff := t.offset
if strOff > uint64(len(str)) {
strOff = uint64(len(str))
}
str = str[strOff:]
t.offset -= strOff
}
if t.haveLimit {
strLim := t.limit
if strLim > uint64(len(str)) {
strLim = uint64(len(str))
}
str = str[:strLim]
t.limit -= strLim
}
if len(str) != 0 {
slices = append(slices, []byte(str))
}
}
return slices
}
func (t blockSeqTest) BlockSeq() BlockSeq {
blocks := make([]Block, 0, len(t.pieces))
for _, str := range t.pieces {
blocks = append(blocks, BlockFromSafeSlice([]byte(str)))
}
bs := BlockSeqFromSlice(blocks)
if t.haveOffset {
bs = bs.DropFirst64(t.offset)
}
if t.haveLimit {
bs = bs.TakeFirst64(t.limit)
}
return bs
}
var blockSeqTests = []blockSeqTest{
{
desc: "Empty sequence",
},
{
desc: "Sequence of length 1",
pieces: []string{"foobar"},
want: "foobar",
},
{
desc: "Sequence of length 2",
pieces: []string{"foo", "bar"},
want: "foobar",
},
{
desc: "Empty Blocks",
pieces: []string{"", "foo", "", "", "bar", ""},
want: "foobar",
},
{
desc: "Sequence with non-zero offset",
pieces: []string{"foo", "bar"},
haveOffset: true,
offset: 2,
want: "obar",
},
{
desc: "Sequence with non-maximal limit",
pieces: []string{"foo", "bar"},
haveLimit: true,
limit: 5,
want: "fooba",
},
{
desc: "Sequence with offset and limit",
pieces: []string{"foo", "bar"},
haveOffset: true,
offset: 2,
haveLimit: true,
limit: 3,
want: "oba",
},
}
func TestBlockSeqNumBytes(t *testing.T) {
for _, test := range blockSeqTests {
t.Run(test.desc, func(t *testing.T) {
if got, want := test.BlockSeq().NumBytes(), uint64(len(test.want)); got != want {
t.Errorf("NumBytes: got %d, wanted %d", got, want)
}
})
}
}
func TestBlockSeqIterBlocks(t *testing.T) {
// Tests BlockSeq iteration using Head/Tail.
for _, test := range blockSeqTests {
t.Run(test.desc, func(t *testing.T) {
srcs := test.BlockSeq()
// "Note that a non-nil empty slice and a nil slice ... are not
// deeply equal." - reflect
slices := make([][]byte, 0, 0)
for !srcs.IsEmpty() {
src := srcs.Head()
slices = append(slices, src.ToSlice())
nextSrcs := srcs.Tail()
if got, want := nextSrcs.NumBytes(), srcs.NumBytes()-uint64(src.Len()); got != want {
t.Fatalf("%v.Tail(): got %v (%d bytes), wanted %d bytes", srcs, nextSrcs, got, want)
}
srcs = nextSrcs
}
if wantSlices := test.NonEmptyByteSlices(); !reflect.DeepEqual(slices, wantSlices) {
t.Errorf("Accumulated slices: got %v, wanted %v", slices, wantSlices)
}
})
}
}
func TestBlockSeqIterBytes(t *testing.T) {
// Tests BlockSeq iteration using Head/DropFirst.
for _, test := range blockSeqTests {
t.Run(test.desc, func(t *testing.T) {
srcs := test.BlockSeq()
var dst bytes.Buffer
for !srcs.IsEmpty() {
src := srcs.Head()
var b [1]byte
n, err := Copy(BlockFromSafeSlice(b[:]), src)
if n != 1 || err != nil {
t.Fatalf("Copy: got (%v, %v), wanted (1, nil)", n, err)
}
dst.WriteByte(b[0])
nextSrcs := srcs.DropFirst(1)
if got, want := nextSrcs.NumBytes(), srcs.NumBytes()-1; got != want {
t.Fatalf("%v.DropFirst(1): got %v (%d bytes), wanted %d bytes", srcs, nextSrcs, got, want)
}
srcs = nextSrcs
}
if got := string(dst.Bytes()); got != test.want {
t.Errorf("Copied string: got %q, wanted %q", got, test.want)
}
})
}
}
func TestBlockSeqDropBeyondLimit(t *testing.T) {
blocks := []Block{BlockFromSafeSlice([]byte("123")), BlockFromSafeSlice([]byte("4"))}
bs := BlockSeqFromSlice(blocks)
if got, want := bs.NumBytes(), uint64(4); got != want {
t.Errorf("%v.NumBytes(): got %d, wanted %d", bs, got, want)
}
bs = bs.TakeFirst(1)
if got, want := bs.NumBytes(), uint64(1); got != want {
t.Errorf("%v.NumBytes(): got %d, wanted %d", bs, got, want)
}
bs = bs.DropFirst(2)
if got, want := bs.NumBytes(), uint64(0); got != want {
t.Errorf("%v.NumBytes(): got %d, wanted %d", bs, got, want)
}
}