Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add golden test for example Daml ledger export #9732

Merged
merged 10 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions bazel_tools/client_server/client_server_build.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
def _client_server_build_impl(ctx):
posix = ctx.toolchains["@rules_sh//sh/posix:toolchain_type"]
ctx.actions.run_shell(
outputs = [ctx.outputs.out],
outputs = ctx.outputs.outs,
inputs = ctx.files.data,
tools = depset([
ctx.executable.runner,
Expand All @@ -21,7 +21,7 @@ def _client_server_build_impl(ctx):
""".format(
cat = posix.commands["cat"],
output_env = ctx.attr.output_env,
output_path = ctx.outputs.out.path,
output_path = " ".join([o.path for o in ctx.outputs.outs]),
runner = ctx.executable.runner.path,
client = ctx.executable.client.path,
server = ctx.executable.server.path,
Expand All @@ -34,7 +34,7 @@ def _client_server_build_impl(ctx):
)
return [
DefaultInfo(
files = depset([ctx.outputs.out]),
files = depset(ctx.outputs.outs),
),
]

Expand All @@ -60,27 +60,26 @@ client_server_build = rule(
),
"server_args": attr.string_list(),
"server_files": attr.label_list(allow_files = True),
"outs": attr.output_list(mandatory = True),
"output_env": attr.string(),
"data": attr.label_list(allow_files = True),
},
outputs = {
"out": "%{name}.out",
},
toolchains = ["@rules_sh//sh/posix:toolchain_type"],
)
"""Creates a build target for a client-server run.

This rule is similar to the client_server_test rule, but
instead of producing a test target it produces a build target
that creates some result file from the run. Useful for producing
that creates some result files from the run. Useful for producing
test data from integration tests that can then be used for
e.g. testing backwards compatibility.

The rule takes mostly the same arguments as the client_server_test
rule, with the exception that {client,server}_files is a label_list.
Additionally it takes the required argument "output_env", which specifies
the environment variable in which to store the output filename. This variable can be
used either by the client or the server to write the output.
The rule takes mostly the same arguments as the client_server_test rule, with
the exception that {client,server}_files is a label_list. Additionally it takes
the required arguments "output" and "output_env", which specify the list of
output files and an environment variable in which to store the paths to the
output files in a space separated list. This variable can be used either by the
client or the server to write the output.

Note that additional data files are not provided as runfiles (as they are
with the client_server_test rule), but rather placed to a relative to working
Expand All @@ -97,6 +96,7 @@ Example:
server = ":my_server",
server_args = ["--fast"],
server_files = [":file-target-for-server"],
outs = ["my_client_server_build.out"],
output_env = "MY_TEST_OUT",
)
```
Expand Down
1 change: 1 addition & 0 deletions bazel_tools/client_server/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ client_server_test(

client_server_build(
name = "build",
outs = ["build.out"],
client = ":client",
client_args = [
"--foobar",
Expand Down
2 changes: 2 additions & 0 deletions bazel_tools/sh/sh.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def sh_inline_test(
name,
cmd,
data = [],
toolchains = [],
**kwargs):
testonly = kwargs.pop("testonly", True)
_sh_inline_script(
Expand All @@ -57,6 +58,7 @@ def sh_inline_test(
output = name + ".sh",
data = data,
testonly = testonly,
toolchains = toolchains,
)
native.sh_test(
name = name,
Expand Down
70 changes: 70 additions & 0 deletions daml-script/export/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

load(
"//bazel_tools/client_server:client_server_build.bzl",
"client_server_build",
)
load(
"//bazel_tools/sh:sh.bzl",
"sh_inline_test",
)
load(
"//bazel_tools:scala.bzl",
"da_scala_binary",
Expand Down Expand Up @@ -111,3 +119,65 @@ da_scala_test(
"@maven//:io_netty_netty_handler",
],
)

da_scala_binary(
name = "example-export-client",
srcs = ["src/example-export/scala/com/daml/script/export/ExampleClient.scala"],
main_class = "com.daml.script.export.ExampleClient",
scala_deps = [
"@maven//:com_github_scopt_scopt",
],
deps = [
":export",
"//daml-lf/data",
"//daml-script/runner:script-runner-lib",
"//language-support/scala/bindings",
"//language-support/scala/bindings-akka",
"//ledger/ledger-api-common",
"//libs-scala/auth-utils",
],
)

client_server_build(
name = "example-export",
outs = [
"example-export/Export.daml",
"example-export/args.json",
],
client = ":example-export-client",
client_files = ["//daml-script/test:script-test.dar"],
data = ["//daml-script/test:script-test.dar"],
output_env = "EXPORT_OUT",
server = "//ledger/sandbox:sandbox-binary",
server_files = ["//daml-script/test:script-test.dar"],
)

# Compare the generated Daml ledger export to the example export used in the
# documentation. This functions as both a golden test on ledger exports and to
# make sure that the documentation stays up-to-date.
Comment on lines +155 to +157
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. 😃

sh_inline_test(
name = "example-export-compare",
cmd = """\
EXPECTED_EXPORT=$$(canonicalize_rlocation $(rootpath //docs:source/tools/export/output-root/Export.daml))
EXPECTED_ARGS=$$(canonicalize_rlocation $(rootpath //docs:source/tools/export/output-root/args.json))
ACTUAL_EXPORT=$$(canonicalize_rlocation $(rootpath :example-export/Export.daml))
ACTUAL_ARGS=$$(canonicalize_rlocation $(rootpath :example-export/args.json))
# Normalize the expected file by removing the copyright header and any documentation import markers.
# Normalize the actual output by adding a newline to the last line if missing.
Comment on lines +169 to +170
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these comments go on top of the test rule? Spotting comments on inlined scripts can be tricky. Duplicating the comment is probably fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I've expanded the top comment and kept the comment in the inline code.

$(POSIX_DIFF) -Naur <($(POSIX_SED) '1,3d;/^-- EXPORT/d' $$EXPECTED_EXPORT) <($(POSIX_SED) '$$a\\' $$ACTUAL_EXPORT) || {
echo "$$EXPECTED_EXPORT did not match $$ACTUAL_EXPORT"
exit 1
}
$(POSIX_DIFF) -Naur $$EXPECTED_ARGS <($(POSIX_SED) '$$a\\' $$ACTUAL_ARGS) || {
echo "$$EXPECTED_ARGS did not match $$ACTUAL_ARGS"
exit 1
}
""",
data = [
":example-export/Export.daml",
":example-export/args.json",
"//docs:source/tools/export/output-root/Export.daml",
"//docs:source/tools/export/output-root/args.json",
],
toolchains = ["@rules_sh//sh/posix:make_variables"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.script.export

import java.io.File
import java.nio.file.{Path, Paths}
import java.time.Duration

import com.daml.ledger.api.tls.TlsConfiguration
import com.daml.lf.engine.script.{RunnerConfig, RunnerMain}

case class ExampleClientConfig(
darPath: File,
targetPort: Int,
outputPath: Path,
)

object ExampleClientConfig {
def parse(args: Array[String]): Option[ExampleClientConfig] =
parser.parse(
args,
ExampleClientConfig(
darPath = null,
targetPort = -1,
outputPath = null,
),
)

private def parseExportOut(
envVar: String
): Either[String, ExampleClientConfig => ExampleClientConfig] = {
if (envVar.isEmpty) Left("Environment variable EXPORT_OUT must not be empty")
else
envVar.split(" ") match {
case Array(export_daml, args_json) =>
val export_daml_path = Paths.get(export_daml)
val args_json_path = Paths.get(args_json)
if (export_daml_path.getParent == null) {
Left("First component in environment variable EXPORT_OUT has no parent")
} else if (export_daml_path.getParent != args_json_path.getParent) {
Left(
"First and second component in environment variable EXPORT_OUT have different parent"
)
} else {
Right(c => c.copy(outputPath = export_daml_path.getParent))
}
case _ => Left("Environment variable EXPORT_OUT must contain one path")
}
}

private val parser = new scopt.OptionParser[ExampleClientConfig]("script-export") {
help("help")
.text("Show this help message.")
opt[Int]("target-port")
.required()
.action((x, c) => c.copy(targetPort = x))
.text("Daml ledger port to connect to.")
opt[String]("output")
.hidden()
.withFallback(() => sys.env.getOrElse("EXPORT_OUT", ""))
.validate(x => parseExportOut(x).map(_ => ()))
.action { (x, c) =>
parseExportOut(x) match {
case Left(msg) =>
throw new RuntimeException(s"Failed to validate EXPORT_OUT environment variable: $msg")
case Right(f) => f(c)
}
}
arg[File]("dar")
.required()
.action((f, c) => c.copy(darPath = f))
.text("Path to the dar file containing the initialization script")
}
}

object ExampleClient {
def main(args: Array[String]): Unit = {
ExampleClientConfig.parse(args) match {
case Some(clientConfig) => main(clientConfig)
case None => sys.exit(1)
}
}
def main(clientConfig: ExampleClientConfig): Unit = {
RunnerMain.main(
RunnerConfig(
darPath = clientConfig.darPath,
scriptIdentifier = "ScriptExample:initializeFixed",
ledgerHost = Some("localhost"),
ledgerPort = Some(clientConfig.targetPort),
participantConfig = None,
timeMode = Some(RunnerConfig.DefaultTimeMode),
commandTtl = Duration.ofSeconds(30L),
inputFile = None,
outputFile = None,
accessTokenFile = None,
tlsConfig = TlsConfiguration(false, None, None, None),
jsonApi = false,
maxInboundMessageSize = RunnerConfig.DefaultMaxInboundMessageSize,
applicationId = None,
)
)
Main.main(
Config.Empty.copy(
ledgerHost = "localhost",
ledgerPort = clientConfig.targetPort,
parties = Seq("Alice", "Bob"),
exportType = Some(
Config.EmptyExportScript.copy(
sdkVersion = "0.0.0",
outputPath = clientConfig.outputPath,
)
),
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ object Config {
)
}

private val EmptyExportScript = ExportScript(
val EmptyExportScript = ExportScript(
outputPath = null,
sdkVersion = "",
acsBatchSize = 10,
Expand All @@ -135,7 +135,7 @@ object Config {
}
}

private val Empty = Config(
val Empty = Config(
ledgerHost = "",
ledgerPort = -1,
tlsConfig = TlsConfiguration(false, None, None, None),
Expand Down
Loading