// Copyright 2022 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" "testing" "github.com/google/go-cmp/cmp" "gvisor.dev/gvisor/pkg/rand" ) func TestNewView(t *testing.T) { for sz := baseChunkSize; sz < MaxChunkSize; sz <<= 1 { v := NewView(sz - 1) defer v.Release() if v.Capacity() != sz { t.Errorf("v.Capacity() = %d, want %d", v.Capacity(), sz) } if v.AvailableSize() != sz { t.Errorf("v.WriteSize() = %d, want %d", v.AvailableSize(), sz) } if v.Size() != 0 { t.Errorf("v.ReadSize() = %d, want %d", v.Size(), 0) } v1 := NewView(sz) defer v1.Release() if v1.Capacity() != sz { t.Errorf("v.Capacity() = %d, want %d", v.Capacity(), sz) } if v1.AvailableSize() != sz { t.Errorf("v.WriteSize() = %d, want %d", v.AvailableSize(), sz) } if v1.Size() != 0 { t.Errorf("v.ReadSize() = %d, want %d", v.Size(), 0) } } // Allocating from heap should produce a chunk with the exact size requested // instead of a chunk where the size is contingent on the pool it came from. viewSize := MaxChunkSize + 1 v := NewView(viewSize) defer v.Release() if v.Capacity() != viewSize { t.Errorf("v.Capacity() = %d, want %d", v.Capacity(), viewSize) } } func TestClone(t *testing.T) { orig := NewView(100) clone := orig.Clone() if orig.chunk != clone.chunk { t.Errorf("orig.Clone().chunk = %p, want %p", clone.chunk, orig.chunk) } if orig.chunk.refCount.Load() != 2 { t.Errorf("got orig.chunk.chunkRefs.Load() = %d, want 2", orig.chunk.refCount.Load()) } orig.Release() if clone.chunk.refCount.Load() != 1 { t.Errorf("got clone.chunk.chunkRefs.Load() = %d, want 1", clone.chunk.refCount.Load()) } clone.Release() } func TestWrite(t *testing.T) { for _, tc := range []struct { name string view *View initSize int writeSize int }{ { name: "empty view", view: NewView(100), writeSize: 50, }, { name: "full view", view: NewView(100), initSize: 100, writeSize: 50, }, { name: "full write to partially full view", view: NewView(100), initSize: 20, writeSize: 50, }, { name: "partial write to partially full view", view: NewView(100), initSize: 80, writeSize: 50, }, } { t.Run(tc.name, func(t *testing.T) { tc.view.Grow(tc.initSize) defer tc.view.Release() orig := append([]byte(nil), tc.view.AsSlice()...) toWrite := make([]byte, tc.writeSize) rand.Read(toWrite) n, err := tc.view.Write(toWrite) if err != nil { t.Errorf("Write failed: %s", err) } if n != tc.writeSize { t.Errorf("got n=%d, want %d", n, tc.writeSize) } if tc.view.Size() != len(orig)+tc.writeSize { t.Errorf("got Size()=%d, want %d", tc.view.Size(), len(orig)+tc.writeSize) } if !cmp.Equal(tc.view.AsSlice(), append(orig, toWrite...)) { t.Errorf("got tc.view.AsSlice() = %d, want %d", tc.view.AsSlice(), toWrite) } }) } } func TestWriteToCloned(t *testing.T) { orig := NewView(100) toWrite := make([]byte, 20) rand.Read(toWrite) orig.Write(toWrite) clone := orig.Clone() clone.Write(toWrite) if !cmp.Equal(orig.AsSlice(), toWrite) { t.Errorf("got orig.ReadSlice() = %v, want %v", orig.AsSlice(), toWrite) } toWrite = append(toWrite, toWrite...) if !cmp.Equal(clone.AsSlice(), toWrite) { t.Errorf("got clone.ReadSlice() = %v, want %v", clone.AsSlice(), toWrite) } } func TestWriteAt(t *testing.T) { size := 10 off := 5 v := NewViewSize(size) p := make([]byte, 20) rand.Read(p) orig := v.Clone() if n, _ := v.WriteAt(p, off); n != size-off { t.Errorf("got v.CopyIn()= %v, want %v", n, size-off) } if !cmp.Equal(v.AsSlice()[off:], p[:size-off]) { t.Errorf("got v.AsSlice()[off:] = %v, want %v", v.AsSlice()[off:], p[off:size]) } if !cmp.Equal(v.AsSlice()[:off], orig.AsSlice()[:off]) { t.Errorf("got v.AsSlice()[:off] = %v, want %v", v.AsSlice()[:off], orig.AsSlice()[:off]) } } func TestWriteTo(t *testing.T) { writeToSize := 100 v := NewViewSize(writeToSize) defer v.Release() w := bytes.NewBuffer(make([]byte, 100)) n, err := v.WriteTo(w) if err != nil { t.Errorf("WriteTo failed: %s", err) } if n != int64(writeToSize) { t.Errorf("got n=%d, want 100", n) } if v.Size() != 0 { t.Errorf("got v.Size()=%d, want 0", v.Size()) } } func TestReadFrom(t *testing.T) { for _, tc := range []struct { name string data []byte view *View }{ { name: "basic", data: []byte{1, 2, 3}, view: NewView(10), }, { name: "requires grow", data: []byte{4, 5, 6}, view: NewViewSize(63), }, } { defer tc.view.Release() clone := tc.view.Clone() defer clone.Release() r := bytes.NewReader(tc.data) n, err := tc.view.ReadFrom(r) if err != nil { t.Errorf("v.ReadFrom failed: %s", err) } if int(n) != len(tc.data) { t.Errorf("v.ReadFrom failed: want n=%d, got %d", len(tc.data), n) } if tc.view.Size() == clone.Size() { t.Errorf("expected clone.Size() != v.Size(), got match") } if !bytes.Equal(tc.view.AsSlice(), append(clone.AsSlice(), tc.data...)) { t.Errorf("v.ReadFrom failed: want %v, got %v", tc.data, tc.view.AsSlice()) } } }