2020-02-28 20:28:10 +00:00
|
|
|
// Copyright 2020 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 buffer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2020-03-12 02:50:59 +00:00
|
|
|
"io"
|
2020-02-28 20:28:10 +00:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2020-03-12 02:50:59 +00:00
|
|
|
func fillAppend(v *View, data []byte) {
|
|
|
|
v.Append(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fillAppendEnd(v *View, data []byte) {
|
|
|
|
v.Grow(bufferSize-1, false)
|
|
|
|
v.Append(data)
|
|
|
|
v.TrimFront(bufferSize - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fillWriteFromReader(v *View, data []byte) {
|
|
|
|
b := bytes.NewBuffer(data)
|
|
|
|
v.WriteFromReader(b, int64(len(data)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func fillWriteFromReaderEnd(v *View, data []byte) {
|
|
|
|
v.Grow(bufferSize-1, false)
|
|
|
|
b := bytes.NewBuffer(data)
|
|
|
|
v.WriteFromReader(b, int64(len(data)))
|
|
|
|
v.TrimFront(bufferSize - 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
var fillFuncs = map[string]func(*View, []byte){
|
|
|
|
"append": fillAppend,
|
|
|
|
"appendEnd": fillAppendEnd,
|
|
|
|
"writeFromReader": fillWriteFromReader,
|
|
|
|
"writeFromReaderEnd": fillWriteFromReaderEnd,
|
|
|
|
}
|
|
|
|
|
|
|
|
func testReadAt(t *testing.T, v *View, offset int64, n int, wantStr string, wantErr error) {
|
|
|
|
t.Helper()
|
|
|
|
d := make([]byte, n)
|
|
|
|
n, err := v.ReadAt(d, offset)
|
|
|
|
if n != len(wantStr) {
|
|
|
|
t.Errorf("got %d, want %d", n, len(wantStr))
|
|
|
|
}
|
|
|
|
if err != wantErr {
|
|
|
|
t.Errorf("got err %v, want %v", err, wantErr)
|
|
|
|
}
|
|
|
|
if !bytes.Equal(d[:n], []byte(wantStr)) {
|
|
|
|
t.Errorf("got %q, want %q", string(d[:n]), wantStr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-28 20:28:10 +00:00
|
|
|
func TestView(t *testing.T) {
|
|
|
|
testCases := []struct {
|
|
|
|
name string
|
|
|
|
input string
|
|
|
|
output string
|
2020-03-12 02:50:59 +00:00
|
|
|
op func(*testing.T, *View)
|
2020-02-28 20:28:10 +00:00
|
|
|
}{
|
2020-03-12 02:50:59 +00:00
|
|
|
// Preconditions.
|
|
|
|
{
|
|
|
|
name: "truncate-check",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello", // Not touched.
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r == nil {
|
|
|
|
t.Errorf("Truncate(-1) did not panic")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
v.Truncate(-1)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "grow-check",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello", // Not touched.
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r == nil {
|
|
|
|
t.Errorf("Grow(-1) did not panic")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
v.Grow(-1, false)
|
|
|
|
},
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "advance-check",
|
|
|
|
input: "hello",
|
|
|
|
output: "", // Consumed.
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r == nil {
|
|
|
|
t.Errorf("advanceRead(Size()+1) did not panic")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
v.advanceRead(v.Size() + 1)
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
2020-03-12 02:50:59 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Prepend.
|
|
|
|
{
|
|
|
|
name: "prepend",
|
|
|
|
input: "world",
|
2020-02-28 20:28:10 +00:00
|
|
|
output: "hello world",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Prepend([]byte("hello "))
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "prepend-backfill-full",
|
|
|
|
input: "hello world",
|
|
|
|
output: "jello world",
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.TrimFront(1)
|
|
|
|
v.Prepend([]byte("j"))
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "prepend-backfill-under",
|
|
|
|
input: "hello world",
|
|
|
|
output: "hola world",
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.TrimFront(5)
|
|
|
|
v.Prepend([]byte("hola"))
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "prepend-backfill-over",
|
|
|
|
input: "hello world",
|
|
|
|
output: "smello world",
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.TrimFront(1)
|
|
|
|
v.Prepend([]byte("sm"))
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
2020-03-12 02:50:59 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "prepend-fill",
|
|
|
|
input: strings.Repeat("1", bufferSize-1),
|
|
|
|
output: "0" + strings.Repeat("1", bufferSize-1),
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Prepend([]byte("0"))
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "prepend-overflow",
|
|
|
|
input: strings.Repeat("1", bufferSize),
|
|
|
|
output: "0" + strings.Repeat("1", bufferSize),
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Prepend([]byte("0"))
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "prepend-multiple-buffers",
|
|
|
|
input: strings.Repeat("1", bufferSize-1),
|
2020-02-28 20:28:10 +00:00
|
|
|
output: strings.Repeat("0", bufferSize*3) + strings.Repeat("1", bufferSize-1),
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Prepend([]byte(strings.Repeat("0", bufferSize*3)))
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
|
2020-03-12 02:50:59 +00:00
|
|
|
// Append and write.
|
2020-02-28 20:28:10 +00:00
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "append",
|
|
|
|
input: "hello",
|
2020-02-28 20:28:10 +00:00
|
|
|
output: "hello world",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Append([]byte(" world"))
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "append-fill",
|
|
|
|
input: strings.Repeat("1", bufferSize-1),
|
2020-02-28 20:28:10 +00:00
|
|
|
output: strings.Repeat("1", bufferSize-1) + "0",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Append([]byte("0"))
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "append-overflow",
|
|
|
|
input: strings.Repeat("1", bufferSize),
|
2020-02-28 20:28:10 +00:00
|
|
|
output: strings.Repeat("1", bufferSize) + "0",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Append([]byte("0"))
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "append-multiple-buffers",
|
|
|
|
input: strings.Repeat("1", bufferSize-1),
|
2020-02-28 20:28:10 +00:00
|
|
|
output: strings.Repeat("1", bufferSize-1) + strings.Repeat("0", bufferSize*3),
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Append([]byte(strings.Repeat("0", bufferSize*3)))
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Truncate.
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "truncate",
|
|
|
|
input: "hello world",
|
2020-02-28 20:28:10 +00:00
|
|
|
output: "hello",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Truncate(5)
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "truncate-noop",
|
|
|
|
input: "hello world",
|
|
|
|
output: "hello world",
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Truncate(v.Size() + 1)
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "truncate-multiple-buffers",
|
|
|
|
input: strings.Repeat("1", bufferSize*2),
|
|
|
|
output: strings.Repeat("1", bufferSize*2-1),
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Truncate(bufferSize*2 - 1)
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
2020-03-12 02:50:59 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "truncate-multiple-buffers-to-one",
|
|
|
|
input: strings.Repeat("1", bufferSize*2),
|
2020-02-28 20:28:10 +00:00
|
|
|
output: "11111",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Truncate(5)
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// TrimFront.
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "trim",
|
|
|
|
input: "hello world",
|
2020-02-28 20:28:10 +00:00
|
|
|
output: "world",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.TrimFront(6)
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "trim-too-large",
|
|
|
|
input: "hello world",
|
|
|
|
output: "",
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.TrimFront(v.Size() + 1)
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "trim-multiple-buffers",
|
|
|
|
input: strings.Repeat("1", bufferSize*2),
|
|
|
|
output: strings.Repeat("1", bufferSize*2-1),
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.TrimFront(1)
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
2020-03-12 02:50:59 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "trim-multiple-buffers-to-one-buffer",
|
|
|
|
input: strings.Repeat("1", bufferSize*2),
|
2020-02-28 20:28:10 +00:00
|
|
|
output: "1",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.TrimFront(bufferSize*2 - 1)
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Grow.
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "grow",
|
|
|
|
input: "hello world",
|
2020-02-28 20:28:10 +00:00
|
|
|
output: "hello world",
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Grow(1, true)
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "grow-from-zero",
|
2020-02-28 20:28:10 +00:00
|
|
|
output: strings.Repeat("\x00", 1024),
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Grow(1024, true)
|
|
|
|
},
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-12 02:50:59 +00:00
|
|
|
name: "grow-from-non-zero",
|
|
|
|
input: strings.Repeat("1", bufferSize),
|
2020-02-28 20:28:10 +00:00
|
|
|
output: strings.Repeat("1", bufferSize) + strings.Repeat("\x00", bufferSize),
|
2020-03-12 02:50:59 +00:00
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
v.Grow(bufferSize*2, true)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// Copy.
|
|
|
|
{
|
|
|
|
name: "copy",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello",
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
other := v.Copy()
|
|
|
|
bs := other.Flatten()
|
|
|
|
want := []byte("hello")
|
|
|
|
if !bytes.Equal(bs, want) {
|
|
|
|
t.Errorf("expected %v, got %v", want, bs)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "copy-large",
|
|
|
|
input: strings.Repeat("1", bufferSize+1),
|
|
|
|
output: strings.Repeat("1", bufferSize+1),
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
other := v.Copy()
|
|
|
|
bs := other.Flatten()
|
|
|
|
want := []byte(strings.Repeat("1", bufferSize+1))
|
|
|
|
if !bytes.Equal(bs, want) {
|
|
|
|
t.Errorf("expected %v, got %v", want, bs)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// Merge.
|
|
|
|
{
|
|
|
|
name: "merge",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello world",
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
var other View
|
|
|
|
other.Append([]byte(" world"))
|
|
|
|
v.Merge(&other)
|
|
|
|
if sz := other.Size(); sz != 0 {
|
|
|
|
t.Errorf("expected 0, got %d", sz)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "merge-large",
|
|
|
|
input: strings.Repeat("1", bufferSize+1),
|
|
|
|
output: strings.Repeat("1", bufferSize+1) + strings.Repeat("0", bufferSize+1),
|
|
|
|
op: func(t *testing.T, v *View) {
|
|
|
|
var other View
|
|
|
|
other.Append([]byte(strings.Repeat("0", bufferSize+1)))
|
|
|
|
v.Merge(&other)
|
|
|
|
if sz := other.Size(); sz != 0 {
|
|
|
|
t.Errorf("expected 0, got %d", sz)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// ReadAt.
|
|
|
|
{
|
|
|
|
name: "readat",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, 0, 6, "hello", io.EOF) },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "readat-long",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, 0, 8, "hello", io.EOF) },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "readat-short",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, 0, 3, "hel", nil) },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "readat-offset",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, 2, 3, "llo", io.EOF) },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "readat-long-offset",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, 2, 8, "llo", io.EOF) },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "readat-short-offset",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, 2, 2, "ll", nil) },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "readat-skip-all",
|
|
|
|
input: "hello",
|
|
|
|
output: "hello",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, bufferSize+1, 1, "", io.EOF) },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "readat-second-buffer",
|
|
|
|
input: strings.Repeat("0", bufferSize+1) + "12",
|
|
|
|
output: strings.Repeat("0", bufferSize+1) + "12",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, bufferSize+1, 1, "1", nil) },
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "readat-second-buffer-end",
|
|
|
|
input: strings.Repeat("0", bufferSize+1) + "12",
|
|
|
|
output: strings.Repeat("0", bufferSize+1) + "12",
|
|
|
|
op: func(t *testing.T, v *View) { testReadAt(t, v, bufferSize+1, 2, "12", io.EOF) },
|
2020-02-28 20:28:10 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
2020-03-12 02:50:59 +00:00
|
|
|
for fillName, fn := range fillFuncs {
|
|
|
|
t.Run(fillName+"/"+tc.name, func(t *testing.T) {
|
|
|
|
// Construct & fill the view.
|
|
|
|
var view View
|
|
|
|
fn(&view, []byte(tc.input))
|
|
|
|
|
|
|
|
// Run the operation.
|
|
|
|
if tc.op != nil {
|
|
|
|
tc.op(t, &view)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flatten and validate.
|
|
|
|
out := view.Flatten()
|
|
|
|
if !bytes.Equal([]byte(tc.output), out) {
|
|
|
|
t.Errorf("expected %q, got %q", tc.output, string(out))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the size is correct.
|
|
|
|
if len(out) != int(view.Size()) {
|
|
|
|
t.Errorf("size is wrong: expected %d, got %d", len(out), view.Size())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate contents via apply.
|
|
|
|
var appliedOut []byte
|
|
|
|
view.Apply(func(b []byte) {
|
|
|
|
appliedOut = append(appliedOut, b...)
|
|
|
|
})
|
|
|
|
if len(appliedOut) != len(out) {
|
|
|
|
t.Errorf("expected %d, got %d", len(out), len(appliedOut))
|
|
|
|
}
|
|
|
|
if !bytes.Equal(appliedOut, out) {
|
|
|
|
t.Errorf("expected %v, got %v", out, appliedOut)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate contents via ReadToWriter.
|
|
|
|
var b bytes.Buffer
|
|
|
|
n, err := view.ReadToWriter(&b, int64(len(out)))
|
|
|
|
if n != int64(len(out)) {
|
|
|
|
t.Errorf("expected %d, got %d", len(out), n)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("expected nil, got %v", err)
|
|
|
|
}
|
|
|
|
if !bytes.Equal(b.Bytes(), out) {
|
|
|
|
t.Errorf("expected %v, got %v", out, b.Bytes())
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2020-02-28 20:28:10 +00:00
|
|
|
}
|
|
|
|
}
|