2 * Copyright (c) 2012-2013 Vojtech Horky
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * The main control loop of the whole library.
35 #include "report/report.h"
37 #pragma warning(push, 0)
44 /** Current running mode. */
45 int pcut_run_mode
= PCUT_RUN_MODE_FORKING
;
47 /** Empty list to bypass special handling for NULL. */
48 static pcut_main_extra_t empty_main_extra
[] = {
49 PCUT_MAIN_EXTRA_SET_LAST
52 /** Helper for iteration over main extras. */
53 #define FOR_EACH_MAIN_EXTRA(extras, it) \
54 for (it = extras; it->type != PCUT_MAIN_EXTRA_LAST; it++)
56 /** Checks whether the argument is an option followed by a number.
58 * @param arg Argument from the user.
59 * @param opt Option, including the leading dashes.
60 * @param value Where to store the integer value.
61 * @return Whether @p arg is @p opt followed by a number.
63 int pcut_is_arg_with_number(const char *arg
, const char *opt
, int *value
) {
64 int opt_len
= pcut_str_size(opt
);
65 if (! pcut_str_start_equals(arg
, opt
, opt_len
)) {
68 *value
= pcut_str_to_int(arg
+ opt_len
);
73 /** Find item by its id.
75 * @param first List to search.
76 * @param id Id to find.
77 * @return The item with given id.
78 * @retval NULL No item with such id exists in the list.
80 static pcut_item_t
*pcut_find_by_id(pcut_item_t
*first
, int id
) {
81 pcut_item_t
*it
= pcut_get_real(first
);
86 it
= pcut_get_real_next(it
);
91 /** Run the whole test suite.
93 * @param suite Suite to run.
94 * @param last Pointer to first item after this suite is stored here.
95 * @param prog_path Path to the current binary (used in forked mode).
98 static int run_suite(pcut_item_t
*suite
, pcut_item_t
**last
, const char *prog_path
) {
99 int is_first_test
= 1;
101 int ret_code
= PCUT_OUTCOME_PASS
;
104 pcut_item_t
*it
= pcut_get_real_next(suite
);
105 if ((it
== NULL
) || (it
->kind
== PCUT_KIND_TESTSUITE
)) {
109 for (; it
!= NULL
; it
= pcut_get_real_next(it
)) {
110 if (it
->kind
== PCUT_KIND_TESTSUITE
) {
113 if (it
->kind
!= PCUT_KIND_TEST
) {
118 pcut_report_suite_start(suite
);
122 if (pcut_run_mode
== PCUT_RUN_MODE_FORKING
) {
123 ret_code_tmp
= pcut_run_test_forking(prog_path
, it
);
125 ret_code_tmp
= pcut_run_test_single(it
);
129 * Override final return code in case of failure.
131 * In this case we suppress any special error codes as
132 * to the outside, there was a failure.
134 if (ret_code_tmp
!= PCUT_OUTCOME_PASS
) {
135 ret_code
= PCUT_OUTCOME_FAIL
;
142 if (total_count
> 0) {
143 pcut_report_suite_done(suite
);
154 /** Add direct pointers to set-up/tear-down functions to a suites.
156 * At start-up, set-up and tear-down functions are scattered in the
157 * list as siblings of suites and tests.
158 * This puts them into the structure describing the suite itself.
160 * @param first First item of the list.
162 static void set_setup_teardown_callbacks(pcut_item_t
*first
) {
163 pcut_item_t
*active_suite
= NULL
;
165 for (it
= first
; it
!= NULL
; it
= pcut_get_real_next(it
)) {
166 if (it
->kind
== PCUT_KIND_TESTSUITE
) {
168 } else if (it
->kind
== PCUT_KIND_SETUP
) {
169 if (active_suite
!= NULL
) {
170 active_suite
->setup_func
= it
->setup_func
;
172 it
->kind
= PCUT_KIND_SKIP
;
173 } else if (it
->kind
== PCUT_KIND_TEARDOWN
) {
174 if (active_suite
!= NULL
) {
175 active_suite
->teardown_func
= it
->teardown_func
;
177 it
->kind
= PCUT_KIND_SKIP
;
179 /* Not interesting right now. */
184 /** The main function of PCUT.
186 * This function is expected to be called as the only function in
189 * @param last Pointer to the last item defined by PCUT_TEST macros.
190 * @param argc Original argc of the program.
191 * @param argv Original argv of the program.
192 * @return Program exit code.
194 int pcut_main(pcut_item_t
*last
, int argc
, char *argv
[]) {
195 pcut_item_t
*items
= pcut_fix_list_get_real_head(last
);
197 pcut_main_extra_t
*main_extras
= last
->main_extras
;
198 pcut_main_extra_t
*main_extras_it
;
200 int run_only_suite
= -1;
201 int run_only_test
= -1;
205 if (main_extras
== NULL
) {
206 main_extras
= empty_main_extra
;
209 pcut_report_register_handler(&pcut_report_tap
);
211 FOR_EACH_MAIN_EXTRA(main_extras
, main_extras_it
) {
212 if (main_extras_it
->type
== PCUT_MAIN_EXTRA_REPORT_XML
) {
213 pcut_report_register_handler(&pcut_report_xml
);
215 if (main_extras_it
->type
== PCUT_MAIN_EXTRA_PREINIT_HOOK
) {
216 main_extras_it
->preinit_hook(&argc
, &argv
);
222 for (i
= 1; i
< argc
; i
++) {
223 pcut_is_arg_with_number(argv
[i
], "-s", &run_only_suite
);
224 pcut_is_arg_with_number(argv
[i
], "-t", &run_only_test
);
225 if (pcut_str_equals(argv
[i
], "-l")) {
226 pcut_print_tests(items
);
227 return PCUT_OUTCOME_PASS
;
229 if (pcut_str_equals(argv
[i
], "-x")) {
230 pcut_report_register_handler(&pcut_report_xml
);
232 #ifndef PCUT_NO_LONG_JUMP
233 if (pcut_str_equals(argv
[i
], "-u")) {
234 pcut_run_mode
= PCUT_RUN_MODE_SINGLE
;
240 setvbuf(stdout
, NULL
, _IONBF
, 0);
241 set_setup_teardown_callbacks(items
);
243 FOR_EACH_MAIN_EXTRA(main_extras
, main_extras_it
) {
244 if (main_extras_it
->type
== PCUT_MAIN_EXTRA_INIT_HOOK
) {
245 main_extras_it
->init_hook();
249 PCUT_DEBUG("run_only_suite = %d run_only_test = %d", run_only_suite
, run_only_test
);
251 if ((run_only_suite
>= 0) && (run_only_test
>= 0)) {
252 printf("Specify either -s or -t!\n");
253 return PCUT_OUTCOME_BAD_INVOCATION
;
256 if (run_only_suite
> 0) {
257 pcut_item_t
*suite
= pcut_find_by_id(items
, run_only_suite
);
259 printf("Suite not found, aborting!\n");
260 return PCUT_OUTCOME_BAD_INVOCATION
;
262 if (suite
->kind
!= PCUT_KIND_TESTSUITE
) {
263 printf("Invalid suite id!\n");
264 return PCUT_OUTCOME_BAD_INVOCATION
;
267 run_suite(suite
, NULL
, argv
[0]);
268 return PCUT_OUTCOME_PASS
;
271 if (run_only_test
> 0) {
272 pcut_item_t
*test
= pcut_find_by_id(items
, run_only_test
);
274 printf("Test not found, aborting!\n");
275 return PCUT_OUTCOME_BAD_INVOCATION
;
277 if (test
->kind
!= PCUT_KIND_TEST
) {
278 printf("Invalid test id!\n");
279 return PCUT_OUTCOME_BAD_INVOCATION
;
282 if (pcut_run_mode
== PCUT_RUN_MODE_SINGLE
) {
283 rc
= pcut_run_test_single(test
);
285 rc
= pcut_run_test_forked(test
);
291 /* Otherwise, run the whole thing. */
292 pcut_report_init(items
);
294 rc
= PCUT_OUTCOME_PASS
;
298 if (it
->kind
== PCUT_KIND_TESTSUITE
) {
300 rc_tmp
= run_suite(it
, &tmp
, argv
[0]);
301 if (rc_tmp
!= PCUT_OUTCOME_PASS
) {
306 it
= pcut_get_real_next(it
);