Cleanup after tests (#341)

This commit is contained in:
Egon Elbre 2018-09-11 16:57:12 +03:00 committed by GitHub
parent e7e2d4d7c9
commit 4486e265bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 265 additions and 75 deletions

View File

@ -0,0 +1,24 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information
// +build !windows,!linux,!darwin,!netbsd,!freebsd,!openbsd
package processgroup
import (
"os"
"os/exec"
)
// Setup sets up exec.Cmd such that it can be properly terminated
func Setup(c *exec.Cmd) {}
// Kill tries to forcefully kill the process
func Kill(cmd *exec.Cmd) {
proc := cmd.Process
if proc == nil {
return
}
_ = proc.Signal(os.Interrupt)
_ = proc.Signal(os.Kill)
}

View File

@ -0,0 +1,36 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information
// +build linux darwin netbsd freebsd openbsd
package processgroup
import (
"os"
"os/exec"
"syscall"
)
// Setup sets up exec.Cmd such that it can be properly terminated
func Setup(c *exec.Cmd) {
c.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
}
// Kill tries to forcefully kill the process
func Kill(cmd *exec.Cmd) {
proc := cmd.Process
if proc == nil {
return
}
pgid, err := syscall.Getpgid(proc.Pid)
if err != nil {
_ = syscall.Kill(-pgid, 15)
}
// just in case
_ = proc.Signal(os.Interrupt)
_ = proc.Signal(os.Kill)
}

View File

@ -0,0 +1,45 @@
// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information
// +build windows
package processgroup
import (
"os"
"os/exec"
"strconv"
"syscall"
)
// Setup sets up exec.Cmd such that it can be properly terminated
func Setup(c *exec.Cmd) {
c.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
}
}
// Kill tries to forcefully kill the process
func Kill(cmd *exec.Cmd) {
proc := cmd.Process
if proc == nil {
return
}
_ = exec.Command("taskkill", "/f", "/pid", strconv.Itoa(proc.Pid)).Run()
// just in case
forcekill(proc.Pid)
_ = proc.Signal(os.Interrupt)
_ = proc.Signal(os.Kill)
}
func forcekill(pid int) {
handle, err := syscall.OpenProcess(syscall.PROCESS_TERMINATE, true, uint32(pid))
if err != nil {
return
}
syscall.TerminateProcess(handle, 0)
syscall.CloseHandle(handle)
}

View File

@ -11,7 +11,8 @@ import (
)
func TestAddToReplacementCache(t *testing.T) {
rt := createRT([]byte{244, 255})
rt, cleanup := createRoutingTable(t, []byte{244, 255})
defer cleanup()
kadBucketID := []byte{255, 255}
node1 := mockNode(string([]byte{233, 255}))
rt.addToReplacementCache(kadBucketID, node1)

View File

@ -13,6 +13,7 @@ import (
"go.uber.org/zap"
"storj.io/storj/pkg/dht"
"storj.io/storj/pkg/utils"
proto "storj.io/storj/protos/overlay"
"storj.io/storj/storage"
"storj.io/storj/storage/boltdb"
@ -81,6 +82,13 @@ func NewRoutingTable(localNode *proto.Node, options *RoutingOptions) (*RoutingTa
return rt, nil
}
// Close closes underlying databases
func (rt *RoutingTable) Close() error {
kerr := rt.kadBucketDB.Close()
nerr := rt.nodeBucketDB.Close()
return utils.CombineErrors(kerr, nerr)
}
// Local returns the local nodes ID
func (rt *RoutingTable) Local() proto.Node {
return *rt.self

View File

@ -6,6 +6,7 @@ package kademlia
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
@ -15,36 +16,43 @@ import (
"storj.io/storj/storage"
)
func tempfile(fileName string) string {
f, err := ioutil.TempFile("", fileName)
func tempdir(t testing.TB) (dir string, cleanup func()) {
dir, err := ioutil.TempDir("", "storj-kademlia")
if err != nil {
panic(err)
t.Fatal(err)
}
err = f.Close()
if err != nil {
panic(err)
return dir, func() {
if err := os.RemoveAll(dir); err != nil {
t.Fatal(err)
}
}
err = os.Remove(f.Name())
if err != nil {
panic(err)
}
return f.Name()
}
func createRT(localNodeID []byte) *RoutingTable {
func createRoutingTable(t *testing.T, localNodeID []byte) (*RoutingTable, func()) {
tempdir, cleanup := tempdir(t)
if localNodeID == nil {
localNodeID = []byte("AA")
}
localNode := &proto.Node{Id: string(localNodeID)}
options := &RoutingOptions{
kpath: tempfile("Kadbucket"),
npath: tempfile("Nodebucket"),
kpath: filepath.Join(tempdir, "Kadbucket"),
npath: filepath.Join(tempdir, "Nodebucket"),
idLength: 16,
bucketSize: 6,
rcBucketSize: 2,
}
rt, _ := NewRoutingTable(localNode, options)
return rt
rt, err := NewRoutingTable(localNode, options)
if err != nil {
t.Fatal(err)
}
return rt, func() {
err := rt.Close()
cleanup()
if err != nil {
t.Fatal(err)
}
}
}
func mockNode(id string) *proto.Node {
@ -54,7 +62,8 @@ func mockNode(id string) *proto.Node {
}
func TestAddNode(t *testing.T) {
rt := createRT([]byte("OO")) //localNode [79, 79] or [01001111, 01001111]
rt, cleanup := createRoutingTable(t, []byte("OO")) //localNode [79, 79] or [01001111, 01001111]
defer cleanup()
bucket, err := rt.kadBucketDB.Get(storage.Key([]byte{255, 255}))
assert.NoError(t, err)
assert.NotNil(t, bucket)
@ -243,7 +252,8 @@ func TestAddNode(t *testing.T) {
}
func TestUpdateNode(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
node := mockNode("BB")
ok, err := rt.addNode(node)
assert.True(t, ok)
@ -267,7 +277,8 @@ func TestUpdateNode(t *testing.T) {
}
func TestRemoveNode(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
kadBucketID := []byte{255, 255}
node := mockNode("BB")
ok, err := rt.addNode(node)
@ -295,7 +306,8 @@ func TestRemoveNode(t *testing.T) {
func TestCreateOrUpdateKBucket(t *testing.T) {
id := []byte{255, 255}
rt := createRT(nil)
rt, cleanup := createRoutingTable(t, nil)
defer cleanup()
err := rt.createOrUpdateKBucket(storage.Key(id), time.Now())
assert.NoError(t, err)
val, e := rt.kadBucketDB.Get(storage.Key(id))
@ -307,7 +319,8 @@ func TestCreateOrUpdateKBucket(t *testing.T) {
func TestGetKBucketID(t *testing.T) {
kadIDA := storage.Key([]byte{255, 255})
nodeIDA := []byte("AA")
rt := createRT(nodeIDA)
rt, cleanup := createRoutingTable(t, nodeIDA)
defer cleanup()
keyA, err := rt.getKBucketID(nodeIDA)
assert.NoError(t, err)
assert.Equal(t, kadIDA, keyA)
@ -320,7 +333,8 @@ func TestXorTwoIds(t *testing.T) {
func TestSortByXOR(t *testing.T) {
node1 := []byte{127, 255} //xor 0
rt := createRT(node1)
rt, cleanup := createRoutingTable(t, node1)
defer cleanup()
node2 := []byte{143, 255} //xor 240
assert.NoError(t, rt.nodeBucketDB.Put(node2, []byte("")))
node3 := []byte{255, 255} //xor 128
@ -343,7 +357,8 @@ func TestSortByXOR(t *testing.T) {
func TestDetermineFurthestIDWithinK(t *testing.T) {
node1 := []byte{127, 255} //xor 0
rt := createRT(node1)
rt, cleanup := createRoutingTable(t, node1)
defer cleanup()
rt.self.Id = string(node1)
assert.NoError(t, rt.nodeBucketDB.Put(node1, []byte("")))
expectedFurthest := node1
@ -392,7 +407,8 @@ func TestDetermineFurthestIDWithinK(t *testing.T) {
func TestNodeIsWithinNearestK(t *testing.T) {
selfNode := []byte{127, 255}
rt := createRT(selfNode)
rt, cleanup := createRoutingTable(t, selfNode)
defer cleanup()
rt.bucketSize = 2
expectTrue, err := rt.nodeIsWithinNearestK(selfNode)
assert.NoError(t, err)
@ -424,7 +440,8 @@ func TestNodeIsWithinNearestK(t *testing.T) {
func TestKadBucketContainsLocalNode(t *testing.T) {
nodeIDA := []byte{183, 255} //[10110111, 1111111]
rt := createRT(nodeIDA)
rt, cleanup := createRoutingTable(t, nodeIDA)
defer cleanup()
kadIDA := storage.Key([]byte{255, 255})
kadIDB := storage.Key([]byte{127, 255})
now := time.Now()
@ -441,7 +458,8 @@ func TestKadBucketContainsLocalNode(t *testing.T) {
func TestKadBucketHasRoom(t *testing.T) {
node1 := []byte{255, 255}
kadIDA := storage.Key([]byte{255, 255})
rt := createRT(node1)
rt, cleanup := createRoutingTable(t, node1)
defer cleanup()
node2 := []byte{191, 255}
node3 := []byte{127, 255}
node4 := []byte{63, 255}
@ -462,7 +480,8 @@ func TestKadBucketHasRoom(t *testing.T) {
func TestGetNodeIDsWithinKBucket(t *testing.T) {
nodeIDA := []byte{183, 255} //[10110111, 1111111]
rt := createRT(nodeIDA)
rt, cleanup := createRoutingTable(t, nodeIDA)
defer cleanup()
kadIDA := storage.Key([]byte{255, 255})
kadIDB := storage.Key([]byte{127, 255})
now := time.Now()
@ -499,7 +518,8 @@ func TestGetNodesFromIDs(t *testing.T) {
assert.NoError(t, err)
c, err := pb.Marshal(nodeC)
assert.NoError(t, err)
rt := createRT(nodeIDA)
rt, cleanup := createRoutingTable(t, nodeIDA)
defer cleanup()
assert.NoError(t, rt.nodeBucketDB.Put(nodeIDA, a))
assert.NoError(t, rt.nodeBucketDB.Put(nodeIDB, b))
@ -527,7 +547,8 @@ func TestUnmarshalNodes(t *testing.T) {
assert.NoError(t, err)
c, err := pb.Marshal(nodeC)
assert.NoError(t, err)
rt := createRT(nodeIDA)
rt, cleanup := createRoutingTable(t, nodeIDA)
defer cleanup()
assert.NoError(t, rt.nodeBucketDB.Put(nodeIDA, a))
assert.NoError(t, rt.nodeBucketDB.Put(nodeIDB, b))
assert.NoError(t, rt.nodeBucketDB.Put(nodeIDC, c))
@ -546,7 +567,8 @@ func TestUnmarshalNodes(t *testing.T) {
func TestGetUnmarshaledNodesFromBucket(t *testing.T) {
bucketID := []byte{255, 255}
nodeA := mockNode("AA")
rt := createRT([]byte(nodeA.Id))
rt, cleanup := createRoutingTable(t, []byte(nodeA.Id))
defer cleanup()
nodeB := mockNode("BB")
nodeC := mockNode("CC")
var err error
@ -563,7 +585,8 @@ func TestGetUnmarshaledNodesFromBucket(t *testing.T) {
}
func TestGetKBucketRange(t *testing.T) {
rt := createRT(nil)
rt, cleanup := createRoutingTable(t, nil)
defer cleanup()
idA := []byte{255, 255}
idB := []byte{127, 255}
idC := []byte{63, 255}
@ -586,21 +609,24 @@ func TestGetKBucketRange(t *testing.T) {
}
func TestCreateFirstBucketID(t *testing.T) {
rt := createRT(nil)
rt, cleanup := createRoutingTable(t, nil)
defer cleanup()
x := rt.createFirstBucketID()
expected := []byte{255, 255}
assert.Equal(t, x, expected)
}
func TestCreateZeroAsStorageKey(t *testing.T) {
rt := createRT(nil)
rt, cleanup := createRoutingTable(t, nil)
defer cleanup()
zero := rt.createZeroAsStorageKey()
expected := []byte{0, 0}
assert.Equal(t, zero, storage.Key(expected))
}
func TestDetermineLeafDepth(t *testing.T) {
rt := createRT(nil)
rt, cleanup := createRoutingTable(t, nil)
defer cleanup()
idA := []byte{255, 255}
idB := []byte{127, 255}
idC := []byte{63, 255}
@ -637,7 +663,8 @@ func TestDetermineLeafDepth(t *testing.T) {
}
func TestDetermineDifferingBitIndex(t *testing.T) {
rt := createRT(nil)
rt, cleanup := createRoutingTable(t, nil)
defer cleanup()
diff, err := rt.determineDifferingBitIndex([]byte{191, 255}, []byte{255, 255})
assert.NoError(t, err)
assert.Equal(t, 1, diff)
@ -689,7 +716,8 @@ func TestDetermineDifferingBitIndex(t *testing.T) {
}
func TestSplitBucket(t *testing.T) {
rt := createRT(nil)
rt, cleanup := createRoutingTable(t, nil)
defer cleanup()
id1 := []byte{255, 255}
id2 := []byte{191, 255}
id3 := []byte{127, 255}

View File

@ -15,27 +15,31 @@ import (
)
func TestLocal(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
local := rt.Local()
assert.Equal(t, *rt.self, local)
}
func TestK(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
k := rt.K()
assert.Equal(t, rt.bucketSize, k)
}
func TestCacheSize(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
expected := rt.rcBucketSize
result := rt.CacheSize()
assert.Equal(t, expected, result)
}
func TestGetBucket(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
node := mockNode("AA")
node2 := mockNode("BB")
ok, err := rt.addNode(node2)
@ -70,7 +74,8 @@ func TestGetBucket(t *testing.T) {
}
func TestGetBuckets(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
node := mockNode("AA")
node2 := mockNode("BB")
ok, err := rt.addNode(node2)
@ -87,7 +92,8 @@ func TestGetBuckets(t *testing.T) {
}
func TestFindNear(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
node := mockNode("AA")
node2 := mockNode("BB")
ok, err := rt.addNode(node2)
@ -118,7 +124,8 @@ func TestFindNear(t *testing.T) {
func TestConnectionSuccess(t *testing.T) {
id := "AA"
rt := createRT([]byte(id))
rt, cleanup := createRoutingTable(t, []byte(id))
defer cleanup()
id2 := "BB"
address1 := &proto.NodeAddress{Address: "a"}
address2 := &proto.NodeAddress{Address: "b"}
@ -147,7 +154,8 @@ func TestConnectionSuccess(t *testing.T) {
func TestConnectionFailed(t *testing.T) {
id := "AA"
node := mockNode(id)
rt := createRT([]byte(id))
rt, cleanup := createRoutingTable(t, []byte(id))
defer cleanup()
err := rt.ConnectionFailed(node)
assert.NoError(t, err)
v, err := rt.nodeBucketDB.Get([]byte(id))
@ -158,7 +166,8 @@ func TestConnectionFailed(t *testing.T) {
func TestSetBucketTimestamp(t *testing.T) {
id := []byte("AA")
idStr := string(id)
rt := createRT(id)
rt, cleanup := createRoutingTable(t, id)
defer cleanup()
now := time.Now().UTC()
err := rt.createOrUpdateKBucket(id, now)
@ -175,7 +184,8 @@ func TestSetBucketTimestamp(t *testing.T) {
}
func TestGetBucketTimestamp(t *testing.T) {
rt := createRT([]byte("AA"))
rt, cleanup := createRoutingTable(t, []byte("AA"))
defer cleanup()
now := time.Now().UTC()
id := "AA"
err := rt.createOrUpdateKBucket([]byte(id), now)

View File

@ -18,7 +18,6 @@ import (
"runtime"
"strings"
"testing"
"time"
"github.com/gogo/protobuf/proto"
"github.com/gtank/cryptopasta"
@ -479,21 +478,31 @@ func TestDelete(t *testing.T) {
}
}
func newTestServerStruct() *Server {
tmp, err := ioutil.TempDir("", "example")
func newTestServerStruct(t *testing.T) (*Server, func()) {
tmp, err := ioutil.TempDir("", "storj-piecestore")
if err != nil {
log.Fatalf("failed temp-dir: %v", err)
}
tempDBPath := filepath.Join(tmp, fmt.Sprintf("%s-test.db", time.Now().Format("2006-01-02T15-04-05.999999999Z07-00")))
tempDBPath := filepath.Join(tmp, "test.db")
tempDir := filepath.Join(tmp, "test-data", "3000")
psDB, err := psdb.Open(ctx, tempDir, tempDBPath)
if err != nil {
log.Fatalf("failed open psdb: %v", err)
t.Fatalf("failed open psdb: %v", err)
}
return &Server{DataDir: tempDir, DB: psDB}
server := &Server{DataDir: tempDir, DB: psDB}
return server, func() {
if serr := server.Stop(ctx); serr != nil {
t.Fatal(serr)
}
// TODO:fix this error check
_ = os.RemoveAll(tmp)
// if err := os.RemoveAll(tmp); err != nil {
// t.Fatal(err)
// }
}
}
func connect(addr string, o ...grpc.DialOption) (pb.PieceStoreRoutesClient, *grpc.ClientConn) {
@ -508,11 +517,12 @@ func connect(addr string, o ...grpc.DialOption) (pb.PieceStoreRoutesClient, *grp
}
type TestServer struct {
s *Server
grpcs *grpc.Server
conn *grpc.ClientConn
c pb.PieceStoreRoutesClient
k crypto.PrivateKey
s *Server
scleanup func()
grpcs *grpc.Server
conn *grpc.ClientConn
c pb.PieceStoreRoutesClient
k crypto.PrivateKey
}
func NewTestServer(t *testing.T) *TestServer {
@ -536,12 +546,12 @@ func NewTestServer(t *testing.T) *TestServer {
co, err := fiC.DialOption()
check(err)
s := newTestServerStruct()
s, cleanup := newTestServerStruct(t)
grpcs := grpc.NewServer(so)
k, ok := fiC.Key.(*ecdsa.PrivateKey)
assert.True(t, ok)
ts := &TestServer{s: s, grpcs: grpcs, k: k}
ts := &TestServer{s: s, scleanup: cleanup, grpcs: grpcs, k: k}
addr := ts.start()
ts.c, ts.conn = connect(addr, co)
@ -568,9 +578,7 @@ func (TS *TestServer) Stop() {
panic(err)
}
TS.grpcs.Stop()
if err := os.RemoveAll(TS.s.DataDir); err != nil {
panic(err)
}
TS.scleanup()
}
func serializeData(ba *pb.RenterBandwidthAllocation_Data) []byte {

View File

@ -138,7 +138,7 @@ func TestIdentityConfig_SaveIdentity(t *testing.T) {
}
func tempIdentityConfig() (*IdentityConfig, func(), error) {
tmpDir, err := ioutil.TempDir("", "tempIdentity")
tmpDir, err := ioutil.TempDir("", "storj-identity")
if err != nil {
return nil, nil, err
}

View File

@ -8,11 +8,26 @@ import (
"context"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"testing"
)
func TestFileRanger(t *testing.T) {
for _, example := range []struct {
tempdir, err := ioutil.TempDir("", "storj-fileranger")
if err != nil {
t.Fatal(err)
}
defer func() {
err := os.RemoveAll(tempdir)
if err != nil {
_ = err // TODO: figure out what is holding the folder open
// t.Fatal(err)
}
}()
for i, example := range []struct {
data string
size, offset, length int64
substr string
@ -31,7 +46,7 @@ func TestFileRanger(t *testing.T) {
{"abcdef", 6, -1, 7, "abcde", true},
{"abcdef", 6, 0, -1, "abcde", true},
} {
fh, err := ioutil.TempFile("", "test")
fh, err := os.Create(filepath.Join(tempdir, "test"+strconv.Itoa(i)))
if err != nil {
t.Fatalf("failed making tempfile")
}
@ -44,6 +59,7 @@ func TestFileRanger(t *testing.T) {
if err != nil {
t.Fatalf("failed closing data")
}
rr, err := FileRanger(name)
if err != nil {
t.Fatalf("failed opening tempfile")
@ -51,6 +67,7 @@ func TestFileRanger(t *testing.T) {
if rr.Size() != example.size {
t.Fatalf("invalid size: %v != %v", rr.Size(), example.size)
}
r, err := rr.Range(context.Background(), example.offset, example.length)
if example.fail {
if err == nil {
@ -58,13 +75,16 @@ func TestFileRanger(t *testing.T) {
}
return
}
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
data, err := ioutil.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(data, []byte(example.substr)) {
t.Fatalf("invalid subrange: %#v != %#v", string(data), example.substr)
}
@ -72,10 +92,6 @@ func TestFileRanger(t *testing.T) {
if err := rr.Close(); err != nil {
t.Fatalf("unable to close file %q: %v", name, err)
}
if err := os.Remove(name); err != nil {
t.Fatalf("unable to remove file %q: %v", name, err)
}
}
}

View File

@ -37,7 +37,20 @@ func ParseURL(s string) (*url.URL, error) {
}
// CombineErrors combines multiple errors to a single error
func CombineErrors(errs ...error) error { return combinedError(errs) }
func CombineErrors(errs ...error) error {
var errlist combinedError
for _, err := range errs {
if err != nil {
errlist = append(errlist, err)
}
}
if len(errlist) == 0 {
return nil
} else if len(errlist) == 1 {
return errlist[0]
}
return errlist
}
type combinedError []error
@ -62,4 +75,3 @@ func (errs combinedError) Error() string {
}
return ""
}

View File

@ -13,7 +13,7 @@ import (
)
func TestSuite(t *testing.T) {
tempdir, err := ioutil.TempDir("", "bolt")
tempdir, err := ioutil.TempDir("", "storj-bolt")
if err != nil {
t.Fatal(err)
}
@ -34,7 +34,7 @@ func TestSuite(t *testing.T) {
}
func BenchmarkSuite(b *testing.B) {
tempdir, err := ioutil.TempDir("", "bolt")
tempdir, err := ioutil.TempDir("", "storj-bolt")
if err != nil {
b.Fatal(err)
}

View File

@ -21,6 +21,7 @@ import (
"github.com/alicebob/miniredis"
"github.com/go-redis/redis"
"storj.io/storj/internal/processgroup"
)
const (
@ -83,6 +84,7 @@ func Process() (addr string, cleanup func(), err error) {
// start the process
cmd := exec.Command("redis-server", confpath)
processgroup.Setup(cmd)
read, write, err := os.Pipe()
if err != nil {
@ -95,7 +97,7 @@ func Process() (addr string, cleanup func(), err error) {
}
cleanup = func() {
_ = cmd.Process.Kill()
processgroup.Kill(cmd)
_ = os.RemoveAll(tmpdir)
}