unit-tests: add if_test
The macro TEST only allows defining a test that consists of a single expression. Add a new macro, if_test, which provides a way to define unit tests that are made up of one or more statements. if_test allows defining self-contained tests en bloc, a bit like test_expect_success does for regular tests. It acts like a conditional; the test body is executed if test_skip_all() had not been called before. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
René Scharfe committed
Jul 30, 2024 at 16:08 UTC
96c6304c1866d8dd2f17208e906749f999df0911
5 files changed
+121
-1
.clang-format
+5
@@ -169,6 +169,11 @@ ForEachMacros:
169
- 'strmap_for_each_entry'
170
- 'strset_for_each_entry'
171
172
+# A list of macros that should be interpreted as conditionals instead of as
173
+# function calls.
174
+IfMacros:
175
+ - 'if_test'
176
+
177
# The maximum number of consecutive empty lines to keep.
178
MaxEmptyLinesToKeep: 1
179
t/helper/test-example-tap.c
+33
@@ -94,5 +94,38 @@ int cmd__example_tap(int argc, const char **argv)
94
test_res = TEST(t_empty(), "test with no checks");
95
TEST(check_int(test_res, ==, 0), "test with no checks returns 0");
96
97
+ if_test ("if_test passing test")
98
+ check_int(1, ==, 1);
99
+ if_test ("if_test failing test")
100
+ check_int(1, ==, 2);
101
+ if_test ("if_test passing TEST_TODO()")
102
+ TEST_TODO(check(0));
103
+ if_test ("if_test failing TEST_TODO()")
104
+ TEST_TODO(check(1));
105
+ if_test ("if_test test_skip()") {
106
+ check(0);
107
+ test_skip("missing prerequisite");
108
+ check(1);
109
+ }
110
+ if_test ("if_test test_skip() inside TEST_TODO()")
111
+ TEST_TODO((test_skip("missing prerequisite"), 1));
112
+ if_test ("if_test TEST_TODO() after failing check") {
113
+ check(0);
114
+ TEST_TODO(check(0));
115
+ }
116
+ if_test ("if_test failing check after TEST_TODO()") {
117
+ check(1);
118
+ TEST_TODO(check(0));
119
+ check(0);
120
+ }
121
+ if_test ("if_test messages from failing string and char comparison") {
122
+ check_str("\thello\\", "there\"\n");
123
+ check_str("NULL", NULL);
124
+ check_char('a', ==, '\n');
125
+ check_char('\\', ==, '\'');
126
+ }
127
+ if_test ("if_test test with no checks")
128
+ ; /* nothing */
129
+
130
return test_done();
131
}
t/t0080-unit-test-output.sh
+34
-1
@@ -50,7 +50,40 @@ test_expect_success 'TAP output from unit tests' - <<\EOT
50
# BUG: test has no checks at t/helper/test-example-tap.c:94
51
not ok 18 - test with no checks
52
ok 19 - test with no checks returns 0
53
- 1..19
53
+ ok 20 - if_test passing test
54
+ # check "1 == 2" failed at t/helper/test-example-tap.c:100
55
+ # left: 1
56
+ # right: 2
57
+ not ok 21 - if_test failing test
58
+ not ok 22 - if_test passing TEST_TODO() # TODO
59
+ # todo check 'check(1)' succeeded at t/helper/test-example-tap.c:104
60
+ not ok 23 - if_test failing TEST_TODO()
61
+ # check "0" failed at t/helper/test-example-tap.c:106
62
+ # skipping test - missing prerequisite
63
+ # skipping check '1' at t/helper/test-example-tap.c:108
64
+ ok 24 - if_test test_skip() # SKIP
65
+ # skipping test - missing prerequisite
66
+ ok 25 - if_test test_skip() inside TEST_TODO() # SKIP
67
+ # check "0" failed at t/helper/test-example-tap.c:113
68
+ not ok 26 - if_test TEST_TODO() after failing check
69
+ # check "0" failed at t/helper/test-example-tap.c:119
70
+ not ok 27 - if_test failing check after TEST_TODO()
71
+ # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/helper/test-example-tap.c:122
72
+ # left: "\011hello\\\\"
73
+ # right: "there\"\012"
74
+ # check "!strcmp("NULL", NULL)" failed at t/helper/test-example-tap.c:123
75
+ # left: "NULL"
76
+ # right: NULL
77
+ # check "'a' == '\n'" failed at t/helper/test-example-tap.c:124
78
+ # left: 'a'
79
+ # right: '\012'
80
+ # check "'\\\\' == '\\''" failed at t/helper/test-example-tap.c:125
81
+ # left: '\\\\'
82
+ # right: '\\''
83
+ not ok 28 - if_test messages from failing string and char comparison
84
+ # BUG: test has no checks at t/helper/test-example-tap.c:127
85
+ not ok 29 - if_test test with no checks
86
+ 1..29
87
EOF
88
89
! test-tool example-tap >actual &&
t/unit-tests/test-lib.c
+29
@@ -16,6 +16,8 @@ static struct {
16
unsigned running :1;
17
unsigned skip_all :1;
18
unsigned todo :1;
19
+ char location[100];
20
+ char description[100];
21
} ctx = {
22
.lazy_plan = 1,
23
.result = RESULT_NONE,
@@ -125,6 +127,8 @@ void test_plan(int count)
127
128
int test_done(void)
129
{
130
+ if (ctx.running && ctx.location[0] && ctx.description[0])
131
+ test__run_end(1, ctx.location, "%s", ctx.description);
132
assert(!ctx.running);
133
134
if (ctx.lazy_plan)
@@ -167,13 +171,38 @@ void test_skip_all(const char *format, ...)
171
va_end(ap);
172
}
173
174
+void test__run_describe(const char *location, const char *format, ...)
175
+{
176
+ va_list ap;
177
+ int len;
178
+
179
+ assert(ctx.running);
180
+ assert(!ctx.location[0]);
181
+ assert(!ctx.description[0]);
182
+
183
+ xsnprintf(ctx.location, sizeof(ctx.location), "%s",
184
+ make_relative(location));
185
+
186
+ va_start(ap, format);
187
+ len = vsnprintf(ctx.description, sizeof(ctx.description), format, ap);
188
+ va_end(ap);
189
+ if (len < 0)
190
+ die("unable to format message: %s", format);
191
+ if (len >= sizeof(ctx.description))
192
+ BUG("ctx.description too small to format %s", format);
193
+}
194
+
195
int test__run_begin(void)
196
{
197
+ if (ctx.running && ctx.location[0] && ctx.description[0])
198
+ test__run_end(1, ctx.location, "%s", ctx.description);
199
assert(!ctx.running);
200
201
ctx.count++;
202
ctx.result = RESULT_NONE;
203
ctx.running = 1;
204
+ ctx.location[0] = '\0';
205
+ ctx.description[0] = '\0';
206
207
return ctx.skip_all;
208
}
t/unit-tests/test-lib.h
+20
@@ -14,6 +14,23 @@
14
test__run_end(test__run_begin() ? 0 : (t, 1), \
15
TEST_LOCATION(), __VA_ARGS__)
16
17
+/*
18
+ * Run a test unless test_skip_all() has been called. Acts like a
19
+ * conditional; the test body is expected as a statement or block after
20
+ * the closing parenthesis. The description for each test should be
21
+ * unique. E.g.:
22
+ *
23
+ * if_test ("something else %d %d", arg1, arg2) {
24
+ * prepare();
25
+ * test_something_else(arg1, arg2);
26
+ * cleanup();
27
+ * }
28
+ */
29
+#define if_test(...) \
30
+ if (test__run_begin() ? \
31
+ (test__run_end(0, TEST_LOCATION(), __VA_ARGS__), 0) : \
32
+ (test__run_describe(TEST_LOCATION(), __VA_ARGS__), 1))
33
+
34
/*
35
* Print a test plan, should be called before any tests. If the number
36
* of tests is not known in advance test_done() will automatically
@@ -153,6 +170,9 @@ union test__tmp {
170
171
extern union test__tmp test__tmp[2];
172
173
+__attribute__((format (printf, 2, 3)))
174
+void test__run_describe(const char *, const char *, ...);
175
+
176
int test__run_begin(void);
177
__attribute__((format (printf, 3, 4)))
178
int test__run_end(int, const char *, const char *, ...);