tests: fix defer+goroutine by returning previous test structure (without goroutine)

This commit is contained in:
Andrey Samokhvalov 2016-10-26 15:28:05 +03:00
parent f37956e38e
commit 75dd860ac9

@ -18,18 +18,16 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
// harnessTest wraps a regular testing.T providing enchanced error detection // harnessTest wraps a regular testing.T providing enhanced error detection
// and propagation. Any fatal errors encurred by any running lnd processes will // and propagation. All error will be augmented with a full stack-trace in
// bubble up to the error channel embedded within this struct. Additionally, // order to aide in debugging. Additionally, any panics caused by active
// any panics caused by active test cases will also be proxied over the // test cases will also be handled and represented as fatals.
// errChan. Finally, all error sent through the error channel will be augmented
// with a full stack-trace in order to aide in debugging.
type harnessTest struct { type harnessTest struct {
*testing.T t *testing.T
// errChan is a channel for sending retransmitted panic errors and // testCase is populated during test execution and represents the
// fatal errors which occur while running an integration tests. // current test case.
ErrChan chan error testCase *testCase
} }
// newHarnessTest creates a new instance of a harnessTest from a regular // newHarnessTest creates a new instance of a harnessTest from a regular
@ -42,40 +40,44 @@ func newHarnessTest(t *testing.T) *harnessTest {
// integration tests should mark test failures soley with this method due to // integration tests should mark test failures soley with this method due to
// the error stack traces it produces. // the error stack traces it produces.
func (h *harnessTest) Fatalf(format string, a ...interface{}) { func (h *harnessTest) Fatalf(format string, a ...interface{}) {
if h.ErrChan != nil { stacktrace := errors.Wrap(fmt.Sprintf(format, a...), 1).ErrorStack()
description := fmt.Sprintf(format, a...)
h.ErrChan <- fmt.Errorf(errors.Wrap(description, 1).ErrorStack())
h.FailNow() if h.testCase != nil {
return h.t.Fatalf("Failed: (%v): exited with error: \n" +
"%v", h.testCase.name, stacktrace)
} else {
h.t.Fatalf("Error outside of test: %v", stacktrace)
} }
h.Fatal("cannot send an error when a test isn't running")
} }
// RunTest executes a harness test-case. Any errors or panics will be
// re-directed to the structs' errChan.
func (h *harnessTest) RunTest(net *networkHarness, test testCase) chan error {
h.ErrChan = make(chan error)
// Launch a goroutine to execute the acutal test-case. If the test // RunTestCase executes a harness test-case. Any errors or panics will be
// pases then the error channel returned will be closed. Otherwise, a // represented as fatal.
// non-nil error will be sent over the error channel. func (h *harnessTest) RunTestCase(testCase *testCase, net *networkHarness) {
go func() { h.testCase = testCase
defer func() { defer func() {
if err := recover(); err != nil { h.testCase = nil
h.ErrChan <- fmt.Errorf(err.(string))
}
close(h.ErrChan)
h.ErrChan = nil
}()
test(net, h)
}() }()
return h.ErrChan defer func() {
if err := recover(); err != nil {
description := errors.Wrap(err, 2).ErrorStack()
h.t.Fatalf("Failed: (%v) paniced with: \n%v",
h.testCase.name, description)
}
}()
testCase.test(net, h)
h.t.Logf("Passed: (%v)", h.testCase.name)
return
}
func (h *harnessTest) Logf(format string, args ...interface{}) {
h.t.Logf(format, args...)
}
func (h *harnessTest) Log(args ...interface{}) {
h.t.Log(args...)
} }
func assertTxInBlock(t *harnessTest, block *btcutil.Block, txid *wire.ShaHash) { func assertTxInBlock(t *harnessTest, block *btcutil.Block, txid *wire.ShaHash) {
@ -863,17 +865,44 @@ func testMaxPendingChannels(net *networkHarness, t *harnessTest) {
} }
} }
type testCase func(net *networkHarness, t *harnessTest) type testCase struct {
name string
test func(net *networkHarness, t *harnessTest)
}
var testCases = map[string]testCase{ var testsCases = []*testCase{
"basic funding flow": testBasicChannelFunding, {
"channel force closure": testChannelForceClosure, name: "basic funding flow",
"channel balance": testChannelBalance, test: testBasicChannelFunding,
"single hop invoice": testSingleHopInvoice, },
"max pending channel": testMaxPendingChannels, {
"multi-hop payments": testMultiHopPayments, name: "channel force closure",
"multiple channel creation": testBasicChannelCreation, test: testChannelForceClosure,
"invoice update subscription": testInvoiceSubscriptions, },
{
name: "channel balance",
test: testChannelBalance,
},
{
name: "single hop invoice",
test: testSingleHopInvoice,
},
{
name: "max pending channel",
test: testMaxPendingChannels,
},
{
name: "multi-hop payments",
test: testMultiHopPayments,
},
{
name: "multiple channel creation",
test: testBasicChannelCreation,
},
{
name: "invoice update subscription",
test: testInvoiceSubscriptions,
},
} }
// TestLightningNetworkDaemon performs a series of integration tests amongst a // TestLightningNetworkDaemon performs a series of integration tests amongst a
@ -885,7 +914,7 @@ func TestLightningNetworkDaemon(t *testing.T) {
// 'OnTxAccepted' call back. // 'OnTxAccepted' call back.
lndHarness, err := newNetworkHarness() lndHarness, err := newNetworkHarness()
if err != nil { if err != nil {
t.Fatalf("unable to create lightning network harness: %v", err) ht.Fatalf("unable to create lightning network harness: %v", err)
} }
defer lndHarness.TearDownAll() defer lndHarness.TearDownAll()
@ -898,14 +927,14 @@ func TestLightningNetworkDaemon(t *testing.T) {
// drive blockchain related events within the network. // drive blockchain related events within the network.
btcdHarness, err := rpctest.New(harnessNetParams, handlers, nil) btcdHarness, err := rpctest.New(harnessNetParams, handlers, nil)
if err != nil { if err != nil {
t.Fatalf("unable to create mining node: %v", err) ht.Fatalf("unable to create mining node: %v", err)
} }
defer btcdHarness.TearDown() defer btcdHarness.TearDown()
if err := btcdHarness.SetUp(true, 50); err != nil { if err := btcdHarness.SetUp(true, 50); err != nil {
t.Fatalf("unable to set up mining node: %v", err) ht.Fatalf("unable to set up mining node: %v", err)
} }
if err := btcdHarness.Node.NotifyNewTransactions(false); err != nil { if err := btcdHarness.Node.NotifyNewTransactions(false); err != nil {
t.Fatalf("unable to request transaction notifications: %v", err) ht.Fatalf("unable to request transaction notifications: %v", err)
} }
// With the btcd harness created, we can now complete the // With the btcd harness created, we can now complete the
@ -913,32 +942,25 @@ func TestLightningNetworkDaemon(t *testing.T) {
// example: "--debuglevel=debug" // example: "--debuglevel=debug"
// TODO(roasbeef): create master balanced channel with all the monies? // TODO(roasbeef): create master balanced channel with all the monies?
if err := lndHarness.InitializeSeedNodes(btcdHarness, nil); err != nil { if err := lndHarness.InitializeSeedNodes(btcdHarness, nil); err != nil {
t.Fatalf("unable to initialize seed nodes: %v", err) ht.Fatalf("unable to initialize seed nodes: %v", err)
} }
if err = lndHarness.SetUp(); err != nil { if err = lndHarness.SetUp(); err != nil {
t.Fatalf("unable to set up test lightning network: %v", err) ht.Fatalf("unable to set up test lightning network: %v", err)
} }
t.Logf("Running %v integration tests", len(testCases)) // Spawn a new goroutine to watch for any fatal errors that any of the
for name, test := range testCases { // running lnd processes encounter. If an error occurs, then the test
errChan := ht.RunTest(lndHarness, test) // fails immediately with a fatal error, as far as fatal is happening
// inside goroutine main goroutine would not be finished at the same
// time as we receive fatal error from lnd process.
go func() {
err := <-lndHarness.ProcessErrors()
ht.Fatalf("lnd finished with error (stderr): "+
"\n%v", err)
}()
select { t.Logf("Running %v integration tests", len(testsCases))
// Attempt to read from the error channel created for this for _, testCase := range testsCases {
// specific test. If this error is non-nil then the test passed ht.RunTestCase(testCase, lndHarness)
// without any problems.
case err := <-errChan:
if err != nil {
t.Fatalf("Fail: (%v): exited with error: \n%v",
name, err)
}
t.Logf("Passed: (%v)", name)
// If a read from this channel succeeeds then one of the
// running lnd nodes has exited with a fatal erorr.
case err := <-lndHarness.ProcessErrors():
t.Fatalf("Fail: (%v): lnd finished with error "+
"(stderr): \n%v", name, err)
}
} }
} }