lnd/lntest/itest/lnd_test.go
2021-08-24 11:06:44 +08:00

260 lines
8.2 KiB
Go

package itest
import (
"flag"
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/btcsuite/btcd/integration/rpctest"
"github.com/btcsuite/btcd/rpcclient"
"github.com/lightningnetwork/lnd/lntest"
"github.com/stretchr/testify/require"
)
const (
// defaultSplitTranches is the default number of tranches we split the
// test cases into.
defaultSplitTranches uint = 1
// defaultRunTranche is the default index of the test cases tranche that
// we run.
defaultRunTranche uint = 0
)
var (
// testCasesSplitParts is the number of tranches the test cases should
// be split into. By default this is set to 1, so no splitting happens.
// If this value is increased, then the -runtranche flag must be
// specified as well to indicate which part should be run in the current
// invocation.
testCasesSplitTranches = flag.Uint(
"splittranches", defaultSplitTranches, "split the test cases "+
"in this many tranches and run the tranche at "+
"0-based index specified by the -runtranche flag",
)
// testCasesRunTranche is the 0-based index of the split test cases
// tranche to run in the current invocation.
testCasesRunTranche = flag.Uint(
"runtranche", defaultRunTranche, "run the tranche of the "+
"split test cases with the given (0-based) index",
)
// dbBackendFlag specifies the backend to use
dbBackendFlag = flag.String("dbbackend", "bbolt", "Database backend (bbolt, etcd)")
)
// getTestCaseSplitTranche returns the sub slice of the test cases that should
// be run as the current split tranche as well as the index and slice offset of
// the tranche.
func getTestCaseSplitTranche() ([]*testCase, uint, uint) {
numTranches := defaultSplitTranches
if testCasesSplitTranches != nil {
numTranches = *testCasesSplitTranches
}
runTranche := defaultRunTranche
if testCasesRunTranche != nil {
runTranche = *testCasesRunTranche
}
// There's a special flake-hunt mode where we run the same test multiple
// times in parallel. In that case the tranche index is equal to the
// thread ID, but we need to actually run all tests for the regex
// selection to work.
threadID := runTranche
if numTranches == 1 {
runTranche = 0
}
numCases := uint(len(allTestCases))
testsPerTranche := numCases / numTranches
trancheOffset := runTranche * testsPerTranche
trancheEnd := trancheOffset + testsPerTranche
if trancheEnd > numCases || runTranche == numTranches-1 {
trancheEnd = numCases
}
return allTestCases[trancheOffset:trancheEnd], threadID, trancheOffset
}
// TestLightningNetworkDaemon performs a series of integration tests amongst a
// programmatically driven network of lnd nodes.
func TestLightningNetworkDaemon(t *testing.T) {
// If no tests are registered, then we can exit early.
if len(allTestCases) == 0 {
t.Skip("integration tests not selected with flag 'rpctest'")
}
// Parse testing flags that influence our test execution.
logDir := lntest.GetLogDir()
require.NoError(t, os.MkdirAll(logDir, 0700))
testCases, trancheIndex, trancheOffset := getTestCaseSplitTranche()
lntest.ApplyPortOffset(uint32(trancheIndex) * 1000)
// Before we start any node, we need to make sure that any btcd node
// that is started through the RPC harness uses a unique port as well to
// avoid any port collisions.
rpctest.ListenAddressGenerator = lntest.GenerateBtcdListenerAddresses
// Declare the network harness here to gain access to its
// 'OnTxAccepted' call back.
var lndHarness *lntest.NetworkHarness
// Create an instance of the btcd's rpctest.Harness that will act as
// the miner for all tests. This will be used to fund the wallets of
// the nodes within the test network and to drive blockchain related
// events within the network. Revert the default setting of accepting
// non-standard transactions on simnet to reject them. Transactions on
// the lightning network should always be standard to get better
// guarantees of getting included in to blocks.
//
// We will also connect it to our chain backend.
minerLogDir := fmt.Sprintf("%s/.minerlogs", logDir)
miner, minerCleanUp, err := lntest.NewMiner(
minerLogDir, "output_btcd_miner.log", harnessNetParams,
&rpcclient.NotificationHandlers{}, lntest.GetBtcdBinary(),
)
require.NoError(t, err, "failed to create new miner")
defer func() {
require.NoError(t, minerCleanUp(), "failed to clean up miner")
}()
// Start a chain backend.
chainBackend, cleanUp, err := lntest.NewBackend(
miner.P2PAddress(), harnessNetParams,
)
require.NoError(t, err, "new backend")
defer func() {
require.NoError(t, cleanUp(), "cleanup")
}()
// Before we start anything, we want to overwrite some of the connection
// settings to make the tests more robust. We might need to restart the
// miner while there are already blocks present, which will take a bit
// longer than the 1 second the default settings amount to. Doubling
// both values will give us retries up to 4 seconds.
miner.MaxConnRetries = rpctest.DefaultMaxConnectionRetries * 2
miner.ConnectionRetryTimeout = rpctest.DefaultConnectionRetryTimeout * 2
// Set up miner and connect chain backend to it.
require.NoError(t, miner.SetUp(true, 50))
require.NoError(t, miner.Client.NotifyNewTransactions(false))
require.NoError(t, chainBackend.ConnectMiner(), "connect miner")
// Parse database backend
var dbBackend lntest.DatabaseBackend
switch *dbBackendFlag {
case "bbolt":
dbBackend = lntest.BackendBbolt
case "etcd":
dbBackend = lntest.BackendEtcd
default:
require.Fail(t, "unknown db backend")
}
// Now we can set up our test harness (LND instance), with the chain
// backend we just created.
ht := newHarnessTest(t, nil)
binary := ht.getLndBinary()
lndHarness, err = lntest.NewNetworkHarness(
miner, chainBackend, binary, dbBackend,
)
if err != nil {
ht.Fatalf("unable to create lightning network harness: %v", err)
}
defer lndHarness.Stop()
// Spawn a new goroutine to watch for any fatal errors that any of the
// running lnd processes encounter. If an error occurs, then the test
// case should naturally as a result and we log the server error here to
// help debug.
go func() {
for {
select {
case err, more := <-lndHarness.ProcessErrors():
if !more {
return
}
ht.Logf("lnd finished with error (stderr):\n%v",
err)
}
}
}()
// Next mine enough blocks in order for segwit and the CSV package
// soft-fork to activate on SimNet.
numBlocks := harnessNetParams.MinerConfirmationWindow * 2
if _, err := miner.Client.Generate(numBlocks); err != nil {
ht.Fatalf("unable to generate blocks: %v", err)
}
// With the btcd harness created, we can now complete the
// initialization of the network. args - list of lnd arguments,
// example: "--debuglevel=debug"
// TODO(roasbeef): create master balanced channel with all the monies?
aliceBobArgs := []string{
"--default-remote-max-htlcs=483",
}
// Run the subset of the test cases selected in this tranche.
for idx, testCase := range testCases {
testCase := testCase
name := fmt.Sprintf("tranche%02d/%02d-of-%d/%s/%s",
trancheIndex, trancheOffset+uint(idx)+1,
len(allTestCases), chainBackend.Name(), testCase.name)
success := t.Run(name, func(t1 *testing.T) {
cleanTestCaseName := strings.ReplaceAll(
testCase.name, " ", "_",
)
err = lndHarness.SetUp(
t1, cleanTestCaseName, aliceBobArgs,
)
require.NoError(t1,
err, "unable to set up test lightning network",
)
defer func() {
require.NoError(t1, lndHarness.TearDown())
}()
lndHarness.EnsureConnected(
t1, lndHarness.Alice, lndHarness.Bob,
)
logLine := fmt.Sprintf(
"STARTING ============ %v ============\n",
testCase.name,
)
AddToNodeLog(t, lndHarness.Alice, logLine)
AddToNodeLog(t, lndHarness.Bob, logLine)
// Start every test with the default static fee estimate.
lndHarness.SetFeeEstimate(12500)
// Create a separate harness test for the testcase to
// avoid overwriting the external harness test that is
// tied to the parent test.
ht := newHarnessTest(t1, lndHarness)
ht.RunTestCase(testCase)
})
// Stop at the first failure. Mimic behavior of original test
// framework.
if !success {
// Log failure time to help relate the lnd logs to the
// failure.
t.Logf("Failure time: %v", time.Now().Format(
"2006-01-02 15:04:05.000",
))
break
}
}
}