Skip to content

Commit a41cbda

Browse files
author
Crispin Dent-Young
committed
Support arbitrary tagging and selection of testcases.
A testcase can optionally have a list of tags associated with it. Srunner can be run with an optional include list of tags and an optional exclude list of tags. These will have the effect of filtering testcases that would otherwise be run.
1 parent 1271915 commit a41cbda

11 files changed

Lines changed: 799 additions & 10 deletions

doc/check.texi

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,7 @@ easier for the developer to write, run, and analyze tests.
974974
* Test Fixtures::
975975
* Multiple Suites in one SRunner::
976976
* Selective Running of Tests::
977+
* Selecting Tests Based on Arbitrary Tags::
977978
* Testing Signal Handling and Exit Values::
978979
* Looping Tests::
979980
* Test Timeouts::
@@ -1301,11 +1302,12 @@ srunner_add_suite (sr, make_pack_suite ());
13011302
@end verbatim
13021303
@end example
13031304

1304-
@node Selective Running of Tests, Testing Signal Handling and Exit Values, Multiple Suites in one SRunner, Advanced Features
1305+
@node Selective Running of Tests, Selecting Tests Based on Arbitrary Tags, Multiple Suites in one SRunner, Advanced Features
13051306
@section Selective Running of Tests
13061307

13071308
@vindex CK_RUN_SUITE
13081309
@vindex CK_RUN_CASE
1310+
13091311
After adding a couple of suites and some test cases in each, it is
13101312
sometimes practical to be able to run only one suite, or one
13111313
specific test case, without recompiling the test code. There are
@@ -1314,8 +1316,70 @@ two environment variables available that offers this ability,
13141316
the name of the suite and/or test case you want to run. These
13151317
environment variables can also be a good integration tool for
13161318
running specific tests from within another tool, e.g. an IDE.
1317-
1318-
@node Testing Signal Handling and Exit Values, Looping Tests, Selective Running of Tests, Advanced Features
1319+
1320+
@node Selecting Tests Based on Arbitrary Tags, Testing Signal Handling and Exit Values, Selective Running of Tests, Advanced Features
1321+
@section Selecting Tests Based on Arbitrary Tags
1322+
1323+
@vindex CK_INCLUDE_TAGS
1324+
@vindex CK_EXCLUDE_TAGS
1325+
1326+
It can also be useful to be able to dynamically select or exclude
1327+
groups of tests to be run based on criteria other than the suite or
1328+
testcase name. For example certain test cases can be tagged as
1329+
requiring a long run time and so quick sanity tests can be run that
1330+
exclude all test cases with such a tag. Alternately tags may be used
1331+
where tests cover multiple functional areas in order to indicate which
1332+
areas a test case covers. Tests can then be run that include all test
1333+
cases for a given set of areas.
1334+
1335+
A tag is a string of characters excluding spaces.
1336+
1337+
Tags are applied to a testcase by passing a space separated list of
1338+
tags to @code{tcase_set_tags} once it has been created. For example in
1339+
function @code{make_tagged_suite()} in @file{check_check_tags.c} -
1340+
1341+
@example
1342+
@verbatim
1343+
Suite *s;
1344+
1345+
TCase *red, *blue, *purple, *yellow, *black;
1346+
1347+
s = suite_create("Check Tag Filtering");
1348+
1349+
red = tcase_create("Red");
1350+
tcase_set_tags(red, "Red");
1351+
suite_add_tcase (s, red);
1352+
tcase_add_test(red, red_test1);
1353+
1354+
blue = tcase_create("Blue");
1355+
tcase_set_tags(blue, "Blue");
1356+
suite_add_tcase (s, blue);
1357+
tcase_add_test(blue, blue_test1);
1358+
1359+
purple = tcase_create("Purple");
1360+
tcase_set_tags(purple, "Red Blue");
1361+
suite_add_tcase (s, purple);
1362+
tcase_add_test(purple, purple_test1);
1363+
1364+
@end verbatim
1365+
@end example
1366+
1367+
Once test cases are tagged then there are two environment variables
1368+
available for selecting testcases based on these tags
1369+
@code{CK_INCLUDE_TAGS} and @code{CK_EXCLUDE_TAGS}. These can be set to
1370+
a space separated list of tag names. If @code{CK_INCLUDE_TAGS} is set
1371+
then only testcases which include at least one tag in common with
1372+
@code{CK_INCLUDE_TAGS} will be run. If @code{CK_EXCLUDE_TAGS} is set
1373+
then testcases with one tag in common with @code{CK_EXCLUDE_TAGS} will
1374+
not be run. In cases where both @code{CK_INCLUDE_TAGS} and
1375+
@code{CK_EXCLUDE_TAGS} match then the test will be excluded.
1376+
1377+
Both @code{CK_INCLUDE_TAGS} and @code{CK_EXCLUDE_TAGS} can be
1378+
specified in conjunction with @code{CK_RUN_SUITE} or even
1379+
@code{CK_RUN_CASE} in which case they will have the effect of further
1380+
narrowing the selection.
1381+
1382+
@node Testing Signal Handling and Exit Values, Looping Tests, Selecting Tests Based on Arbitrary Tags, Advanced Features
13191383
@section Testing Signal Handling and Exit Values
13201384

13211385
@findex tcase_add_test_raise_signal
@@ -1975,6 +2039,10 @@ CK_RUN_CASE: Name of a test case, runs only that test. See section @ref{Selectiv
19752039

19762040
CK_RUN_SUITE: Name of a test suite, runs only that suite. See section @ref{Selective Running of Tests}.
19772041

2042+
CK_INCLUDE_TAGS: String of space separated tags, runs only test cases associated with at least one of the tags, See section @ref{Selecting Tests Based on Arbitrary Tags}.
2043+
2044+
CK_EXCLUDE_TAGS: String of space separated tags, runs only test cases not associated with any of the tags, See section @ref{Selecting Tests Based on Arbitrary Tags}.
2045+
19782046
CK_VERBOSITY: How much output to emit, accepts: ``silent'', ``minimal'', ``normal'', ``subunit'', or ``verbose''. See section @ref{SRunner Output}.
19792047

19802048
CK_FORK: Set to ``no'' to disable using fork() to run unit tests in their own process. This is useful for debugging segmentation faults. See section @ref{No Fork Mode}.

src/check.c

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ static void suite_free(Suite * s)
103103
free(s);
104104
}
105105

106+
106107
TCase *tcase_create(const char *name)
107108
{
108109
char *env;
@@ -149,10 +150,49 @@ TCase *tcase_create(const char *name)
149150
tc->ch_sflst = check_list_create();
150151
tc->unch_tflst = check_list_create();
151152
tc->ch_tflst = check_list_create();
153+
tc->tags = check_list_create();
152154

153155
return tc;
154156
}
155157

158+
/*
159+
* Helper function to create a list of tags from
160+
* a space separated string.
161+
*/
162+
List *tag_string_to_list(const char *tags_string)
163+
{
164+
List *list;
165+
char *tags;
166+
char *tag;
167+
168+
list = check_list_create();
169+
170+
if (NULL == tags_string)
171+
{
172+
return list;
173+
}
174+
175+
tags = strdup(tags_string);
176+
tag = strtok(tags, " ");
177+
while (tag)
178+
{
179+
check_list_add_end(list, strdup(tag));
180+
tag = strtok(NULL, " ");
181+
}
182+
free(tags);
183+
return list;
184+
}
185+
186+
void tcase_set_tags(TCase * tc, const char *tags_orig)
187+
{
188+
/* replace any pre-existing list */
189+
if (tc->tags)
190+
{
191+
check_list_apply(tc->tags, free);
192+
check_list_free(tc->tags);
193+
}
194+
tc->tags = tag_string_to_list(tags_orig);
195+
}
156196

157197
static void tcase_free(TCase * tc)
158198
{
@@ -161,15 +201,40 @@ static void tcase_free(TCase * tc)
161201
check_list_apply(tc->ch_sflst, free);
162202
check_list_apply(tc->unch_tflst, free);
163203
check_list_apply(tc->ch_tflst, free);
204+
check_list_apply(tc->tags, free);
164205
check_list_free(tc->tflst);
165206
check_list_free(tc->unch_sflst);
166207
check_list_free(tc->ch_sflst);
167208
check_list_free(tc->unch_tflst);
168209
check_list_free(tc->ch_tflst);
169-
210+
check_list_free(tc->tags);
170211
free(tc);
171212
}
172213

214+
unsigned int tcase_matching_tag(TCase *tc, List *check_for)
215+
{
216+
217+
if (NULL == check_for)
218+
{
219+
return 0;
220+
}
221+
222+
for(check_list_front(check_for); !check_list_at_end(check_for);
223+
check_list_advance(check_for))
224+
{
225+
for(check_list_front(tc->tags); !check_list_at_end(tc->tags);
226+
check_list_advance(tc->tags))
227+
{
228+
if (0 == strcmp((const char *)check_list_val(tc->tags),
229+
(const char *)check_list_val(check_for)))
230+
{
231+
return 1;
232+
}
233+
}
234+
}
235+
return 0;
236+
}
237+
173238
void suite_add_tcase(Suite * s, TCase * tc)
174239
{
175240
if(s == NULL || tc == NULL || check_list_contains(s->tclst, tc))

src/check.h.in

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,19 @@ CK_DLL_EXP void CK_EXPORT suite_add_tcase(Suite * s, TCase * tc);
172172
* */
173173
CK_DLL_EXP TCase *CK_EXPORT tcase_create(const char *name);
174174

175+
/**
176+
* Associate a test case with certain tags.
177+
* Replaces any existing tags with the new set.
178+
*
179+
* @param tc the test case
180+
*
181+
* @param tags string containing arbitrary tags separated by spaces.
182+
* This will be copied. Passing NULL clears all tags.
183+
*
184+
* @since 0.11.0
185+
* */
186+
CK_DLL_EXP void CK_EXPORT tcase_set_tags(TCase * tc,
187+
const char *tags);
175188
/**
176189
* Add a test function to a test case
177190
*
@@ -986,6 +999,33 @@ CK_DLL_EXP void CK_EXPORT srunner_run(SRunner * sr, const char *sname,
986999
enum print_output print_mode);
9871000

9881001

1002+
/**
1003+
* Run a specific suite or test case or testcases with specific tags
1004+
* from a suite runner, printing results to stdout as specified by the
1005+
* print_mode.
1006+
*
1007+
* In addition to running any applicable suites or test cases, if the
1008+
* suite runner has been configured to output to a log, that is also
1009+
* performed.
1010+
*
1011+
* @param sr suite runner where the given suite or test case must be
1012+
* @param sname suite name to run. A NULL means "any suite".
1013+
* @param tcname test case name to run. A NULL means "any test case"
1014+
* @param include_tags space separate list of tags. Only run test cases
1015+
* that share one of these tags. A NULL means "any test case".
1016+
* @param exclude_tags space separate list of tags. Only run test cases
1017+
* that do not share one of these tags. A NULL means "any test case".
1018+
* Overrides any include criteria.
1019+
* @param print_mode the verbosity in which to report results to stdout
1020+
*
1021+
* @since 0.11.0
1022+
*/
1023+
CK_DLL_EXP void CK_EXPORT srunner_run_tagged(SRunner * sr, const char *sname,
1024+
const char *tcname,
1025+
const char *include_tags,
1026+
const char *exclude_tags,
1027+
enum print_output print_mode);
1028+
9891029
/**
9901030
* Retrieve the number of failed tests executed by a suite runner.
9911031
*

src/check_impl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct TCase
6565
List *unch_tflst;
6666
List *ch_sflst;
6767
List *ch_tflst;
68+
List *tags;
6869
};
6970

7071
typedef struct TestStats
@@ -134,4 +135,7 @@ enum fork_status cur_fork_status(void);
134135

135136
clockid_t check_get_clockid(void);
136137

138+
unsigned int tcase_matching_tag(TCase *tc, List *check_for);
139+
List *tag_string_to_list(const char *tags_string);
140+
137141
#endif /* CHECK_IMPL_H */

src/check_run.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ static void srunner_run_init(SRunner * sr, enum print_output print_mode);
6060
static void srunner_run_end(SRunner * sr, enum print_output print_mode);
6161
static void srunner_iterate_suites(SRunner * sr,
6262
const char *sname, const char *tcname,
63+
const char *include_tags,
64+
const char *exclude_tags,
6365
enum print_output print_mode);
6466
static void srunner_iterate_tcase_tfuns(SRunner * sr, TCase * tc);
6567
static void srunner_add_failure(SRunner * sr, TestResult * tf);
@@ -160,15 +162,22 @@ static void srunner_run_end(SRunner * sr,
160162

161163
static void srunner_iterate_suites(SRunner * sr,
162164
const char *sname, const char *tcname,
165+
const char *include_tags,
166+
const char *exclude_tags,
163167
enum print_output CK_ATTRIBUTE_UNUSED
164168
print_mode)
165169
{
170+
List *include_tag_lst;
171+
List *exclude_tag_lst;
166172
List *slst;
167173
List *tcl;
168174
TCase *tc;
169175

170176
slst = sr->slst;
171177

178+
include_tag_lst = tag_string_to_list(include_tags);
179+
exclude_tag_lst = tag_string_to_list(exclude_tags);
180+
172181
for(check_list_front(slst); !check_list_at_end(slst);
173182
check_list_advance(slst))
174183
{
@@ -191,12 +200,31 @@ static void srunner_iterate_suites(SRunner * sr,
191200
{
192201
continue;
193202
}
203+
if (include_tags != NULL)
204+
{
205+
if (!tcase_matching_tag(tc, include_tag_lst))
206+
{
207+
continue;
208+
}
209+
}
210+
if (exclude_tags != NULL)
211+
{
212+
if (tcase_matching_tag(tc, exclude_tag_lst))
213+
{
214+
continue;
215+
}
216+
}
194217

195218
srunner_run_tcase(sr, tc);
196219
}
197220

198221
log_suite_end(sr, s);
199222
}
223+
224+
check_list_apply(include_tag_lst, free);
225+
check_list_apply(exclude_tag_lst, free);
226+
check_list_free(include_tag_lst);
227+
check_list_free(exclude_tag_lst);
200228
}
201229

202230
static void srunner_iterate_tcase_tfuns(SRunner * sr, TCase * tc)
@@ -741,8 +769,9 @@ void srunner_run_all(SRunner * sr, enum print_output print_mode)
741769
print_mode);
742770
}
743771

744-
void srunner_run(SRunner * sr, const char *sname, const char *tcname,
745-
enum print_output print_mode)
772+
void srunner_run_tagged(SRunner * sr, const char *sname, const char *tcname,
773+
const char *include_tags, const char *exclude_tags,
774+
enum print_output print_mode)
746775
{
747776
#if defined(HAVE_SIGACTION) && defined(HAVE_FORK)
748777
static struct sigaction sigalarm_old_action;
@@ -756,7 +785,11 @@ void srunner_run(SRunner * sr, const char *sname, const char *tcname,
756785
if(!tcname)
757786
tcname = getenv("CK_RUN_CASE");
758787
if(!sname)
759-
sname = getenv("CK_RUN_SUITE");
788+
sname = getenv("CK_RUN_SUITE");
789+
if(!include_tags)
790+
include_tags = getenv("CK_INCLUDE_TAGS");
791+
if(!exclude_tags)
792+
exclude_tags = getenv("CK_EXCLUDE_TAGS");
760793

761794
if(sr == NULL)
762795
return;
@@ -779,7 +812,8 @@ void srunner_run(SRunner * sr, const char *sname, const char *tcname,
779812
sigaction(SIGTERM, &sigterm_new_action, &sigterm_old_action);
780813
#endif /* HAVE_SIGACTION && HAVE_FORK */
781814
srunner_run_init(sr, print_mode);
782-
srunner_iterate_suites(sr, sname, tcname, print_mode);
815+
srunner_iterate_suites(sr, sname, tcname, include_tags, exclude_tags,
816+
print_mode);
783817
srunner_run_end(sr, print_mode);
784818
#if defined(HAVE_SIGACTION) && defined(HAVE_FORK)
785819
sigaction(SIGALRM, &sigalarm_old_action, NULL);
@@ -788,6 +822,12 @@ void srunner_run(SRunner * sr, const char *sname, const char *tcname,
788822
#endif /* HAVE_SIGACTION && HAVE_FORK */
789823
}
790824

825+
void srunner_run(SRunner * sr, const char *sname, const char *tcname,
826+
enum print_output print_mode)
827+
{
828+
srunner_run_tagged(sr, sname, tcname, NULL, NULL, print_mode);
829+
}
830+
791831
pid_t check_fork(void)
792832
{
793833
#if defined(HAVE_FORK) && HAVE_FORK==1

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ set(CHECK_CHECK_SOURCES
5353
check_check_pack.c
5454
check_check_selective.c
5555
check_check_sub.c
56+
check_check_tags.c
5657
check_list.c)
5758
set(CHECK_CHECK_HEADERS check_check.h)
5859
add_executable(check_check ${CHECK_CHECK_HEADERS} ${CHECK_CHECK_SOURCES})

0 commit comments

Comments
 (0)