From 4bfd605dd2b17f1a7f588bf16e3c95a737437a2e Mon Sep 17 00:00:00 2001 From: Oliver Lee Date: Fri, 5 Jan 2024 19:04:14 -0800 Subject: [PATCH] add build failure example with `ctest` (#29) Change-Id: I49f2a9991686715dda2e529b19206909ec1a7c79 --- README.md | 33 ++++++++++++++++++++++++ example/BUILD.bazel | 12 +++++++++ example/ctest_fail.cpp | 11 ++++++++ example/ctest_fail.log | 6 +++++ example/ctest_fail.sh.tmpl | 43 ++++++++++++++++++++++++++++++++ rules/BUILD.bazel | 32 ++++++++++++++++++++++++ rules/binary_log.bzl | 14 +++++------ {test => rules}/prelude.sh.tmpl | 7 ++++-- rules/sh_binary_template.bzl | 13 ++++++---- rules/skytest_test.bzl | 1 - scripts/README.md.tmpl | 13 ++++++++++ test/BUILD.bazel | 28 --------------------- test/ctest_build_failure.sh.tmpl | 4 +-- test/noreturn_expect.sh.tmpl | 2 +- 14 files changed, 172 insertions(+), 47 deletions(-) create mode 100644 example/ctest_fail.cpp create mode 100644 example/ctest_fail.log create mode 100644 example/ctest_fail.sh.tmpl rename {test => rules}/prelude.sh.tmpl (91%) diff --git a/README.md b/README.md index e5f94ab..57677d7 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,43 @@ When running tests, `skytest` will attempt to invoke test closures at compile-time. If able to, results will be classified `CONSTEXPR PASS` or `CONSTEXPR FAIL` instead of `PASS` or `FAIL`. +
requiring compile-time evaluation of tests + + The `ctest` literal can be used to require closures to be tested at compile-time. In order to be usable with `ctest`, test closures must be empty and non-constexpr functions must not be invoked. +```cpp:example/ctest_fail.cpp +#include "skytest/skytest.hpp" + +auto main() -> int +{ + using namespace skytest::literals; + using ::skytest::expect; + using ::skytest::lt; + + static auto n = 0; + "read non-const"_ctest = [] { return expect(lt(0, n)); }; +} + +``` + +results in the follow build error (snippet): + +```console:example/ctest_fail.log +external/skytest/src/detail/test_style.hpp:43:27: error: the value of 'n' is not usable in a constant expression + 43 | static constexpr auto value = std::optional{bool{F{}()}}; + | ^~~~~ +ctest_fail.cpp:9:15: note: 'int n' is not const + 9 | static auto n = 0; + | ^ + +``` + +
+ + ## examples #### binary comparisons diff --git a/example/BUILD.bazel b/example/BUILD.bazel index 58b456e..80c3e2d 100644 --- a/example/BUILD.bazel +++ b/example/BUILD.bazel @@ -1,4 +1,5 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") +load("//rules:sh_binary_template.bzl", "sh_binary_template") load("//rules:binary_log.bzl", "synchronized_binary_log") cc_library( @@ -30,6 +31,17 @@ synchronized_binary_log( src = ":minimal_fail", ) +sh_binary_template( + name = "ctest_fail", + srcs = ["ctest_fail.sh.tmpl"], + data = ["ctest_fail.cpp"], +) + +synchronized_binary_log( + name = "ctest_fail_log", + src = ":ctest_fail", +) + cc_test( name = "binary_comparisons", srcs = ["binary_comparisons.cpp"], diff --git a/example/ctest_fail.cpp b/example/ctest_fail.cpp new file mode 100644 index 0000000..42898c1 --- /dev/null +++ b/example/ctest_fail.cpp @@ -0,0 +1,11 @@ +#include "skytest/skytest.hpp" + +auto main() -> int +{ + using namespace skytest::literals; + using ::skytest::expect; + using ::skytest::lt; + + static auto n = 0; + "read non-const"_ctest = [] { return expect(lt(0, n)); }; +} diff --git a/example/ctest_fail.log b/example/ctest_fail.log new file mode 100644 index 0000000..2583bb7 --- /dev/null +++ b/example/ctest_fail.log @@ -0,0 +1,6 @@ +external/skytest/src/detail/test_style.hpp:43:27: error: the value of 'n' is not usable in a constant expression + 43 | static constexpr auto value = std::optional{bool{F{}()}}; + | ^~~~~ +ctest_fail.cpp:9:15: note: 'int n' is not const + 9 | static auto n = 0; + | ^ diff --git a/example/ctest_fail.sh.tmpl b/example/ctest_fail.sh.tmpl new file mode 100644 index 0000000..a10d7a3 --- /dev/null +++ b/example/ctest_fail.sh.tmpl @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail + +source $(find -L . -path \*/rules/prelude.sh -print -quit) +prelude "${BASH_SOURCE[0]}" + +cp $(find -L . -path \*/example/ctest_fail.cpp -print -quit) . + +cat >>BUILD.bazel <&1 \ + | sed -n "/$start/,/$stop/ p" `# only print from start to stop` \ + | sed '$d' # remove the stop line + diff --git a/rules/BUILD.bazel b/rules/BUILD.bazel index a90d915..7cc2835 100644 --- a/rules/BUILD.bazel +++ b/rules/BUILD.bazel @@ -1,4 +1,36 @@ +load("@bazel_skylib//rules:expand_template.bzl", "expand_template") +load( + "@local_config_info//:defs.bzl", + "BAZEL_BIN", + "BAZEL_EXTERNAL_DIR", + "BAZEL_WORKSPACE_ROOT", + "XDG_CACHE_HOME", +) + exports_files( ["skytest_test.sh"], visibility = ["//test:__pkg__"], ) + +expand_template( + name = "gen_prelude_sh", + out = "prelude.sh", + substitutions = { + "$BAZEL_BIN": BAZEL_BIN, + "$BAZEL_EXTERNAL_DIR": BAZEL_EXTERNAL_DIR, + "$BAZEL_WORKSPACE_ROOT": BAZEL_WORKSPACE_ROOT, + "$XDG_CACHE_HOME": XDG_CACHE_HOME, + }, + tags = ["manual"], + template = "prelude.sh.tmpl", + visibility = ["//visibility:private"], +) + +sh_library( + name = "prelude_sh", + srcs = ["prelude.sh"], + visibility = [ + "//example:__pkg__", + "//test:__pkg__", + ], +) diff --git a/rules/binary_log.bzl b/rules/binary_log.bzl index 173cc2d..c6adf78 100644 --- a/rules/binary_log.bzl +++ b/rules/binary_log.bzl @@ -22,9 +22,9 @@ def binary_log( """ native.genrule( name = name, - srcs = [src], + tools = [src], outs = [log], - cmd = "$(execpath {}) > $@ || true".format(src), + cmd = "$(execpath {}) &> $@ || true".format(src), testonly = True, tags = ["manual"], visibility = ["//visibility:private"], @@ -64,17 +64,14 @@ def synchronized_binary_log( native.genrule( name = name + "_update_sh", - srcs = [ - src, - log, - ], + srcs = [log], + tools = [src], outs = [name + ".update.sh"], cmd = """ set -euo pipefail echo "set -euo pipefail" > $@ echo "" >> $@ -echo "cd {workspace_dir}" >> $@ -echo "$(execpath {src}) > $(rootpath {log}) || true" >> $@ +echo "{workspace_dir}/$(execpath {src}) &> {workspace_dir}/$(rootpath {log}) || true" >> $@ """.format( src = src, log = log, @@ -96,4 +93,5 @@ echo "$(execpath {src}) > $(rootpath {log}) || true" >> $@ native.sh_binary( name = name + ".update", srcs = [name + ".update.sh"], + data = [src], ) diff --git a/test/prelude.sh.tmpl b/rules/prelude.sh.tmpl similarity index 91% rename from test/prelude.sh.tmpl rename to rules/prelude.sh.tmpl index f0018ff..85b2a26 100644 --- a/test/prelude.sh.tmpl +++ b/rules/prelude.sh.tmpl @@ -4,8 +4,11 @@ set -euo pipefail function prelude() { TEST_PATH="$(readlink -f $1)" -SCRIPT_NAME="${TEST_PATH##*/test/}}" -WORKSPACE="${SCRIPT_NAME%.*}" + +PRUNE_TEST="${TEST_PATH##*/test/}}" +PRUNE_EXAMPLE="${PRUNE_TEST##*/example/}}" + +WORKSPACE="${PRUNE_EXAMPLE%.*}" cat >> .bazelrc <> $@ "$CC_BINARY_CXXSTD": std, "$CC_BINARY_MALLOC": malloc, }, - "deps": ["//test:prelude_sh"], } else: fail("unhandled binary_type: {}".format(binary_type)) diff --git a/scripts/README.md.tmpl b/scripts/README.md.tmpl index 2371692..3da4259 100644 --- a/scripts/README.md.tmpl +++ b/scripts/README.md.tmpl @@ -29,10 +29,23 @@ When running tests, `skytest` will attempt to invoke test closures at compile-time. If able to, results will be classified `CONSTEXPR PASS` or `CONSTEXPR FAIL` instead of `PASS` or `FAIL`. +
requiring compile-time evaluation of tests + + The `ctest` literal can be used to require closures to be tested at compile-time. In order to be usable with `ctest`, test closures must be empty and non-constexpr functions must not be invoked. +```cpp:example/ctest_fail.cpp +``` + +results in the follow build error (snippet): + +```console:example/ctest_fail.log +``` + +
+ ## examples #### binary comparisons diff --git a/test/BUILD.bazel b/test/BUILD.bazel index f069684..f28d8c5 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -1,34 +1,6 @@ load("//rules:skytest_test.bzl", "skytest_test") load("//rules:sh_binary_template.bzl", "sh_binary_template") load("@rules_cc//cc:defs.bzl", "cc_library") -load("@bazel_skylib//rules:expand_template.bzl", "expand_template") -load( - "@local_config_info//:defs.bzl", - "BAZEL_BIN", - "BAZEL_EXTERNAL_DIR", - "BAZEL_WORKSPACE_ROOT", - "XDG_CACHE_HOME", -) - -expand_template( - name = "gen_prelude_sh", - testonly = True, - out = "prelude.sh", - substitutions = { - "$BAZEL_BIN": BAZEL_BIN, - "$BAZEL_EXTERNAL_DIR": BAZEL_EXTERNAL_DIR, - "$BAZEL_WORKSPACE_ROOT": BAZEL_WORKSPACE_ROOT, - "$XDG_CACHE_HOME": XDG_CACHE_HOME, - }, - tags = ["manual"], - template = "prelude.sh.tmpl", - visibility = ["//visibility:private"], -) - -sh_library( - name = "prelude_sh", - srcs = ["prelude.sh"], -) skytest_test( name = "pass_test", diff --git a/test/ctest_build_failure.sh.tmpl b/test/ctest_build_failure.sh.tmpl index 6364483..ba64eea 100644 --- a/test/ctest_build_failure.sh.tmpl +++ b/test/ctest_build_failure.sh.tmpl @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -source test/prelude.sh +source rules/prelude.sh prelude "${BASH_SOURCE[0]}" cat >>BUILD.bazel < int } EOF -bazel build -s //:ctest_build_failure \ No newline at end of file +bazel build -s //:ctest_build_failure diff --git a/test/noreturn_expect.sh.tmpl b/test/noreturn_expect.sh.tmpl index 0118429..2b67e98 100644 --- a/test/noreturn_expect.sh.tmpl +++ b/test/noreturn_expect.sh.tmpl @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -source test/prelude.sh +source rules/prelude.sh prelude "${BASH_SOURCE[0]}" cat >>BUILD.bazel <