The macro TEST only allows defining a test that consists of a single expression. Add a new macro, for_test, which provides a way to define unit tests that are made up of one or more statements. for_test allows defining self-contained tests en bloc, a bit like test_expect_success does for regular tests. It acts like a for loop that runs at most once; the test body is executed if test_skip_all() had not been called before. Signed-off-by: René Scharfe <l.s.r@xxxxxx> --- .clang-format | 2 ++ t/helper/test-example-tap.c | 33 +++++++++++++++++++++++++++++++++ t/t0080-unit-test-output.sh | 35 ++++++++++++++++++++++++++++++++++- t/unit-tests/test-lib.h | 20 ++++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 6408251577..863dc87dfc 100644 --- a/.clang-format +++ b/.clang-format @@ -151,6 +151,7 @@ Cpp11BracedListStyle: false # function calls. Taken from: # git grep -h '^#define [^[:space:]]*for_\?each[^[:space:]]*(' | # sed "s/^#define / - '/; s/(.*$/'/" | sort | uniq +# Added for_test from t/unit-tests/test-lib.h manually as a special case. ForEachMacros: - 'for_each_builtin' - 'for_each_string_list_item' @@ -168,6 +169,7 @@ ForEachMacros: - 'strintmap_for_each_entry' - 'strmap_for_each_entry' - 'strset_for_each_entry' + - 'for_test' # The maximum number of consecutive empty lines to keep. MaxEmptyLinesToKeep: 1 diff --git a/t/helper/test-example-tap.c b/t/helper/test-example-tap.c index 79c12b01cd..5e49fb1e7e 100644 --- a/t/helper/test-example-tap.c +++ b/t/helper/test-example-tap.c @@ -94,5 +94,38 @@ int cmd__example_tap(int argc, const char **argv) test_res = TEST(t_empty(), "test with no checks"); TEST(check_int(test_res, ==, 0), "test with no checks returns 0"); + for_test ("for_test passing test") + check_int(1, ==, 1); + for_test ("for_test failing test") + check_int(1, ==, 2); + for_test ("for_test passing TEST_TODO()") + TEST_TODO(check(0)); + for_test ("for_test failing TEST_TODO()") + TEST_TODO(check(1)); + for_test ("for_test test_skip()") { + check(0); + test_skip("missing prerequisite"); + check(1); + } + for_test ("for_test test_skip() inside TEST_TODO()") + TEST_TODO((test_skip("missing prerequisite"), 1)); + for_test ("for_test TEST_TODO() after failing check") { + check(0); + TEST_TODO(check(0)); + } + for_test ("for_test failing check after TEST_TODO()") { + check(1); + TEST_TODO(check(0)); + check(0); + } + for_test ("for_test messages from failing string and char comparison") { + check_str("\thello\\", "there\"\n"); + check_str("NULL", NULL); + check_char('a', ==, '\n'); + check_char('\\', ==, '\''); + } + for_test ("for_test test with no checks") + ; /* nothing */ + return test_done(); } diff --git a/t/t0080-unit-test-output.sh b/t/t0080-unit-test-output.sh index fe221f3bdb..5185154414 100755 --- a/t/t0080-unit-test-output.sh +++ b/t/t0080-unit-test-output.sh @@ -50,7 +50,40 @@ test_expect_success 'TAP output from unit tests' - <<\EOT # BUG: test has no checks at t/helper/test-example-tap.c:94 not ok 18 - test with no checks ok 19 - test with no checks returns 0 - 1..19 + ok 20 - for_test passing test + # check "1 == 2" failed at t/helper/test-example-tap.c:100 + # left: 1 + # right: 2 + not ok 21 - for_test failing test + not ok 22 - for_test passing TEST_TODO() # TODO + # todo check 'check(1)' succeeded at t/helper/test-example-tap.c:104 + not ok 23 - for_test failing TEST_TODO() + # check "0" failed at t/helper/test-example-tap.c:106 + # skipping test - missing prerequisite + # skipping check '1' at t/helper/test-example-tap.c:108 + ok 24 - for_test test_skip() # SKIP + # skipping test - missing prerequisite + ok 25 - for_test test_skip() inside TEST_TODO() # SKIP + # check "0" failed at t/helper/test-example-tap.c:113 + not ok 26 - for_test TEST_TODO() after failing check + # check "0" failed at t/helper/test-example-tap.c:119 + not ok 27 - for_test failing check after TEST_TODO() + # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/helper/test-example-tap.c:122 + # left: "\011hello\\\\" + # right: "there\"\012" + # check "!strcmp("NULL", NULL)" failed at t/helper/test-example-tap.c:123 + # left: "NULL" + # right: NULL + # check "'a' == '\n'" failed at t/helper/test-example-tap.c:124 + # left: 'a' + # right: '\012' + # check "'\\\\' == '\\''" failed at t/helper/test-example-tap.c:125 + # left: '\\\\' + # right: '\\'' + not ok 28 - for_test messages from failing string and char comparison + # BUG: test has no checks at t/helper/test-example-tap.c:127 + not ok 29 - for_test test with no checks + 1..29 EOF ! test-tool example-tap >actual && diff --git a/t/unit-tests/test-lib.h b/t/unit-tests/test-lib.h index 2de6d715d5..598c6ff9f3 100644 --- a/t/unit-tests/test-lib.h +++ b/t/unit-tests/test-lib.h @@ -14,6 +14,26 @@ test__run_end(test__run_begin() ? 0 : (t, 1), \ TEST_LOCATION(), __VA_ARGS__) +/* + * Run a test unless test_skip_all() has been called. Acts like a for + * loop that runs at most once, with the test description between the + * parentheses and the test body as a statement or block after them. + * Supports continue to end the test early, but not break. The + * description for each test should be unique. E.g.: + * + * for_test ("something else %d %d", arg1, arg2) { + * prepare(); + * test_something_else(arg1, arg2); + * cleanup(); + * } + */ +#define for_test(...) \ + for (int for_test_running_ = test__run_begin() ? \ + (test__run_end(0, TEST_LOCATION(), __VA_ARGS__), 0) : 1;\ + for_test_running_; \ + test__run_end(1, TEST_LOCATION(), __VA_ARGS__), \ + for_test_running_ = 0) + /* * Print a test plan, should be called before any tests. If the number * of tests is not known in advance test_done() will automatically -- 2.45.2