Merge pull request #4655 from guggero/itest-parallel

Travis: run itests in parallel, get ~60% speedup overall
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2020-11-04 16:16:49 -08:00
commit 0552ba93c8
15 files changed
+257 -91

No files matched your search

+2
View File
@@ -36,6 +36,8 @@ lntest/itest/output*.log
lntest/itest/pprof*.log lntest/itest/pprof*.log
lntest/itest/.backendlogs lntest/itest/.backendlogs
lntest/itest/.minerlogs lntest/itest/.minerlogs
lntest/itest/lnd-itest
lntest/itest/.logs-*
cmd/cmd cmd/cmd
*.key *.key
+10 -11
View File
@@ -50,32 +50,30 @@ jobs:
- stage: Integration Test - stage: Integration Test
name: Btcd Integration name: Btcd Integration
script: script:
- make itest - make itest-parallel
- name: Bitcoind Integration (txindex enabled) - name: Bitcoind Integration (txindex enabled)
script: script:
- bash ./scripts/install_bitcoind.sh - bash ./scripts/install_bitcoind.sh
- make itest backend=bitcoind - make itest-parallel backend=bitcoind
- name: Bitcoind Integration (txindex disabled) - name: Bitcoind Integration (txindex disabled)
script: script:
- bash ./scripts/install_bitcoind.sh - bash ./scripts/install_bitcoind.sh
- make itest backend="bitcoind notxindex" - make itest-parallel backend="bitcoind notxindex"
- name: Neutrino Integration - name: Neutrino Integration
script: script:
- make itest backend=neutrino - make itest-parallel backend=neutrino
- name: Btcd Integration ARM - name: Btcd Integration ARM
script: script:
- GOARM=7 GOARCH=arm GOOS=linux CGO_ENABLED=0 make btcd build-itest - GOARM=7 GOARCH=arm GOOS=linux make itest-parallel
- file lnd-itest
- GOARM=7 GOARCH=arm GOOS=linux CGO_ENABLED=0 make itest-only
arch: arm64 arch: arm64
- name: Btcd Integration Windows - name: Btcd Integration Windows
script: script:
- make itest-windows - make itest-parallel-windows
os: windows os: windows
before_install: before_install:
- choco upgrade --no-progress -y make netcat curl findutils - choco upgrade --no-progress -y make netcat curl findutils
@@ -85,7 +83,8 @@ jobs:
case $TRAVIS_OS_NAME in case $TRAVIS_OS_NAME in
windows) windows)
echo "Uploading to termbin.com..." echo "Uploading to termbin.com..."
for f in ./lntest/itest/*.log; do cat $f | nc termbin.com 9999 | xargs -r0 printf "$f"' uploaded to %s'; done LOG_FILES=$(find ./lntest/itest -name '*.log')
for f in $LOG_FILES; do echo -n $f; cat $f | nc termbin.com 9999 | xargs -r0 printf ' uploaded to %s'; done
;; ;;
esac esac
@@ -97,8 +96,8 @@ after_failure:
;; ;;
*) *)
LOG_FILES=./lntest/itest/*.log LOG_FILES=$(find ./lntest/itest -name '*.log')
echo "Uploading to termbin.com..." && find $LOG_FILES | xargs -I{} sh -c "cat {} | nc termbin.com 9999 | xargs -r0 printf '{} uploaded to %s'" echo "Uploading to termbin.com..." && for f in $LOG_FILES; do echo -n $f; cat $f | nc termbin.com 9999 | xargs -r0 printf ' uploaded to %s'; done
echo "Uploading to file.io..." && tar -zcvO $LOG_FILES | curl -s -F 'file=@-;filename=logs.tar.gz' https://file.io | xargs -r0 printf 'logs.tar.gz uploaded to %s\n' echo "Uploading to file.io..." && tar -zcvO $LOG_FILES | curl -s -F 'file=@-;filename=logs.tar.gz' https://file.io | xargs -r0 printf 'logs.tar.gz uploaded to %s\n'
;; ;;
esac esac
+21
View File
@@ -175,6 +175,27 @@ itest-only:
itest: btcd build-itest itest-only itest: btcd build-itest itest-only
itest-parallel: btcd
@$(call print, "Building lnd binary")
CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o lntest/itest/lnd-itest $(ITEST_LDFLAGS) $(PKG)/cmd/lnd
@$(call print, "Building itest binary for $(backend) backend")
CGO_ENABLED=0 $(GOTEST) -v ./lntest/itest -tags="$(DEV_TAGS) $(RPC_TAGS) rpctest $(backend)" -logoutput -goroutinedump -c -o lntest/itest/itest.test
@$(call print, "Running tests")
rm -rf lntest/itest/*.log lntest/itest/.logs-*
echo -n "$$(seq 0 $$(expr $(NUM_ITEST_TRANCHES) - 1))" | xargs -P $(NUM_ITEST_TRANCHES) -n 1 -I {} scripts/itest_part.sh {} $(NUM_ITEST_TRANCHES) $(TEST_FLAGS)
itest-parallel-windows: btcd
@$(call print, "Building lnd binary")
CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o lntest/itest/lnd-itest.exe $(ITEST_LDFLAGS) $(PKG)/cmd/lnd
@$(call print, "Building itest binary for $(backend) backend")
CGO_ENABLED=0 $(GOTEST) -v ./lntest/itest -tags="$(DEV_TAGS) $(RPC_TAGS) rpctest $(backend)" -logoutput -goroutinedump -c -o lntest/itest/itest.test.exe
@$(call print, "Running tests")
EXEC_SUFFIX=".exe" echo -n "$$(seq 0 $$(expr $(NUM_ITEST_TRANCHES) - 1))" | xargs -P $(NUM_ITEST_TRANCHES) -n 1 -I {} scripts/itest_part.sh {} $(NUM_ITEST_TRANCHES) $(TEST_FLAGS)
itest-windows: btcd build-itest-windows itest-only itest-windows: btcd build-itest-windows itest-only
unit: btcd unit: btcd
+19 -16
View File
@@ -6,7 +6,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/rand"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@@ -16,8 +15,8 @@ import (
"github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/rpcclient"
) )
// logDir is the name of the temporary log directory. // logDirPattern is the pattern of the name of the temporary log directory.
const logDir = "./.backendlogs" const logDirPattern = "%s/.backendlogs"
// BitcoindBackendConfig is an implementation of the BackendConfig interface // BitcoindBackendConfig is an implementation of the BackendConfig interface
// backed by a Bitcoind node. // backed by a Bitcoind node.
@@ -74,15 +73,16 @@ func (b BitcoindBackendConfig) Name() string {
func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string) ( func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string) (
*BitcoindBackendConfig, func() error, error) { *BitcoindBackendConfig, func() error, error) {
baseLogDir := fmt.Sprintf(logDirPattern, GetLogDir())
if netParams != &chaincfg.RegressionNetParams { if netParams != &chaincfg.RegressionNetParams {
return nil, nil, fmt.Errorf("only regtest supported") return nil, nil, fmt.Errorf("only regtest supported")
} }
if err := os.MkdirAll(logDir, 0700); err != nil { if err := os.MkdirAll(baseLogDir, 0700); err != nil {
return nil, nil, err return nil, nil, err
} }
logFile, err := filepath.Abs(logDir + "/bitcoind.log") logFile, err := filepath.Abs(baseLogDir + "/bitcoind.log")
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -93,10 +93,10 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string) (
fmt.Errorf("unable to create temp directory: %v", err) fmt.Errorf("unable to create temp directory: %v", err)
} }
zmqBlockPath := "ipc:///" + tempBitcoindDir + "/blocks.socket" zmqBlockAddr := fmt.Sprintf("tcp://127.0.0.1:%d", nextAvailablePort())
zmqTxPath := "ipc:///" + tempBitcoindDir + "/txs.socket" zmqTxAddr := fmt.Sprintf("tcp://127.0.0.1:%d", nextAvailablePort())
rpcPort := rand.Int()%(65536-1024) + 1024 rpcPort := nextAvailablePort()
p2pPort := rand.Int()%(65536-1024) + 1024 p2pPort := nextAvailablePort()
cmdArgs := []string{ cmdArgs := []string{
"-datadir=" + tempBitcoindDir, "-datadir=" + tempBitcoindDir,
@@ -106,8 +106,8 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string) (
"220110063096c221be9933c82d38e1", "220110063096c221be9933c82d38e1",
fmt.Sprintf("-rpcport=%d", rpcPort), fmt.Sprintf("-rpcport=%d", rpcPort),
fmt.Sprintf("-port=%d", p2pPort), fmt.Sprintf("-port=%d", p2pPort),
"-zmqpubrawblock=" + zmqBlockPath, "-zmqpubrawblock=" + zmqBlockAddr,
"-zmqpubrawtx=" + zmqTxPath, "-zmqpubrawtx=" + zmqTxAddr,
"-debuglogfile=" + logFile, "-debuglogfile=" + logFile,
} }
cmdArgs = append(cmdArgs, extraArgs...) cmdArgs = append(cmdArgs, extraArgs...)
@@ -129,13 +129,16 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string) (
var errStr string var errStr string
// After shutting down the chain backend, we'll make a copy of // After shutting down the chain backend, we'll make a copy of
// the log file before deleting the temporary log dir. // the log file before deleting the temporary log dir.
err := CopyFile("./output_bitcoind_chainbackend.log", logFile) logDestination := fmt.Sprintf(
"%s/output_bitcoind_chainbackend.log", GetLogDir(),
)
err := CopyFile(logDestination, logFile)
if err != nil { if err != nil {
errStr += fmt.Sprintf("unable to copy file: %v\n", err) errStr += fmt.Sprintf("unable to copy file: %v\n", err)
} }
if err = os.RemoveAll(logDir); err != nil { if err = os.RemoveAll(baseLogDir); err != nil {
errStr += fmt.Sprintf( errStr += fmt.Sprintf(
"cannot remove dir %s: %v\n", logDir, err, "cannot remove dir %s: %v\n", baseLogDir, err,
) )
} }
if err := os.RemoveAll(tempBitcoindDir); err != nil { if err := os.RemoveAll(tempBitcoindDir); err != nil {
@@ -178,8 +181,8 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string) (
rpcHost: rpcHost, rpcHost: rpcHost,
rpcUser: rpcUser, rpcUser: rpcUser,
rpcPass: rpcPass, rpcPass: rpcPass,
zmqBlockPath: zmqBlockPath, zmqBlockPath: zmqBlockAddr,
zmqTxPath: zmqTxPath, zmqTxPath: zmqTxAddr,
p2pPort: p2pPort, p2pPort: p2pPort,
rpcClient: client, rpcClient: client,
minerAddr: miner, minerAddr: miner,
+11 -7
View File
@@ -14,8 +14,8 @@ import (
"github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/rpcclient"
) )
// logDir is the name of the temporary log directory. // logDirPattern is the pattern of the name of the temporary log directory.
const logDir = "./.backendlogs" const logDirPattern = "%s/.backendlogs"
// temp is used to signal we want to establish a temporary connection using the // temp is used to signal we want to establish a temporary connection using the
// btcd Node API. // btcd Node API.
@@ -75,12 +75,13 @@ func (b BtcdBackendConfig) Name() string {
func NewBackend(miner string, netParams *chaincfg.Params) ( func NewBackend(miner string, netParams *chaincfg.Params) (
*BtcdBackendConfig, func() error, error) { *BtcdBackendConfig, func() error, error) {
baseLogDir := fmt.Sprintf(logDirPattern, GetLogDir())
args := []string{ args := []string{
"--rejectnonstd", "--rejectnonstd",
"--txindex", "--txindex",
"--trickleinterval=100ms", "--trickleinterval=100ms",
"--debuglevel=debug", "--debuglevel=debug",
"--logdir=" + logDir, "--logdir=" + baseLogDir,
"--nowinservice", "--nowinservice",
// The miner will get banned and disconnected from the node if // The miner will get banned and disconnected from the node if
// its requested data are not found. We add a nobanning flag to // its requested data are not found. We add a nobanning flag to
@@ -110,14 +111,17 @@ func NewBackend(miner string, netParams *chaincfg.Params) (
// After shutting down the chain backend, we'll make a copy of // After shutting down the chain backend, we'll make a copy of
// the log file before deleting the temporary log dir. // the log file before deleting the temporary log dir.
logFile := logDir + "/" + netParams.Name + "/btcd.log" logFile := baseLogDir + "/" + netParams.Name + "/btcd.log"
err := CopyFile("./output_btcd_chainbackend.log", logFile) logDestination := fmt.Sprintf(
"%s/output_btcd_chainbackend.log", GetLogDir(),
)
err := CopyFile(logDestination, logFile)
if err != nil { if err != nil {
errStr += fmt.Sprintf("unable to copy file: %v\n", err) errStr += fmt.Sprintf("unable to copy file: %v\n", err)
} }
if err = os.RemoveAll(logDir); err != nil { if err = os.RemoveAll(baseLogDir); err != nil {
errStr += fmt.Sprintf( errStr += fmt.Sprintf(
"cannot remove dir %s: %v\n", logDir, err, "cannot remove dir %s: %v\n", baseLogDir, err,
) )
} }
if errStr != "" { if errStr != "" {
+3 -7
View File
@@ -16,9 +16,6 @@ const (
// is returned. Requests for higher confirmation targets will fall back // is returned. Requests for higher confirmation targets will fall back
// to this. // to this.
feeServiceTarget = 2 feeServiceTarget = 2
// feeServicePort is the tcp port on which the service runs.
feeServicePort = 16534
) )
// feeService runs a web service that provides fee estimation information. // feeService runs a web service that provides fee estimation information.
@@ -40,16 +37,15 @@ type feeEstimates struct {
// startFeeService spins up a go-routine to serve fee estimates. // startFeeService spins up a go-routine to serve fee estimates.
func startFeeService() *feeService { func startFeeService() *feeService {
port := nextAvailablePort()
f := feeService{ f := feeService{
url: fmt.Sprintf( url: fmt.Sprintf("http://localhost:%v/fee-estimates.json", port),
"http://localhost:%v/fee-estimates.json", feeServicePort,
),
} }
// Initialize default fee estimate. // Initialize default fee estimate.
f.Fees = map[uint32]uint32{feeServiceTarget: 50000} f.Fees = map[uint32]uint32{feeServiceTarget: 50000}
listenAddr := fmt.Sprintf(":%v", feeServicePort) listenAddr := fmt.Sprintf(":%v", port)
f.srv = &http.Server{ f.srv = &http.Server{
Addr: listenAddr, Addr: listenAddr,
} }
+4
View File
@@ -1012,6 +1012,10 @@ func testChanRestoreScenario(t *harnessTest, net *lntest.NetworkHarness,
require.Contains(t.t, err.Error(), "cannot close channel with state: ") require.Contains(t.t, err.Error(), "cannot close channel with state: ")
require.Contains(t.t, err.Error(), "ChanStatusRestored") require.Contains(t.t, err.Error(), "ChanStatusRestored")
// Increase the fee estimate so that the following force close tx will
// be cpfp'ed in case of anchor commitments.
net.SetFeeEstimate(30000)
// Now that we have ensured that the channels restored by the backup are // Now that we have ensured that the channels restored by the backup are
// in the correct state even without the remote peer telling us so, // in the correct state even without the remote peer telling us so,
// let's start up Carol again. // let's start up Carol again.
@@ -101,15 +101,17 @@ func testMultiHopHtlcRemoteChainClaim(net *lntest.NetworkHarness, t *harnessTest
// bob will attempt to redeem his anchor commitment (if the channel // bob will attempt to redeem his anchor commitment (if the channel
// type is of that type). // type is of that type).
if c == commitTypeAnchors { if c == commitTypeAnchors {
_, err = waitForNTxsInMempool(net.Miner.Node, 1, minerMempoolTimeout) _, err = waitForNTxsInMempool(
net.Miner.Node, 1, minerMempoolTimeout,
)
if err != nil { if err != nil {
t.Fatalf("unable to find bob's anchor commit sweep: %v", err) t.Fatalf("unable to find bob's anchor commit sweep: %v",
err)
} }
} }
// Mine enough blocks for Alice to sweep her funds from the force // Mine enough blocks for Alice to sweep her funds from the force
// closed channel. closeCHannelAndAssertType() already mined a block // closed channel. closeChannelAndAssertType() already mined a block
// containing the commitment tx and the commit sweep tx will be // containing the commitment tx and the commit sweep tx will be
// broadcast immediately before it can be included in a block, so mine // broadcast immediately before it can be included in a block, so mine
// one less than defaultCSV in order to perform mempool assertions. // one less than defaultCSV in order to perform mempool assertions.
+78 -27
View File
@@ -6,6 +6,7 @@ import (
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"flag"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@@ -13,7 +14,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@@ -53,6 +53,60 @@ import (
"github.com/stretchr/testify/require" "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",
)
)
// 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
}
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], runTranche, trancheOffset
}
func rpcPointToWirePoint(t *harnessTest, chanPoint *lnrpc.ChannelPoint) wire.OutPoint { func rpcPointToWirePoint(t *harnessTest, chanPoint *lnrpc.ChannelPoint) wire.OutPoint {
txid, err := lnd.GetChanPointFundingTxid(chanPoint) txid, err := lnd.GetChanPointFundingTxid(chanPoint)
if err != nil { if err != nil {
@@ -2380,7 +2434,7 @@ func testOpenChannelAfterReorg(net *lntest.NetworkHarness, t *harnessTest) {
) )
// Set up a new miner that we can use to cause a reorg. // Set up a new miner that we can use to cause a reorg.
tempLogDir := "./.tempminerlogs" tempLogDir := fmt.Sprintf("%s/.tempminerlogs", lntest.GetLogDir())
logFilename := "output-open_channel_reorg-temp_miner.log" logFilename := "output-open_channel_reorg-temp_miner.log"
tempMiner, tempMinerCleanUp, err := lntest.NewMiner( tempMiner, tempMinerCleanUp, err := lntest.NewMiner(
tempLogDir, logFilename, tempLogDir, logFilename,
@@ -14098,10 +14152,16 @@ func getPaymentResult(stream routerrpc.Router_SendPaymentV2Client) (
// programmatically driven network of lnd nodes. // programmatically driven network of lnd nodes.
func TestLightningNetworkDaemon(t *testing.T) { func TestLightningNetworkDaemon(t *testing.T) {
// If no tests are registered, then we can exit early. // If no tests are registered, then we can exit early.
if len(testsCases) == 0 { if len(allTestCases) == 0 {
t.Skip("integration tests not selected with flag 'rpctest'") 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)
ht := newHarnessTest(t, nil) ht := newHarnessTest(t, nil)
// Declare the network harness here to gain access to its // Declare the network harness here to gain access to its
@@ -14117,7 +14177,7 @@ func TestLightningNetworkDaemon(t *testing.T) {
// guarantees of getting included in to blocks. // guarantees of getting included in to blocks.
// //
// We will also connect it to our chain backend. // We will also connect it to our chain backend.
minerLogDir := "./.minerlogs" minerLogDir := fmt.Sprintf("%s/.minerlogs", logDir)
miner, minerCleanUp, err := lntest.NewMiner( miner, minerCleanUp, err := lntest.NewMiner(
minerLogDir, "output_btcd_miner.log", minerLogDir, "output_btcd_miner.log",
harnessNetParams, &rpcclient.NotificationHandlers{}, harnessNetParams, &rpcclient.NotificationHandlers{},
@@ -14149,27 +14209,12 @@ func TestLightningNetworkDaemon(t *testing.T) {
// Connect chainbackend to miner. // Connect chainbackend to miner.
require.NoError( require.NoError(
t, chainBackend.ConnectMiner(), t, chainBackend.ConnectMiner(), "failed to connect to miner",
"failed to connect to miner",
) )
binary := itestLndBinary
if 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()
if err != nil {
ht.Fatalf("unable to get working directory: %v", err)
}
targetPath := filepath.Join(currentDir, "../../lnd-itest.exe")
binary, err = filepath.Abs(targetPath)
if err != nil {
ht.Fatalf("unable to get absolute path: %v", err)
}
}
// Now we can set up our test harness (LND instance), with the chain // Now we can set up our test harness (LND instance), with the chain
// backend we just created. // backend we just created.
binary := ht.getLndBinary()
lndHarness, err = lntest.NewNetworkHarness(miner, chainBackend, binary) lndHarness, err = lntest.NewNetworkHarness(miner, chainBackend, binary)
if err != nil { if err != nil {
ht.Fatalf("unable to create lightning network harness: %v", err) ht.Fatalf("unable to create lightning network harness: %v", err)
@@ -14187,7 +14232,8 @@ func TestLightningNetworkDaemon(t *testing.T) {
if !more { if !more {
return return
} }
ht.Logf("lnd finished with error (stderr):\n%v", err) ht.Logf("lnd finished with error (stderr):\n%v",
err)
} }
} }
}() }()
@@ -14210,8 +14256,9 @@ func TestLightningNetworkDaemon(t *testing.T) {
ht.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(testsCases)) // Run the subset of the test cases selected in this tranche.
for _, testCase := range testsCases { for idx, testCase := range testCases {
testCase := testCase
logLine := fmt.Sprintf("STARTING ============ %v ============\n", logLine := fmt.Sprintf("STARTING ============ %v ============\n",
testCase.name) testCase.name)
@@ -14232,7 +14279,10 @@ func TestLightningNetworkDaemon(t *testing.T) {
// Start every test with the default static fee estimate. // Start every test with the default static fee estimate.
lndHarness.SetFeeEstimate(12500) lndHarness.SetFeeEstimate(12500)
success := t.Run(testCase.name, func(t1 *testing.T) { name := fmt.Sprintf("%02d-of-%d/%s/%s",
trancheOffset+uint(idx)+1, len(allTestCases),
chainBackend.Name(), testCase.name)
success := t.Run(name, func(t1 *testing.T) {
ht := newHarnessTest(t1, lndHarness) ht := newHarnessTest(t1, lndHarness)
ht.RunTestCase(testCase) ht.RunTestCase(testCase)
}) })
@@ -14242,8 +14292,9 @@ func TestLightningNetworkDaemon(t *testing.T) {
if !success { if !success {
// Log failure time to help relate the lnd logs to the // Log failure time to help relate the lnd logs to the
// failure. // failure.
t.Logf("Failure time: %v", t.Logf("Failure time: %v", time.Now().Format(
time.Now().Format("2006-01-02 15:04:05.000")) "2006-01-02 15:04:05.000",
))
break break
} }
} }
+1 -1
View File
@@ -2,4 +2,4 @@
package itest package itest
var testsCases = []*testCase{} var allTestCases = []*testCase{}
+5 -5
View File
@@ -2,7 +2,11 @@
package itest package itest
var testsCases = []*testCase{ var allTestCases = []*testCase{
{
name: "test multi-hop htlc",
test: testMultiHopHtlcClaims,
},
{ {
name: "sweep coins", name: "sweep coins",
test: testSweepAllCoins, test: testSweepAllCoins,
@@ -144,10 +148,6 @@ var testsCases = []*testCase{
name: "async bidirectional payments", name: "async bidirectional payments",
test: testBidirectionalAsyncPayments, test: testBidirectionalAsyncPayments,
}, },
{
name: "test multi-hop htlc",
test: testMultiHopHtlcClaims,
},
{ {
name: "switch circuit persistence", name: "switch circuit persistence",
test: testSwitchCircuitPersistence, test: testSwitchCircuitPersistence,
+34
View File
@@ -3,8 +3,12 @@ package itest
import ( import (
"bytes" "bytes"
"context" "context"
"flag"
"fmt" "fmt"
"math" "math"
"os"
"path/filepath"
"runtime"
"testing" "testing"
"time" "time"
@@ -20,6 +24,11 @@ import (
var ( var (
harnessNetParams = &chaincfg.RegressionNetParams harnessNetParams = &chaincfg.RegressionNetParams
// lndExecutable is the full path to the lnd binary.
lndExecutable = flag.String(
"lndexec", itestLndBinary, "full path to lnd binary",
)
) )
const ( const (
@@ -111,6 +120,31 @@ func (h *harnessTest) Log(args ...interface{}) {
h.t.Log(args...) h.t.Log(args...)
} }
func (h *harnessTest) getLndBinary() 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()
if err != nil {
h.Fatalf("unable to get working directory: %v", err)
}
targetPath := filepath.Join(currentDir, "../../lnd-itest.exe")
binary, err = filepath.Abs(targetPath)
if err != nil {
h.Fatalf("unable to get absolute path: %v", err)
}
} else if lndExec != "" {
binary = lndExec
}
return binary
}
type testCase struct { type testCase struct {
name string name string
test func(net *lntest.NetworkHarness, t *harnessTest) test func(net *lntest.NetworkHarness, t *harnessTest)
+33 -12
View File
@@ -43,7 +43,7 @@ const (
// defaultNodePort is the start of the range for listening ports of // defaultNodePort is the start of the range for listening ports of
// harness nodes. Ports are monotonically increasing starting from this // harness nodes. Ports are monotonically increasing starting from this
// number and are determined by the results of nextAvailablePort(). // number and are determined by the results of nextAvailablePort().
defaultNodePort = 19555 defaultNodePort = 5555
// logPubKeyBytes is the number of bytes of the node's PubKey that will // logPubKeyBytes is the number of bytes of the node's PubKey that will
// be appended to the log file name. The whole PubKey is too long and // be appended to the log file name. The whole PubKey is too long and
@@ -70,6 +70,10 @@ var (
logOutput = flag.Bool("logoutput", false, logOutput = flag.Bool("logoutput", false,
"log output from node n to file output-n.log") "log output from node n to file output-n.log")
// logSubDir is the default directory where the logs are written to if
// logOutput is true.
logSubDir = flag.String("logdir", ".", "default dir to write logs to")
// goroutineDump is a flag that can be set to dump the active // goroutineDump is a flag that can be set to dump the active
// goroutines of test nodes on failure. // goroutines of test nodes on failure.
goroutineDump = flag.Bool("goroutinedump", false, goroutineDump = flag.Bool("goroutinedump", false,
@@ -104,6 +108,21 @@ func nextAvailablePort() int {
panic("no ports available for listening") panic("no ports available for listening")
} }
// ApplyPortOffset adds the given offset to the lastPort variable, making it
// possible to run the tests in parallel without colliding on the same ports.
func ApplyPortOffset(offset uint32) {
_ = atomic.AddUint32(&lastPort, offset)
}
// GetLogDir returns the passed --logdir flag or the default value if it wasn't
// set.
func GetLogDir() string {
if logSubDir != nil && *logSubDir != "" {
return *logSubDir
}
return "."
}
// generateListeningPorts returns four ints representing ports to listen on // generateListeningPorts returns four ints representing ports to listen on
// designated for the current lightning network test. This returns the next // designated for the current lightning network test. This returns the next
// available ports for the p2p, rpc, rest and profiling services. // available ports for the p2p, rpc, rest and profiling services.
@@ -386,11 +405,9 @@ func NewMiner(logDir, logFilename string, netParams *chaincfg.Params,
// After shutting down the miner, we'll make a copy of the log // After shutting down the miner, we'll make a copy of the log
// file before deleting the temporary log dir. // file before deleting the temporary log dir.
logFile := fmt.Sprintf( logFile := fmt.Sprintf("%s/%s/btcd.log", logDir, netParams.Name)
"%s/%s/btcd.log", logDir, netParams.Name, copyPath := fmt.Sprintf("%s/../%s", logDir, logFilename)
) err := CopyFile(filepath.Clean(copyPath), logFile)
copyPath := fmt.Sprintf("./%s", logFilename)
err := CopyFile(copyPath, logFile)
if err != nil { if err != nil {
return fmt.Errorf("unable to copy file: %v", err) return fmt.Errorf("unable to copy file: %v", err)
} }
@@ -475,24 +492,28 @@ func (hn *HarnessNode) start(lndBinary string, lndError chan<- error) error {
// If the logoutput flag is passed, redirect output from the nodes to // If the logoutput flag is passed, redirect output from the nodes to
// log files. // log files.
if *logOutput { if *logOutput {
fileName := fmt.Sprintf("output-%d-%s-%s.log", hn.NodeID, dir := GetLogDir()
fileName := fmt.Sprintf("%s/output-%d-%s-%s.log", dir, hn.NodeID,
hn.Cfg.Name, hex.EncodeToString(hn.PubKey[:logPubKeyBytes])) hn.Cfg.Name, hex.EncodeToString(hn.PubKey[:logPubKeyBytes]))
// If the node's PubKey is not yet initialized, create a temporary // If the node's PubKey is not yet initialized, create a temporary
// file name. Later, after the PubKey has been initialized, the // file name. Later, after the PubKey has been initialized, the
// file can be moved to its final name with the PubKey included. // file can be moved to its final name with the PubKey included.
if bytes.Equal(hn.PubKey[:4], []byte{0, 0, 0, 0}) { if bytes.Equal(hn.PubKey[:4], []byte{0, 0, 0, 0}) {
fileName = fmt.Sprintf("output-%d-%s-tmp__.log", hn.NodeID, fileName = fmt.Sprintf("%s/output-%d-%s-tmp__.log",
hn.Cfg.Name) dir, hn.NodeID, hn.Cfg.Name)
// Once the node has done its work, the log file can be renamed. // Once the node has done its work, the log file can be renamed.
finalizeLogfile = func() { finalizeLogfile = func() {
if hn.logFile != nil { if hn.logFile != nil {
hn.logFile.Close() hn.logFile.Close()
newFileName := fmt.Sprintf("output-%d-%s-%s.log", pubKeyHex := hex.EncodeToString(
hn.NodeID, hn.Cfg.Name, hn.PubKey[:logPubKeyBytes],
hex.EncodeToString(hn.PubKey[:logPubKeyBytes])) )
newFileName := fmt.Sprintf("%s/output"+
"-%d-%s-%s.log", dir, hn.NodeID,
hn.Cfg.Name, pubKeyHex)
err := os.Rename(fileName, newFileName) err := os.Rename(fileName, newFileName)
if err != nil { if err != nil {
fmt.Printf("could not rename "+ fmt.Printf("could not rename "+
+7 -1
View File
@@ -3,12 +3,18 @@ RPC_TAGS = autopilotrpc chainrpc invoicesrpc routerrpc signrpc verrpc walletrpc
LOG_TAGS = LOG_TAGS =
TEST_FLAGS = TEST_FLAGS =
COVER_PKG = $$(go list -deps ./... | grep '$(PKG)' | grep -v lnrpc) COVER_PKG = $$(go list -deps ./... | grep '$(PKG)' | grep -v lnrpc)
NUM_ITEST_TRANCHES = 6
# If rpc option is set also add all extra RPC tags to DEV_TAGS # If rpc option is set also add all extra RPC tags to DEV_TAGS
ifneq ($(with-rpc),) ifneq ($(with-rpc),)
DEV_TAGS += $(RPC_TAGS) DEV_TAGS += $(RPC_TAGS)
endif endif
# Scale the number of parallel running itest tranches.
ifneq ($(tranches),)
NUM_ITEST_TRANCHES = $(tranches)
endif
# If specific package is being unit tested, construct the full name of the # If specific package is being unit tested, construct the full name of the
# subpackage. # subpackage.
ifneq ($(pkg),) ifneq ($(pkg),)
@@ -25,7 +31,7 @@ endif
# Define the integration test.run filter if the icase argument was provided. # Define the integration test.run filter if the icase argument was provided.
ifneq ($(icase),) ifneq ($(icase),)
TEST_FLAGS += -test.run=TestLightningNetworkDaemon/$(icase) TEST_FLAGS += -test.run="TestLightningNetworkDaemon/.*-of-.*/.*/$(icase)"
endif endif
ifneq ($(tags),) ifneq ($(tags),)
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
# Let's work with absolute paths only, we run in the itest directory itself.
WORKDIR=$(pwd)/lntest/itest
TRANCHE=$1
NUM_TRANCHES=$2
# Shift the passed parameters by two, giving us all remaining testing flags in
# the $@ special variable.
shift
shift
# Windows insists on having the .exe suffix for an executable, we need to add
# that here if necessary.
EXEC="$WORKDIR"/itest.test"$EXEC_SUFFIX"
LND_EXEC="$WORKDIR"/lnd-itest"$EXEC_SUFFIX"
echo $EXEC -test.v "$@" -logoutput -goroutinedump -logdir=.logs-tranche$TRANCHE -lndexec=$LND_EXEC -splittranches=$NUM_TRANCHES -runtranche=$TRANCHE
# Exit code 255 causes the parallel jobs to abort, so if one part fails the
# other is aborted too.
cd "$WORKDIR" || exit 255
$EXEC -test.v "$@" -logoutput -goroutinedump -logdir=.logs-tranche$TRANCHE -lndexec=$LND_EXEC -splittranches=$NUM_TRANCHES -runtranche=$TRANCHE || exit 255