Skip to content
Snippets Groups Projects
log_test.go 2.53 KiB
Newer Older
ale's avatar
ale committed
package memlog
ale's avatar
ale committed

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"testing"
	"time"

	pb "git.autistici.org/ai3/tools/replds2/proto"
)

type testMap struct {
	m map[string]*pb.Node
}

func (m *testMap) set(node *pb.Node) error {
	m.m[node.Path] = node
	return nil
}

func (m *testMap) dump(f func(*pb.Node) error) error {
	for _, node := range m.m {
		if err := f(node); err != nil {
			return err
		}
	}
	return nil
}

func TestLog(t *testing.T) {
	// Make smaller log files
	maxLogSize = 1024 * 1024

	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(dir)

	m := &testMap{m: make(map[string]*pb.Node)}
	l, err := openLog(dir, m.set, m.dump)
	if err != nil {
		t.Fatal(err)
	}

	// Create a bunch of entries, with repeated updates.
	log.Printf(">>> writing test entries...")
	numEntries := 100000
	maxSz := 5000
	for i := 0; i < numEntries; i++ {
		node := &pb.Node{
			Path:    fmt.Sprintf("/path/to/%08d", (i % maxSz)),
ale's avatar
ale committed
			Version: time.Now().Unix(),
ale's avatar
ale committed
			Data:    []byte("just some random data, blah  blah, blah blah!"),
		}
		if err := l.Write(node); err != nil {
			t.Fatal(err)
		}
		m.set(node) // nolint: errcheck
ale's avatar
ale committed
	}

	// We should be now on log #8.
	if l.curIdx != 8 {
		t.Fatalf("expected to be on log #9, actually #%d", l.curIdx)
	}

	log.Printf(">>> forcing a checkpoint...")
	if err := l.Rotate(true, m.dump); err != nil {
		t.Fatal(err)
	}

	// Ensure that there is a single log file post checkpoint.
	if n := len(listLogfiles(l.path)); n != 1 {
		t.Fatalf("expected just a single log after checkpoint, found %d", n)
	}

	// Now we close the log, and re-open it.
	log.Printf(">>> closing and re-opening log...")
	l.Close()
	m = &testMap{m: make(map[string]*pb.Node)}
	l, err = openLog(dir, m.set, m.dump)
	if err != nil {
		t.Fatal(err)
	}

	testPath := fmt.Sprintf("/path/to/%08d", 10)
	if _, ok := m.m[testPath]; !ok {
ale's avatar
ale committed
		t.Fatalf("expected value not found post reload")
	}

	// Modify the log, close it and reopen it, expecting it to be
	// dirty, and re-checkpointing on open.
	log.Printf(">>> writing new data, closing and re-opening log...")
	err = l.Write(&pb.Node{
ale's avatar
ale committed
		Path:    testPath,
ale's avatar
ale committed
		Version: time.Now().Unix(),
ale's avatar
ale committed
		Data:    []byte("new data"),
	})
	if err != nil {
		t.Fatal(err)
	}
ale's avatar
ale committed

	l.Close()
	m = &testMap{m: make(map[string]*pb.Node)}
	l, err = openLog(dir, m.set, m.dump)
	if err != nil {
		t.Fatal(err)
	}
	value, ok := m.m[testPath]
ale's avatar
ale committed
	if !ok {
		t.Fatal("expected value not found post reload")
	}
	if s := string(value.Data); s != "new data" {
		t.Fatalf("data of %s is wrong post-reload: %s", testPath, s)
	}

	l.Close()
}