Start refactoring testkit to allow for specifying a different binary … (#2325)

* Start refactoring testkit to allow for specifying a different binary directory than the sbt default

* Fix BitcoindRpcTestUtil

* Add BitcoindRpcTestClient
This commit is contained in:
Chris Stewart 2020-12-07 13:14:40 -06:00 committed by GitHub
parent 097398499a
commit 7df02c4198
16 changed files with 405 additions and 159 deletions

View file

@ -3,7 +3,6 @@ package org.bitcoins.rpc
import java.io.{File, PrintWriter}
import java.net.URI
import java.nio.file.{Files, Path}
import akka.stream.StreamTcpException
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.currency.Bitcoins
@ -16,7 +15,7 @@ import org.bitcoins.rpc.config.{
import org.bitcoins.rpc.util.RpcUtil
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil.newestBitcoindBinary
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.testkit.util.{BitcoindRpcTest, FileUtil}
import org.scalatest.compatible.Assertion
import scala.concurrent.Future
@ -66,7 +65,7 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|rpcport=${RpcUtil.randomPort}
""".stripMargin
val conf = BitcoindConfig(confStr, BitcoindRpcTestUtil.tmpDir())
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
val instance = BitcoindInstance.fromConfig(conf, newestBitcoindBinary)
assert(
instance.authCredentials
@ -86,7 +85,7 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|rpcport=${RpcUtil.randomPort}
""".stripMargin
val conf = BitcoindConfig(confStr, BitcoindRpcTestUtil.tmpDir())
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
val instance = BitcoindInstance.fromConfig(conf, newestBitcoindBinary)
assert(
instance.authCredentials
@ -113,7 +112,7 @@ class BitcoindInstanceTest extends BitcoindRpcTest {
|rpcport=${RpcUtil.randomPort}
""".stripMargin
val conf = BitcoindConfig(confStr, BitcoindRpcTestUtil.tmpDir())
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
val authCredentials =
BitcoindAuthCredentials.PasswordBased(username = "bitcoin-s",
password = "strong_password")

View file

@ -24,8 +24,8 @@ class BlockchainRpcTest extends BitcoindRpcTest {
case (_, _) =>
val pruneClient =
BitcoindRpcClient.withActorSystem(
BitcoindRpcTestUtil.instance(pruneMode = true,
versionOpt = Some(BitcoindVersion.V17)))
BitcoindRpcTestUtil
.instance(pruneMode = true, versionOpt = Some(BitcoindVersion.V17)))
clientAccum += pruneClient

View file

@ -1,6 +1,6 @@
package org.bitcoins.rpc.config
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, FileUtil}
import org.bitcoins.rpc.config.BitcoindAuthCredentials.CookieBased
import org.bitcoins.rpc.config.BitcoindAuthCredentials.PasswordBased
import org.bitcoins.core.config.RegTest
@ -11,7 +11,7 @@ class BitcoindAuthCredentialsTest extends BitcoinSUnitTest {
val confStr = """
|regtest=1
""".stripMargin
val conf = BitcoindConfig(confStr, BitcoindRpcTestUtil.tmpDir())
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
val auth = BitcoindAuthCredentials.fromConfig(conf)
val cookie = auth match {
case cookie: CookieBased => cookie
@ -29,7 +29,7 @@ class BitcoindAuthCredentialsTest extends BitcoinSUnitTest {
|rpcuser=foo
|rpcpassword=bar
""".stripMargin
val conf = BitcoindConfig(confStr, BitcoindRpcTestUtil.tmpDir())
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
val auth = BitcoindAuthCredentials.fromConfig(conf)
val pass = auth match {
@ -49,7 +49,7 @@ class BitcoindAuthCredentialsTest extends BitcoinSUnitTest {
|rpcpassword=bar
""".stripMargin
val conf = BitcoindConfig(confStr, BitcoindRpcTestUtil.tmpDir())
val conf = BitcoindConfig(confStr, FileUtil.tmpDir())
BitcoindAuthCredentials.fromConfig(conf) match {
case _: CookieBased => fail
case PasswordBased(username, password) =>

View file

@ -1,15 +1,12 @@
package org.bitcoins.rpc.config
import org.bitcoins.testkit.util.BitcoinSUnitTest
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.config.TestNet3
import org.bitcoins.core.config.MainNet
import org.bitcoins.core.config.{MainNet, RegTest, TestNet3}
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoindRpcTest
import org.bitcoins.testkit.util.{BitcoinSUnitTest, FileUtil}
class BitcoindConfigTest extends BitcoinSUnitTest {
def tmpDir = BitcoindRpcTestUtil.tmpDir()
def tmpDir = FileUtil.tmpDir()
it must "have to/fromString symmetry" in {
val conf = BitcoindRpcTestUtil.standardConfig
val confStr = conf.toWriteableString

View file

@ -2,7 +2,6 @@ package org.bitcoins.eclair.rpc
import java.nio.file.Files
import java.time.Instant
import org.bitcoins.commons.jsonmodels.eclair._
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.currency.{CurrencyUnits, Satoshis}
@ -29,7 +28,7 @@ import org.bitcoins.rpc.util.AsyncUtil
import org.bitcoins.testkit.async.TestAsyncUtil
import org.bitcoins.testkit.eclair.rpc.{EclairNodes4, EclairRpcTestUtil}
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoinSAsyncTest
import org.bitcoins.testkit.util.{BitcoinSAsyncTest, EclairRpcTestClient}
import org.scalatest.Assertion
import scala.concurrent._
@ -37,10 +36,10 @@ import scala.concurrent.duration.{DurationInt, _}
class EclairRpcClientTest extends BitcoinSAsyncTest {
private val dirExists = Files.exists(EclairRpcTestUtil.binaryDirectory)
private val dirExists = Files.exists(EclairRpcTestClient.sbtBinaryDirectory)
private val hasContents = dirExists && Files
.list(EclairRpcTestUtil.binaryDirectory)
.list(EclairRpcTestClient.sbtBinaryDirectory)
.toArray()
.nonEmpty
@ -50,7 +49,7 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
printerr(s"Run 'sbt downloadEclair' to fetch needed binaries")
sys.error {
val msg =
s""""Eclair binary directory (${BitcoindRpcTestUtil.binaryDirectory}) is empty.
s""""Eclair binary directory (${EclairRpcTestClient.sbtBinaryDirectory}) is empty.
|Run 'sbt downloadEclair' to fetch needed binaries""".stripMargin
msg
}
@ -407,14 +406,12 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
}
it should "be able to start and shutdown a node" in {
val eclairTestClient =
EclairRpcTestClient.fromSbtDownload(eclairVersionOpt = None,
eclairCommitOpt = None,
bitcoindRpcClientOpt = None)
for {
bitcoind <- EclairRpcTestUtil.startedBitcoindRpcClient()
eclair <- {
val server = EclairRpcTestUtil.eclairInstance(bitcoind)
val eclair =
new EclairRpcClient(server, EclairRpcTestUtil.binary(None, None))
eclair.start().map(_ => eclair)
}
eclair <- eclairTestClient.start()
_ <- TestAsyncUtil.retryUntilSatisfiedF(conditionF =
() => eclair.isStarted(),
interval = 1.second,
@ -542,7 +539,9 @@ class EclairRpcClientTest extends BitcoinSAsyncTest {
val badClientF =
badInstanceF.map(
new EclairRpcClient(_, EclairRpcTestUtil.binary(None, None)))
new EclairRpcClient(_,
Some(
EclairRpcTestClient.sbtBinaryDirectory.toFile)))
badClientF.flatMap { badClient =>
recoverToSucceededIf[RuntimeException](badClient.getInfo)

View file

@ -1,7 +1,6 @@
package org.bitcoins.testkit.chain
import java.net.InetSocketAddress
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
import org.bitcoins.chain.ChainVerificationLogger
@ -24,6 +23,7 @@ import org.bitcoins.testkit.chain.fixture._
import org.bitcoins.testkit.fixtures.BitcoinSFixture
import org.bitcoins.testkit.node.CachedChainAppConfig
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.ScalaTestUtil
import org.bitcoins.testkit.{chain, BitcoinSTestAppConfig}
import org.bitcoins.zmq.ZMQSubscriber

View file

@ -1,9 +1,5 @@
package org.bitcoins.testkit.eclair.rpc
import java.io.{File, PrintWriter}
import java.net.URI
import java.nio.file.Files
import akka.actor.ActorSystem
import com.typesafe.config.{Config, ConfigFactory}
import org.bitcoins.commons.jsonmodels.eclair.{
@ -13,7 +9,6 @@ import org.bitcoins.commons.jsonmodels.eclair.{
PaymentId
}
import org.bitcoins.core.compat.JavaConverters._
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
import org.bitcoins.core.protocol.ln.channel.{
ChannelId,
@ -32,8 +27,11 @@ import org.bitcoins.rpc.config.{BitcoindAuthCredentials, BitcoindInstance}
import org.bitcoins.rpc.util.RpcUtil
import org.bitcoins.testkit.async.TestAsyncUtil
import org.bitcoins.testkit.rpc.{BitcoindRpcTestUtil, TestRpcUtil}
import org.bitcoins.testkit.util.TestkitBinaries
import org.bitcoins.testkit.util.{EclairRpcTestClient, FileUtil}
import java.io.{File, PrintWriter}
import java.net.URI
import java.nio.file.Path
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.{ExecutionContext, Future}
import scala.reflect.ClassTag
@ -51,42 +49,11 @@ import scala.util.{Failure, Success}
*/
trait EclairRpcTestUtil extends BitcoinSLogger {
/** Directory where sbt downloads Eclair binaries */
private[bitcoins] val binaryDirectory =
TestkitBinaries.baseBinaryDirectory.resolve("eclair")
/** Path to Jar downloaded by Eclair, if it exists */
private[bitcoins] def binary(
eclairVersionOpt: Option[String],
eclairCommitOpt: Option[String]): Option[File] = {
val path = binaryDirectory
.resolve(eclairVersionOpt.getOrElse(EclairRpcClient.version))
.resolve(
s"eclair-node-${EclairRpcClient.version}-${eclairCommitOpt.getOrElse(EclairRpcClient.commit)}")
.resolve("bin")
.resolve(
if (sys.props("os.name").toLowerCase.contains("windows"))
"eclair-node.bat"
else
"eclair-node.sh")
if (Files.exists(path)) {
Some(path.toFile)
} else {
None
}
}
def randomDirName: String =
0.until(5).map(_ => scala.util.Random.alphanumeric.head).mkString
def randomEclairDatadir(): File =
new File(s"/tmp/eclair-test/${randomDirName}/.eclair/")
new File(s"/tmp/eclair-test/${FileUtil.randomDirName}/.eclair/")
def cannonicalDatadir = new File(s"${System.getenv("HOME")}/.reg_eclair/")
lazy val network = RegTest
/**
* Makes a best effort to get a 0.16 bitcoind instance
*/
@ -204,39 +171,25 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
eclairVersionOpt: Option[String] = None,
eclairCommitOpt: Option[String] = None)(implicit
system: ActorSystem): Future[EclairRpcClient] = {
import system.dispatcher
val bitcoindRpcF: Future[BitcoindRpcClient] = {
if (bitcoindRpcOpt.isDefined) {
Future.successful(bitcoindRpcOpt.get)
} else {
EclairRpcTestUtil.startedBitcoindRpcClient()
}
}
val eclairRpcTestClient = EclairRpcTestClient.fromSbtDownload(
eclairVersionOpt = eclairVersionOpt,
eclairCommitOpt = eclairCommitOpt,
bitcoindRpcClientOpt = bitcoindRpcOpt)
val randInstanceF = bitcoindRpcF.map(randomEclairInstance(_))
val eclairRpcF = randInstanceF.map(i =>
new EclairRpcClient(i, binary(eclairVersionOpt, eclairCommitOpt)))
val startedF = eclairRpcF.flatMap(_.start())
startedF.flatMap(_ => eclairRpcF)
eclairRpcTestClient.start()
}
def cannonicalEclairClient(
eclairVersionOpt: Option[String] = None,
eclairCommitOpt: Option[String] = None)(implicit
eclairCommitOpt: Option[String] = None,
binaryDirectory: Path = EclairRpcTestClient.sbtBinaryDirectory)(implicit
system: ActorSystem): EclairRpcClient = {
val inst = cannonicalEclairInstance()
new EclairRpcClient(inst, binary(eclairVersionOpt, eclairCommitOpt))
}
def deleteTmpDir(dir: File): Boolean = {
if (!dir.isDirectory) {
dir.delete()
} else {
dir.listFiles().foreach(deleteTmpDir)
dir.delete()
}
new EclairRpcClient(inst,
EclairRpcTestClient.getBinary(
eclairVersionOpt = eclairVersionOpt,
eclairCommitOpt = eclairCommitOpt,
binaryDirectory = binaryDirectory))
}
/**
@ -542,7 +495,8 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
eclairVersionOpt1: Option[String] = None,
eclairCommitOpt1: Option[String] = None,
eclairVersionOpt2: Option[String] = None,
eclairCommitOpt2: Option[String] = None)(implicit
eclairCommitOpt2: Option[String] = None,
binaryDirectory: Path = EclairRpcTestClient.sbtBinaryDirectory)(implicit
system: ActorSystem): Future[(EclairRpcClient, EclairRpcClient)] = {
import system.dispatcher
val bitcoindRpcClientF: Future[BitcoindRpcClient] = {
@ -561,14 +515,20 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
val clientF = e1InstanceF.flatMap { e1 =>
val e =
new EclairRpcClient(e1, binary(eclairVersionOpt1, eclairCommitOpt1))
new EclairRpcClient(e1,
EclairRpcTestClient.getBinary(eclairVersionOpt1,
eclairCommitOpt1,
binaryDirectory))
logger.debug(
s"Temp eclair directory created ${e.getDaemon.authCredentials.datadir}")
e.start().map(_ => e)
}
val otherClientF = e2InstanceF.flatMap { e2 =>
val e =
new EclairRpcClient(e2, binary(eclairVersionOpt2, eclairCommitOpt2))
new EclairRpcClient(e2,
EclairRpcTestClient.getBinary(eclairVersionOpt2,
eclairCommitOpt2,
binaryDirectory))
logger.debug(
s"Temp eclair directory created ${e.getDaemon.authCredentials.datadir}")
e.start().map(_ => e)
@ -805,16 +765,19 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
senderEclairCommit: Option[String],
networkSize: Int,
channelAmount: MilliSatoshis,
logbackXml: Option[String])(implicit
logbackXml: Option[String],
binaryDirectory: Path = EclairRpcTestClient.sbtBinaryDirectory)(implicit
system: ActorSystem): Future[EclairNetwork] = {
import system.dispatcher
for {
bitcoind <- startedBitcoindRpcClient()
testEclairInstance =
EclairRpcTestUtil.eclairInstance(bitcoind, logbackXml = logbackXml)
testEclairNode = new EclairRpcClient(
testEclairInstance,
binary(testEclairVersion, testEclairCommit))
testEclairNode = new EclairRpcClient(testEclairInstance,
EclairRpcTestClient.getBinary(
testEclairVersion,
testEclairCommit,
binaryDirectory))
_ <- testEclairNode.start()
_ <- awaitEclairInSync(testEclairNode, bitcoind)
networkEclairInstances =
@ -826,7 +789,9 @@ trait EclairRpcTestUtil extends BitcoinSLogger {
logbackXml = logbackXml))
networkEclairNodes = networkEclairInstances.map(
new EclairRpcClient(_,
binary(senderEclairVersion, senderEclairCommit)))
EclairRpcTestClient.getBinary(senderEclairVersion,
senderEclairCommit,
binaryDirectory)))
_ <- Future.sequence(networkEclairNodes.map(_.start()))
_ <- Future.sequence(
networkEclairNodes.map(awaitEclairInSync(_, bitcoind)))

View file

@ -3,6 +3,7 @@ package org.bitcoins.testkit.fixtures
import akka.actor.ActorSystem
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import org.bitcoins.testkit.util.BitcoinSAsyncFixtureTest
import org.scalatest._

View file

@ -2,8 +2,7 @@ package org.bitcoins.testkit.rpc
import java.io.File
import java.net.URI
import java.nio.file.{Files, Path, Paths}
import java.nio.file.{Files, Path}
import akka.actor.ActorSystem
import org.bitcoins.commons.jsonmodels.bitcoind.RpcOpts.AddNodeArgument
import org.bitcoins.commons.jsonmodels.bitcoind.{
@ -44,10 +43,9 @@ import org.bitcoins.rpc.config.{
ZmqConfig
}
import org.bitcoins.rpc.util.{AsyncUtil, RpcUtil}
import org.bitcoins.testkit.util.{FileUtil, TestkitBinaries}
import org.bitcoins.testkit.util.{BitcoindRpcTestClient, FileUtil}
import org.bitcoins.util.ListUtil
import scala.annotation.tailrec
import scala.collection.immutable.Map
import scala.collection.mutable
import scala.concurrent._
@ -57,26 +55,11 @@ import scala.util._
//noinspection AccessorLikeMethodIsEmptyParen
trait BitcoindRpcTestUtil extends BitcoinSLogger {
lazy val network: RegTest.type = RegTest
type RpcClientAccum =
mutable.Builder[BitcoindRpcClient, Vector[BitcoindRpcClient]]
@tailrec
private def randomDirName: String = {
val dirname = 0.until(5).map(_ => Random.alphanumeric.head).mkString
val dir = new File(dirname)
if (!dir.exists()) {
dirname
} else {
randomDirName
}
}
def tmpDir(): File = {
val f = Paths.get(Properties.tmpDir, randomDirName).toFile
f.mkdirs()
f
}
/**
* Standard config used for testing purposes
*/
@ -94,7 +77,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
zmqPort: Int,
pruneMode: Boolean,
blockFilterIndex: Boolean = false): BitcoindConfig = {
val pass = randomDirName
val pass = FileUtil.randomDirName
val username = "random_user_name"
val conf = s"""
|regtest=1
@ -123,7 +106,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|""".stripMargin
else
conf
BitcoindConfig(config = config, datadir = BitcoindRpcTestUtil.tmpDir())
BitcoindConfig(config = config, datadir = FileUtil.tmpDir())
}
/**
@ -146,19 +129,14 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
written
}
lazy val network: RegTest.type = RegTest
/** The directory that sbt downloads bitcoind binaries into */
private[bitcoins] val binaryDirectory = {
TestkitBinaries.baseBinaryDirectory.resolve("bitcoind")
}
def newestBitcoindBinary: File = getBinary(BitcoindVersion.newest)
def getBinary(version: BitcoindVersion): File =
def getBinary(
version: BitcoindVersion,
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory): File =
version match {
// default to newest version
case Unknown => getBinary(BitcoindVersion.newest)
case Unknown => getBinary(BitcoindVersion.newest, binaryDirectory)
case known @ (Experimental | V16 | V17 | V18 | V19 | V20) =>
val fileList = Files
.list(binaryDirectory)
@ -200,7 +178,9 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
rpcPort: Int = RpcUtil.randomPort,
zmqPort: Int = RpcUtil.randomPort,
pruneMode: Boolean = false,
versionOpt: Option[BitcoindVersion] = None): BitcoindInstance = {
versionOpt: Option[BitcoindVersion] = None,
binaryDirectory: Path =
BitcoindRpcTestClient.sbtBinaryDirectory): BitcoindInstance = {
val uri = new URI("http://localhost:" + port)
val rpcUri = new URI("http://localhost:" + rpcPort)
val hasNeutrinoSupport = versionOpt match {
@ -220,7 +200,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
val binary: File = versionOpt match {
case Some(version) => getBinary(version)
case None =>
if (Files.exists(BitcoindRpcTestUtil.binaryDirectory)) {
if (Files.exists(binaryDirectory)) {
newestBitcoindBinary
} else {
throw new RuntimeException(
@ -244,73 +224,138 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
port: Int = RpcUtil.randomPort,
rpcPort: Int = RpcUtil.randomPort,
zmqPort: Int = RpcUtil.randomPort,
pruneMode: Boolean = false
pruneMode: Boolean = false,
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
): BitcoindInstance =
instance(port = port,
rpcPort = rpcPort,
zmqPort = zmqPort,
pruneMode = pruneMode,
versionOpt = Some(BitcoindVersion.V16))
versionOpt = Some(BitcoindVersion.V16),
binaryDirectory = binaryDirectory)
def v17Instance(
port: Int = RpcUtil.randomPort,
rpcPort: Int = RpcUtil.randomPort,
zmqPort: Int = RpcUtil.randomPort,
pruneMode: Boolean = false
pruneMode: Boolean = false,
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
): BitcoindInstance =
instance(port = port,
rpcPort = rpcPort,
zmqPort = zmqPort,
pruneMode = pruneMode,
versionOpt = Some(BitcoindVersion.V17))
versionOpt = Some(BitcoindVersion.V17),
binaryDirectory = binaryDirectory)
def v18Instance(
port: Int = RpcUtil.randomPort,
rpcPort: Int = RpcUtil.randomPort,
zmqPort: Int = RpcUtil.randomPort,
pruneMode: Boolean = false
pruneMode: Boolean = false,
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
): BitcoindInstance =
instance(port = port,
rpcPort = rpcPort,
zmqPort = zmqPort,
pruneMode = pruneMode,
versionOpt = Some(BitcoindVersion.V18))
versionOpt = Some(BitcoindVersion.V18),
binaryDirectory = binaryDirectory)
def v19Instance(
port: Int = RpcUtil.randomPort,
rpcPort: Int = RpcUtil.randomPort,
zmqPort: Int = RpcUtil.randomPort,
pruneMode: Boolean = false
pruneMode: Boolean = false,
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
): BitcoindInstance =
instance(port = port,
rpcPort = rpcPort,
zmqPort = zmqPort,
pruneMode = pruneMode,
versionOpt = Some(BitcoindVersion.V19))
versionOpt = Some(BitcoindVersion.V19),
binaryDirectory = binaryDirectory)
def v20Instance(
port: Int = RpcUtil.randomPort,
rpcPort: Int = RpcUtil.randomPort,
zmqPort: Int = RpcUtil.randomPort,
pruneMode: Boolean = false
pruneMode: Boolean = false,
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
): BitcoindInstance =
instance(port = port,
rpcPort = rpcPort,
zmqPort = zmqPort,
pruneMode = pruneMode,
versionOpt = Some(BitcoindVersion.V20))
versionOpt = Some(BitcoindVersion.V20),
binaryDirectory = binaryDirectory)
def vExperimentalInstance(
port: Int = RpcUtil.randomPort,
rpcPort: Int = RpcUtil.randomPort,
zmqPort: Int = RpcUtil.randomPort,
pruneMode: Boolean = false
pruneMode: Boolean = false,
binaryDirectory: Path = BitcoindRpcTestClient.sbtBinaryDirectory
): BitcoindInstance =
instance(port = port,
rpcPort = rpcPort,
zmqPort = zmqPort,
pruneMode = pruneMode,
versionOpt = Some(BitcoindVersion.Experimental))
versionOpt = Some(BitcoindVersion.Experimental),
binaryDirectory = binaryDirectory)
/** Gets an instance of bitcoind with the given version */
def getInstance(
bitcoindVersion: BitcoindVersion,
port: Int = RpcUtil.randomPort,
rpcPort: Int = RpcUtil.randomPort,
zmqPort: Int = RpcUtil.randomPort,
pruneMode: Boolean = false,
binaryDirectory: Path =
BitcoindRpcTestClient.sbtBinaryDirectory): BitcoindInstance = {
bitcoindVersion match {
case BitcoindVersion.V16 =>
BitcoindRpcTestUtil.v16Instance(port,
rpcPort,
zmqPort,
pruneMode,
binaryDirectory = binaryDirectory)
case BitcoindVersion.V17 =>
BitcoindRpcTestUtil.v17Instance(port,
rpcPort,
zmqPort,
pruneMode,
binaryDirectory = binaryDirectory)
case BitcoindVersion.V18 =>
BitcoindRpcTestUtil.v18Instance(port,
rpcPort,
zmqPort,
pruneMode,
binaryDirectory = binaryDirectory)
case BitcoindVersion.V19 =>
BitcoindRpcTestUtil.v19Instance(port,
rpcPort,
zmqPort,
pruneMode,
binaryDirectory = binaryDirectory)
case BitcoindVersion.V20 =>
BitcoindRpcTestUtil.v20Instance(port,
rpcPort,
zmqPort,
pruneMode,
binaryDirectory = binaryDirectory)
case BitcoindVersion.Experimental =>
BitcoindRpcTestUtil.vExperimentalInstance(port,
rpcPort,
zmqPort,
pruneMode,
binaryDirectory =
binaryDirectory)
case BitcoindVersion.Unknown =>
sys.error(
s"Could not create a bitcoind version with version=${BitcoindVersion.Unknown}")
}
}
def startServers(servers: Vector[BitcoindRpcClient])(implicit
ec: ExecutionContext): Future[Unit] = {
@ -1023,7 +1068,7 @@ object BitcoindRpcTestUtil extends BitcoindRpcTestUtil {
/**
* Used for long running async tasks
*/
private val DEFAULT_LONG_INTERVAL = {
val DEFAULT_LONG_INTERVAL = {
if (EnvUtil.isMac && EnvUtil.isCI) 10.seconds
else 3.seconds
}

View file

@ -1,7 +1,6 @@
package org.bitcoins.testkit.util
import java.nio.file.Files
import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
@ -10,10 +9,11 @@ import scala.collection.mutable
abstract class BitcoindRpcTest extends BitcoinSAsyncTest {
private val dirExists = Files.exists(BitcoindRpcTestUtil.binaryDirectory)
private val dirExists =
Files.exists(BitcoindRpcTestClient.sbtBinaryDirectory)
private val hasContents = dirExists && Files
.list(BitcoindRpcTestUtil.binaryDirectory)
.list(BitcoindRpcTestClient.sbtBinaryDirectory)
.toArray()
.nonEmpty
@ -23,7 +23,7 @@ abstract class BitcoindRpcTest extends BitcoinSAsyncTest {
printerr(s"Run 'sbt downloadBitcoind' to fetch needed binaries")
sys.error {
val msg =
s""""bitcoind binary directory (${BitcoindRpcTestUtil.binaryDirectory}) is empty.
s""""bitcoind binary directory (${BitcoindRpcTestClient.sbtBinaryDirectory}) is empty.
|Run 'sbt downloadBitcoind' to fetch needed binaries""".stripMargin
msg
}

View file

@ -0,0 +1,65 @@
package org.bitcoins.testkit.util
import akka.actor.ActorSystem
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
import org.bitcoins.rpc.config.BitcoindInstance
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
import java.nio.file.{Files, Path}
import scala.concurrent.Future
/** Helper class to start a bitcoind client with the given binary */
case class BitcoindRpcTestClient(
override val binary: Path,
version: BitcoindVersion)(implicit system: ActorSystem)
extends RpcBinaryUtil[BitcoindRpcClient] {
require(Files.exists(binary),
s"Path did not exist! got=${binary.toAbsolutePath.toString}")
import system.dispatcher
private lazy val bitcoindInstance: BitcoindInstance = {
BitcoindRpcTestUtil.getInstance(bitcoindVersion = version,
binaryDirectory = binaryDirectory)
}
/** Cached client. This is defined if start() has been called
* else None
*/
private var clientOpt: Option[BitcoindRpcClient] = None
override def start(): Future[BitcoindRpcClient] = {
clientOpt match {
case Some(client) => Future.successful(client)
case None =>
val clientF =
BitcoindRpcTestUtil.startedBitcoindRpcClient(bitcoindInstance)
clientF.map { c =>
clientOpt = Some(c)
c
}
}
}
override def stop(): Future[BitcoindRpcClient] = {
clientOpt match {
case Some(cli) => cli.stop()
case None =>
Future.failed(
new RuntimeException(s"BitcoindRpcClient was not defined!"))
}
}
}
object BitcoindRpcTestClient extends SbtBinaryFactory {
override val sbtBinaryDirectory: Path =
TestkitBinaries.baseBinaryDirectory.resolve("bitcoind")
def fromSbtDownload(bitcoindVersion: BitcoindVersion)(implicit
system: ActorSystem): BitcoindRpcTestClient = {
val binary =
BitcoindRpcTestUtil.getBinary(bitcoindVersion, sbtBinaryDirectory)
BitcoindRpcTestClient(binary.toPath, bitcoindVersion)
}
}

View file

@ -0,0 +1,121 @@
package org.bitcoins.testkit.util
import akka.actor.ActorSystem
import org.bitcoins.eclair.rpc.client.EclairRpcClient
import org.bitcoins.eclair.rpc.config.EclairInstance
import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.testkit.eclair.rpc.EclairRpcTestUtil
import java.io.File
import java.nio.file.{Files, Path}
import scala.concurrent.Future
/** Helper class to start a eclair client with the given binary */
case class EclairRpcTestClient(
override val binary: Path,
bitcoindRpcClientOpt: Option[BitcoindRpcClient])(implicit
system: ActorSystem)
extends RpcBinaryUtil[EclairRpcClient] {
require(Files.exists(binary),
s"Path did not exist! got=${binary.toAbsolutePath.toString}")
import system.dispatcher
private lazy val bitcoindRpcClientF: Future[BitcoindRpcClient] = {
bitcoindRpcClientOpt match {
case Some(bitcoindRpcClient) => Future.successful(bitcoindRpcClient)
case None =>
EclairRpcTestUtil.startedBitcoindRpcClient()
}
}
private lazy val eclairInstanceF: Future[EclairInstance] = {
bitcoindRpcClientF.map { bitcoind =>
EclairRpcTestUtil.eclairInstance(bitcoind, None)
}
}
private lazy val eclairRpcClientF: Future[EclairRpcClient] = {
eclairInstanceF.map(new EclairRpcClient(_, Some(binary.toFile)))
}
override def start(): Future[EclairRpcClient] = {
//should we start bitcoind rpc client here too?
for {
rpcClient <- eclairRpcClientF
started <- rpcClient.start()
} yield started
}
override def stop(): Future[EclairRpcClient] = {
//should we stop bitcoind rpc client here too?
for {
rpcClient <- eclairRpcClientF
stopped <- rpcClient.stop()
} yield stopped
}
}
object EclairRpcTestClient extends SbtBinaryFactory {
/** Directory where sbt downloads Eclair binaries */
override val sbtBinaryDirectory: Path =
TestkitBinaries.baseBinaryDirectory.resolve("eclair")
def fromSbtDownloadOpt(
eclairVersionOpt: Option[String],
eclairCommitOpt: Option[String],
bitcoindRpcClientOpt: Option[BitcoindRpcClient])(implicit
system: ActorSystem): Option[EclairRpcTestClient] = {
val fileOpt =
getBinary(eclairVersionOpt = eclairVersionOpt,
eclairCommitOpt = eclairCommitOpt,
binaryDirectory = sbtBinaryDirectory)
fileOpt.map(f =>
EclairRpcTestClient(binary = f.toPath, bitcoindRpcClientOpt))
}
def fromSbtDownload(
eclairVersionOpt: Option[String],
eclairCommitOpt: Option[String],
bitcoindRpcClientOpt: Option[BitcoindRpcClient])(implicit
system: ActorSystem): EclairRpcTestClient = {
val eclairOpt = fromSbtDownloadOpt(eclairVersionOpt = eclairCommitOpt,
eclairCommitOpt = eclairCommitOpt,
bitcoindRpcClientOpt =
bitcoindRpcClientOpt)
eclairOpt match {
case Some(client) => client
case None =>
sys.error(
s"Could not find eclair that was downloaded by sbt " +
s"with version=$eclairVersionOpt " +
s"commit=$eclairCommitOpt at " +
s"path=${sbtBinaryDirectory.toAbsolutePath.toString}")
}
}
/** Path to Jar downloaded by Eclair, if it exists */
def getBinary(
eclairVersionOpt: Option[String],
eclairCommitOpt: Option[String],
binaryDirectory: Path): Option[File] = {
val path = binaryDirectory
.resolve(eclairVersionOpt.getOrElse(EclairRpcClient.version))
.resolve(
s"eclair-node-${EclairRpcClient.version}-${eclairCommitOpt.getOrElse(EclairRpcClient.commit)}")
.resolve("bin")
.resolve(
if (sys.props("os.name").toLowerCase.contains("windows"))
"eclair-node.bat"
else
"eclair-node.sh")
if (Files.exists(path)) {
Some(path.toFile)
} else {
None
}
}
}

View file

@ -1,11 +1,11 @@
package org.bitcoins.testkit.util
import java.io.File
import java.nio.file.Path
import java.nio.file.{Path, Paths}
import org.bitcoins.core.util.BitcoinSLogger
import scala.util.Properties
import scala.annotation.tailrec
import scala.util.{Properties, Random}
object FileUtil extends BitcoinSLogger {
@ -38,4 +38,21 @@ object FileUtil extends BitcoinSLogger {
def deleteTmpDir(path: Path): Boolean = {
deleteTmpDir(path.toFile)
}
@tailrec
final def randomDirName: String = {
val dirname = 0.until(5).map(_ => Random.alphanumeric.head).mkString
val dir = new File(dirname)
if (!dir.exists()) {
dirname
} else {
randomDirName
}
}
def tmpDir(): File = {
val f = Paths.get(Properties.tmpDir, FileUtil.randomDirName).toFile
f.mkdirs()
f
}
}

View file

@ -0,0 +1,23 @@
package org.bitcoins.testkit.util
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.util.{BitcoinSLogger, StartStopAsync}
import java.nio.file.Path
/** A utility trait for handling binaries like bitcoind/eclair.
* All common utility methods should go in this trait
*/
trait RpcBinaryUtil[T] extends StartStopAsync[T] with BitcoinSLogger {
/** The path to the binary, an example is
* '/home/suredbits/.bitcoin-s/binaries/bitcoind/bitcoin-0.20.1/bin/bitcoind'
* or
* '/home/suredbits/.bitcoin-s/binaries/eclair/0.4.1/eclair-node-0.4.1-e5fb281/bin/eclair-node.sh'
*/
def binary: Path
def binaryDirectory: Path = binary.getParent
lazy val network = RegTest
}

View file

@ -0,0 +1,7 @@
package org.bitcoins.testkit.util
trait SbtBinaryFactory {
/** The path where binaries are stored by sbt */
def sbtBinaryDirectory: java.nio.file.Path
}

View file

@ -6,12 +6,19 @@ import scala.util.Properties
object TestkitBinaries {
private val base: Path = Paths.get(".bitcoin-s", "binaries")
/** The base directory where binaries needed in tests
* are located.
*/
lazy val baseBinaryDirectory: Path = {
val home = Paths.get(Properties.userHome, ".bitcoin-s", "binaries")
home
val home = Paths.get(Properties.userHome)
fromRoot(home)
}
/** Gives you an arbitrary root path, and then tacks on .bitcoin-s/binaries/ onto the end of it
*/
def fromRoot(path: Path): Path = {
path.resolve(base)
}
}