blob: 60bf0efb90e48729cf3ad7dea1c59c17c0743d32 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package util_test
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"testing"
"v.io/v23/services/syncbase"
"v.io/v23/verror"
"v.io/x/ref/services/syncbase/store/util"
)
func makeRootDir(t *testing.T) string {
rootDir, err := ioutil.TempDir("", "syncbase_leveldb")
if err != nil {
t.Fatalf("can't create temp dir: %v", err)
}
return rootDir
}
func TestOpenNoCreate(t *testing.T) {
rootDir := makeRootDir(t)
_, err := util.OpenStore("leveldb", rootDir, util.OpenOptions{
CreateIfMissing: false,
ErrorIfExists: false,
})
if err == nil {
t.Fatalf("expected error opening nonexistent leveldb")
}
}
func TestOpenDestroy(t *testing.T) {
rootDir := makeRootDir(t)
st, err := util.OpenStore("leveldb", rootDir, util.OpenOptions{
CreateIfMissing: true,
ErrorIfExists: true,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := st.Close(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := util.DestroyStore("leveldb", rootDir); err != nil {
t.Fatalf("unexpected error: %v", err)
}
files, err := ioutil.ReadDir(rootDir)
// ReadDir() returns an error if the directory does not exist.
if err == nil && len(files) != 0 {
t.Fatalf("unexpected files in rootDir: %v", files)
}
// Trying to reopen the leveldb should fail.
_, err = util.OpenStore("leveldb", rootDir, util.OpenOptions{
CreateIfMissing: false,
ErrorIfExists: false,
})
if err == nil {
t.Fatalf("expected error opening nonexistent leveldb")
}
}
func TestCorruptStore(t *testing.T) {
rootDir := makeRootDir(t)
// Open the store, put some data, close.
st, err := util.OpenStore("leveldb", rootDir, util.OpenOptions{
CreateIfMissing: true,
ErrorIfExists: true,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := st.Put([]byte("the key"), []byte("the value")); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := st.Close(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Corrupt a log file.
var fileToCorrupt string
filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
if fileToCorrupt != "" {
return nil
}
if match, _ := regexp.MatchString(`.*\.log$`, path); match {
fileToCorrupt = path
return errors.New("found match, stop walking")
}
return nil
})
if fileToCorrupt == "" {
t.Fatalf("Could not find file")
}
fileBytes, err := ioutil.ReadFile(fileToCorrupt)
if err != nil {
t.Fatalf("Could not read log file: %v", err)
}
// Overwrite last 20 bytes.
offset := len(fileBytes) - 20 - 1
if offset < 0 {
t.Fatalf("Expected bigger log file. Found: %d", len(fileBytes))
}
for i := 0; i < 20; i++ {
fileBytes[i+offset] = 0x80
}
if err := ioutil.WriteFile(fileToCorrupt, fileBytes, 0); err != nil {
t.Fatalf("Could not corrupt file: %v", err)
}
// The leveldb should fail to open since it is corrupt.
_, err = util.OpenStore("leveldb", rootDir, util.OpenOptions{
CreateIfMissing: false,
ErrorIfExists: false,
})
if verror.ErrorID(err) != syncbase.ErrCorruptDatabase.ID {
t.Fatalf("wrong error opening corrupt leveldb: %v", err)
}
// Look for the leveldb directory to be moved aside.
matches, err := filepath.Glob(rootDir + ".corrupt.*")
if err != nil {
t.Fatalf("bad Glob %v", err)
}
if len(matches) != 1 {
t.Fatalf("Got matches: %v, want one match", matches)
}
// Opening (with create) a second time should succeed.
st, err = util.OpenStore("leveldb", rootDir, util.OpenOptions{
CreateIfMissing: true,
ErrorIfExists: true,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := st.Close(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}