source: mainline/uspace/lib/pcut/src/main.c

Last change on this file was 4b54bd9, checked in by Vojtech Horky <vojtech.horky@…>, 7 years ago

Update PCUT to latest revision

  • Property mode set to 100644
File size: 8.3 KB
Line 
1/*
2 * Copyright (c) 2012-2013 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
16 *
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.
27 */
28
29/** @file
30 *
31 * The main control loop of the whole library.
32 */
33
34#include "internal.h"
35#include "report/report.h"
36
37#pragma warning(push, 0)
38#include <assert.h>
39#include <stdlib.h>
40#include <stdio.h>
41#pragma warning(pop)
42
43
44/** Current running mode. */
45int pcut_run_mode = PCUT_RUN_MODE_FORKING;
46
47/** Empty list to bypass special handling for NULL. */
48static pcut_main_extra_t empty_main_extra[] = {
49 PCUT_MAIN_EXTRA_SET_LAST
50};
51
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++)
55
56/** Checks whether the argument is an option followed by a number.
57 *
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.
62 */
63int 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)) {
66 return 0;
67 }
68 *value = pcut_str_to_int(arg + opt_len);
69 return 1;
70}
71
72
73/** Find item by its id.
74 *
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.
79 */
80static pcut_item_t *pcut_find_by_id(pcut_item_t *first, int id) {
81 pcut_item_t *it = pcut_get_real(first);
82 while (it != NULL) {
83 if (it->id == id) {
84 return it;
85 }
86 it = pcut_get_real_next(it);
87 }
88 return NULL;
89}
90
91/** Run the whole test suite.
92 *
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).
96 * @return Error code.
97 */
98static int run_suite(pcut_item_t *suite, pcut_item_t **last, const char *prog_path) {
99 int is_first_test = 1;
100 int total_count = 0;
101 int ret_code = PCUT_OUTCOME_PASS;
102 int ret_code_tmp;
103
104 pcut_item_t *it = pcut_get_real_next(suite);
105 if ((it == NULL) || (it->kind == PCUT_KIND_TESTSUITE)) {
106 goto leave_no_print;
107 }
108
109 for (; it != NULL; it = pcut_get_real_next(it)) {
110 if (it->kind == PCUT_KIND_TESTSUITE) {
111 goto leave_ok;
112 }
113 if (it->kind != PCUT_KIND_TEST) {
114 continue;
115 }
116
117 if (is_first_test) {
118 pcut_report_suite_start(suite);
119 is_first_test = 0;
120 }
121
122 if (pcut_run_mode == PCUT_RUN_MODE_FORKING) {
123 ret_code_tmp = pcut_run_test_forking(prog_path, it);
124 } else {
125 ret_code_tmp = pcut_run_test_single(it);
126 }
127
128 /*
129 * Override final return code in case of failure.
130 *
131 * In this case we suppress any special error codes as
132 * to the outside, there was a failure.
133 */
134 if (ret_code_tmp != PCUT_OUTCOME_PASS) {
135 ret_code = PCUT_OUTCOME_FAIL;
136 }
137
138 total_count++;
139 }
140
141leave_ok:
142 if (total_count > 0) {
143 pcut_report_suite_done(suite);
144 }
145
146leave_no_print:
147 if (last != NULL) {
148 *last = it;
149 }
150
151 return ret_code;
152}
153
154/** Add direct pointers to set-up/tear-down functions to a suites.
155 *
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.
159 *
160 * @param first First item of the list.
161 */
162static void set_setup_teardown_callbacks(pcut_item_t *first) {
163 pcut_item_t *active_suite = NULL;
164 pcut_item_t *it;
165 for (it = first; it != NULL; it = pcut_get_real_next(it)) {
166 if (it->kind == PCUT_KIND_TESTSUITE) {
167 active_suite = it;
168 } else if (it->kind == PCUT_KIND_SETUP) {
169 if (active_suite != NULL) {
170 active_suite->setup_func = it->setup_func;
171 }
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;
176 }
177 it->kind = PCUT_KIND_SKIP;
178 } else {
179 /* Not interesting right now. */
180 }
181 }
182}
183
184/** The main function of PCUT.
185 *
186 * This function is expected to be called as the only function in
187 * normal main().
188 *
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.
193 */
194int pcut_main(pcut_item_t *last, int argc, char *argv[]) {
195 pcut_item_t *items = pcut_fix_list_get_real_head(last);
196 pcut_item_t *it;
197 pcut_main_extra_t *main_extras = last->main_extras;
198 pcut_main_extra_t *main_extras_it;
199
200 int run_only_suite = -1;
201 int run_only_test = -1;
202
203 int rc, rc_tmp;
204
205 if (main_extras == NULL) {
206 main_extras = empty_main_extra;
207 }
208
209 pcut_report_register_handler(&pcut_report_tap);
210
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);
214 }
215 if (main_extras_it->type == PCUT_MAIN_EXTRA_PREINIT_HOOK) {
216 main_extras_it->preinit_hook(&argc, &argv);
217 }
218 }
219
220 if (argc > 1) {
221 int i;
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;
228 }
229 if (pcut_str_equals(argv[i], "-x")) {
230 pcut_report_register_handler(&pcut_report_xml);
231 }
232#ifndef PCUT_NO_LONG_JUMP
233 if (pcut_str_equals(argv[i], "-u")) {
234 pcut_run_mode = PCUT_RUN_MODE_SINGLE;
235 }
236#endif
237 }
238 }
239
240 setvbuf(stdout, NULL, _IONBF, 0);
241 set_setup_teardown_callbacks(items);
242
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();
246 }
247 }
248
249 PCUT_DEBUG("run_only_suite = %d run_only_test = %d", run_only_suite, run_only_test);
250
251 if ((run_only_suite >= 0) && (run_only_test >= 0)) {
252 printf("Specify either -s or -t!\n");
253 return PCUT_OUTCOME_BAD_INVOCATION;
254 }
255
256 if (run_only_suite > 0) {
257 pcut_item_t *suite = pcut_find_by_id(items, run_only_suite);
258 if (suite == NULL) {
259 printf("Suite not found, aborting!\n");
260 return PCUT_OUTCOME_BAD_INVOCATION;
261 }
262 if (suite->kind != PCUT_KIND_TESTSUITE) {
263 printf("Invalid suite id!\n");
264 return PCUT_OUTCOME_BAD_INVOCATION;
265 }
266
267 run_suite(suite, NULL, argv[0]);
268 return PCUT_OUTCOME_PASS;
269 }
270
271 if (run_only_test > 0) {
272 pcut_item_t *test = pcut_find_by_id(items, run_only_test);
273 if (test == NULL) {
274 printf("Test not found, aborting!\n");
275 return PCUT_OUTCOME_BAD_INVOCATION;
276 }
277 if (test->kind != PCUT_KIND_TEST) {
278 printf("Invalid test id!\n");
279 return PCUT_OUTCOME_BAD_INVOCATION;
280 }
281
282 if (pcut_run_mode == PCUT_RUN_MODE_SINGLE) {
283 rc = pcut_run_test_single(test);
284 } else {
285 rc = pcut_run_test_forked(test);
286 }
287
288 return rc;
289 }
290
291 /* Otherwise, run the whole thing. */
292 pcut_report_init(items);
293
294 rc = PCUT_OUTCOME_PASS;
295
296 it = items;
297 while (it != NULL) {
298 if (it->kind == PCUT_KIND_TESTSUITE) {
299 pcut_item_t *tmp;
300 rc_tmp = run_suite(it, &tmp, argv[0]);
301 if (rc_tmp != PCUT_OUTCOME_PASS) {
302 rc = rc_tmp;
303 }
304 it = tmp;
305 } else {
306 it = pcut_get_real_next(it);
307 }
308 }
309
310 pcut_report_done();
311
312 return rc;
313}
Note: See TracBrowser for help on using the repository browser.