Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 0 additions & 4 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,6 @@ build:darwin --host_platform=@rules_haskell//haskell/platforms:darwin_x86_64_nix
# and GHC's gcc on Windows
build:windows --crosstool_top=@rules_haskell_ghc_windows_amd64//:cc_toolchain

# Bazel 1.0 disabled the bash test-runner on Windows. However, some of our
# test-cases are implemented as bash scripts and rely on the bash test-runner.
build:windows --noincompatible_windows_native_test_wrapper

# Caching is currently disabled on Windows.
# See: https://github.com/tweag/rules_haskell/issues/744 for details.
build:windows --noremote_accept_cached
Expand Down
182 changes: 73 additions & 109 deletions bazel_tools/client_server/client_server_test.bzl
Original file line number Diff line number Diff line change
@@ -1,127 +1,91 @@
# Copyright (c) 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

def _expand_args(ctx, args):
return " ".join([ctx.expand_location(a, ctx.attr.data).replace("'", "'\\''") for a in args])
load("//bazel_tools/sh:sh.bzl", "sh_inline_test")

def _client_server_test_impl(ctx):
# Construct wrapper to execute the runner, which in turn
# will start the client and server.
wrapper = ctx.actions.declare_file(ctx.label.name + "_wrapper.sh")
ctx.actions.write(
output = wrapper,
content = """#!/usr/bin/env bash
set -eou pipefail
canonicalize_rlocation() {{
# Note (MK): This is a fun one: Let’s say $TEST_WORKSPACE is "compatibility"
# and the argument points to a target from an external workspace, e.g.,
# @daml-sdk-0.0.0//:daml. Then the short path will point to
# ../daml-sdk-0.0.0/daml. Putting things together we end up with
# compatibility/../daml-sdk-0.0.0/daml. On Linux and MacOS this works
# just fine. However, on windows we need to normalize the path
# or rlocation will fail to find the path in the manifest file.
rlocation $(realpath -L -s -m --relative-to=$PWD $TEST_WORKSPACE/$1)
}}
def _escape_args(args):
return " ".join([
a.replace("'", "'\\''")
for a in args
])

runner=$(canonicalize_rlocation "{runner}")
def client_server_test(
name,
runner = "//bazel_tools/client_server/runner:runner",
runner_args = [],
client = None,
client_args = [],
client_files = [],
server = None,
server_args = [],
server_files = [],
data = [],
**kwargs):
"""Create a client-server test.

The rule takes a client and server executables and their
arguments as parameters. The server port is passed via a
temporary file, which is passed to the server executable via the
"--port-file" parameter. This file is parsed and the port number
is passed to the client application via the "--target-port" argument.

The server process is killed after the client process exits.

The client and server executables can be any Bazel target that
is executable, e.g. scala_binary, sh_binary, etc.

The client and server files must be valid arguments to rlocation, as
can be obtained using $(rootpath ...) or $(rootpaths ...). (See
https://docs.bazel.build/versions/master/be/make-variables.html#predefined_label_variables.)
Once expended using rlocation, those are simply appended to client
and server arguments, respectively.

Example:
```bzl
client_server_test(
name = "my_test",
runner_args = [],
client = ":my_client",
client_args = ["--extra-argument"],
client_files = ["$(rootpath :target-for-client)"]
server = ":my_server",
server_args = ["--fast"],
server_files = ["$(rootpath :target-for-client)"]
)
```
"""
sh_inline_test(
name = name,
# Deduplicate in case any of runner, client, server are identical.
data = depset([runner, client, server]).to_list() + data,
cmd = """\
runner=$$(canonicalize_rlocation $$(get_exe $(rootpaths {runner})))
runner_args="{runner_args}"
client=$(canonicalize_rlocation "{client}")
server=$(canonicalize_rlocation "{server}")
client=$$(canonicalize_rlocation $$(get_exe $(rootpaths {client})))
server=$$(canonicalize_rlocation $$(get_exe $(rootpaths {server})))
server_args="{server_args}"
for file in {server_files}; do
server_args+=" $(canonicalize_rlocation $file)"
server_args+=" $$(canonicalize_rlocation $$file)"
done

client_args="$@"
if [ -z "$client_args" ]; then
client_args="$$@"
if [ -z "$$client_args" ]; then
client_args="{client_args}"
for file in {client_files}; do
client_args+=" $(canonicalize_rlocation $file)"
client_args+=" $$(canonicalize_rlocation $$file)"
done
fi

$runner $client "$client_args" $server "$server_args" "$runner_args"
$$runner $$client "$$client_args" $$server "$$server_args" "$$runner_args"
""".format(
runner = ctx.executable.runner.short_path,
runner_args = _expand_args(ctx, ctx.attr.runner_args),
client = ctx.executable.client.short_path,
client_args = _expand_args(ctx, ctx.attr.client_args),
client_files = _expand_args(ctx, ctx.attr.client_files),
server = ctx.executable.server.short_path,
server_args = _expand_args(ctx, ctx.attr.server_args),
server_files = _expand_args(ctx, ctx.attr.server_files),
runner = runner,
runner_args = _escape_args(runner_args),
client = client,
client_args = _escape_args(client_args),
client_files = _escape_args(client_files),
server = server,
server_args = _escape_args(server_args),
server_files = _escape_args(server_files),
),
is_executable = True,
**kwargs
)

runfiles = ctx.runfiles(files = [wrapper], collect_data = True)
runfiles = runfiles.merge(ctx.attr.runner[DefaultInfo].default_runfiles)
runfiles = runfiles.merge(ctx.attr.client[DefaultInfo].default_runfiles)
runfiles = runfiles.merge(ctx.attr.server[DefaultInfo].default_runfiles)

return DefaultInfo(
executable = wrapper,
files = depset([wrapper]),
runfiles = runfiles,
)

client_server_test = rule(
implementation = _client_server_test_impl,
test = True,
executable = True,
attrs = {
"runner": attr.label(
cfg = "host",
allow_single_file = True,
executable = True,
default = Label("@//bazel_tools/client_server/runner:runner"),
),
"runner_args": attr.string_list(),
"client": attr.label(
cfg = "target",
executable = True,
),
"client_args": attr.string_list(),
"client_files": attr.string_list(),
"server": attr.label(
cfg = "target",
executable = True,
),
"server_args": attr.string_list(),
"server_files": attr.string_list(),
"data": attr.label_list(allow_files = True),
},
)
"""Create a client-server test.

The rule takes a client and server executables and their
arguments as parameters. The server port is passed via a
temporary file, which is passed to the server executable via the
"--port-file" parameter. This file is parsed and the port number
is passed to the client application via the "--target-port" argument.

The server process is killed after the client process exits.

The client and server executables can be any Bazel target that
is executable, e.g. scala_binary, sh_binary, etc.

The client and server files must be valid arguments to rlocation, as
can be obtained using $(rootpath ...) or $(rootpaths ...). (See
https://docs.bazel.build/versions/master/be/make-variables.html#predefined_label_variables.)
Once expended using rlocation, those are simply appended to client
and server arguments, respectively.

Example:
```bzl
client_server_test(
name = "my_test",
runner_args = [],
client = ":my_client",
client_args = ["--extra-argument"],
client_files = ["$(rootpath :target-for-client)"]
server = ":my_server",
server_args = ["--fast"],
server_files = ["$(rootpath :target-for-client)"]
)
```
"""
2 changes: 1 addition & 1 deletion bazel_tools/client_server/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ client_server_test(
client_args = [
"--foobar",
# Use the input file passed in via `data`.
"$(rlocation $TEST_WORKSPACE/$(rootpath :client_input_file))",
"$$(rlocation $$TEST_WORKSPACE/$(rootpath :client_input_file))",
],

# Data files available to both client and server.
Expand Down
Loading