[v4] fix out-of-bounds behavior in seeker buffer and add tests (#219)
* add check negative position to Read() and write tests * add tests for Write() method * add tests for Write() method * add checks of whence and negative position to Seek() and write tests * add tests for Rewind() * add tests for Close() * add tests for Reset() * add tests for Len() * add tests for Bytes() * tests polishing * tests polishing * tests polishing * tests polishing
This commit is contained in:
@@ -80,7 +80,7 @@ func TestTime(t *testing.T) {
|
|||||||
WithHandlerFunc(slog.NewTextHandler),
|
WithHandlerFunc(slog.NewTextHandler),
|
||||||
logger.WithAddStacktrace(true),
|
logger.WithAddStacktrace(true),
|
||||||
logger.WithTimeFunc(func() time.Time {
|
logger.WithTimeFunc(func() time.Time {
|
||||||
return time.Unix(0, 0)
|
return time.Unix(0, 0).UTC()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
if err := l.Init(logger.WithFields("key1", "val1")); err != nil {
|
if err := l.Init(logger.WithFields("key1", "val1")); err != nil {
|
||||||
@@ -89,8 +89,7 @@ func TestTime(t *testing.T) {
|
|||||||
|
|
||||||
l.Error(ctx, "msg1", errors.New("err"))
|
l.Error(ctx, "msg1", errors.New("err"))
|
||||||
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`timestamp=1970-01-01T03:00:00.000000000+03:00`)) &&
|
if !bytes.Contains(buf.Bytes(), []byte(`timestamp=1970-01-01T00:00:00.000000000Z`)) {
|
||||||
!bytes.Contains(buf.Bytes(), []byte(`timestamp=1970-01-01T00:00:00.000000000Z`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,7 +26,8 @@ func TestHook(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Exists(context.TODO(), "test"); err != nil {
|
err := s.Exists(context.TODO(), "test")
|
||||||
|
if !errors.Is(err, ErrNotFound) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,13 +1,16 @@
|
|||||||
package buffer
|
package buffer
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
io.ReadCloser
|
io.ReadCloser
|
||||||
io.ReadSeeker
|
io.ReadSeeker
|
||||||
} = (*SeekerBuffer)(nil)
|
} = (*SeekerBuffer)(nil)
|
||||||
|
|
||||||
// Buffer is a ReadWriteCloser that supports seeking. It's intended to
|
// SeekerBuffer is a ReadWriteCloser that supports seeking. It's intended to
|
||||||
// replicate the functionality of bytes.Buffer that I use in my projects.
|
// replicate the functionality of bytes.Buffer that I use in my projects.
|
||||||
//
|
//
|
||||||
// Note that the seeking is limited to the read marker; all writes are
|
// Note that the seeking is limited to the read marker; all writes are
|
||||||
@@ -23,6 +26,7 @@ func NewSeekerBuffer(data []byte) *SeekerBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read reads up to len(p) bytes into p from the current read position.
|
||||||
func (b *SeekerBuffer) Read(p []byte) (int, error) {
|
func (b *SeekerBuffer) Read(p []byte) (int, error) {
|
||||||
if b.pos >= int64(len(b.data)) {
|
if b.pos >= int64(len(b.data)) {
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
@@ -30,29 +34,51 @@ func (b *SeekerBuffer) Read(p []byte) (int, error) {
|
|||||||
|
|
||||||
n := copy(p, b.data[b.pos:])
|
n := copy(p, b.data[b.pos:])
|
||||||
b.pos += int64(n)
|
b.pos += int64(n)
|
||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write appends the contents of p to the end of the buffer. It does not affect the read position.
|
||||||
func (b *SeekerBuffer) Write(p []byte) (int, error) {
|
func (b *SeekerBuffer) Write(p []byte) (int, error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
b.data = append(b.data, p...)
|
b.data = append(b.data, p...)
|
||||||
|
|
||||||
return len(p), nil
|
return len(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek sets the read pointer to pos.
|
// Seek sets the offset for the next Read operation.
|
||||||
|
// The offset is interpreted according to whence:
|
||||||
|
// - io.SeekStart: relative to the beginning of the buffer
|
||||||
|
// - io.SeekCurrent: relative to the current position
|
||||||
|
// - io.SeekEnd: relative to the end of the buffer
|
||||||
|
//
|
||||||
|
// Returns an error if the resulting position is negative or if whence is invalid.
|
||||||
func (b *SeekerBuffer) Seek(offset int64, whence int) (int64, error) {
|
func (b *SeekerBuffer) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
var newPos int64
|
||||||
|
|
||||||
switch whence {
|
switch whence {
|
||||||
case io.SeekStart:
|
case io.SeekStart:
|
||||||
b.pos = offset
|
newPos = offset
|
||||||
case io.SeekEnd:
|
case io.SeekEnd:
|
||||||
b.pos = int64(len(b.data)) + offset
|
newPos = int64(len(b.data)) + offset
|
||||||
case io.SeekCurrent:
|
case io.SeekCurrent:
|
||||||
b.pos += offset
|
newPos = b.pos + offset
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("invalid whence: %d", whence)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if newPos < 0 {
|
||||||
|
return 0, fmt.Errorf("invalid seek: resulting position %d is negative", newPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.pos = newPos
|
||||||
return b.pos, nil
|
return b.pos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewind resets the read pointer to 0.
|
// Rewind resets the read position to 0.
|
||||||
func (b *SeekerBuffer) Rewind() error {
|
func (b *SeekerBuffer) Rewind() error {
|
||||||
if _, err := b.Seek(0, io.SeekStart); err != nil {
|
if _, err := b.Seek(0, io.SeekStart); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -75,10 +101,16 @@ func (b *SeekerBuffer) Reset() {
|
|||||||
|
|
||||||
// Len returns the length of data remaining to be read.
|
// Len returns the length of data remaining to be read.
|
||||||
func (b *SeekerBuffer) Len() int {
|
func (b *SeekerBuffer) Len() int {
|
||||||
|
if b.pos >= int64(len(b.data)) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
return len(b.data[b.pos:])
|
return len(b.data[b.pos:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes returns the underlying bytes from the current position.
|
// Bytes returns the underlying bytes from the current position.
|
||||||
func (b *SeekerBuffer) Bytes() []byte {
|
func (b *SeekerBuffer) Bytes() []byte {
|
||||||
|
if b.pos >= int64(len(b.data)) {
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
return b.data[b.pos:]
|
return b.data[b.pos:]
|
||||||
}
|
}
|
||||||
|
@@ -2,54 +2,384 @@ package buffer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func noErrorT(t *testing.T, err error) {
|
func TestNewSeekerBuffer(t *testing.T) {
|
||||||
if nil != err {
|
input := []byte{'a', 'b', 'c', 'd', 'e'}
|
||||||
t.Fatalf("%s", err)
|
expected := &SeekerBuffer{data: []byte{'a', 'b', 'c', 'd', 'e'}, pos: 0}
|
||||||
|
require.Equal(t, expected, NewSeekerBuffer(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeekerBuffer_Read(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
initPos int64
|
||||||
|
readBuf []byte
|
||||||
|
expectedN int
|
||||||
|
expectedData []byte
|
||||||
|
expectedErr error
|
||||||
|
expectedPos int64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "read with empty buffer",
|
||||||
|
data: []byte("hello"),
|
||||||
|
initPos: 0,
|
||||||
|
readBuf: []byte{},
|
||||||
|
expectedN: 0,
|
||||||
|
expectedData: []byte{},
|
||||||
|
expectedErr: nil,
|
||||||
|
expectedPos: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "read with nil buffer",
|
||||||
|
data: []byte("hello"),
|
||||||
|
initPos: 0,
|
||||||
|
readBuf: nil,
|
||||||
|
expectedN: 0,
|
||||||
|
expectedData: nil,
|
||||||
|
expectedErr: nil,
|
||||||
|
expectedPos: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "read full buffer",
|
||||||
|
data: []byte("hello"),
|
||||||
|
initPos: 0,
|
||||||
|
readBuf: make([]byte, 5),
|
||||||
|
expectedN: 5,
|
||||||
|
expectedData: []byte("hello"),
|
||||||
|
expectedErr: nil,
|
||||||
|
expectedPos: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "read partial buffer",
|
||||||
|
data: []byte("hello"),
|
||||||
|
initPos: 2,
|
||||||
|
readBuf: make([]byte, 2),
|
||||||
|
expectedN: 2,
|
||||||
|
expectedData: []byte("ll"),
|
||||||
|
expectedErr: nil,
|
||||||
|
expectedPos: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "read after end",
|
||||||
|
data: []byte("hello"),
|
||||||
|
initPos: 5,
|
||||||
|
readBuf: make([]byte, 5),
|
||||||
|
expectedN: 0,
|
||||||
|
expectedData: make([]byte, 5),
|
||||||
|
expectedErr: io.EOF,
|
||||||
|
expectedPos: 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
sb := NewSeekerBuffer(tt.data)
|
||||||
|
sb.pos = tt.initPos
|
||||||
|
|
||||||
|
n, err := sb.Read(tt.readBuf)
|
||||||
|
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
require.Equal(t, err, tt.expectedErr)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, tt.expectedN, n)
|
||||||
|
require.Equal(t, tt.expectedData, tt.readBuf)
|
||||||
|
require.Equal(t, tt.expectedPos, sb.pos)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func boolT(t *testing.T, cond bool, s ...string) {
|
func TestSeekerBuffer_Write(t *testing.T) {
|
||||||
if !cond {
|
tests := []struct {
|
||||||
what := strings.Join(s, ", ")
|
name string
|
||||||
if len(what) > 0 {
|
initialData []byte
|
||||||
what = ": " + what
|
initialPos int64
|
||||||
|
writeData []byte
|
||||||
|
expectedData []byte
|
||||||
|
expectedN int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "write empty slice",
|
||||||
|
initialData: []byte("data"),
|
||||||
|
initialPos: 0,
|
||||||
|
writeData: []byte{},
|
||||||
|
expectedData: []byte("data"),
|
||||||
|
expectedN: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "write nil slice",
|
||||||
|
initialData: []byte("data"),
|
||||||
|
initialPos: 0,
|
||||||
|
writeData: nil,
|
||||||
|
expectedData: []byte("data"),
|
||||||
|
expectedN: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "write to empty buffer",
|
||||||
|
initialData: nil,
|
||||||
|
initialPos: 0,
|
||||||
|
writeData: []byte("abc"),
|
||||||
|
expectedData: []byte("abc"),
|
||||||
|
expectedN: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "write to existing buffer",
|
||||||
|
initialData: []byte("hello"),
|
||||||
|
initialPos: 0,
|
||||||
|
writeData: []byte(" world"),
|
||||||
|
expectedData: []byte("hello world"),
|
||||||
|
expectedN: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "write after read",
|
||||||
|
initialData: []byte("abc"),
|
||||||
|
initialPos: 2,
|
||||||
|
writeData: []byte("XYZ"),
|
||||||
|
expectedData: []byte("abcXYZ"),
|
||||||
|
expectedN: 3,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
t.Fatalf("assert.Bool failed%s", what)
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
sb := NewSeekerBuffer(tt.initialData)
|
||||||
|
sb.pos = tt.initialPos
|
||||||
|
|
||||||
|
n, err := sb.Write(tt.writeData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tt.expectedN, n)
|
||||||
|
require.Equal(t, tt.expectedData, sb.data)
|
||||||
|
require.Equal(t, tt.initialPos, sb.pos)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSeeking(t *testing.T) {
|
func TestSeekerBuffer_Seek(t *testing.T) {
|
||||||
partA := []byte("hello, ")
|
tests := []struct {
|
||||||
partB := []byte("world!")
|
name string
|
||||||
|
initialData []byte
|
||||||
|
initialPos int64
|
||||||
|
offset int64
|
||||||
|
whence int
|
||||||
|
expectedPos int64
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "seek with invalid whence",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 0,
|
||||||
|
offset: 1,
|
||||||
|
whence: 12345,
|
||||||
|
expectedPos: 0,
|
||||||
|
expectedErr: fmt.Errorf("invalid whence: %d", 12345),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "seek negative from start",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 0,
|
||||||
|
offset: -1,
|
||||||
|
whence: io.SeekStart,
|
||||||
|
expectedPos: 0,
|
||||||
|
expectedErr: fmt.Errorf("invalid seek: resulting position %d is negative", -1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "seek from start to 0",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 0,
|
||||||
|
offset: 0,
|
||||||
|
whence: io.SeekStart,
|
||||||
|
expectedPos: 0,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "seek from start to 3",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 0,
|
||||||
|
offset: 3,
|
||||||
|
whence: io.SeekStart,
|
||||||
|
expectedPos: 3,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "seek from end to -1 (last byte)",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 0,
|
||||||
|
offset: -1,
|
||||||
|
whence: io.SeekEnd,
|
||||||
|
expectedPos: 5,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "seek from current forward",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 2,
|
||||||
|
offset: 2,
|
||||||
|
whence: io.SeekCurrent,
|
||||||
|
expectedPos: 4,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "seek from current backward",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 4,
|
||||||
|
offset: -2,
|
||||||
|
whence: io.SeekCurrent,
|
||||||
|
expectedPos: 2,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "seek to end exactly",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 0,
|
||||||
|
offset: 0,
|
||||||
|
whence: io.SeekEnd,
|
||||||
|
expectedPos: 6,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "seek to out of range",
|
||||||
|
initialData: []byte("abcdef"),
|
||||||
|
initialPos: 0,
|
||||||
|
offset: 2,
|
||||||
|
whence: io.SeekEnd,
|
||||||
|
expectedPos: 8,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
buf := NewSeekerBuffer(partA)
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
sb := NewSeekerBuffer(tt.initialData)
|
||||||
|
sb.pos = tt.initialPos
|
||||||
|
|
||||||
boolT(t, buf.Len() == len(partA), fmt.Sprintf("on init: have length %d, want length %d", buf.Len(), len(partA)))
|
newPos, err := sb.Seek(tt.offset, tt.whence)
|
||||||
|
|
||||||
b := make([]byte, 32)
|
if tt.expectedErr != nil {
|
||||||
|
require.Equal(t, tt.expectedErr, err)
|
||||||
n, err := buf.Read(b)
|
} else {
|
||||||
noErrorT(t, err)
|
require.NoError(t, err)
|
||||||
boolT(t, buf.Len() == 0, fmt.Sprintf("after reading 1: have length %d, want length 0", buf.Len()))
|
require.Equal(t, tt.expectedPos, newPos)
|
||||||
boolT(t, n == len(partA), fmt.Sprintf("after reading 2: have length %d, want length %d", n, len(partA)))
|
require.Equal(t, tt.expectedPos, sb.pos)
|
||||||
|
}
|
||||||
n, err = buf.Write(partB)
|
})
|
||||||
noErrorT(t, err)
|
}
|
||||||
boolT(t, n == len(partB), fmt.Sprintf("after writing: have length %d, want length %d", n, len(partB)))
|
}
|
||||||
|
|
||||||
n, err = buf.Read(b)
|
func TestSeekerBuffer_Rewind(t *testing.T) {
|
||||||
noErrorT(t, err)
|
buf := NewSeekerBuffer([]byte("hello world"))
|
||||||
boolT(t, buf.Len() == 0, fmt.Sprintf("after rereading 1: have length %d, want length 0", buf.Len()))
|
buf.pos = 4
|
||||||
boolT(t, n == len(partB), fmt.Sprintf("after rereading 2: have length %d, want length %d", n, len(partB)))
|
|
||||||
|
require.NoError(t, buf.Rewind())
|
||||||
partsLen := len(partA) + len(partB)
|
require.Equal(t, []byte("hello world"), buf.data)
|
||||||
_ = buf.Rewind()
|
require.Equal(t, int64(0), buf.pos)
|
||||||
boolT(t, buf.Len() == partsLen, fmt.Sprintf("after rewinding: have length %d, want length %d", buf.Len(), partsLen))
|
}
|
||||||
|
|
||||||
buf.Close()
|
func TestSeekerBuffer_Close(t *testing.T) {
|
||||||
boolT(t, buf.Len() == 0, fmt.Sprintf("after closing, have length %d, want length 0", buf.Len()))
|
buf := NewSeekerBuffer([]byte("hello world"))
|
||||||
|
buf.pos = 2
|
||||||
|
|
||||||
|
require.NoError(t, buf.Close())
|
||||||
|
require.Nil(t, buf.data)
|
||||||
|
require.Equal(t, int64(0), buf.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeekerBuffer_Reset(t *testing.T) {
|
||||||
|
buf := NewSeekerBuffer([]byte("hello world"))
|
||||||
|
buf.pos = 2
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
require.Nil(t, buf.data)
|
||||||
|
require.Equal(t, int64(0), buf.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeekerBuffer_Len(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
pos int64
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "full buffer",
|
||||||
|
data: []byte("abcde"),
|
||||||
|
pos: 0,
|
||||||
|
expected: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial read",
|
||||||
|
data: []byte("abcde"),
|
||||||
|
pos: 2,
|
||||||
|
expected: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fully read",
|
||||||
|
data: []byte("abcde"),
|
||||||
|
pos: 5,
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pos > len",
|
||||||
|
data: []byte("abcde"),
|
||||||
|
pos: 10,
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
buf := NewSeekerBuffer(tt.data)
|
||||||
|
buf.pos = tt.pos
|
||||||
|
require.Equal(t, tt.expected, buf.Len())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeekerBuffer_Bytes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
pos int64
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "start of buffer",
|
||||||
|
data: []byte("abcde"),
|
||||||
|
pos: 0,
|
||||||
|
expected: []byte("abcde"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "middle of buffer",
|
||||||
|
data: []byte("abcde"),
|
||||||
|
pos: 2,
|
||||||
|
expected: []byte("cde"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "end of buffer",
|
||||||
|
data: []byte("abcde"),
|
||||||
|
pos: 5,
|
||||||
|
expected: []byte{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pos beyond end",
|
||||||
|
data: []byte("abcde"),
|
||||||
|
pos: 10,
|
||||||
|
expected: []byte{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
buf := NewSeekerBuffer(tt.data)
|
||||||
|
buf.pos = tt.pos
|
||||||
|
require.Equal(t, tt.expected, buf.Bytes())
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user