summaryrefslogtreecommitdiffhomepage
path: root/pkg/safemem/seq_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/safemem/seq_test.go')
-rw-r--r--pkg/safemem/seq_test.go217
1 files changed, 217 insertions, 0 deletions
diff --git a/pkg/safemem/seq_test.go b/pkg/safemem/seq_test.go
new file mode 100644
index 000000000..de34005e9
--- /dev/null
+++ b/pkg/safemem/seq_test.go
@@ -0,0 +1,217 @@
+// 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)
+ }
+}