mirror of
https://github.com/mumble-voip/grumble.git
synced 2025-12-19 21:59:59 -08:00
296 lines
5.8 KiB
Go
296 lines
5.8 KiB
Go
// Copyright (c) 2011 The Grumble Authors
|
|
// The use of this source code is goverened by a BSD-style
|
|
// license that can be found in the LICENSE-file.
|
|
|
|
package freezer
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"hash/crc32"
|
|
"io"
|
|
"math"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
)
|
|
|
|
var testValues []proto.Message = []proto.Message{
|
|
&ConfigKeyValuePair{Key: proto.String("Foo")},
|
|
&BanList{Bans: []*Ban{&Ban{Mask: proto.Uint32(32)}}},
|
|
&User{Id: proto.Uint32(0), Name: proto.String("SuperUser")},
|
|
&UserRemove{Id: proto.Uint32(0)},
|
|
&Channel{Id: proto.Uint32(0), Name: proto.String("RootChannel")},
|
|
&ChannelRemove{Id: proto.Uint32(0)},
|
|
}
|
|
|
|
// Generate a byet slice representing an entry in a Tx record
|
|
func genTxValue(kind uint16, val []byte) (chunk []byte, crc32sum uint32, err error) {
|
|
buf := new(bytes.Buffer)
|
|
|
|
err = binary.Write(buf, binary.LittleEndian, kind)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
err = binary.Write(buf, binary.LittleEndian, uint16(len(val)))
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
_, err = buf.Write(val)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
summer := crc32.NewIEEE()
|
|
_, err = summer.Write(val)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return buf.Bytes(), summer.Sum32(), nil
|
|
}
|
|
|
|
// Generate the header of a Tx record
|
|
func genTestCaseHeader(chunk []byte, numops uint32, crc32sum uint32) (r io.Reader, err error) {
|
|
buf := new(bytes.Buffer)
|
|
|
|
err = binary.Write(buf, binary.LittleEndian, uint32(4+4+len(chunk)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = binary.Write(buf, binary.LittleEndian, numops)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = binary.Write(buf, binary.LittleEndian, crc32sum)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, err = buf.Write(chunk)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return buf, nil
|
|
}
|
|
|
|
// Test that the Walker and the Writer agree on the
|
|
// protocol.
|
|
func TestCreation(t *testing.T) {
|
|
l, err := NewLogFile("creation.log")
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
l.Close()
|
|
os.Remove("creation.log")
|
|
}
|
|
|
|
func TestLogging(t *testing.T) {
|
|
l, err := NewLogFile("logging.log")
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer os.Remove("logging.log")
|
|
|
|
for _, val := range testValues {
|
|
err = l.Put(val)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
err = l.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
f, err := os.Open("logging.log")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
walker, err := NewReaderWalker(f)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
i := 0
|
|
for {
|
|
entries, err := walker.Next()
|
|
if err == io.EOF {
|
|
err = f.Close()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
break
|
|
} else if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
if len(entries) != 1 {
|
|
t.Error("> 1 entry in log tx")
|
|
return
|
|
}
|
|
val, ok := entries[0].(proto.Message)
|
|
if !ok {
|
|
t.Fatal("val does not implement proto.Message")
|
|
}
|
|
if !proto.Equal(val, testValues[i]) {
|
|
t.Error("proto message mismatch")
|
|
}
|
|
i += 1
|
|
}
|
|
}
|
|
|
|
// Check that we correctly catch CRC32 mismatches
|
|
func TestCRC32MismatchLog(t *testing.T) {
|
|
chunk, _, err := genTxValue(0xff, []byte{0xff, 0xff, 0xff, 0xff, 0xff})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
buf, err := genTestCaseHeader(chunk, 1, 0xcafebabe)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
walker, err := NewReaderWalker(buf)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
_, err = walker.Next()
|
|
if err != ErrCRC32Mismatch {
|
|
t.Errorf("exepcted CRC32 mismatch, got %v", err)
|
|
}
|
|
_, err = walker.Next()
|
|
if err != io.EOF {
|
|
t.Errorf("expected EOF, got %v", err)
|
|
}
|
|
}
|
|
|
|
// Test that unknown TxGroup values are not attempted to be
|
|
// decoded.
|
|
func TestUnknownTypeDecode(t *testing.T) {
|
|
buf, crc32sum, err := genTxValue(0xfa, []byte{0xfa, 0xfa, 0xfa})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
r, err := genTestCaseHeader(buf, 1, crc32sum)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
walker, err := NewReaderWalker(r)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
entries, err := walker.Next()
|
|
// The bytes above should not decode to anything useful
|
|
// (because they have an unknown type kind)
|
|
if len(entries) != 0 && err != nil {
|
|
t.Errorf("expected empty entries and non-nil err (got %v entries and %v)", len(entries), err)
|
|
}
|
|
_, err = walker.Next()
|
|
if err != io.EOF {
|
|
t.Errorf("expected EOF, got %v", err)
|
|
}
|
|
}
|
|
|
|
// Test a TxRecord with some trailing bytes
|
|
func TestTrailingBytesTxRecord(t *testing.T) {
|
|
buf, _, err := genTxValue(0xfa, []byte{0xff, 0xff, 0xff})
|
|
// Add some trailing bytes to the tx record
|
|
buf = append(buf, byte(0xff))
|
|
buf = append(buf, byte(0xff))
|
|
buf = append(buf, byte(0xff))
|
|
|
|
summer := crc32.NewIEEE()
|
|
_, err = summer.Write(buf)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
crc32sum := summer.Sum32()
|
|
|
|
r, err := genTestCaseHeader(buf, 1, crc32sum)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
walker, err := NewReaderWalker(r)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
_, err = walker.Next()
|
|
if err != ErrRemainingBytesForRecord {
|
|
t.Error(err)
|
|
}
|
|
|
|
_, err = walker.Next()
|
|
if err != io.EOF {
|
|
t.Errorf("expected EOF, got %v", err)
|
|
}
|
|
}
|
|
|
|
// Test that we check for TxRecords that are too big.
|
|
// A TxRecord can hold 255 entries, and each of those can be
|
|
// up to 16KB.
|
|
func TestTooBigTxRecord(t *testing.T) {
|
|
bigValue := make([]byte, math.MaxUint16*math.MaxUint8+4)
|
|
r, err := genTestCaseHeader(bigValue, 1, 0)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
walker, err := NewReaderWalker(r)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
_, err = walker.Next()
|
|
if err != ErrRecordTooBig {
|
|
t.Errorf("expected ErrRecordTooBig, got %v", err)
|
|
}
|
|
}
|
|
|
|
// Test that we correctly enforce the 255 entry limit of TxGroups.
|
|
func TestTxGroupCapacityEnforcement(t *testing.T) {
|
|
l, err := NewLogFile("capacity-enforcement.log")
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer l.Close()
|
|
defer os.Remove("capacity-enforcement.log")
|
|
|
|
tx := l.BeginTx()
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
for i := 0; i <= 255; i++ {
|
|
entry := testValues[i%len(testValues)]
|
|
err = tx.Put(entry)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
entry := testValues[0]
|
|
err = tx.Put(entry)
|
|
if err != ErrTxGroupFull {
|
|
t.Error(err)
|
|
}
|
|
}
|