lnd/itest/lnd_test.go
2024-07-23 21:30:08 +08:00

227 lines
6.8 KiB
Go

package itest
import (
"flag"
"fmt"
"io"
"math"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/integration/rpctest"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lntest/node"
"github.com/lightningnetwork/lnd/lntest/port"
"github.com/lightningnetwork/lnd/lntest/wait"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/grpclog"
)
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
defaultTimeout = wait.DefaultTimeout
itestLndBinary = "../lnd-itest"
// TODO(yy): remove the following defined constants and put them in the
// specific tests where they are used?
testFeeBase = 1e+6
anchorSize = 330
defaultCSV = node.DefaultCSV
noFeeLimitMsat = math.MaxInt64
AddrTypeWitnessPubkeyHash = lnrpc.AddressType_WITNESS_PUBKEY_HASH
AddrTypeNestedPubkeyHash = lnrpc.AddressType_NESTED_PUBKEY_HASH
AddrTypeTaprootPubkey = lnrpc.AddressType_TAPROOT_PUBKEY
)
var (
harnessNetParams = &chaincfg.RegressionNetParams
// 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, postgres)")
nativeSQLFlag = flag.Bool("nativesql", false, "Database backend to "+
"use native SQL when applicable (only for sqlite and postgres")
// lndExecutable is the full path to the lnd binary.
lndExecutable = flag.String(
"lndexec", itestLndBinary, "full path to lnd binary",
)
)
// 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 'integration'")
}
// Get the test cases to be run in this tranche.
testCases, trancheIndex, trancheOffset := getTestCaseSplitTranche()
// Create a simple fee service.
feeService := lntest.NewFeeService(t)
// Get the binary path and setup the harness test.
binary := getLndBinary(t)
harnessTest := lntest.SetupHarness(
t, binary, *dbBackendFlag, *nativeSQLFlag, feeService,
)
defer harnessTest.Stop()
// Setup standby nodes, Alice and Bob, which will be alive and shared
// among all the test cases.
harnessTest.SetupStandbyNodes()
// 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), harnessTest.ChainBackendName(),
testCase.Name)
success := t.Run(name, func(t1 *testing.T) {
// Create a separate harness test for the testcase to
// avoid overwriting the external harness test that is
// tied to the parent test.
ht := harnessTest.Subtest(t1)
// TODO(yy): split log files.
cleanTestCaseName := strings.ReplaceAll(
testCase.Name, " ", "_",
)
ht.SetTestName(cleanTestCaseName)
logLine := fmt.Sprintf(
"STARTING ============ %v ============\n",
testCase.Name,
)
ht.Alice.AddToLogf(logLine)
ht.Bob.AddToLogf(logLine)
ht.EnsureConnected(ht.Alice, ht.Bob)
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
}
}
height := harnessTest.CurrentHeight()
t.Logf("=========> tests finished for tranche: %v, tested %d "+
"cases, end height: %d\n", trancheIndex, len(testCases), height)
}
// 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() ([]*lntest.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
}
func getLndBinary(t *testing.T) string {
binary := itestLndBinary
lndExec := ""
if lndExecutable != nil && *lndExecutable != "" {
lndExec = *lndExecutable
}
if lndExec == "" && runtime.GOOS == "windows" {
// Windows (even in a bash like environment like git bash as on
// Travis) doesn't seem to like relative paths to exe files...
currentDir, err := os.Getwd()
require.NoError(t, err, "unable to get working directory")
targetPath := filepath.Join(currentDir, "../../lnd-itest.exe")
binary, err = filepath.Abs(targetPath)
require.NoError(t, err, "unable to get absolute path")
} else if lndExec != "" {
binary = lndExec
}
return binary
}
func init() {
// 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 =
port.GenerateSystemUniqueListenerAddresses
// Swap out grpc's default logger with our fake logger which drops the
// statements on the floor.
fakeLogger := grpclog.NewLoggerV2(io.Discard, io.Discard, io.Discard)
grpclog.SetLoggerV2(fakeLogger)
}