From a27c37793bd4b829e5740ba2708d7288704cf6e3 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 13 Oct 2013 18:16:11 -0500 Subject: [PATCH] Add basic infrastructure for interface tests. This is simply at start at providing generic interface tests. The only thing is tests so far is the empty database conditions on NewestSha, but it adds infrastructure for creating and opening databases with special type handling based on the database and necessary logic to teardown so multiple backends can be tested simultaneously. This and the next series of commits all discussed with drahn@. --- common_test.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++ interface_test.go | 54 +++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 common_test.go create mode 100644 interface_test.go diff --git a/common_test.go b/common_test.go new file mode 100644 index 00000000..89f7dc69 --- /dev/null +++ b/common_test.go @@ -0,0 +1,131 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcdb_test + +import ( + "fmt" + "github.com/conformal/btcdb" + _ "github.com/conformal/btcdb/ldb" + _ "github.com/conformal/btcdb/sqlite3" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "os" + "path/filepath" +) + +var zeroHash = btcwire.ShaHash{} + +// testDbRoot is the root directory used to create all test databases. +const testDbRoot = "testdbs" + +// filesExists returns whether or not the named file or directory exists. +func fileExists(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// openDB is used to open an existing database based on the database type and +// name. +func openDB(dbType, dbName string) (btcdb.Db, error) { + // Handle memdb specially since it has no files on disk. + if dbType == "memdb" { + db, err := btcdb.OpenDB(dbType, "") + if err != nil { + return nil, fmt.Errorf("error opening db: %v", err) + } + return db, nil + } + + dbPath := filepath.Join(testDbRoot, dbName) + db, err := btcdb.OpenDB(dbType, dbPath) + if err != nil { + return nil, fmt.Errorf("error opening db: %v", err) + } + + return db, nil +} + +// createDB creates a new db instance and returns a teardown function the caller +// should invoke when done testing to clean up. The close flag indicates +// whether or not the teardown function should sync and close the database +// during teardown. +func createDB(dbType, dbName string, close bool) (btcdb.Db, func(), error) { + // Handle memory database specially since it doesn't need the disk + // specific handling. + if dbType == "memdb" { + db, err := btcdb.CreateDB(dbType, "") + if err != nil { + return nil, nil, fmt.Errorf("error creating db: %v", err) + } + + // Setup a teardown function for cleaning up. This function is + // returned to the caller to be invoked when it is done testing. + teardown := func() { + if close { + db.Close() + } + } + + return db, teardown, nil + } + + // Create the root directory for test databases. + if !fileExists(testDbRoot) { + if err := os.MkdirAll(testDbRoot, 0700); err != nil { + err := fmt.Errorf("unable to create test db "+ + "root: %v", err) + return nil, nil, err + } + } + + // Create a new database to store the accepted blocks into. + dbPath := filepath.Join(testDbRoot, dbName) + _ = os.RemoveAll(dbPath) + db, err := btcdb.CreateDB(dbType, dbPath) + if err != nil { + return nil, nil, fmt.Errorf("error creating db: %v", err) + } + + // Setup a teardown function for cleaning up. This function is + // returned to the caller to be invoked when it is done testing. + teardown := func() { + dbVersionPath := filepath.Join(testDbRoot, dbName+".ver") + if close { + db.Sync() + db.Close() + } + os.RemoveAll(dbPath) + os.Remove(dbVersionPath) + os.RemoveAll(testDbRoot) + } + + return db, teardown, nil +} + +// setupDB is used to create a new db instance with the genesis block already +// inserted. In addition to the new db instance, it returns a teardown function +// the caller should invoke when done testing to clean up. +func setupDB(dbType, dbName string) (btcdb.Db, func(), error) { + db, teardown, err := createDB(dbType, dbName, true) + if err != nil { + return nil, nil, err + } + + // Insert the main network genesis block. This is part of the initial + // database setup. + genesisBlock := btcutil.NewBlock(&btcwire.GenesisBlock) + _, err = db.InsertBlock(genesisBlock) + if err != nil { + teardown() + err := fmt.Errorf("failed to insert genesis block: %v", err) + return nil, nil, err + } + + return db, teardown, nil +} diff --git a/interface_test.go b/interface_test.go new file mode 100644 index 00000000..ce3c50a8 --- /dev/null +++ b/interface_test.go @@ -0,0 +1,54 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcdb_test + +import ( + "github.com/conformal/btcdb" + "testing" +) + +// testNewestShaEmpty ensures the NewestSha returns the values expected by +// the interface contract. +func testNewestShaEmpty(t *testing.T, db btcdb.Db) { + sha, height, err := db.NewestSha() + if err != nil { + t.Errorf("NewestSha error %v", err) + } + if !sha.IsEqual(&zeroHash) { + t.Errorf("NewestSha wrong hash got: %s, want %s", sha, &zeroHash) + + } + if height != -1 { + t.Errorf("NewestSha wrong height got: %s, want %s", height, -1) + } +} + +// TestEmptyDB tests that empty databases are handled properly. +func TestEmptyDB(t *testing.T) { + for _, dbType := range btcdb.SupportedDBs() { + // Ensure NewestSha returns expected values for a newly created + // db. + db, teardown, err := createDB(dbType, "emptydb", false) + if err != nil { + t.Errorf("Failed to create test database %v", err) + return + } + testNewestShaEmpty(t, db) + + // Ensure NewestSha still returns expected values for an empty + // database after reopen. + db.Close() + db, err = openDB(dbType, "emptydb") + if err != nil { + t.Errorf("Failed to open test database %v", err) + return + } + testNewestShaEmpty(t, db) + db.Close() + + // Clean up the old db. + teardown() + } +}