Run Transaction Service IT as part of Ledger API Test Tool (#1434)

Add tTansactionServiceIT into Ledger API Test Tool as an optional test.

Available tests can be listed with the --list option.
Tests can be included with --include, and excluded with --exclude.

Fixes #1372, #1472
This commit is contained in:
gleber 2019-06-06 13:32:55 +02:00 committed by Gerolf Seitz
parent 217c56d072
commit b61203d1cd
25 changed files with 431 additions and 225 deletions

View File

@ -397,17 +397,17 @@ implementation of the :doc:`Ledger API
</app-dev/index>`. For example, it will show you if
there are consistency or conformance problem with your implementation.
Assuming that your Ledger API endpoint is accessible at ``localhost:6864``, you can use the tool in the following manner:
Assuming that your Ledger API endpoint is accessible at ``localhost:6865``, you can use the tool in the following manner:
#. Obtain the tool:
``curl -L 'https://bintray.com/api/v1/content/digitalassetsdk/DigitalAssetSDK/com/daml/ledger/testtool/ledger-api-test-tool_2.12/$latest/ledger-api-test-tool_2.12-$latest.jar?bt_package=sdk-components' -o ledger-api-test-tool.jar``
#. Obtain the DAML archive required to run the tests:
#. Obtain the DAML archives required to run the tests:
``java -jar ledger-api-test-tool.jar --extract``
#. Load ``SemanticTests.dar`` which was created in the current directory into your Ledger.
#. Load all ``.dar`` files extracted in the current directory into your Ledger.
#. Run the tool against your ledger:

View File

@ -58,6 +58,17 @@ Sandbox
- Added recovery around failing ledger entry persistence queries using Postgres. See `#1505 <https://github.com/digital-asset/daml/pull/1505>`__.
DAML Integration Kit
~~~~~~~~~~~~~~~~~~~~
- The :doc:`Ledger API Test Tool </tools/ledger-api-test-tool/index>` can now optionally run ``TransactionServiceIT`` as part of the conformance tests.
This means you need to load additional ``.dar`` files into the ledger under test. Please refer to the updated instructions in the :doc:`documentation </tools/ledger-api-test-tool/index>`.
- Added new CLI options to the :doc:`Ledger API Test Tool </tools/ledger-api-test-tool/index>`:
- ``--list`` prints all available tests to the console
- ``--include`` takes a comma-separated list of test names that should be run
- ``--exclude`` takes a comma-separated list of test names that should not be run
0.12.22 - 2019-05-29
--------------------

View File

@ -35,21 +35,21 @@ Run the following command to fetch the tool:
This will create a file ``ledger-api-test-tool.jar`` in your current directory.
Extracting ``.dar`` file required to run the tests
Extracting ``.dar`` files required to run the tests
======================================================
Before you can run the Ledger API test tool on your ledger, you need to load a
specific set of DAML templates onto your ledger.
#. To obtain the corresponding ``.dar`` file, run:
#. To obtain the corresponding ``.dar`` files, run:
.. code-block:: console
$ java -jar ledger-api-test-tool.jar --extract
This creates a file ``SemanticTests.dar`` in the current directory.
This writes all ``.dar`` files required for the tests into the current directory.
#. Load ``SemanticTests.dar`` into your Ledger.
#. Load all ``.dar`` files into your Ledger.
Running the tool against a custom Ledger API endpoint
=====================================================
@ -79,7 +79,22 @@ Run the tool with ``--help`` flag to obtain the list of options the tool provide
$ java -jar ledger-api-test-tool.jar --help
|
Filtering tests
~~~~~~~~~~~~~~~
You can list the available tests with the ``--list`` flag. Some tests are not run by default. You can run them with the ``--include`` flag. To exclude tests, use the ``--exclude`` flag.
This command only runs the test ``TestA``:
.. code-block:: console
$ java -jar ledger-api-test-tool.jar --include TestA
This command runs all tests except the test ``TestB``:
.. code-block:: console
$ java -jar ledger-api-test-tool.jar --exclude TestB
Try out the Ledger API Test Tool against DAML Sandbox
=====================================================
@ -90,7 +105,7 @@ If you wanted to test out the tool, you can run it against :doc:`DAML Sandbox
.. code-block:: console
$ java -jar ledger-api-test-tool.jar --extract
$ da sandbox -- SemanticTests.dar
$ da sandbox -- *.dar
$ java -jar ledger-api-test-tool.jar
This should always succeed, as the Sandbox is tested to correctly implement the

View File

@ -19,7 +19,7 @@ trait AkkaBeforeAndAfterAll extends BeforeAndAfterAll {
protected def actorSystemName = this.getClass.getSimpleName
private val logger = LoggerFactory.getLogger(getClass)
private val executorContext = ExecutionContext.fromExecutorService(
private lazy val executorContext = ExecutionContext.fromExecutorService(
Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder()
.setDaemon(true)
@ -28,10 +28,10 @@ trait AkkaBeforeAndAfterAll extends BeforeAndAfterAll {
logger.error(s"got an uncaught exception on thread: ${thread.getName}"))
.build()))
protected implicit val system: ActorSystem =
protected implicit lazy val system: ActorSystem =
ActorSystem(actorSystemName, defaultExecutionContext = Some(executorContext))
protected implicit val materializer: ActorMaterializer = ActorMaterializer()
protected implicit lazy val materializer: ActorMaterializer = ActorMaterializer()
override protected def afterAll(): Unit = {
materializer.shutdown()

View File

@ -27,7 +27,6 @@ import org.scalatest.concurrent.{AsyncTimeLimitedTests, ScalaFutures}
import org.scalatest.time.SpanSugar._
import org.scalatest.time.{Millis, Span}
import org.scalatest.{Assertion, AsyncWordSpec, Matchers, OptionValues}
import scalaz.syntax.tag._
/**
@ -46,11 +45,13 @@ class ActiveContractsServiceIT
with ScalaFutures
with AsyncTimeLimitedTests
with Matchers
with OptionValues
with TestCommands {
with OptionValues {
override def timeLimit: Span = 60.seconds
protected val testCommands = new TestCommands(config)
protected val templateIds = testCommands.templateIds
override implicit def patienceConfig: PatienceConfig =
PatienceConfig(scaled(Span(30000, Millis)), scaled(Span(500, Millis)))
@ -97,7 +98,7 @@ class ActiveContractsServiceIT
}.size should equal(occurrence)
def threeCommands(ledgerId: domain.LedgerId, commandId: String): SubmitAndWaitRequest =
super.dummyCommands(ledgerId, commandId, "Alice").toWait
testCommands.toWait(testCommands.dummyCommands(ledgerId, commandId, "Alice"))
private def filter = TransactionFilter(Map(config.parties.head -> Filters()))
@ -190,11 +191,13 @@ class ActiveContractsServiceIT
contractId = extractContractId(responses1)
_ <- submitRequest(
ctx,
buildRequest(
ctx.ledgerId,
"exercise-test-exercised",
Seq(exerciseWithUnit(templateIds.dummy, contractId, "DummyChoice1")),
"Alice").toWait)
testCommands.toWait(
testCommands.buildRequest(
ctx.ledgerId,
"exercise-test-exercised",
Seq(testCommands.exerciseWithUnit(templateIds.dummy, contractId, "DummyChoice1")),
"Alice"))
)
responses2 <- waitForActiveContracts(
ctx.acsService,
ctx.ledgerId,
@ -223,11 +226,12 @@ class ActiveContractsServiceIT
val resultsF = for {
_ <- submitRequest(
ctx,
buildRequest(
ctx.ledgerId,
"commandId1",
Seq(createWithOperator(templateIds.dummy, "Alice")),
"Alice").toWait)
testCommands.toWait(
testCommands.buildRequest(
ctx.ledgerId,
"commandId1",
Seq(testCommands.createWithOperator(templateIds.dummy, "Alice")),
"Alice")))
responses1 <- waitForActiveContracts(
ctx.acsService,
ctx.ledgerId,
@ -236,11 +240,12 @@ class ActiveContractsServiceIT
offset = extractOffset(responses1)
_ <- submitRequest(
ctx,
buildRequest(
ctx.ledgerId,
"commandId2",
Seq(createWithOperator(templateIds.dummyWithParam, "Alice")),
"Alice").toWait
testCommands.toWait(
testCommands.buildRequest(
ctx.ledgerId,
"commandId2",
Seq(testCommands.createWithOperator(templateIds.dummyWithParam, "Alice")),
"Alice"))
)
responses2 <- transactionClient(ctx)
.getTransactions(
@ -283,8 +288,12 @@ class ActiveContractsServiceIT
"multi-party request comes" should {
"return the correct set of related contracts" in allFixtures { ctx =>
val resultsF = for {
_ <- submitRequest(ctx, dummyCommands(ctx.ledgerId, "acsCommand-1", "Alice").toWait)
_ <- submitRequest(ctx, dummyCommands(ctx.ledgerId, "acsCommand-2", "Bob").toWait)
_ <- submitRequest(
ctx,
testCommands.toWait(testCommands.dummyCommands(ctx.ledgerId, "acsCommand-1", "Alice")))
_ <- submitRequest(
ctx,
testCommands.toWait(testCommands.dummyCommands(ctx.ledgerId, "acsCommand-2", "Bob")))
allContractsForAlice <- waitForActiveContracts(
ctx.acsService,
ctx.ledgerId,

View File

@ -45,10 +45,12 @@ class DivulgenceIT
with ScalaFutures
with AsyncTimeLimitedTests
with Matchers
with OptionValues
with TestTemplateIds {
with OptionValues {
override protected def config: Config = Config.default
protected val testTemplateIds = new TestTemplateIds(config)
protected val templateIds = testTemplateIds.templateIds
private implicit def party(s: String): Ref.Party = Ref.Party.assertFromString(s)
private implicit def pkgId(s: String): Ref.PackageId = Ref.PackageId.assertFromString(s)
private implicit def id(s: String): Ref.Name = Ref.Name.assertFromString(s)

View File

@ -27,8 +27,10 @@ class LotsOfPartiesIT
with MultiLedgerFixture
with SuiteResourceManagementAroundAll
with AsyncTimeLimitedTests
with Matchers
with TestTemplateIds {
with Matchers {
protected lazy val testTemplateIds = new TestTemplateIds(config)
protected lazy val templateIds = testTemplateIds.templateIds
private val numParties = 1024
private val allParties: List[String] =

View File

@ -8,8 +8,10 @@ import java.time.{Duration, Instant}
import akka.Done
import akka.stream.scaladsl.{Flow, Sink}
import com.digitalasset.daml.lf.data.Ref
import com.digitalasset.daml.lf.data.Ref.QualifiedName
import com.digitalasset.daml.lf.types.Ledger
import com.digitalasset.grpc.adapter.utils.DirectExecutionContext
import com.digitalasset.ledger.api.domain.EventId
import com.digitalasset.ledger.api.domain.{EventId, LedgerId}
import com.digitalasset.ledger.api.testing.utils.MockMessages.{party, _}
import com.digitalasset.ledger.api.testing.utils.{
AkkaBeforeAndAfterAll,
@ -17,6 +19,7 @@ import com.digitalasset.ledger.api.testing.utils.{
MockMessages,
SuiteResourceManagementAroundAll
}
import com.digitalasset.ledger.api.v1.command_service.SubmitAndWaitRequest
import com.digitalasset.ledger.api.v1.commands.Command.Command.Create
import com.digitalasset.ledger.api.v1.commands.{Command, CreateCommand, ExerciseCommand}
import com.digitalasset.ledger.api.v1.event.Event.Event.{Archived, Created}
@ -35,6 +38,7 @@ import com.digitalasset.ledger.api.v1.value.{
Value,
Variant
}
import com.digitalasset.ledger.client.services.commands.CommandUpdater
import com.digitalasset.ledger.client.services.transactions.TransactionClient
import com.digitalasset.platform.api.v1.event.EventOps._
import com.digitalasset.platform.apitesting.LedgerContextExtensions._
@ -53,8 +57,7 @@ import scalaz.{ICons, NonEmptyList, Tag}
import scala.collection.{breakOut, immutable}
import scala.concurrent.Future
import com.digitalasset.ledger.api.domain.LedgerId
import scala.util.Random
@SuppressWarnings(Array("org.wartremover.warts.Any"))
class TransactionServiceIT
@ -65,15 +68,25 @@ class TransactionServiceIT
with Inside
with AsyncTimeLimitedTests
with TestExecutionSequencerFactory
with TransactionServiceHelpers
with ParameterShowcaseTesting
with OptionValues
with Matchers
with TestTemplateIds {
with Matchers {
override protected val config: Config =
override protected def config: Config =
Config.default.withTimeProvider(TimeProviderType.WallClock)
protected val helpers = new TransactionServiceHelpers(config)
protected val testTemplateIds = new TestTemplateIds(config)
protected val templateIds = testTemplateIds.templateIds
val runSuffix = "-" + Random.alphanumeric.take(10).mkString
val partyNameMangler =
(partyText: String) => partyText + runSuffix + Random.alphanumeric.take(10).mkString
val commandIdMangler: ((QualifiedName, Int, Ledger.ScenarioNodeId) => String) =
(testName, stepId, nodeId) => {
s"ledger-api-test-tool-$testName-$stepId-$nodeId-$runSuffix"
}
override val timeLimit: Span = 300.seconds
private def newClient(stub: TransactionService, ledgerId: LedgerId): TransactionClient =
@ -118,7 +131,11 @@ class TransactionServiceIT
val elemsToTake = 10L
for {
_ <- insertCommands(getTrackerFlow(context), "cancellation-test", 14, context.ledgerId)
_ <- insertCommandsUnique(
"cancellation-test",
14,
context
)
transactions <- context.transactionClient
.getTransactions(ledgerBegin, None, getAllContracts)
.take(elemsToTake)
@ -133,8 +150,8 @@ class TransactionServiceIT
val client = context.transactionClient
for {
le <- client.getLedgerEnd
_ <- insertCommands("deduplicated", 1, context)
_ = insertCommands("deduplicated", 1, context) // we don't wait for this since the result won't be seen
_ <- insertCommandsUnique("deduplicated", 1, context)
_ = insertCommandsUnique("deduplicated", 1, context) // we don't wait for this since the result won't be seen
txs <- client
.getTransactions(le.getOffset, None, getAllContracts)
.takeWithin(2.seconds)
@ -161,11 +178,7 @@ class TransactionServiceIT
.runWith(Sink.seq)
for {
_ <- insertCommands(
getTrackerFlow(context),
"stream-completion-test",
14,
context.ledgerId)
_ <- insertCommandsUnique("stream-completion-test", 14, context)
_ <- resultsF
} yield {
succeed // resultF would not complete unless the server terminates the connection
@ -182,7 +195,7 @@ class TransactionServiceIT
for {
ledgerEndResponse <- client.getLedgerEnd
_ <- insertCommands(firstSectionPrefix, commandsPerSection, context)
_ <- insertCommandsUnique(firstSectionPrefix, commandsPerSection, context)
firstSection <- client
.getTransactions(ledgerEndResponse.getOffset, None, getAllContracts)
.filter(_.commandId.startsWith(sharedPrefix))
@ -191,7 +204,7 @@ class TransactionServiceIT
_ = firstSection should have size commandsPerSection.toLong
ledgerEndAfterFirstSection = lastOffsetIn(firstSection).value
_ <- insertCommands(sharedPrefix + "-2", commandsPerSection, context)
_ <- insertCommandsUnique(sharedPrefix + "-2", commandsPerSection, context)
secondSection <- client
.getTransactions(ledgerEndAfterFirstSection, None, getAllContracts)
@ -223,7 +236,7 @@ class TransactionServiceIT
for {
ledgerEndOnStart <- client.getLedgerEnd
_ <- insertCommands(commandPrefix, smallCommandCount, context)
_ <- insertCommandsUnique(commandPrefix, smallCommandCount, context)
readTransactions = () =>
client
.getTransactions(ledgerEndOnStart.getOffset, None, getAllContracts)
@ -251,7 +264,7 @@ class TransactionServiceIT
val anotherParty = "Alice"
for {
ledgerEndResponse <- client.getLedgerEnd
_ <- insertCommands(commandPrefix, 1, context)
_ <- insertCommandsUnique(commandPrefix, 1, context)
// At this point we verified that the value has been written to the submitter's LSM.
// This test code assumes that the value would be written to other parties' LSMs within 100 ms.
transactions <- client
@ -299,7 +312,7 @@ class TransactionServiceIT
for {
savedLedgerEnd <- client.getLedgerEnd
_ <- insertCommands("end-before-start-test", 1, context)
_ <- insertCommandsUnique(s"end-before-start-test", 1, context)
tx <- client
.getTransactions(savedLedgerEnd.getOffset, None, getAllContracts)
.runWith(Sink.head)
@ -319,7 +332,7 @@ class TransactionServiceIT
"expose transactions to non-submitting stakeholders without the commandId" in allFixtures {
c =>
c.submitCreateAndReturnTransaction(
"Checking_commandId_visibility_for_non-submitter_party",
s"Checking_commandId_visibility_for_non-submitter_party-${runSuffix}",
templateIds.agreementFactory,
List("receiver" -> party1.asParty, "giver" -> party2.asParty).asRecordFields,
party2,
@ -330,7 +343,7 @@ class TransactionServiceIT
}
"expose only the requested templates to the client" in allFixtures { context =>
val commandId = "Client_should_see_only_the_Dummy_create."
val commandId = s"Client_should_see_only_the_Dummy_create-${runSuffix}"
val templateInSubscription = templateIds.dummy
val otherTemplateCreated = templateIds.dummyFactory
for {
@ -353,8 +366,8 @@ class TransactionServiceIT
"expose contract Ids that are ready to be used for exercising choices" in allFixtures {
context =>
val factoryCreation = "Creating_factory"
val exercisingChoice = "Exercising_choice_on_factory"
val factoryCreation = s"Creating_factory-${runSuffix}"
val exercisingChoice = s"Exercising_choice_on_factory-${runSuffix}"
val exercisedTemplate = templateIds.dummyFactory
for {
createdEvent <- context.submitCreate(
@ -379,8 +392,8 @@ class TransactionServiceIT
"expose contract Ids that are results of exercising choices when filtering by template" in allFixtures {
context =>
val factoryCreation = "Creating_second_factory"
val exercisingChoice = "Exercising_choice_on_second_factory"
val factoryCreation = s"Creating_second_factory-${runSuffix}"
val exercisingChoice = s"Exercising_choice_on_second_factory-${runSuffix}"
val exercisedTemplate = templateIds.dummyFactory
for {
creation <- context.submitCreate(
@ -423,7 +436,7 @@ class TransactionServiceIT
"reject exercising a choice where an assertion fails" in allFixtures { c =>
for {
dummy <- c.submitCreate(
"Create_for_assertion_failing_test",
s"Create_for_assertion_failing_test-${runSuffix}",
templateIds.dummy,
List("operator" -> party.asParty).asRecordFields,
party)
@ -452,7 +465,7 @@ class TransactionServiceIT
val expectedArg = paramShowcaseArgsWithoutLabels
for {
create <- c.submitCreate(
"Creating_contract_with_a_multitude_of_param_types",
s"Creating_contract_with_a_multitude_of_param_types-${runSuffix}",
template,
paramShowcaseArgs(templateIds.testPackageId),
"party",
@ -470,7 +483,7 @@ class TransactionServiceIT
val variant = Value(Value.Sum.Variant(Variant(None, "SomeInteger", 1.asInt64)))
for {
create <- c.submitCreate(
"Creating_contract_with_a_multitude_of_verbose_param_types",
s"Creating_contract_with_a_multitude_of_verbose_param_types-${runSuffix}",
template,
arg,
"party",
@ -523,7 +536,7 @@ class TransactionServiceIT
val template = templateIds.parameterShowcase
for {
create <- c.submitCreate(
"Huge_command_with_a_long_list",
s"Huge_command_with_a_long_list-${runSuffix}",
template,
arg,
"party"
@ -538,15 +551,16 @@ class TransactionServiceIT
val giver = "Alice"
for {
created <- c.submitCreateWithListenerAndReturnEvent(
"Creating_Agreement_Factory",
s"Creating_Agreement_Factory-${runSuffix}",
templateIds.agreementFactory,
List("receiver" -> receiver.asParty, "giver" -> giver.asParty).asRecordFields,
giver,
giver)
giver
)
choiceResult <- c.testingHelpers.submitAndListenForSingleResultOfCommand(
c.command(
"Calling_non-consuming_choice",
s"Calling_non-consuming_choice-${runSuffix}",
List(
ExerciseCommand(
Some(templateIds.agreementFactory),
@ -572,7 +586,7 @@ class TransactionServiceIT
for {
branchingSignatories <- c.submitCreateWithListenerAndReturnEvent(
"BranchingSignatoriesTrue",
s"BranchingSignatoriesTrue-${runSuffix}",
templateIds.branchingSignatories,
branchingSignatoriesArg,
party1,
@ -586,7 +600,7 @@ class TransactionServiceIT
val branchingSignatoriesArg =
getBranchingSignatoriesArg(false, party1, party2)
c.submitCreateWithListenerAndAssertNotVisible(
"BranchingSignatoriesFalse",
s"BranchingSignatoriesFalse-${runSuffix}",
templateIds.branchingSignatories,
branchingSignatoriesArg,
party2,
@ -599,7 +613,7 @@ class TransactionServiceIT
val expectedArg = branchingControllersArgs.map(_.copy(label = ""))
for {
branchingControllers <- c.submitCreateWithListenerAndReturnEvent(
"BranchingControllersTrue",
s"BranchingControllersTrue-${runSuffix}",
templateId,
branchingControllersArgs,
party1,
@ -614,7 +628,7 @@ class TransactionServiceIT
val branchingControllersArgs =
getBranchingControllerArgs(party1, party2, party3, false)
c.submitCreateWithListenerAndAssertNotVisible(
"BranchingControllersFalse",
s"BranchingControllersFalse-${runSuffix}",
templateId,
branchingControllersArgs,
party1,
@ -634,7 +648,7 @@ class TransactionServiceIT
.sequence(observers.map(observer =>
for {
withObservers <- c.submitCreateWithListenerAndReturnEvent(
"Obs1create:" + observer,
s"Obs1create:${observer}-${runSuffix}",
templateIds.withObservers,
withObserversArg,
giver,
@ -655,7 +669,7 @@ class TransactionServiceIT
val expectedArgs = createArguments.map(_.copy(label = ""))
c.submitCreate(
"Creating_contract_with_a_Nothing_argument",
s"Creating_contract_with_a_Nothing_argument-${runSuffix}",
templateIds.nothingArgument,
createArguments,
"party")
@ -673,7 +687,7 @@ class TransactionServiceIT
"expose the default agreement text in CreatedEvents for templates with no explicit agreement text" in allFixtures {
c =>
val resultF = c.submitCreate(
"Creating_dummy_contract_for_default_agreement_text_test",
s"Creating_dummy_contract_for_default_agreement_text_test-${runSuffix}",
templateIds.dummy,
List(RecordField("operator", party1.asParty)),
party1)
@ -690,12 +704,12 @@ class TransactionServiceIT
for {
agreement <- createAgreement(c, "MA1", receiver, giver)
triProposal <- c.submitCreate(
"MA1proposal",
s"MA1proposal-${runSuffix}",
templateIds.triProposal,
triProposalArg,
operator)
tx <- c.submitExercise(
"MA1acceptance",
s"MA1acceptance-${runSuffix}",
templateIds.agreement,
List("cid" -> Value(ContractId(triProposal.contractId))).asRecordValue,
"AcceptTriProposal",
@ -716,12 +730,12 @@ class TransactionServiceIT
val expectedArg = triProposalArg.map(_.copy(label = ""))
for {
triProposal <- c.submitCreate(
"MA2proposal",
s"MA2proposal-${runSuffix}",
templateIds.triProposal,
triProposalArg,
operator)
tx <- c.submitExercise(
"MA2acceptance",
s"MA2acceptance-${runSuffix}",
templateIds.triProposal,
unitArg,
"TriProposalAccept",
@ -738,7 +752,7 @@ class TransactionServiceIT
val triProposalArg = mkTriProposalArg(operator, receiver, giver)
for {
triProposal <- c.submitCreate(
"MA3proposal",
s"MA3proposal-${runSuffix}",
templateIds.triProposal,
triProposalArg,
operator)
@ -769,7 +783,7 @@ class TransactionServiceIT
for {
agreement <- createAgreement(c, "MA4", receiver, giver)
triProposal <- c.submitCreate(
"MA4proposal",
s"MA4proposal-${runSuffix}",
templateIds.triProposal,
triProposalArg,
operator)
@ -794,12 +808,12 @@ class TransactionServiceIT
val arguments = List("street", "city", "state", "zip")
for {
dummy <- c.submitCreate(
"Create_dummy_for_creating_AddressWrapper",
s"Create_dummy_for_creating_AddressWrapper-${runSuffix}",
templateIds.dummy,
List("operator" -> party.asParty).asRecordFields,
party)
exercise <- c.submitExercise(
"Creating_AddressWrapper",
s"Creating_AddressWrapper-${runSuffix}",
templateIds.dummy,
List("address" -> arguments.map(e => e -> e.asText).asRecordValue).asRecordValue,
"WrapWithAddress",
@ -860,13 +874,13 @@ class TransactionServiceIT
val createAndFetchTid = templateIds.createAndFetch
for {
createdEvent <- context.submitCreate(
"CreateAndFetch_Create",
s"CreateAndFetch_Create-$runSuffix",
createAndFetchTid,
List("p" -> party.asParty).asRecordFields,
party)
cid = createdEvent.contractId
exerciseTx <- context.submitExercise(
"CreateAndFetch_Run",
s"CreateAndFetch_Run-$runSuffix",
createAndFetchTid,
Value(Value.Sum.Record(Record())),
"CreateAndFetch_Run",
@ -916,11 +930,7 @@ class TransactionServiceIT
val beginOffset =
LedgerOffset(LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN))
for {
_ <- insertCommands(
getTrackerFlow(context),
"tree-provenance-by-id",
1,
context.ledgerId)
_ <- insertCommandsUnique("tree-provenance-by-id", 1, context)
firstTransaction <- context.transactionClient
.getTransactions(beginOffset, None, transactionFilter)
.runWith(Sink.head)
@ -1015,11 +1025,7 @@ class TransactionServiceIT
val beginOffset =
LedgerOffset(LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN))
for {
_ <- insertCommands(
getTrackerFlow(context),
"flat-provenance-by-id",
1,
context.ledgerId)
_ <- insertCommandsUnique("flat-provenance-by-id", 1, context)
firstTransaction <- context.transactionClient
.getTransactions(beginOffset, None, transactionFilter)
.runWith(Sink.head)
@ -1085,11 +1091,7 @@ class TransactionServiceIT
val beginOffset =
LedgerOffset(LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN))
for {
_ <- insertCommands(
getTrackerFlow(context),
"tree-provenance-by-event-id",
1,
context.ledgerId)
_ <- insertCommandsUnique("tree-provenance-by-event-id", 1, context)
tx <- context.transactionClient
.getTransactions(beginOffset, None, transactionFilter)
.runWith(Sink.head)
@ -1154,11 +1156,7 @@ class TransactionServiceIT
val beginOffset =
LedgerOffset(LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN))
for {
_ <- insertCommands(
getTrackerFlow(context),
"flat-provenance-by-event-id",
1,
context.ledgerId)
_ <- insertCommandsUnique("flat-provenance-by-event-id", 1, context)
tx <- context.transactionClient
.getTransactions(beginOffset, None, transactionFilter)
.runWith(Sink.head)
@ -1305,11 +1303,7 @@ class TransactionServiceIT
.runWith(Sink.seq)
for {
_ <- insertCommands(
getTrackerFlow(context),
"cancellation-test-tree",
commandsToSend,
context.ledgerId)
_ <- insertCommandsUnique("cancellation-test-tree", commandsToSend, context)
elems <- resultsF
} yield (elems should have length elemsToTake)
}
@ -1368,11 +1362,7 @@ class TransactionServiceIT
r1 <- context.transactionClient
.getTransactionTrees(ledgerBegin, Some(ledgerEnd), transactionFilter)
.runWith(Sink.seq)
_ <- insertCommands(
getTrackerFlow(context),
"complete_test",
noOfCommands,
context.ledgerId)
_ <- insertCommandsUnique("complete_test", noOfCommands, context)
r2 <- context.transactionClient
.getTransactionTrees(ledgerBegin, Some(ledgerEnd), transactionFilter)
.runWith(Sink.seq)
@ -1509,7 +1499,27 @@ class TransactionServiceIT
prefix: String,
commandsPerSection: Int,
context: LedgerContext): Future[Done] = {
insertCommands(getTrackerFlow(context), prefix, commandsPerSection, context.ledgerId)
helpers.insertCommands(
request => applyTimeAndSubmit(request, context),
prefix,
commandsPerSection,
context.ledgerId)
}
private def applyTimeAndSubmit(req: SubmitAndWaitRequest, context: LedgerContext) = {
context.commandClient().flatMap { client =>
val ttl = Duration.ofMillis(config.commandConfiguration.commandTtl.toMillis)
val updater = new CommandUpdater(client.timeProviderO, ttl, true)
val reqToSend = req.copy(commands = req.commands.map(updater.applyOverrides))
context.commandService.submitAndWaitForTransactionId(reqToSend)
}
}
private def insertCommandsUnique(
prefix: String,
commandsPerSection: Int,
context: LedgerContext): Future[Done] = {
insertCommands(s"${prefix}-${runSuffix}", commandsPerSection, context)
}
private def lastOffsetIn(secondSection: immutable.Seq[Transaction]): Option[LedgerOffset] = {
@ -1600,15 +1610,16 @@ class TransactionServiceIT
): Future[CreatedEvent] = {
for {
agreementFactory <- c.submitCreate(
commandId + "factory_creation",
commandId + s"factory_creation-${runSuffix}",
templateIds.agreementFactory,
List(
"receiver" -> receiver.asParty,
"giver" -> giver.asParty
).asRecordFields,
giver)
giver
)
tx <- c.submitExercise(
commandId + "_acceptance",
commandId + s"_acceptance-${runSuffix}",
templateIds.agreementFactory,
unitArg,
"AgreementFactoryAccept",
@ -1632,7 +1643,7 @@ class TransactionServiceIT
): Future[Assertion] =
c.testingHelpers.assertCommandFailsWithCode(
c.command(
commandId,
s"${commandId}-${runSuffix}",
List(ExerciseCommand(Some(template), contractId, choice, Some(arg)).wrap))
.update(
_.commands.party := submitter
@ -1650,7 +1661,7 @@ class TransactionServiceIT
for {
creation <- context.submitCreate(
s"Creating_contract_with_a_multitude_of_param_types_for_exercising_$choice#$lbl",
s"Creating_contract_with_a_multitude_of_param_types_for_exercising-${runSuffix}_$choice#$lbl",
templateIds.parameterShowcase,
paramShowcaseArgs(templateIds.testPackageId),
MockMessages.party
@ -1664,7 +1675,9 @@ class TransactionServiceIT
exerciseArg).wrap
exerciseTx <- context.testingHelpers.submitAndListenForSingleResultOfCommand(
context
.command(s"Exercising_with_a_multitiude_of_params_$choice#$lbl", List(exerciseCommand)),
.command(
s"Exercising_with_a_multitiude_of_params-${runSuffix}_$choice#$lbl",
List(exerciseCommand)),
getAllContracts
)
} yield {

View File

@ -33,15 +33,17 @@ class TransactionServiceLargeCommandIT
with Inside
with AsyncTimeLimitedTests
with TestExecutionSequencerFactory
with TransactionServiceHelpers
with ParameterShowcaseTesting
with OptionValues
with Matchers
with TestTemplateIds {
with Matchers {
override protected val config: Config =
Config.default.withTimeProvider(TimeProviderType.Static)
protected val helpers = new TransactionServiceHelpers(config)
protected val testTemplateIds = new TestTemplateIds(config)
protected val templateIds = testTemplateIds.templateIds
override val timeLimit: Span = 300.seconds
private val getAllContracts = transactionFilter

View File

@ -29,10 +29,12 @@ class WitnessesIT
with SuiteResourceManagementAroundEach
with ScalaFutures
with AsyncTimeLimitedTests
with Matchers
with TestTemplateIds {
with Matchers {
override protected def config: Config = Config.default
protected val testTemplateIds = new TestTemplateIds(config)
protected val templateIds = testTemplateIds.templateIds
private def commandClient(ctx: LedgerContext): SynchronousCommandClient =
new SynchronousCommandClient(ctx.commandService)

View File

@ -22,10 +22,12 @@ class WronglyTypedContractIdIT
with SuiteResourceManagementAroundEach
with ScalaFutures
with AsyncTimeLimitedTests
with Matchers
with TestTemplateIds {
with Matchers {
override protected def config: Config = Config.default
protected val testTemplateIds = new TestTemplateIds(config)
protected val templateIds = testTemplateIds.templateIds
def createDummy(ctx: LedgerContext) = ctx.testingHelpers.simpleCreate(
"create-dummy",
"alice",

View File

@ -46,8 +46,10 @@ class CommandClientIT
with SuiteResourceManagementAroundAll
with ParameterShowcaseTesting
with TryValues
with MultiLedgerCommandUtils
with TestTemplateIds {
with MultiLedgerCommandUtils {
protected val testTemplateIds = new TestTemplateIds(config)
protected val templateIds = testTemplateIds.templateIds
private val submittingParty: String = submitRequest.getCommands.party
private val submittingPartyList = List(submittingParty)

View File

@ -46,9 +46,11 @@ class CommandStaticTimeIT
with ScalaFutures
with SuiteResourceManagementAroundAll
with MultiLedgerCommandUtils
with TestTemplateIds
with OptionValues {
protected val testTemplateIds = new TestTemplateIds(config)
protected val templateIds = testTemplateIds.templateIds
override def timeLimit: Span = 15.seconds
private val tenDays: time.Duration = java.time.Duration.ofDays(10L)

View File

@ -78,7 +78,7 @@ class FailingCommandsIT
.runWith(Sink.head)
} yield {
result.value should matchPattern {
case Completion(`failingCommandId`, Some(status), _, _) if status.code == 3 =>
case Completion(helpers.failingCommandId, Some(status), _, _) if status.code == 3 =>
}
}
}

View File

@ -35,8 +35,9 @@ class TransactionBackpressureIT
with ScalaFutures
with AkkaBeforeAndAfterAll
with SuiteResourceManagementAroundAll
with MultiLedgerFixture
with TestCommands {
with MultiLedgerFixture {
protected val testCommands = new TestCommands(config)
override def timeLimit: Span = 300.seconds
@ -63,7 +64,8 @@ class TransactionBackpressureIT
Source(1 to noOfCommands)
.throttle(10, 1.second)
.mapAsync(10)(i =>
commandClient.submitSingleCommand(oneKbCommandRequest(ctx.ledgerId, s"command-$i")))
commandClient.submitSingleCommand(
testCommands.oneKbCommandRequest(ctx.ledgerId, s"command-$i")))
.runWith(Sink.ignore)
def subscribe(rate: Int) =

View File

@ -60,11 +60,13 @@ abstract class CommandTransactionChecks
with ScalaFutures
with AsyncTimeLimitedTests
with Matchers
with OptionValues
with TestTemplateIds {
with OptionValues {
protected def submitCommand(ctx: LedgerContext, req: SubmitRequest): Future[Completion]
override protected val config: Config = Config.default
protected val testTemplateIds = new TestTemplateIds(config)
protected val templateIds = testTemplateIds.templateIds
override protected def config: Config = Config.default
private lazy val dummyTemplates =
List(templateIds.dummy, templateIds.dummyFactory, templateIds.dummyWithParam)

View File

@ -3,8 +3,10 @@
package com.digitalasset.platform.apitesting
import java.io.File
import java.util
import com.digitalasset.daml.lf.UniversalArchiveReader
import com.digitalasset.ledger.api.domain
import com.digitalasset.ledger.api.testing.utils.MockMessages.{
applicationId,
@ -19,19 +21,22 @@ import com.digitalasset.ledger.api.v1.commands.{Command, CreateCommand, Exercise
import com.digitalasset.ledger.api.v1.value.Value.Sum
import com.digitalasset.ledger.api.v1.value.Value.Sum.{Party, Text}
import com.digitalasset.ledger.api.v1.value.{Identifier, Record, RecordField, Value}
import com.digitalasset.platform.tests.integration.ledger.api.TransactionServiceHelpers
import com.digitalasset.platform.PlatformApplications
import com.google.protobuf.timestamp.Timestamp
import scalaz.syntax.tag._
trait TestTemplateIds extends TransactionServiceHelpers {
protected lazy val templateIds: TestTemplateIdentifiers = {
new TestTemplateIdentifiers(parsedPackageId)
}
class TestTemplateIds(config: PlatformApplications.Config) {
lazy val defaultDar: File = config.darFiles.head.toFile
lazy val parsedPackageId: String =
UniversalArchiveReader().readFile(defaultDar).get.main._1
lazy val templateIds: TestTemplateIdentifiers = new TestTemplateIdentifiers(parsedPackageId)
}
trait TestCommands extends TestTemplateIds {
protected def buildRequest(
class TestCommands(config: PlatformApplications.Config) {
protected val testIds = new TestTemplateIds(config)
val templateIds = testIds.templateIds
def buildRequest(
ledgerId: domain.LedgerId,
commandId: String,
commands: Seq[Command],
@ -49,10 +54,7 @@ trait TestCommands extends TestTemplateIds {
_.commands.maximumRecordTime := maxRecordTime
)
protected def dummyCommands(
ledgerId: domain.LedgerId,
commandId: String,
party: String = "party") =
def dummyCommands(ledgerId: domain.LedgerId, commandId: String, party: String = "party") =
buildRequest(
ledgerId,
commandId,
@ -64,7 +66,7 @@ trait TestCommands extends TestTemplateIds {
party
)
protected def createWithOperator(templateId: Identifier, party: String = "party") =
def createWithOperator(templateId: Identifier, party: String = "party") =
Command(
Create(CreateCommand(
Some(templateId),
@ -77,7 +79,7 @@ trait TestCommands extends TestTemplateIds {
new String(array)
}
protected def oneKbCommand(templateId: Identifier) =
def oneKbCommand(templateId: Identifier) =
Command(
Create(
CreateCommand(
@ -91,21 +93,19 @@ trait TestCommands extends TestTemplateIds {
)))
)))
protected def oneKbCommandRequest(
def oneKbCommandRequest(
ledgerId: domain.LedgerId,
commandId: String,
party: String = "party"): SubmitRequest =
buildRequest(ledgerId, commandId, List(oneKbCommand(templateIds.textContainer)), party)
protected def exerciseWithUnit(
def exerciseWithUnit(
templateId: Identifier,
contractId: String,
choice: String,
args: Option[Value] = Some(Value(Sum.Record(Record.defaultInstance)))) =
Command(Exercise(ExerciseCommand(Some(templateId), contractId, choice, args)))
implicit class SubmitRequestEnhancer(request: SubmitRequest) {
def toWait: SubmitAndWaitRequest = SubmitAndWaitRequest(request.commands)
}
def toWait(request: SubmitRequest): SubmitAndWaitRequest = SubmitAndWaitRequest(request.commands)
}

View File

@ -27,8 +27,8 @@ import com.google.rpc.status.Status
import io.grpc.StatusRuntimeException
import org.scalatest.concurrent.Waiters
import org.scalatest.{Assertion, Inside, Matchers, OptionValues}
import scalaz.syntax.tag._
import scala.collection.{breakOut, immutable}
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, Future}

View File

@ -7,25 +7,26 @@ import java.io.File
import akka.Done
import akka.stream.Materializer
import akka.stream.scaladsl.{Flow, Sink, Source}
import akka.stream.scaladsl.{Sink, Source}
import com.digitalasset.daml.lf.UniversalArchiveReader
import com.digitalasset.ledger.api.domain
import com.digitalasset.ledger.api.testing.utils.MockMessages.submitRequest
import com.digitalasset.ledger.api.v1.command_submission_service.SubmitRequest
import com.digitalasset.ledger.api.testing.utils.MockMessages.submitAndWaitRequest
import com.digitalasset.ledger.api.v1.command_service.{
SubmitAndWaitForTransactionIdResponse,
SubmitAndWaitRequest
}
import com.digitalasset.ledger.api.v1.commands.Command.Command.Create
import com.digitalasset.ledger.api.v1.commands.{Command, CreateCommand}
import com.digitalasset.ledger.api.v1.completion.Completion
import com.digitalasset.ledger.api.v1.value.Value.Sum
import com.digitalasset.ledger.api.v1.value.{Identifier, Record, RecordField, Value}
import com.digitalasset.platform.PlatformApplications
import com.digitalasset.util.Ctx
import org.scalatest.Matchers
import scalaz.syntax.tag._
import scala.concurrent.Future
trait TransactionServiceHelpers extends Matchers {
lazy val defaultDar: File = PlatformApplications.Config.defaultDarFile
class TransactionServiceHelpers(config: PlatformApplications.Config) extends Matchers {
lazy val defaultDar: File = config.darFiles.head.toFile
lazy val parsedPackageId: String =
UniversalArchiveReader().readFile(defaultDar).get.main._1
@ -47,15 +48,15 @@ trait TransactionServiceHelpers extends Matchers {
.withTemplateId(dummyTemplate)
.withCreateArguments(wrongArgs))
def submitRequestWithId(id: String, command: Command, ledgerId: domain.LedgerId) =
submitRequest
def submitAndWaitRequestWithId(id: String, command: Command, ledgerId: domain.LedgerId) =
submitAndWaitRequest
.update(_.commands.commandId := id)
.update(_.commands.commands := Seq(command))
.update(_.commands.ledgerId := ledgerId.unwrap)
// TODO command tracking should be used here
def insertCommands(
trackingFlow: Flow[Ctx[Int, SubmitRequest], Ctx[Int, Completion], _],
submit: SubmitAndWaitRequest => Future[SubmitAndWaitForTransactionIdResponse],
prefix: String,
i: Int,
ledgerId: domain.LedgerId,
@ -63,15 +64,11 @@ trait TransactionServiceHelpers extends Matchers {
val arg =
Record(Some(dummyTemplate), Vector(RecordField("operator", Some(Value(Sum.Party(party))))))
val command = Create(CreateCommand(Some(dummyTemplate), Some(arg)))
Source(for {
x <- Range(0, i)
req = submitRequestWithId(s"$prefix-$x", Command(command), ledgerId)
c = Ctx(x, req)
} yield c)
.via(trackingFlow)
Source(Range(0, i))
.map(x => submitAndWaitRequestWithId(s"$prefix-$x", Command(command), ledgerId))
.mapAsync(1)(submit)
.runWith(Sink.foreach {
case Ctx(i, Completion(_, Some(status), transactionId, _)) =>
status should have('code (0))
case SubmitAndWaitForTransactionIdResponse(transactionId) =>
transactionId should not be empty
()
})

View File

@ -21,12 +21,14 @@ import scalaz.syntax.tag._
Array(
"org.wartremover.warts.Any"
))
trait MultiLedgerCommandUtils extends TransactionServiceHelpers with MultiLedgerFixture {
trait MultiLedgerCommandUtils extends MultiLedgerFixture {
self: AsyncTestSuite =>
protected final def newSynchronousCommandClient(ctx: LedgerContext): SynchronousCommandClient =
new SynchronousCommandClient(ctx.commandService)
protected val helpers = new TransactionServiceHelpers(config)
protected val testLedgerId = domain.LedgerId("ledgerId")
protected val testNotLedgerId = domain.LedgerId("hotdog")
protected val submitRequest: SubmitRequest =
@ -38,10 +40,10 @@ trait MultiLedgerCommandUtils extends TransactionServiceHelpers with MultiLedger
Commands()
.withParty("Alice")
.withLedgerId(testLedgerId.unwrap)
.withCommandId(failingCommandId)
.withCommandId(helpers.failingCommandId)
.withWorkflowId(workflowId)
.withApplicationId(applicationId)
.withCommands(Seq(wrongCreate))))
.withCommands(Seq(helpers.wrongCreate))))
protected val submitAndWaitRequest: SubmitAndWaitRequest =
MockMessages.submitAndWaitRequest

View File

@ -64,6 +64,7 @@ da_scala_binary(
resources = [
"src/main/resources/logback.xml",
"//ledger/ledger-api-integration-tests:SemanticTests.dar",
"//ledger/sandbox:Test.dar",
],
tags = [
"maven_coordinates=com.daml.ledger.testtool:ledger-api-test-tool:__VERSION__",

View File

@ -64,6 +64,10 @@ object Cli {
|Defaults to 1.0. Use numbers higher than 1.0 to make test timeouts more lax,
|use numbers lower than 1.0 to make test timeouts more strict.""".stripMargin)
opt[Unit]("verbose")
.action((_, c) => c.copy(verbose = true))
.text("Prints full stacktraces on failures.")
opt[Unit]("must-fail")
.action((_, c) => c.copy(mustFail = true))
.text("""Reverse success status logic of the tool. Use this flag if you expect one or
@ -76,6 +80,19 @@ object Cli {
.text("""Extract a DAR necessary to test a DAML ledger and exit without running tests.
|The DAR needs to be manually loaded into a DAML ledger for the tool to work.""".stripMargin)
opt[Seq[String]]("exclude")
.action((ex, c) => c.copy(excluded = ex.toSet))
.text("""A comma-separated list of tests that should NOT be run. By default, no tests are excluded.""")
opt[Seq[String]]("include")
.action((inc, c) => c.copy(included = inc.toSet))
.text(
"""A comma-separated list of tests that should be run. By default, all tests are run.""")
opt[Unit]("list")
.action((_, c) => c.copy(listTests = true))
.text("""Lists all available tests that can be used in the include and exclude options.""")
}
def parse(args: Array[String]): Option[Config] =

View File

@ -11,9 +11,13 @@ final case class Config(
port: Int,
packageContainer: DamlPackageContainer,
mustFail: Boolean,
verbose: Boolean,
timeoutScaleFactor: Double,
extract: Boolean,
tlsConfig: Option[TlsConfiguration]
tlsConfig: Option[TlsConfiguration],
excluded: Set[String],
included: Set[String],
listTests: Boolean
)
object Config {
@ -22,8 +26,12 @@ object Config {
port = 6865,
packageContainer = DamlPackageContainer(),
mustFail = false,
verbose = false,
timeoutScaleFactor = 1.0,
extract = false,
tlsConfig = None
tlsConfig = None,
excluded = Set.empty,
included = Set.empty,
listTests = false
)
}
}

View File

@ -6,18 +6,26 @@ package com.daml.ledger.api.testtool
import java.io.File
import java.nio.file.{Files, Path, Paths, StandardCopyOption}
import com.digitalasset.platform.PlatformApplications
import com.digitalasset.platform.PlatformApplications.RemoteApiEndpoint
import com.digitalasset.platform.common.LedgerIdMode
import com.digitalasset.platform.semantictest.SandboxSemanticTestsLfRunner
import com.digitalasset.platform.services.time.TimeProviderType
import com.digitalasset.platform.testing.LedgerBackend
import org.scalatest.Args
import com.digitalasset.platform.tests.integration.ledger.api.TransactionServiceIT
import org.scalatest.time.{Seconds, Span}
import org.scalatest.{Args, Suite}
import scala.util.control.NonFatal
object LedgerApiTestTool {
val semanticTestsResource = "/ledger/ledger-api-integration-tests/SemanticTests.dar"
val integrationTestResource = "/ledger/sandbox/Test.dar"
val testResources = List(
integrationTestResource,
semanticTestsResource,
)
def main(args: Array[String]): Unit = {
val testResources = List("/ledger/ledger-api-integration-tests/SemanticTests.dar")
val toolConfig = Cli
.parse(args)
@ -25,53 +33,68 @@ object LedgerApiTestTool {
if (toolConfig.extract) {
extractTestFiles(testResources)
System.exit(0)
sys.exit(0)
}
val commonConfig = PlatformApplications.Config.default
.withTimeProvider(TimeProviderType.WallClock)
.withLedgerIdMode(LedgerIdMode.Dynamic())
.withRemoteApiEndpoint(
RemoteApiEndpoint.default
.withHost(toolConfig.host)
.withPort(toolConfig.port)
.withTlsConfig(toolConfig.tlsConfig))
val default = defaultTests(commonConfig)
val optional = optionalTests(commonConfig)
val allTests = default ++ optional
if (toolConfig.listTests) {
println("Tests marked with * are run by default.\n")
println(default.keySet.toSeq.sorted.map(_ + " *").mkString("\n"))
println(optional.keySet.toSeq.sorted.mkString("\n"))
sys.exit(0)
}
var failed = false
try {
val integrationTestResourceStream =
testResources.headOption.map(getClass.getResourceAsStream(_)).flatMap(Option(_))
require(
integrationTestResourceStream.isDefined,
"Unable to load the required test DAR from resources.")
val targetPath: Path = Files.createTempFile("ledger-api-test-tool-", "-test.dar")
Files.copy(
integrationTestResourceStream.get,
targetPath,
StandardCopyOption.REPLACE_EXISTING);
val testsToRun = (if (toolConfig.included.isEmpty) default.keySet else toolConfig.included)
.filterNot(toolConfig.excluded)
val reporter = new ToolReporter
if (testsToRun.isEmpty) {
println("No tests to run.")
sys.exit(0)
}
val reporter = new ToolReporter(toolConfig.verbose)
val sorter = new ToolSorter
var semanticTestsRunner = new SandboxSemanticTestsLfRunner {
override def suiteName: String = "Semantic Tests"
override def actorSystemName = "SandboxSemanticTestsLfRunnerTestToolActorSystem"
testsToRun
.find(n => !allTests.contains(n))
.foreach { unknownSuite =>
println(s"Unknown Test: $unknownSuite")
sys.exit(1)
}
override def fixtureIdsEnabled: Set[LedgerBackend] =
Set(LedgerBackend.RemoteApiProxy)
override implicit lazy val patienceConfig: PatienceConfig = PatienceConfig(
Span(60L, Seconds))
override protected def config: Config =
Config.default
.withTimeProvider(TimeProviderType.WallClock)
.withLedgerIdMode(LedgerIdMode.Dynamic())
.withRemoteApiEndpoint(
RemoteApiEndpoint.default
.withHost(toolConfig.host)
.withPort(toolConfig.port)
.withTlsConfig(toolConfig.tlsConfig))
.withDarFile(targetPath)
testsToRun.foreach { suiteName =>
try {
allTests(suiteName)()
.run(None, Args(reporter = reporter, distributedTestSorter = Some(sorter)))
} catch {
case NonFatal(t) =>
failed = true
}
()
}
semanticTestsRunner.run(None, Args(reporter = reporter, distributedTestSorter = Some(sorter)))
failed |= reporter.statistics.testsStarted != reporter.statistics.testsSucceeded
reporter.printStatistics
} catch {
case (t: Throwable) =>
case NonFatal(t) if toolConfig.mustFail =>
failed = true
if (!toolConfig.mustFail) throw t
}
if (toolConfig.mustFail) {
@ -79,9 +102,66 @@ object LedgerApiTestTool {
else
throw new RuntimeException(
"None of the scenarios failed, yet the --must-fail flag was specified!")
} else {
if (failed) {
sys.exit(1)
}
}
}
private def defaultTests(commonConfig: PlatformApplications.Config): Map[String, () => Suite] = {
val semanticTestsRunner = lazyInit(
"SemanticTests",
name =>
new SandboxSemanticTestsLfRunner {
override def suiteName: String = name
override def actorSystemName = s"${name}TestToolActorSystem"
override def fixtureIdsEnabled: Set[LedgerBackend] = Set(LedgerBackend.RemoteApiProxy)
override implicit lazy val patienceConfig: PatienceConfig =
PatienceConfig(Span(60L, Seconds))
override protected def config: Config =
commonConfig.withDarFile(resourceAsFile(semanticTestsResource))
}
)
Map(semanticTestsRunner)
}
private def optionalTests(commonConfig: PlatformApplications.Config): Map[String, () => Suite] = {
val transactionServiceIT = lazyInit(
"TransactionServiceTests",
name =>
new TransactionServiceIT {
override def suiteName: String = name
override def actorSystemName = s"${name}ToolActorSystem"
override def fixtureIdsEnabled: Set[LedgerBackend] = Set(LedgerBackend.RemoteApiProxy)
override protected def config: Config =
commonConfig.withDarFile(resourceAsFile(integrationTestResource))
}
)
Map(transactionServiceIT)
}
def lazyInit[A](name: String, factory: String => A): (String, () => A) = {
(name, () => factory(name))
}
private def resourceAsFile(testResource: String): Path = {
val integrationTestResourceStream =
Option(getClass.getResourceAsStream(testResource))
require(
integrationTestResourceStream.isDefined,
"Unable to load the required test DAR from resources.")
val targetPath: Path = Files.createTempFile("ledger-api-test-tool-", "-test.dar")
Files.copy(integrationTestResourceStream.get, targetPath, StandardCopyOption.REPLACE_EXISTING);
targetPath
}
private def extractTestFiles(testResources: List[String]): Unit = {
val pwd = Paths.get(".").toAbsolutePath
println(s"Extracting all DAML resources necessary to run the tests into $pwd.")

View File

@ -12,7 +12,12 @@ import org.scalatest.Reporter
* Ledger API Test Tool CLI reporter. Implements scalatest's Reporter interface and prints out colorized reports to the
* stdout. Supports very limited set of scalatest events.
*/
class ToolReporter extends Reporter {
class ToolReporter(verbose: Boolean) extends Reporter {
case class Statistics(
testsStarted: Int,
testsSucceeded: Int,
testsCancelled: Int,
testsFailed: Int)
final val ansiReset = "\u001b[0m"
final val ansiBlue = "\u001b[34m"
@ -24,6 +29,10 @@ class ToolReporter extends Reporter {
private def repeatChar(char: Char, n: Int) = char.toString * n
private var depth = 0
private var testsStarted = 0
private var testsSucceeded = 0
private var testsCancelled = 0
private var testsFailed = 0
private def indented(s: String, extra: Integer = 0, prefix: Char = '-') = {
s.split("\n").map(indentedSingle(_, extra, prefix)).mkString("\n")
@ -54,6 +63,7 @@ class ToolReporter extends Reporter {
payload,
threadName,
timeStamp) =>
testsStarted += 1
print(indented(ansiBlue + testText + "... "))
case e.TestSucceeded(
@ -71,6 +81,7 @@ class ToolReporter extends Reporter {
payload,
threadName,
timeStamp) =>
testsSucceeded += 1
println(ansiGreen + "✓")
case e.TestCanceled(
@ -90,6 +101,7 @@ class ToolReporter extends Reporter {
payload,
threadName,
timeStamp) =>
testsCancelled += 1
println(ansiRed + "cancelled.")
case e.TestFailed(
@ -109,13 +121,18 @@ class ToolReporter extends Reporter {
payload,
threadName,
timeStamp) =>
testsFailed += 1
println(ansiRed + "✗" + ansiReset)
throwable match {
case None =>
println(indented(ansiRed + s"Exception details missing!", 1, ' '))
case Some(e) =>
println(indented(s"Failure details:", 1, ' '))
val st = ExceptionUtils.getStackTrace(e)
val st = if (verbose) {
ExceptionUtils.getStackTrace(e)
} else {
e.getMessage
}
println(indented(st, 2, '|'))
}
@ -151,4 +168,22 @@ class ToolReporter extends Reporter {
print(ansiReset)
}
def statistics = Statistics(testsStarted, testsSucceeded, testsCancelled, testsFailed)
def printStatistics = {
statistics match {
case Statistics(0, _, _, _) =>
println(ansiYellow + "No tests were run" + ansiReset)
case Statistics(a, s, 0, 0) =>
println(ansiGreen + s"All ${s}/${a} tests were successful!" + ansiReset)
case Statistics(a, s, c, 0) =>
println(ansiYellow + s"${s}/${a} tests were successful, but ${c} were skipped." + ansiReset)
case Statistics(a, s, 0, f) =>
println(ansiRed + s"${s} were successful and ${f} failed out of ${a} tests." + ansiReset)
case _ =>
println("BUG")
}
}
}