2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// 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"
|
|
|
|
)
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|