Files

220 lines
5.6 KiB
Go

// Copyright The OpenTelemetry Authors
// Copyright 2021 gRPC authors.
// SPDX-License-Identifier: Apache-2.0
package zstd
import (
"bytes"
"errors"
"io"
"sync"
"testing"
kzstd "github.com/klauspost/compress/zstd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/encoding"
)
func TestRegisteredCompression(t *testing.T) {
for _, tt := range []struct {
name string
payload []byte
}{
{name: "empty", payload: nil},
{name: "small", payload: []byte("message request zstd")},
{name: "large", payload: bytes.Repeat([]byte("zstd payload "), 4096)},
} {
t.Run(tt.name, func(t *testing.T) {
comp := &compressor{}
got := roundTripCompression(t, comp, tt.payload)
if len(tt.payload) == 0 {
assert.Empty(t, got)
return
}
assert.Equal(t, tt.payload, got)
})
}
}
func TestInitRegistration(t *testing.T) {
comp := encoding.GetCompressor(Name)
require.NotNil(t, comp)
assert.Equal(t, Name, comp.Name())
}
func TestRegisterCompressor(t *testing.T) {
t.Run("registers when missing", func(t *testing.T) {
var registered encoding.Compressor
ok := registerCompressor(
func(string) encoding.Compressor { return nil },
func(c encoding.Compressor) { registered = c },
)
require.True(t, ok)
require.NotNil(t, registered)
assert.Equal(t, Name, registered.Name())
})
t.Run("skips when already registered", func(t *testing.T) {
var called bool
ok := registerCompressor(
func(string) encoding.Compressor { return &compressor{} },
func(encoding.Compressor) { called = true },
)
require.False(t, ok)
assert.False(t, called)
})
}
func TestCompressReusesPooledEncoder(t *testing.T) {
c := &compressor{}
seed, err := kzstd.NewWriter(io.Discard, encoderOptions...)
require.NoError(t, err)
c.encoderPool.New = func() any { return seed }
old := zstdEncoderReset
t.Cleanup(func() { zstdEncoderReset = old })
var resetTarget io.Writer
zstdEncoderReset = func(enc *kzstd.Encoder, w io.Writer) {
assert.Same(t, seed, enc)
resetTarget = w
enc.Reset(w)
}
var buf bytes.Buffer
w, err := c.Compress(&buf)
require.NoError(t, err)
_, ok := w.(*encoderWrapper)
require.True(t, ok)
assert.Same(t, &buf, resetTarget)
_, err = w.Write([]byte("zstd pool check"))
require.NoError(t, err)
require.NoError(t, w.Close())
}
func TestDecompressReusesPooledDecoder(t *testing.T) {
c := &compressor{}
seed, err := kzstd.NewReader(bytes.NewReader(nil), decoderOptions...)
require.NoError(t, err)
c.decoderPool.New = func() any { return seed }
compressed := compressPayload(t, c, []byte("zstd decode pool check"))
r, err := c.Decompress(bytes.NewReader(compressed))
require.NoError(t, err)
got, ok := r.(*decoderWrapper)
require.True(t, ok)
assert.Same(t, seed, got.Decoder)
out, err := io.ReadAll(got)
require.NoError(t, err)
assert.Equal(t, []byte("zstd decode pool check"), out)
}
func TestCompressReturnsEncoderCreationError(t *testing.T) {
old := zstdNewWriter
t.Cleanup(func() { zstdNewWriter = old })
zstdNewWriter = func(io.Writer, ...kzstd.EOption) (*kzstd.Encoder, error) {
return nil, errors.New("new writer failed")
}
_, err := (&compressor{}).Compress(io.Discard)
require.EqualError(t, err, "new writer failed")
}
func TestEncoderWrapperFinalizeReturnsEncoderToPool(t *testing.T) {
enc, err := kzstd.NewWriter(io.Discard, encoderOptions...)
require.NoError(t, err)
var resetTarget io.Writer
old := zstdEncoderReset
t.Cleanup(func() { zstdEncoderReset = old })
zstdEncoderReset = func(got *kzstd.Encoder, w io.Writer) {
assert.Same(t, enc, got)
resetTarget = w
got.Reset(w)
}
w := &encoderWrapper{
Encoder: enc,
pool: &sync.Pool{},
}
w.finalize()
assert.Nil(t, resetTarget)
}
func TestDecompressReturnsDecoderCreationError(t *testing.T) {
old := zstdNewReader
t.Cleanup(func() { zstdNewReader = old })
zstdNewReader = func(io.Reader, ...kzstd.DOption) (*kzstd.Decoder, error) {
return nil, errors.New("new reader failed")
}
_, err := (&compressor{}).Decompress(bytes.NewReader(nil))
require.EqualError(t, err, "new reader failed")
}
func TestDecompressReturnsDecoderResetError(t *testing.T) {
old := zstdDecoderReset
t.Cleanup(func() { zstdDecoderReset = old })
zstdDecoderReset = func(*kzstd.Decoder, io.Reader) error {
return errors.New("reset failed")
}
c := &compressor{}
seed, err := kzstd.NewReader(bytes.NewReader(nil), decoderOptions...)
require.NoError(t, err)
c.decoderPool.New = func() any { return seed }
_, err = c.Decompress(bytes.NewReader(nil))
require.EqualError(t, err, "reset failed")
}
func TestDecoderWrapperFinalizeReturnsDecoderToPool(t *testing.T) {
dec, err := kzstd.NewReader(bytes.NewReader(nil), decoderOptions...)
require.NoError(t, err)
var resetTarget io.Reader = bytes.NewReader(nil)
old := zstdDecoderReset
t.Cleanup(func() { zstdDecoderReset = old })
zstdDecoderReset = func(got *kzstd.Decoder, r io.Reader) error {
assert.Same(t, dec, got)
resetTarget = r
return got.Reset(r)
}
d := &decoderWrapper{
Decoder: dec,
pool: &sync.Pool{},
}
d.finalize()
assert.Nil(t, resetTarget)
}
func compressPayload(t *testing.T, comp encoding.Compressor, payload []byte) []byte {
t.Helper()
var buf bytes.Buffer
w, err := comp.Compress(&buf)
require.NoError(t, err)
_, err = w.Write(payload)
require.NoError(t, err)
require.NoError(t, w.Close())
return buf.Bytes()
}
func roundTripCompression(t *testing.T, comp encoding.Compressor, payload []byte) []byte {
t.Helper()
compressed := compressPayload(t, comp, payload)
r, err := comp.Decompress(bytes.NewReader(compressed))
require.NoError(t, err)
got, err := io.ReadAll(r)
require.NoError(t, err)
return got
}