From 11d7cae82bd51ebcd11d20d72c881868ce87f2c9 Mon Sep 17 00:00:00 2001 From: Jim Posen Date: Fri, 29 Sep 2017 14:36:37 -0700 Subject: [PATCH] rpctest: Compile current version of btcd and run that. Previously, rpctest would start a btcd node using the btcd executable in the environment PATH. This caused difficult-to-find issues where the code would be tested against an older version of btcd, or another fork entirely. Now it compiles btcd the first time it is needed and uses that fresh version when launching nodes. --- integration/rpctest/btcd.go | 73 ++++++++++++++++++++++++++++++ integration/rpctest/node.go | 14 ++++-- integration/rpctest/rpc_harness.go | 14 +++++- 3 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 integration/rpctest/btcd.go diff --git a/integration/rpctest/btcd.go b/integration/rpctest/btcd.go new file mode 100644 index 00000000..3c875197 --- /dev/null +++ b/integration/rpctest/btcd.go @@ -0,0 +1,73 @@ +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package rpctest + +import ( + "fmt" + "go/build" + "os/exec" + "path/filepath" + "runtime" + "sync" +) + +var ( + // compileMtx guards access to the executable path so that the project is + // only compiled once. + compileMtx sync.Mutex + + // executablePath is the path to the compiled executable. This is the empty + // string until btcd is compiled. This should not be accessed directly; + // instead use the function btcdExecutablePath(). + executablePath string +) + +// btcdExecutablePath returns a path to the btcd executable to be used by +// rpctests. To ensure the code tests against the most up-to-date version of +// btcd, this method compiles btcd the first time it is called. After that, the +// generated binary is used for subsequent test harnesses. The executable file +// is not cleaned up, but since it lives at a static path in a temp directory, +// it is not a big deal. +func btcdExecutablePath() (string, error) { + compileMtx.Lock() + defer compileMtx.Unlock() + + // If btcd has already been compiled, just use that. + if len(executablePath) != 0 { + return executablePath, nil + } + + testDir, err := baseDir() + if err != nil { + return "", err + } + + // Determine import path of this package. Not necessarily btcsuite/btcd if + // this is a forked repo. + _, rpctestDir, _, ok := runtime.Caller(1) + if !ok { + return "", fmt.Errorf("Cannot get path to btcd source code") + } + btcdPkgPath := filepath.Join(rpctestDir, "..", "..", "..") + btcdPkg, err := build.ImportDir(btcdPkgPath, build.FindOnly) + if err != nil { + return "", fmt.Errorf("Failed to build btcd: %v", err) + } + + // Build btcd and output an executable in a static temp path. + outputPath := filepath.Join(testDir, "btcd") + if runtime.GOOS == "windows" { + outputPath += ".exe" + } + cmd := exec.Command("go", "build", "-o", outputPath, btcdPkg.ImportPath) + err = cmd.Run() + if err != nil { + return "", fmt.Errorf("Failed to build btcd: %v", err) + } + + // Save executable path so future calls do not recompile. + executablePath = outputPath + return executablePath, nil +} diff --git a/integration/rpctest/node.go b/integration/rpctest/node.go index f56a088a..4d224130 100644 --- a/integration/rpctest/node.go +++ b/integration/rpctest/node.go @@ -42,6 +42,11 @@ type nodeConfig struct { // newConfig returns a newConfig with all default values. func newConfig(prefix, certFile, keyFile string, extra []string) (*nodeConfig, error) { + btcdPath, err := btcdExecutablePath() + if err != nil { + return nil, err + } + a := &nodeConfig{ listen: "127.0.0.1:18555", rpcListen: "127.0.0.1:18556", @@ -49,11 +54,10 @@ func newConfig(prefix, certFile, keyFile string, extra []string) (*nodeConfig, e rpcPass: "pass", extra: extra, prefix: prefix, - - exe: "btcd", - endpoint: "ws", - certFile: certFile, - keyFile: keyFile, + exe: btcdPath, + endpoint: "ws", + certFile: certFile, + keyFile: keyFile, } if err := a.setDefaults(); err != nil { return nil, err diff --git a/integration/rpctest/rpc_harness.go b/integration/rpctest/rpc_harness.go index 145c2938..653a3782 100644 --- a/integration/rpctest/rpc_harness.go +++ b/integration/rpctest/rpc_harness.go @@ -119,8 +119,13 @@ func New(activeNet *chaincfg.Params, handlers *rpcclient.NotificationHandlers, "of the supported chain networks") } + testDir, err := baseDir() + if err != nil { + return nil, err + } + harnessID := strconv.Itoa(numTestInstances) - nodeTestData, err := ioutil.TempDir("", "rpctest-"+harnessID) + nodeTestData, err := ioutil.TempDir(testDir, "harness-"+harnessID) if err != nil { return nil, err } @@ -453,3 +458,10 @@ func generateListeningAddresses() (string, string) { rpc := net.JoinHostPort(localhost, portString(minRPCPort, maxRPCPort)) return p2p, rpc } + +// baseDir is the directory path of the temp directory for all rpctest files. +func baseDir() (string, error) { + dirPath := filepath.Join(os.TempDir(), "btcd", "rpctest") + err := os.MkdirAll(dirPath, 0755) + return dirPath, err +}