mirror of
https://github.com/digital-asset/daml.git
synced 2024-11-05 03:56:26 +03:00
scenario-tester: Allow to mangle names used in scenarios before their executions. (#795)
* Allow to mangle names used in scenarios before their executions. This allows to run a scenario against a long-running server repeatedly and avoid clashes between runs, since each party is unique (up to the randomness used). * ScenarioRunner: add test for partyNameMangler
This commit is contained in:
parent
debef0ec89
commit
b3e2e10897
@ -6,6 +6,7 @@ load(
|
|||||||
"da_scala_binary",
|
"da_scala_binary",
|
||||||
"da_scala_library",
|
"da_scala_library",
|
||||||
"da_scala_test_suite",
|
"da_scala_test_suite",
|
||||||
|
"lf_scalacopts",
|
||||||
)
|
)
|
||||||
|
|
||||||
da_scala_library(
|
da_scala_library(
|
||||||
@ -23,3 +24,16 @@ da_scala_library(
|
|||||||
"//daml-lf/transaction",
|
"//daml-lf/transaction",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
da_scala_test_suite(
|
||||||
|
name = "scenario-interpreter_tests",
|
||||||
|
size = "small",
|
||||||
|
srcs = glob(["src/test/**/*.scala"]),
|
||||||
|
scalacopts = lf_scalacopts,
|
||||||
|
deps = [
|
||||||
|
":scenario-interpreter",
|
||||||
|
"//daml-lf/data",
|
||||||
|
"//daml-lf/interpreter",
|
||||||
|
"//daml-lf/lfpackage",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@ -13,14 +13,22 @@ import com.digitalasset.daml.lf.speedy.SError._
|
|||||||
import com.digitalasset.daml.lf.speedy.SResult._
|
import com.digitalasset.daml.lf.speedy.SResult._
|
||||||
import com.digitalasset.daml.lf.transaction.Node.GlobalKey
|
import com.digitalasset.daml.lf.transaction.Node.GlobalKey
|
||||||
|
|
||||||
//
|
|
||||||
// Speedy scenario runner that uses the reference ledger.
|
|
||||||
//
|
|
||||||
|
|
||||||
private case class SRunnerException(err: SError) extends RuntimeException(err.toString)
|
private case class SRunnerException(err: SError) extends RuntimeException(err.toString)
|
||||||
|
|
||||||
final case class ScenarioRunner(machine: Speedy.Machine) {
|
/** Speedy scenario runner that uses the reference ledger.
|
||||||
|
*
|
||||||
|
* @constructor Creates a runner using an instance of [[Speedy.Machine]].
|
||||||
|
* @param partyNameMangler allows to amend party names defined in scenarios,
|
||||||
|
* before they are executed against a ledger. The function should be idempotent
|
||||||
|
* in the context of a single {@code ScenarioRunner} life-time, i.e. return the
|
||||||
|
* same result each time given the same argument. Should return values compatible
|
||||||
|
* with [[com.digitalasset.daml.lf.data.Ref.SimpleString]].
|
||||||
|
*/
|
||||||
|
final case class ScenarioRunner(
|
||||||
|
machine: Speedy.Machine,
|
||||||
|
partyNameMangler: (String => String) = identity) {
|
||||||
var ledger: Ledger = Ledger.initialLedger(Time.Timestamp.Epoch)
|
var ledger: Ledger = Ledger.initialLedger(Time.Timestamp.Epoch)
|
||||||
|
|
||||||
import scala.util.{Try, Success, Failure}
|
import scala.util.{Try, Success, Failure}
|
||||||
|
|
||||||
def run(): Either[(SError, Ledger), (Double, Int, Ledger)] =
|
def run(): Either[(SError, Ledger), (Double, Int, Ledger)] =
|
||||||
@ -83,11 +91,13 @@ final case class ScenarioRunner(machine: Speedy.Machine) {
|
|||||||
private def crash(reason: String) =
|
private def crash(reason: String) =
|
||||||
throw SRunnerException(SErrorCrash(reason))
|
throw SRunnerException(SErrorCrash(reason))
|
||||||
|
|
||||||
private def getParty(partyText: String, callback: Party => Unit) =
|
private def getParty(partyText: String, callback: Party => Unit) = {
|
||||||
SimpleString.fromString(partyText) match {
|
val mangledPartyText = partyNameMangler(partyText)
|
||||||
|
SimpleString.fromString(mangledPartyText) match {
|
||||||
case Right(s) => callback(s)
|
case Right(s) => callback(s)
|
||||||
case _ => throw SRunnerException(ScenarioErrorInvalidPartyName(partyText))
|
case _ => throw SRunnerException(ScenarioErrorInvalidPartyName(partyText))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def mustFail(tx: Transaction, committer: Party) = {
|
private def mustFail(tx: Transaction, committer: Party) = {
|
||||||
// Update expression evaluated successfully,
|
// Update expression evaluated successfully,
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package com.digitalasset.daml.lf.speedy
|
||||||
|
|
||||||
|
import com.digitalasset.daml.lf.PureCompiledPackages
|
||||||
|
import com.digitalasset.daml.lf.data.Ref
|
||||||
|
import com.digitalasset.daml.lf.lfpackage.Ast
|
||||||
|
import com.digitalasset.daml.lf.lfpackage.Ast.ScenarioGetParty
|
||||||
|
import org.scalatest._
|
||||||
|
import org.scalatest.concurrent.ScalaFutures
|
||||||
|
|
||||||
|
class ScenarioRunnerTest extends AsyncWordSpec with Matchers with ScalaFutures {
|
||||||
|
|
||||||
|
"ScenarioRunner" can {
|
||||||
|
"mangle party names correctly" in {
|
||||||
|
val e = Ast.EScenario(ScenarioGetParty(Ast.EPrimLit(Ast.PLText("foo-bar"))))
|
||||||
|
val m = Speedy.Machine.fromExpr(e, PureCompiledPackages(Map.empty).right.get, true)
|
||||||
|
val sr = ScenarioRunner(m, (s) => s + "-XXX")
|
||||||
|
sr.run()
|
||||||
|
m.ctrl shouldBe Speedy.CtrlValue(SValue.SParty(Ref.Party.assertFromString("foo-bar-XXX")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,10 +27,18 @@ import scala.annotation.tailrec
|
|||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
import scala.reflect.{ClassTag, classTag}
|
import scala.reflect.{ClassTag, classTag}
|
||||||
|
|
||||||
|
/** Scenario tester.
|
||||||
|
*
|
||||||
|
* @constructor Creates new tester.
|
||||||
|
* @param partyNameMangler allows to amend party names defined in scenarios,
|
||||||
|
* before they are executed against ledger created with {@code createLedger}.
|
||||||
|
* See {@code ScenarioRunner.partyNameMangler} for details.
|
||||||
|
*/
|
||||||
class SemanticTester(
|
class SemanticTester(
|
||||||
createLedger: Set[SimpleString] => SemanticTester.GenericLedger,
|
createLedger: Set[SimpleString] => SemanticTester.GenericLedger,
|
||||||
packageToTest: PackageId,
|
packageToTest: PackageId,
|
||||||
packages: Map[PackageId, Package])(implicit ec: ExecutionContext) {
|
packages: Map[PackageId, Package],
|
||||||
|
partyNameMangler: (String => String) = identity)(implicit ec: ExecutionContext) {
|
||||||
import SemanticTester._
|
import SemanticTester._
|
||||||
|
|
||||||
// result ledgers from all scenarios found in packages
|
// result ledgers from all scenarios found in packages
|
||||||
@ -48,7 +56,7 @@ class SemanticTester(
|
|||||||
case (name, DValue(_, _, body, isTest)) if isTest =>
|
case (name, DValue(_, _, body, isTest)) if isTest =>
|
||||||
val qualifiedName = QualifiedName(module.name, name)
|
val qualifiedName = QualifiedName(module.name, name)
|
||||||
val machine = buildMachine(body)
|
val machine = buildMachine(body)
|
||||||
ScenarioRunner(machine).run() match {
|
ScenarioRunner(machine, partyNameMangler = partyNameMangler).run() match {
|
||||||
case Left((err, _ledger @ _)) =>
|
case Left((err, _ledger @ _)) =>
|
||||||
sys.error(s"error running scenario $err in scenario: $qualifiedName")
|
sys.error(s"error running scenario $err in scenario: $qualifiedName")
|
||||||
case Right((_time @ _, _steps @ _, ledger)) =>
|
case Right((_time @ _, _steps @ _, ledger)) =>
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
package com.daml.ledger.api.testtool
|
package com.daml.ledger.api.testtool
|
||||||
|
|
||||||
import java.io.{File, StringWriter, PrintWriter}
|
import java.io.{File, PrintWriter, StringWriter}
|
||||||
import java.nio.file.{Files, StandardCopyOption, Paths, Path}
|
import java.nio.file.{Files, Path, Paths, StandardCopyOption}
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import akka.stream.ActorMaterializer
|
import akka.stream.ActorMaterializer
|
||||||
@ -19,6 +19,7 @@ import com.digitalasset.platform.semantictest.SemanticTestAdapter
|
|||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{Await, ExecutionContext}
|
import scala.concurrent.{Await, ExecutionContext}
|
||||||
import scala.collection.breakOut
|
import scala.collection.breakOut
|
||||||
|
import scala.util.Random
|
||||||
|
|
||||||
object LedgerApiTestTool {
|
object LedgerApiTestTool {
|
||||||
|
|
||||||
@ -61,13 +62,17 @@ object LedgerApiTestTool {
|
|||||||
}
|
}
|
||||||
var failed = false
|
var failed = false
|
||||||
|
|
||||||
|
val runSuffix = Random.alphanumeric.take(10).mkString
|
||||||
|
var partyNameMangler = (partyText: String) => s"$partyText-$runSuffix"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
scenarios.foreach {
|
scenarios.foreach {
|
||||||
case (pkgId, names) =>
|
case (pkgId, names) =>
|
||||||
val tester = new SemanticTester(
|
val tester = new SemanticTester(
|
||||||
parties => new SemanticTestAdapter(ledger, packages, parties.map(_.underlyingString)),
|
parties => new SemanticTestAdapter(ledger, packages, parties.map(_.underlyingString)),
|
||||||
pkgId,
|
pkgId,
|
||||||
packages)
|
packages,
|
||||||
|
partyNameMangler)
|
||||||
names
|
names
|
||||||
.foreach { name =>
|
.foreach { name =>
|
||||||
println(s"Testing scenario: $name")
|
println(s"Testing scenario: $name")
|
||||||
|
Loading…
Reference in New Issue
Block a user