fix small bug, 100% test coverage

This commit is contained in:
2025-06-26 19:35:45 +01:00
parent 9c4013d25d
commit ec0d5cceb0
2 changed files with 216 additions and 23 deletions

View File

@@ -27,12 +27,6 @@ func Encode(w io.Writer, v uint64) {
func Decode(r io.Reader) (v uint64, err error) {
x := []byte{0}
// _, _ = r.Read(x)
// if x[0] > 128 {
// v += uint64(x[0] & 127)
// return
// } else {
v += uint64(x[0])
var i uint64
for {
if _, err = r.Read(x); chk.E(err) {
@@ -46,5 +40,4 @@ func Decode(r io.Reader) (v uint64, err error) {
i++
}
}
// }
}

View File

@@ -2,28 +2,228 @@ package varint
import (
"bytes"
"fmt"
"io"
"math"
"strings"
"testing"
"lukechampine.com/frand"
"realy.lol/chk"
)
func TestEncode_Decode(t *testing.T) {
var v uint64
for range 10000000 {
v = uint64(frand.Intn(math.MaxInt64))
buf1 := new(bytes.Buffer)
Encode(buf1, v)
buf2 := bytes.NewBuffer(buf1.Bytes())
u, err := Decode(buf2)
if chk.E(err) {
t.Fatal(err)
}
if u != v {
t.Fatalf("expected %d got %d", v, u)
}
func TestEncode_Decode_Comprehensive(t *testing.T) {
// Test specific edge cases
testCases := []uint64{
0, // minimum value
1, // simple case
127, // max single byte value (before terminal bit)
128, // first two-byte value
255, // max value in first byte + some in second
256, // clean two-byte boundary
16383, // max two-byte value
16384, // first three-byte value
math.MaxUint32, // 32-bit max
math.MaxUint64, // 64-bit max
math.MaxUint64 - 1, // near max
1<<7 - 1, // 127
1<<14 - 1, // 16383
1<<21 - 1, // 2097151
1<<28 - 1, // 268435455
1<<35 - 1, // 34359738367
1<<42 - 1, // 4398046511103
1<<49 - 1, // 562949953421311
1<<56 - 1, // 72057594037927935
}
for _, v := range testCases {
t.Run(fmt.Sprintf("value_%d", v), func(t *testing.T) {
buf := new(bytes.Buffer)
Encode(buf, v)
decoded, err := Decode(bytes.NewBuffer(buf.Bytes()))
if err != nil {
t.Fatalf("Failed to decode %d: %v", v, err)
}
if decoded != v {
t.Fatalf("Value %d: expected %d, got %d", v, v, decoded)
}
})
}
}
func TestEncode_Decode_Random(t *testing.T) {
// Test with random values (reduced from 10M to 100K for faster testing)
for i := 0; i < 100000; i++ {
v := uint64(frand.Intn(math.MaxInt64))
buf := new(bytes.Buffer)
Encode(buf, v)
decoded, err := Decode(bytes.NewBuffer(buf.Bytes()))
if err != nil {
t.Fatalf("Failed to decode %d: %v", v, err)
}
if decoded != v {
t.Fatalf("Value %d: expected %d, got %d", v, v, decoded)
}
}
}
func TestDecode_ErrorConditions(t *testing.T) {
tests := []struct {
name string
reader io.Reader
wantErr bool
errType error
}{
{
name: "empty_reader",
reader: strings.NewReader(""),
wantErr: true,
errType: io.EOF,
},
{
name: "empty_buffer",
reader: &bytes.Buffer{},
wantErr: true,
errType: io.EOF,
},
{
name: "single_byte_incomplete",
reader: bytes.NewBuffer([]byte{0}), // non-terminal byte with no following bytes
wantErr: true,
errType: io.EOF,
},
{
name: "multi_byte_incomplete",
reader: bytes.NewBuffer([]byte{0, 0}), // two non-terminal bytes with no terminal
wantErr: true,
errType: io.EOF,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := Decode(tt.reader)
if !tt.wantErr {
if err != nil {
t.Errorf("Expected no error, got %v", err)
}
} else {
if err == nil {
t.Error("Expected error, got nil")
}
if tt.errType != nil && err != tt.errType {
t.Errorf("Expected error %v, got %v", tt.errType, err)
}
}
})
}
}
func TestEncode_SingleByte(t *testing.T) {
// Test that values 0-127 encode to single bytes
for i := uint64(0); i <= 127; i++ {
buf := new(bytes.Buffer)
Encode(buf, i)
encoded := buf.Bytes()
if len(encoded) != 1 {
t.Errorf("Value %d should encode to 1 byte, got %d bytes: %v", i, len(encoded), encoded)
}
expected := byte(i) | 128 // value with terminal bit set
if encoded[0] != expected {
t.Errorf("Value %d: expected byte %d, got %d", i, expected, encoded[0])
}
}
}
func TestEncode_MultiByte(t *testing.T) {
// Test specific multi-byte cases
tests := []struct {
value uint64
expected []byte
}{
{128, []byte{0, 129}}, // 128 = 0 + 1*128
{129, []byte{1, 129}}, // 129 = 1 + 1*128
{255, []byte{127, 129}}, // 255 = 127 + 1*128
{256, []byte{0, 130}}, // 256 = 0 + 2*128
{16383, []byte{127, 255}}, // 16383 = 127 + 127*128
{16384, []byte{0, 0, 129}}, // 16384 = 0 + 0*128 + 1*128*128
}
for _, tt := range tests {
t.Run(fmt.Sprintf("value_%d", tt.value), func(t *testing.T) {
buf := new(bytes.Buffer)
Encode(buf, tt.value)
encoded := buf.Bytes()
if !bytes.Equal(encoded, tt.expected) {
t.Errorf("Value %d: expected %v, got %v", tt.value, tt.expected, encoded)
}
})
}
}
func TestDecode_ValidInputs(t *testing.T) {
tests := []struct {
name string
input []byte
expected uint64
}{
{"single_byte_zero", []byte{128}, 0},
{"single_byte_one", []byte{129}, 1},
{"single_byte_max", []byte{255}, 127},
{"two_byte_128", []byte{0, 129}, 128},
{"two_byte_255", []byte{127, 129}, 255},
{"three_byte_16384", []byte{0, 0, 129}, 16384},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
decoded, err := Decode(bytes.NewBuffer(tt.input))
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if decoded != tt.expected {
t.Errorf("Expected %d, got %d", tt.expected, decoded)
}
})
}
}
func TestEncode_WriteError(t *testing.T) {
// Test with a writer that always fails
failWriter := &failingWriter{}
// This should not panic even if write fails
// The current implementation ignores write errors with _, _
Encode(failWriter, 123)
}
// Helper type for testing write failures
type failingWriter struct{}
func (fw *failingWriter) Write(p []byte) (n int, err error) {
return 0, io.ErrShortWrite
}
func BenchmarkEncode(b *testing.B) {
buf := new(bytes.Buffer)
for i := 0; i < b.N; i++ {
buf.Reset()
Encode(buf, uint64(i))
}
}
func BenchmarkDecode(b *testing.B) {
// Pre-encode a value
buf := new(bytes.Buffer)
Encode(buf, 12345)
encoded := buf.Bytes()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = Decode(bytes.NewBuffer(encoded))
}
}