Skip to content

Commit

Permalink
More silent and faster nodes setup
Browse files Browse the repository at this point in the history
- Update comments for test helper methods
- Remove test logs except when node is ready
- Init nodes in parallel
- Wait for nodes after starting them all
- Call `t.FailNow()` when in main test goroutine
  • Loading branch information
qdm12 committed Apr 6, 2022
1 parent 71f0d61 commit 65af058
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 23 deletions.
13 changes: 5 additions & 8 deletions tests/utils/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,24 +224,21 @@ func (n *Node) StartAndWait(ctx context.Context, waitErrCh chan<- error) (startE
return nil
}

// InitAndStartTest is a test helper method to initialise and start nodes,
// InitAndStartTest is a test helper method to initialise and start the node,
// as well as registering appriopriate test handlers.
// It monitors each node for failure, and terminates all of them if any fails.
// It also shuts down all the nodes on test cleanup.
// Finally it calls the passed `failNow` argument when the test should be failed
// because the nodes got shut down.
// If initialising or starting fails, cleanup is done and the test fails instantly.
// If the node crashes during runtime, the passed `signalTestToStop` argument is
// called since the test cannot be failed from outside the main test goroutine.
func (n Node) InitAndStartTest(ctx context.Context, t *testing.T,
signalTestToStop context.CancelFunc) {
t.Helper()

t.Logf("Node %s is initialising", n)
err := n.Init(ctx)
require.NoError(t, err)

nodeCtx, nodeCancel := context.WithCancel(ctx)
waitErr := make(chan error)

t.Logf("Node %s is starting", n)
err = n.StartAndWait(nodeCtx, waitErr)
if err != nil {
t.Errorf("failed to start node %s: %s", n, err)
Expand All @@ -251,7 +248,7 @@ func (n Node) InitAndStartTest(ctx context.Context, t *testing.T,
t.FailNow()
}

t.Logf("Node %s started successfully", n)
t.Logf("Node %s is ready", n)

// watch for runtime fatal node error
watchDogCtx, watchDogCancel := context.WithCancel(ctx)
Expand Down
54 changes: 39 additions & 15 deletions tests/utils/node/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,33 +82,43 @@ func (nodes Nodes) Start(ctx context.Context, waitErr chan<- error) (

// InitAndStartTest is a test helper method to initialise and start nodes,
// as well as registering appriopriate test handlers.
// It monitors each node for failure, and terminates all of them if any fails.
// It also shuts down all the nodes on test cleanup.
// Finally it calls the passed stop signalling functional argument when the
// test should be failed because the nodes got terminated.
// If any node fails to initialise or start, cleanup is done and the test
// is instantly failed.
// If any node crashes at runtime, all other nodes are shutdown,
// cleanup is done and the passed argument `signalTestToStop`
// is called to signal to the main test goroutine to stop.
func (nodes Nodes) InitAndStartTest(ctx context.Context, t *testing.T,
signalTestToStop context.CancelFunc) {
t.Helper()

initErrors := make(chan error)
for _, node := range nodes {
t.Logf("Node %s initialising", node)
err := node.Init(ctx)
go func(node Node) {
err := node.Init(ctx) // takes 2 seconds
if err != nil {
err = fmt.Errorf("node %s failed to initialise: %w", node, err)
}
initErrors <- err
}(node)
}

for range nodes {
err := <-initErrors
if err != nil {
t.Errorf("node %s failed to initialise: %s", node, err)
signalTestToStop()
return
t.Error(err)
}
}
if t.Failed() {
t.FailNow()
}

var started int
nodesCtx, nodesCancel := context.WithCancel(ctx)
waitErr := make(chan error)

for _, node := range nodes {
t.Logf("Node %s starting", node)
err := node.StartAndWait(nodesCtx, waitErr)
err := node.Start(nodesCtx, waitErr) // takes little time
if err == nil {
t.Logf("Node %s started", node)
started++
continue
}
Expand All @@ -117,10 +127,24 @@ func (nodes Nodes) InitAndStartTest(ctx context.Context, t *testing.T,

stopNodes(t, started, nodesCancel, waitErr)
close(waitErr)
t.FailNow()
}

// Signal calling test to stop.
signalTestToStop()
return
// this is run sequentially since all nodes start almost at the same time
// so waiting for one node will also wait for all the others.
// You can see this since the test logs out that all the nodes are ready
// at the same time.
for _, node := range nodes {
err := waitForNode(ctx, node.GetRPCPort())
if err == nil {
t.Logf("Node %s is ready", node)
continue
}

t.Errorf("Node %s failed to be ready: %s", node, err)
stopNodes(t, started, nodesCancel, waitErr)
close(waitErr)
t.FailNow()
}

// watch for runtime fatal error from any of the nodes
Expand Down

0 comments on commit 65af058

Please sign in to comment.