diff --git a/rpc/src/main/scala/org/bitcoins/rpc/RpcUtil.scala b/rpc/src/main/scala/org/bitcoins/rpc/util/AsyncUtil.scala similarity index 74% rename from rpc/src/main/scala/org/bitcoins/rpc/RpcUtil.scala rename to rpc/src/main/scala/org/bitcoins/rpc/util/AsyncUtil.scala index d9cfcad5bf..365b54ad45 100644 --- a/rpc/src/main/scala/org/bitcoins/rpc/RpcUtil.scala +++ b/rpc/src/main/scala/org/bitcoins/rpc/util/AsyncUtil.scala @@ -1,13 +1,14 @@ -package org.bitcoins.rpc +package org.bitcoins.rpc.util + +import java.io.PrintStream import akka.actor.ActorSystem import org.bitcoins.core.util.BitcoinSLogger -import org.bitcoins.rpc.client.BitcoindRpcClient import scala.concurrent.duration.{DurationInt, FiniteDuration} import scala.concurrent.{Await, Future, Promise} -trait RpcUtil extends BitcoinSLogger { +trait AsyncUtil extends BitcoinSLogger { private def retryRunnable( condition: => Boolean, @@ -38,10 +39,37 @@ trait RpcUtil extends BitcoinSLogger { conditionF: () => Future[Boolean], duration: FiniteDuration = 100.millis, maxTries: Int = 50)(implicit system: ActorSystem): Future[Unit] = { + val stackTrace: Array[StackTraceElement] = + Thread.currentThread().getStackTrace retryUntilSatisfiedWithCounter(conditionF = conditionF, duration = duration, - maxTries = maxTries) + maxTries = maxTries, + stackTrace = stackTrace) + } + + case class RpcRetryException( + message: String, + caller: Array[StackTraceElement]) + extends Exception(message) { + override def printStackTrace(s: PrintStream): Unit = { + super.printStackTrace(s) + + val indexOfLastBitcoinSOpt = caller.reverse.zipWithIndex + .find { + case (element, _) => + element.getClassName.contains("bitcoins") + } + + val indexOfLastRelevantElement = + indexOfLastBitcoinSOpt.map(_._2).getOrElse(0) + + val relevantStackTrace = + caller.dropRight(indexOfLastRelevantElement).drop(1) + + s.println("Called from:") + relevantStackTrace.foreach(element => s.println(s"\t$element")) + } } // Has a different name so that default values are permitted @@ -49,7 +77,9 @@ trait RpcUtil extends BitcoinSLogger { conditionF: () => Future[Boolean], duration: FiniteDuration, counter: Int = 0, - maxTries: Int)(implicit system: ActorSystem): Future[Unit] = { + maxTries: Int, + stackTrace: Array[StackTraceElement])( + implicit system: ActorSystem): Future[Unit] = { implicit val ec = system.dispatcher @@ -57,7 +87,7 @@ trait RpcUtil extends BitcoinSLogger { if (condition) { Future.successful(()) } else if (counter == maxTries) { - Future.failed(new RuntimeException("Condition timed out")) + Future.failed(RpcRetryException("Condition timed out", stackTrace)) } else { val p = Promise[Boolean]() val runnable = retryRunnable(condition, p) @@ -70,7 +100,8 @@ trait RpcUtil extends BitcoinSLogger { retryUntilSatisfiedWithCounter(conditionF, duration, counter + 1, - maxTries) + maxTries, + stackTrace) } } } @@ -116,22 +147,6 @@ trait RpcUtil extends BitcoinSLogger { Await.result(f, overallTimeout) } - - def awaitServer( - server: BitcoindRpcClient, - duration: FiniteDuration = 1.seconds, - maxTries: Int = 50)(implicit system: ActorSystem): Unit = { - val f = () => server.isStarted - awaitCondition(f, duration, maxTries) - } - - def awaitServerShutdown( - server: BitcoindRpcClient, - duration: FiniteDuration = 300.milliseconds, - maxTries: Int = 50)(implicit system: ActorSystem): Unit = { - val f = () => !server.isStarted - awaitCondition(f, duration, maxTries) - } } -object RpcUtil extends RpcUtil +object AsyncUtil extends AsyncUtil diff --git a/rpc/src/main/scala/org/bitcoins/rpc/util/RpcUtil.scala b/rpc/src/main/scala/org/bitcoins/rpc/util/RpcUtil.scala new file mode 100644 index 0000000000..e02167369a --- /dev/null +++ b/rpc/src/main/scala/org/bitcoins/rpc/util/RpcUtil.scala @@ -0,0 +1,27 @@ +package org.bitcoins.rpc.util + +import akka.actor.ActorSystem +import org.bitcoins.rpc.client.BitcoindRpcClient + +import scala.concurrent.duration.{DurationInt, FiniteDuration} + +trait RpcUtil extends AsyncUtil { + + def awaitServer( + server: BitcoindRpcClient, + duration: FiniteDuration = 1.seconds, + maxTries: Int = 50)(implicit system: ActorSystem): Unit = { + val f = () => server.isStarted + awaitCondition(f, duration, maxTries) + } + + def awaitServerShutdown( + server: BitcoindRpcClient, + duration: FiniteDuration = 300.milliseconds, + maxTries: Int = 50)(implicit system: ActorSystem): Unit = { + val f = () => !server.isStarted + awaitCondition(f, duration, maxTries) + } +} + +object RpcUtil extends RpcUtil diff --git a/rpc/src/test/scala/org/bitcoins/rpc/BitcoindInstanceTest.scala b/rpc/src/test/scala/org/bitcoins/rpc/BitcoindInstanceTest.scala index 13703429ce..abe7412e44 100644 --- a/rpc/src/test/scala/org/bitcoins/rpc/BitcoindInstanceTest.scala +++ b/rpc/src/test/scala/org/bitcoins/rpc/BitcoindInstanceTest.scala @@ -7,6 +7,7 @@ import akka.testkit.TestKit import org.bitcoins.core.currency.Bitcoins import org.bitcoins.rpc.client.BitcoindRpcClient import org.bitcoins.rpc.config.BitcoindInstance +import org.bitcoins.rpc.util.RpcUtil import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll} import scala.concurrent.Future diff --git a/rpc/src/test/scala/org/bitcoins/rpc/RpcUtilTest.scala b/rpc/src/test/scala/org/bitcoins/rpc/RpcUtilTest.scala index 94241c2742..18ad1bc1c8 100644 --- a/rpc/src/test/scala/org/bitcoins/rpc/RpcUtilTest.scala +++ b/rpc/src/test/scala/org/bitcoins/rpc/RpcUtilTest.scala @@ -4,7 +4,9 @@ import java.io.File import akka.actor.ActorSystem import akka.stream.ActorMaterializer +import org.bitcoins.rpc.util.RpcUtil.RpcRetryException import org.bitcoins.rpc.client.BitcoindRpcClient +import org.bitcoins.rpc.util.RpcUtil import org.scalatest.{AsyncFlatSpec, BeforeAndAfterAll} import scala.concurrent.duration.DurationInt @@ -44,7 +46,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll { } it should "fail if condition is false" in { - recoverToSucceededIf[RuntimeException] { + recoverToSucceededIf[RpcRetryException] { RpcUtil.retryUntilSatisfiedF(conditionF = () => Future.successful(false), duration = 0.millis) } @@ -59,7 +61,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll { it should "fail if there is a delay and duration is zero" in { val boolLater = trueLater(delay = 250) - recoverToSucceededIf[RuntimeException] { + recoverToSucceededIf[RpcRetryException] { RpcUtil.retryUntilSatisfiedF(boolLaterDoneAndTrue(boolLater), duration = 0.millis) } @@ -71,7 +73,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll { } it should "timeout if condition is false" in { - assertThrows[RuntimeException] { + assertThrows[RpcRetryException] { RpcUtil.awaitCondition(condition = () => false, duration = 0.millis) } } @@ -86,7 +88,7 @@ class RpcUtilTest extends AsyncFlatSpec with BeforeAndAfterAll { it should "timeout if there is a delay and duration is zero" in { val boolLater = trueLater(delay = 250) - assertThrows[RuntimeException] { + assertThrows[RpcRetryException] { RpcUtil.awaitConditionF(boolLaterDoneAndTrue(boolLater), duration = 0.millis) } diff --git a/testkit/src/main/scala/org/bitcoins/eclair/rpc/EclairRpcTestUtil.scala b/testkit/src/main/scala/org/bitcoins/eclair/rpc/EclairRpcTestUtil.scala index 764150144c..b72134b412 100644 --- a/testkit/src/main/scala/org/bitcoins/eclair/rpc/EclairRpcTestUtil.scala +++ b/testkit/src/main/scala/org/bitcoins/eclair/rpc/EclairRpcTestUtil.scala @@ -18,7 +18,8 @@ import org.bitcoins.eclair.rpc.client.EclairRpcClient import org.bitcoins.eclair.rpc.config.EclairInstance import org.bitcoins.rpc.client.BitcoindRpcClient import org.bitcoins.rpc.config.{BitcoindInstance, ZmqConfig} -import org.bitcoins.rpc.{BitcoindRpcTestUtil, RpcUtil} +import org.bitcoins.rpc.BitcoindRpcTestUtil +import org.bitcoins.rpc.util.RpcUtil import scala.concurrent.Future import scala.concurrent.duration.DurationInt diff --git a/testkit/src/main/scala/org/bitcoins/rpc/BitcoindRpcTestUtil.scala b/testkit/src/main/scala/org/bitcoins/rpc/BitcoindRpcTestUtil.scala index d2b5d5d85d..1fb0fa9608 100644 --- a/testkit/src/main/scala/org/bitcoins/rpc/BitcoindRpcTestUtil.scala +++ b/testkit/src/main/scala/org/bitcoins/rpc/BitcoindRpcTestUtil.scala @@ -15,6 +15,7 @@ import org.bitcoins.rpc.config.{ BitcoindInstance, ZmqConfig } +import org.bitcoins.rpc.util.RpcUtil import scala.collection.immutable.Map import scala.collection.JavaConverters.{asScalaSet, mapAsJavaMap} diff --git a/testkit/src/main/scala/org/bitcoins/util/AsyncUtil.scala b/testkit/src/main/scala/org/bitcoins/util/AsyncUtil.scala new file mode 100644 index 0000000000..a652177a49 --- /dev/null +++ b/testkit/src/main/scala/org/bitcoins/util/AsyncUtil.scala @@ -0,0 +1,5 @@ +package org.bitcoins.util + +trait AsyncUtil extends org.bitcoins.rpc.util.AsyncUtil + +object AsyncUtil extends AsyncUtil