"Helpers for making test assertions" load("@bazel_skylib//lib:types.bzl", "types") load("@bazel_skylib//rules:write_file.bzl", "write_file") load("//lib:diff_test.bzl", "diff_test") load("//lib:jq.bzl", "jq") load("//lib:params_file.bzl", "params_file") load("//lib:utils.bzl", "default_timeout") def assert_contains(name, actual, expected, size = None, timeout = None, **kwargs): """Generates a test target which fails if the file doesn't contain the string. Depends on bash, as it creates an sh_test target. Args: name: target to create actual: Label of a file expected: a string which should appear in the file size: the size attribute of the test target timeout: the timeout attribute of the test target **kwargs: additional named arguments for the resulting sh_test """ test_sh = "_{}_test.sh".format(name) expected_file = "_{}_expected.txt".format(name) write_file( name = "_%s_expected" % name, out = expected_file, content = [expected], ) write_file( name = "_" + name, out = test_sh, content = [ "#!/usr/bin/env bash", "set -o errexit", "grep --fixed-strings -f $1 $2", ], ) native.sh_test( name = name, srcs = [test_sh], args = ["$(rootpath %s)" % expected_file, "$(rootpath %s)" % actual], size = size, timeout = default_timeout(size, timeout), data = [actual, expected_file], **kwargs ) def assert_outputs(name, actual, expected, **kwargs): """Assert that the default outputs of a target are the expected ones. Args: name: name of the resulting diff_test actual: string of the label to check the outputs expected: a list of rootpaths of expected outputs, as they would appear in a runfiles manifest **kwargs: additional named arguments for the resulting diff_test """ if not types.is_list(expected): fail("expected should be a list of strings, not " + type(expected)) params_file( name = "_actual_" + name, data = [actual], args = ["$(rootpaths {})".format(actual)], out = "_{}_outputs.txt".format(name), ) write_file( name = "_expected_ " + name, content = expected, out = "_expected_{}.txt".format(name), ) diff_test( name = name, file1 = "_expected_ " + name, file2 = "_actual_" + name, **kwargs ) def assert_json_matches(name, file1, file2, filter1 = ".", filter2 = ".", **kwargs): """Assert that the given json files have the same semantic content. Uses jq to filter each file. The default value of `"."` as the filter means to compare the whole file. See the [jq rule](./jq.md#jq) for more about the filter expressions as well as setup notes for the `jq` toolchain. Args: name: name of resulting diff_test target file1: a json file file2: another json file filter1: a jq filter to apply to file1 filter2: a jq filter to apply to file2 **kwargs: additional named arguments for the resulting diff_test """ name1 = "_{}_jq1".format(name) name2 = "_{}_jq2".format(name) jq( name = name1, srcs = [file1], filter = filter1, ) jq( name = name2, srcs = [file2], filter = filter2, ) diff_test( name = name, file1 = name1, file2 = name2, failure_message = "'{}' from {} doesn't match '{}' from {}".format( filter1, file1, filter2, file2, ), **kwargs ) def assert_archive_contains(name, archive, expected, type = None, **kwargs): """Assert that an archive file contains at least the given file entries. Args: name: name of the resulting sh_test target archive: Label of the the .tar or .zip file expected: a (partial) file listing, either as a Label of a file containing it, or a list of strings type: "tar" or "zip". If None, a type will be inferred from the filename. **kwargs: additional named arguments for the resulting sh_test """ if not type: if archive.endswith(".whl") or archive.endswith(".zip"): type = "zip" elif archive.endswith(".tar"): type = "tar" else: fail("could not infer type from {}, please set the type attribute explicitly".format(archive)) if not type in ["tar", "zip"]: fail("type must be 'tar' or 'zip', not " + type) # Command to list the files in the archive command = "unzip -Z1" if type == "zip" else "tar -tf" # -f $actual: use this file to contain one pattern per line # -F: treat each pattern as a plain string, not a regex # -x: match whole lines only # -v: only print lines which don't match grep = "grep -F -x -v -f $actual" script_name = "_gen_assert_" + name expected_name = "_expected_" + name if types.is_list(expected): write_file( name = expected_name, out = expected_name + ".mf", content = expected, ) else: expected_name = expected write_file( name = script_name, out = "assert_{}.sh".format(name), content = [ "#!/usr/bin/env bash", "actual=$(mktemp)", "{} $1 > $actual".format(command), "# Grep exits 1 if no matches, which is success for this test.", "if {} $2; then".format(grep), " echo", " echo 'ERROR: above line(s) appeared in {} but are not present in the archive' $1".format(expected_name), " exit 1", "fi", ], ) native.sh_test( name = name, srcs = [script_name], args = ["$(rootpath %s)" % archive, "$(rootpath %s)" % expected_name], data = [archive, expected_name], timeout = "short", **kwargs ) def assert_directory_contains(name, directory, expected, **kwargs): """Assert that a directory contains at least the given file entries. Args: name: name of the resulting sh_test target directory: Label of the directory artifact expected: a (partial) file listing, either as a Label of a file containing it, or a list of strings **kwargs: additional named arguments for the resulting sh_test """ # -f $actual: use this file to contain one pattern per line # -F: treat each pattern as a plain string, not a regex # -x: match whole lines only # -v: only print lines which don't match grep = "grep -F -x -v -f $actual" script_name = "_gen_assert_" + name expected_name = "_expected_" + name if types.is_list(expected): write_file( name = expected_name, out = expected_name + ".mf", content = expected, ) else: expected_name = expected write_file( name = script_name, out = "assert_{}.sh".format(name), content = [ "#!/usr/bin/env bash", "actual=$(mktemp)", "pushd $1 > /dev/null", "find . -type l,f | cut -b 3- > $actual", "popd > /dev/null", "# Grep exits 1 if no matches, which is success for this test.", "if {} $2; then".format(grep), " echo", " echo 'ERROR: above line(s) appeared in {} but are not present in the directory' $1".format(expected_name), " exit 1", "fi", ], ) native.sh_test( name = name, srcs = [script_name], args = ["$(rootpath %s)" % directory, "$(rootpath %s)" % expected_name], data = [directory, expected_name], timeout = "short", **kwargs )