diff --git a/go.mod b/go.mod index b75226cfa..2805a3d9d 100644 --- a/go.mod +++ b/go.mod @@ -112,6 +112,7 @@ require ( github.com/prometheus/client_golang v0.9.0-pre1.0.20180416233856-82f5ff156b29 // indirect github.com/segmentio/go-prompt v1.2.1-0.20161017233205-f0d19b6901ad // indirect + golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f ) exclude gopkg.in/olivere/elastic.v5 v5.0.72 // buggy import, see https://github.com/olivere/elastic/pull/869 diff --git a/internal/test/utils.go b/internal/test/utils.go deleted file mode 100644 index 1acb48a0a..000000000 --- a/internal/test/utils.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package test - -import ( - "testing" - - "github.com/gogo/protobuf/proto" - "github.com/stretchr/testify/assert" - - "storj.io/storj/pkg/pb" - "storj.io/storj/storage" -) - -// NewNodeStorageValue provides a convient way to create a node as a storage.Value for testing purposes -func NewNodeStorageValue(t *testing.T, address string) storage.Value { - na := &pb.Node{Id: "", Address: &pb.NodeAddress{Transport: pb.NodeTransport_TCP_TLS_GRPC, Address: address}} - d, err := proto.Marshal(na) - assert.NoError(t, err) - return d -} diff --git a/internal/testcontext/context.go b/internal/testcontext/context.go new file mode 100644 index 000000000..da0d78539 --- /dev/null +++ b/internal/testcontext/context.go @@ -0,0 +1,100 @@ +// Copyright (C) 2018 Storj Labs, Inc. +// See LICENSE for copying information. + +package testcontext + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + "sync" + "testing" + + "golang.org/x/sync/errgroup" +) + +// Context is a context that has utility methods for testing and waiting for asynchronous errors. +type Context struct { + context.Context + group *errgroup.Group + test testing.TB + + once sync.Once + directory string +} + +// New creates a new test context +func New(test testing.TB) *Context { + group, ctx := errgroup.WithContext(context.Background()) + return &Context{ + Context: ctx, + group: group, + test: test, + } +} + +// Go runs fn in a goroutine. +// Call Wait to check the result +func (ctx *Context) Go(fn func() error) { + ctx.test.Helper() + ctx.group.Go(fn) +} + +// Check calls fn and checks result +func (ctx *Context) Check(fn func() error) { + ctx.test.Helper() + err := fn() + if err != nil { + ctx.test.Fatal(err) + } +} + +// Dir returns a directory path inside temp +func (ctx *Context) Dir(subs ...string) string { + ctx.test.Helper() + + ctx.once.Do(func() { + var err error + ctx.directory, err = ioutil.TempDir("", ctx.test.Name()) + if err != nil { + ctx.test.Fatal(err) + } + }) + + dir := filepath.Join(append([]string{ctx.directory}, subs...)...) + _ = os.MkdirAll(dir, 0644) + return dir +} + +// File returns a filepath inside temp +func (ctx *Context) File(subs ...string) string { + ctx.test.Helper() + + if len(subs) == 0 { + ctx.test.Fatal("expected more than one argument") + } + + dir := ctx.Dir(subs[:len(subs)-1]...) + return filepath.Join(dir, subs[len(subs)-1]) +} + +// Cleanup waits everything to be completed, +// checks errors and tries to cleanup directories +func (ctx *Context) Cleanup() { + ctx.test.Helper() + + defer ctx.deleteTemporary() + err := ctx.group.Wait() + if err != nil { + ctx.test.Fatal(err) + } +} + +// deleteTemporary tries to delete temporary directory +func (ctx *Context) deleteTemporary() { + err := os.RemoveAll(ctx.directory) + if err != nil { + ctx.test.Fatal(err) + } +} diff --git a/internal/testcontext/context_test.go b/internal/testcontext/context_test.go new file mode 100644 index 000000000..3b7486aa7 --- /dev/null +++ b/internal/testcontext/context_test.go @@ -0,0 +1,21 @@ +package testcontext_test + +import ( + "testing" + "time" + + "storj.io/storj/internal/testcontext" +) + +func TestBasic(t *testing.T) { + ctx := testcontext.New(t) + defer ctx.Cleanup() + + ctx.Go(func() error { + time.Sleep(time.Millisecond) + return nil + }) + + t.Log(ctx.Dir("a", "b", "c")) + t.Log(ctx.File("a", "w", "c.txt")) +} diff --git a/pkg/node/node_test.go b/pkg/node/node_test.go index d04ff7121..53dc73944 100644 --- a/pkg/node/node_test.go +++ b/pkg/node/node_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "google.golang.org/grpc" + "storj.io/storj/internal/testcontext" "storj.io/storj/pkg/dht/mocks" "storj.io/storj/pkg/pb" "storj.io/storj/pkg/provider" @@ -20,6 +21,9 @@ import ( var ctx = context.Background() func TestLookup(t *testing.T) { + ctx := testcontext.New(t) + defer ctx.Cleanup() + cases := []struct { self pb.Node to pb.Node @@ -43,8 +47,10 @@ func TestLookup(t *testing.T) { id := newTestIdentity(t) srv, mock, err := newTestServer(ctx, &mockNodeServer{queryCalled: 0}, id) assert.NoError(t, err) - go func() { assert.NoError(t, srv.Serve(lis)) }() + + ctx.Go(func() error { return srv.Serve(lis) }) defer srv.Stop() + ctrl := gomock.NewController(t) mdht := mock_dht.NewMockDHT(ctrl) @@ -68,7 +74,9 @@ func TestLookup(t *testing.T) { } func TestPing(t *testing.T) { - ctx := context.Background() + ctx := testcontext.New(t) + defer ctx.Cleanup() + cases := []struct { self pb.Node toID string @@ -96,7 +104,7 @@ func TestPing(t *testing.T) { assert.NoError(t, err) // start gRPC server - go func() { assert.NoError(t, msrv.Serve(lis)) }() + ctx.Go(func() error { return msrv.Serve(lis) }) defer msrv.Stop() nc, err := NewNodeClient(v.toIdentity, v.self, mdht) @@ -116,7 +124,6 @@ func newTestServer(ctx context.Context, ns pb.NodesServer, identity *provider.Fu } grpcServer := grpc.NewServer(identOpt) - pb.RegisterNodesServer(grpcServer, ns) return grpcServer, ns, nil diff --git a/pkg/overlay/overlay_test.go b/pkg/overlay/overlay_test.go index ba71f7459..0c6688771 100644 --- a/pkg/overlay/overlay_test.go +++ b/pkg/overlay/overlay_test.go @@ -8,10 +8,10 @@ import ( "net" "testing" + "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" "google.golang.org/grpc" - "storj.io/storj/internal/test" "storj.io/storj/pkg/node" "storj.io/storj/pkg/pb" "storj.io/storj/storage" @@ -29,10 +29,10 @@ func TestFindStorageNodes(t *testing.T) { srv := NewMockServer([]storage.ListItem{ { Key: storage.Key(fid.ID.String()), - Value: test.NewNodeStorageValue(t, "127.0.0.1:9090"), + Value: newNodeStorageValue(t, "127.0.0.1:9090"), }, { Key: storage.Key(fid2.ID.String()), - Value: test.NewNodeStorageValue(t, "127.0.0.1:9090"), + Value: newNodeStorageValue(t, "127.0.0.1:9090"), }, }) assert.NotNil(t, srv) @@ -62,7 +62,7 @@ func TestOverlayLookup(t *testing.T) { srv := NewMockServer([]storage.ListItem{ { Key: storage.Key(fid.ID.String()), - Value: test.NewNodeStorageValue(t, "127.0.0.1:9090"), + Value: newNodeStorageValue(t, "127.0.0.1:9090"), }, }) go func() { assert.NoError(t, srv.Serve(lis)) }() @@ -89,7 +89,7 @@ func TestOverlayBulkLookup(t *testing.T) { srv := NewMockServer([]storage.ListItem{ { Key: storage.Key(fid.ID.String()), - Value: test.NewNodeStorageValue(t, "127.0.0.1:9090"), + Value: newNodeStorageValue(t, "127.0.0.1:9090"), }, }) go func() { assert.NoError(t, srv.Serve(lis)) }() @@ -106,3 +106,11 @@ func TestOverlayBulkLookup(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, r) } + +// newNodeStorageValue provides a convient way to create a node as a storage.Value for testing purposes +func newNodeStorageValue(t *testing.T, address string) storage.Value { + na := &pb.Node{Id: "", Address: &pb.NodeAddress{Transport: pb.NodeTransport_TCP_TLS_GRPC, Address: address}} + d, err := proto.Marshal(na) + assert.NoError(t, err) + return d +}