Skip to content

Commit 96c6304

Browse files
rscharfegitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1f452d6 commit 96c6304

File tree

5 files changed

+121
-1
lines changed

5 files changed

+121
-1
lines changed

.clang-format

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ ForEachMacros:
169169
- 'strmap_for_each_entry'
170170
- 'strset_for_each_entry'
171171

172+
# A list of macros that should be interpreted as conditionals instead of as
173+
# function calls.
174+
IfMacros:
175+
- 'if_test'
176+
172177
# The maximum number of consecutive empty lines to keep.
173178
MaxEmptyLinesToKeep: 1
174179

t/helper/test-example-tap.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,38 @@ int cmd__example_tap(int argc, const char **argv)
9494
test_res = TEST(t_empty(), "test with no checks");
9595
TEST(check_int(test_res, ==, 0), "test with no checks returns 0");
9696

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+
97130
return test_done();
98131
}

t/t0080-unit-test-output.sh

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,40 @@ test_expect_success 'TAP output from unit tests' - <<\EOT
5050
# BUG: test has no checks at t/helper/test-example-tap.c:94
5151
not ok 18 - test with no checks
5252
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
5487
EOF
5588
5689
! test-tool example-tap >actual &&

t/unit-tests/test-lib.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ static struct {
1616
unsigned running :1;
1717
unsigned skip_all :1;
1818
unsigned todo :1;
19+
char location[100];
20+
char description[100];
1921
} ctx = {
2022
.lazy_plan = 1,
2123
.result = RESULT_NONE,
@@ -125,6 +127,8 @@ void test_plan(int count)
125127

126128
int test_done(void)
127129
{
130+
if (ctx.running && ctx.location[0] && ctx.description[0])
131+
test__run_end(1, ctx.location, "%s", ctx.description);
128132
assert(!ctx.running);
129133

130134
if (ctx.lazy_plan)
@@ -167,13 +171,38 @@ void test_skip_all(const char *format, ...)
167171
va_end(ap);
168172
}
169173

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+
170195
int test__run_begin(void)
171196
{
197+
if (ctx.running && ctx.location[0] && ctx.description[0])
198+
test__run_end(1, ctx.location, "%s", ctx.description);
172199
assert(!ctx.running);
173200

174201
ctx.count++;
175202
ctx.result = RESULT_NONE;
176203
ctx.running = 1;
204+
ctx.location[0] = '\0';
205+
ctx.description[0] = '\0';
177206

178207
return ctx.skip_all;
179208
}

t/unit-tests/test-lib.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@
1414
test__run_end(test__run_begin() ? 0 : (t, 1), \
1515
TEST_LOCATION(), __VA_ARGS__)
1616

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+
1734
/*
1835
* Print a test plan, should be called before any tests. If the number
1936
* of tests is not known in advance test_done() will automatically
@@ -153,6 +170,9 @@ union test__tmp {
153170

154171
extern union test__tmp test__tmp[2];
155172

173+
__attribute__((format (printf, 2, 3)))
174+
void test__run_describe(const char *, const char *, ...);
175+
156176
int test__run_begin(void);
157177
__attribute__((format (printf, 3, 4)))
158178
int test__run_end(int, const char *, const char *, ...);

0 commit comments

Comments
 (0)