tendermint/autofile/group_test.go

403 lines
8.9 KiB
Go

package autofile
import (
"errors"
"io"
"io/ioutil"
"os"
"strconv"
"strings"
"testing"
. "github.com/tendermint/tmlibs/common"
)
// NOTE: Returned group has ticker stopped
func createTestGroup(t *testing.T, headSizeLimit int64) *Group {
testID := RandStr(12)
testDir := "_test_" + testID
err := EnsureDir(testDir, 0700)
if err != nil {
t.Fatal("Error creating dir", err)
}
headPath := testDir + "/myfile"
g, err := OpenGroup(headPath)
if err != nil {
t.Fatal("Error opening Group", err)
}
g.SetHeadSizeLimit(headSizeLimit)
g.stopTicker()
if g == nil {
t.Fatal("Failed to create Group")
}
return g
}
func destroyTestGroup(t *testing.T, g *Group) {
err := os.RemoveAll(g.Dir)
if err != nil {
t.Fatal("Error removing test Group directory", err)
}
}
func assertGroupInfo(t *testing.T, gInfo GroupInfo, minIndex, maxIndex int, totalSize, headSize int64) {
if gInfo.MinIndex != minIndex {
t.Errorf("GroupInfo MinIndex expected %v, got %v", minIndex, gInfo.MinIndex)
}
if gInfo.MaxIndex != maxIndex {
t.Errorf("GroupInfo MaxIndex expected %v, got %v", maxIndex, gInfo.MaxIndex)
}
if gInfo.TotalSize != totalSize {
t.Errorf("GroupInfo TotalSize expected %v, got %v", totalSize, gInfo.TotalSize)
}
if gInfo.HeadSize != headSize {
t.Errorf("GroupInfo HeadSize expected %v, got %v", headSize, gInfo.HeadSize)
}
}
func TestCheckHeadSizeLimit(t *testing.T) {
g := createTestGroup(t, 1000*1000)
// At first, there are no files.
assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 0, 0)
// Write 1000 bytes 999 times.
for i := 0; i < 999; i++ {
err := g.WriteLine(RandStr(999))
if err != nil {
t.Fatal("Error appending to head", err)
}
}
g.Flush()
assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000)
// Even calling checkHeadSizeLimit manually won't rotate it.
g.checkHeadSizeLimit()
assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000)
// Write 1000 more bytes.
if err := g.WriteLine(RandStr(999)); err != nil {
t.Fatal("Error appending to head", err)
}
g.Flush()
// Calling checkHeadSizeLimit this time rolls it.
g.checkHeadSizeLimit()
assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1000000, 0)
// Write 1000 more bytes.
if err := g.WriteLine(RandStr(999)); err != nil {
t.Fatal("Error appending to head", err)
}
g.Flush()
// Calling checkHeadSizeLimit does nothing.
g.checkHeadSizeLimit()
assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1001000, 1000)
// Write 1000 bytes 999 times.
for i := 0; i < 999; i++ {
if err := g.WriteLine(RandStr(999)); err != nil {
t.Fatal("Error appending to head", err)
}
}
g.Flush()
assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 2000000, 1000000)
// Calling checkHeadSizeLimit rolls it again.
g.checkHeadSizeLimit()
assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2000000, 0)
// Write 1000 more bytes.
_, err := g.Head.Write([]byte(RandStr(999) + "\n"))
if err != nil {
t.Fatal("Error appending to head", err)
}
g.Flush()
assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000)
// Calling checkHeadSizeLimit does nothing.
g.checkHeadSizeLimit()
assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000)
// Cleanup
destroyTestGroup(t, g)
}
func TestSearch(t *testing.T) {
g := createTestGroup(t, 10*1000)
// Create some files in the group that have several INFO lines in them.
// Try to put the INFO lines in various spots.
for i := 0; i < 100; i++ {
// The random junk at the end ensures that this INFO linen
// is equally likely to show up at the end.
_, err := g.Head.Write([]byte(Fmt("INFO %v %v\n", i, RandStr(123))))
if err != nil {
t.Error("Failed to write to head")
}
g.checkHeadSizeLimit()
for j := 0; j < 10; j++ {
_, err := g.Head.Write([]byte(RandStr(123) + "\n"))
if err != nil {
t.Error("Failed to write to head")
}
g.checkHeadSizeLimit()
}
}
// Create a search func that searches for line
makeSearchFunc := func(target int) SearchFunc {
return func(line string) (int, error) {
parts := strings.Split(line, " ")
if len(parts) != 3 {
return -1, errors.New("Line did not have 3 parts")
}
i, err := strconv.Atoi(parts[1])
if err != nil {
return -1, errors.New("Failed to parse INFO: " + err.Error())
}
if target < i {
return 1, nil
} else if target == i {
return 0, nil
} else {
return -1, nil
}
}
}
// Now search for each number
for i := 0; i < 100; i++ {
t.Log("Testing for i", i)
gr, match, err := g.Search("INFO", makeSearchFunc(i))
if err != nil {
t.Fatal("Failed to search for line:", err)
}
if !match {
t.Error("Expected Search to return exact match")
}
line, err := gr.ReadLine()
if err != nil {
t.Fatal("Failed to read line after search", err)
}
if !strings.HasPrefix(line, Fmt("INFO %v ", i)) {
t.Fatal("Failed to get correct line")
}
// Make sure we can continue to read from there.
cur := i + 1
for {
line, err := gr.ReadLine()
if err == io.EOF {
if cur == 99+1 {
// OK!
break
} else {
t.Fatal("Got EOF after the wrong INFO #")
}
} else if err != nil {
t.Fatal("Error reading line", err)
}
if !strings.HasPrefix(line, "INFO ") {
continue
}
if !strings.HasPrefix(line, Fmt("INFO %v ", cur)) {
t.Fatalf("Unexpected INFO #. Expected %v got:\n%v", cur, line)
}
cur += 1
}
gr.Close()
}
// Now search for something that is too small.
// We should get the first available line.
{
gr, match, err := g.Search("INFO", makeSearchFunc(-999))
if err != nil {
t.Fatal("Failed to search for line:", err)
}
if match {
t.Error("Expected Search to not return exact match")
}
line, err := gr.ReadLine()
if err != nil {
t.Fatal("Failed to read line after search", err)
}
if !strings.HasPrefix(line, "INFO 0 ") {
t.Error("Failed to fetch correct line, which is the earliest INFO")
}
err = gr.Close()
if err != nil {
t.Error("Failed to close GroupReader", err)
}
}
// Now search for something that is too large.
// We should get an EOF error.
{
gr, _, err := g.Search("INFO", makeSearchFunc(999))
if err != io.EOF {
t.Error("Expected to get an EOF error")
}
if gr != nil {
t.Error("Expected to get nil GroupReader")
}
}
// Cleanup
destroyTestGroup(t, g)
}
func TestRotateFile(t *testing.T) {
g := createTestGroup(t, 0)
g.WriteLine("Line 1")
g.WriteLine("Line 2")
g.WriteLine("Line 3")
g.Flush()
g.RotateFile()
g.WriteLine("Line 4")
g.WriteLine("Line 5")
g.WriteLine("Line 6")
g.Flush()
// Read g.Head.Path+"000"
body1, err := ioutil.ReadFile(g.Head.Path + ".000")
if err != nil {
t.Error("Failed to read first rolled file")
}
if string(body1) != "Line 1\nLine 2\nLine 3\n" {
t.Errorf("Got unexpected contents: [%v]", string(body1))
}
// Read g.Head.Path
body2, err := ioutil.ReadFile(g.Head.Path)
if err != nil {
t.Error("Failed to read first rolled file")
}
if string(body2) != "Line 4\nLine 5\nLine 6\n" {
t.Errorf("Got unexpected contents: [%v]", string(body2))
}
// Cleanup
destroyTestGroup(t, g)
}
func TestFindLast1(t *testing.T) {
g := createTestGroup(t, 0)
g.WriteLine("Line 1")
g.WriteLine("Line 2")
g.WriteLine("# a")
g.WriteLine("Line 3")
g.Flush()
g.RotateFile()
g.WriteLine("Line 4")
g.WriteLine("Line 5")
g.WriteLine("Line 6")
g.WriteLine("# b")
g.Flush()
match, found, err := g.FindLast("#")
if err != nil {
t.Error("Unexpected error", err)
}
if !found {
t.Error("Expected found=True")
}
if match != "# b" {
t.Errorf("Unexpected match: [%v]", match)
}
// Cleanup
destroyTestGroup(t, g)
}
func TestFindLast2(t *testing.T) {
g := createTestGroup(t, 0)
g.WriteLine("Line 1")
g.WriteLine("Line 2")
g.WriteLine("Line 3")
g.Flush()
g.RotateFile()
g.WriteLine("# a")
g.WriteLine("Line 4")
g.WriteLine("Line 5")
g.WriteLine("# b")
g.WriteLine("Line 6")
g.Flush()
match, found, err := g.FindLast("#")
if err != nil {
t.Error("Unexpected error", err)
}
if !found {
t.Error("Expected found=True")
}
if match != "# b" {
t.Errorf("Unexpected match: [%v]", match)
}
// Cleanup
destroyTestGroup(t, g)
}
func TestFindLast3(t *testing.T) {
g := createTestGroup(t, 0)
g.WriteLine("Line 1")
g.WriteLine("# a")
g.WriteLine("Line 2")
g.WriteLine("# b")
g.WriteLine("Line 3")
g.Flush()
g.RotateFile()
g.WriteLine("Line 4")
g.WriteLine("Line 5")
g.WriteLine("Line 6")
g.Flush()
match, found, err := g.FindLast("#")
if err != nil {
t.Error("Unexpected error", err)
}
if !found {
t.Error("Expected found=True")
}
if match != "# b" {
t.Errorf("Unexpected match: [%v]", match)
}
// Cleanup
destroyTestGroup(t, g)
}
func TestFindLast4(t *testing.T) {
g := createTestGroup(t, 0)
g.WriteLine("Line 1")
g.WriteLine("Line 2")
g.WriteLine("Line 3")
g.Flush()
g.RotateFile()
g.WriteLine("Line 4")
g.WriteLine("Line 5")
g.WriteLine("Line 6")
g.Flush()
match, found, err := g.FindLast("#")
if err != nil {
t.Error("Unexpected error", err)
}
if found {
t.Error("Expected found=False")
}
if match != "" {
t.Errorf("Unexpected match: [%v]", match)
}
// Cleanup
destroyTestGroup(t, g)
}