source: mainline/uspace/lib/pcut/src/main.c@ 6ef9d9a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6ef9d9a was 9eb1ff5, checked in by Vojtech Horky <vojtech.horky@…>, 8 years ago

Update PCUT

Updated PCUT to commit 7ce059f.

Notable changes include:

  • overall summary is printed when tests finish
  • when tests passed, the status message does not use the word 'failure'
  • program exit code is zero only when all tests passed

These changes fixes tickets 713 and 714.

http://www.helenos.org/ticket/713
http://www.helenos.org/ticket/714

  • Property mode set to 100644
File size: 8.2 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#include <assert.h>
37#include <stdlib.h>
38#include <stdio.h>
39
40/** Current running mode. */
41int pcut_run_mode = PCUT_RUN_MODE_FORKING;
42
43/** Empty list to bypass special handling for NULL. */
44static pcut_main_extra_t empty_main_extra[] = {
45 PCUT_MAIN_EXTRA_SET_LAST
46};
47
48/** Helper for iteration over main extras. */
49#define FOR_EACH_MAIN_EXTRA(extras, it) \
50 for (it = extras; it->type != PCUT_MAIN_EXTRA_LAST; it++)
51
52/** Checks whether the argument is an option followed by a number.
53 *
54 * @param arg Argument from the user.
55 * @param opt Option, including the leading dashes.
56 * @param value Where to store the integer value.
57 * @return Whether @p arg is @p opt followed by a number.
58 */
59int pcut_is_arg_with_number(const char *arg, const char *opt, int *value) {
60 int opt_len = pcut_str_size(opt);
61 if (! pcut_str_start_equals(arg, opt, opt_len)) {
62 return 0;
63 }
64 *value = pcut_str_to_int(arg + opt_len);
65 return 1;
66}
67
68
69/** Find item by its id.
70 *
71 * @param first List to search.
72 * @param id Id to find.
73 * @return The item with given id.
74 * @retval NULL No item with such id exists in the list.
75 */
76static pcut_item_t *pcut_find_by_id(pcut_item_t *first, int id) {
77 pcut_item_t *it = pcut_get_real(first);
78 while (it != NULL) {
79 if (it->id == id) {
80 return it;
81 }
82 it = pcut_get_real_next(it);
83 }
84 return NULL;
85}
86
87/** Run the whole test suite.
88 *
89 * @param suite Suite to run.
90 * @param last Pointer to first item after this suite is stored here.
91 * @param prog_path Path to the current binary (used in forked mode).
92 * @return Error code.
93 */
94static int run_suite(pcut_item_t *suite, pcut_item_t **last, const char *prog_path) {
95 int is_first_test = 1;
96 int total_count = 0;
97 int ret_code = PCUT_OUTCOME_PASS;
98 int ret_code_tmp;
99
100 pcut_item_t *it = pcut_get_real_next(suite);
101 if ((it == NULL) || (it->kind == PCUT_KIND_TESTSUITE)) {
102 goto leave_no_print;
103 }
104
105 for (; it != NULL; it = pcut_get_real_next(it)) {
106 if (it->kind == PCUT_KIND_TESTSUITE) {
107 goto leave_ok;
108 }
109 if (it->kind != PCUT_KIND_TEST) {
110 continue;
111 }
112
113 if (is_first_test) {
114 pcut_report_suite_start(suite);
115 is_first_test = 0;
116 }
117
118 if (pcut_run_mode == PCUT_RUN_MODE_FORKING) {
119 ret_code_tmp = pcut_run_test_forking(prog_path, it);
120 } else {
121 ret_code_tmp = pcut_run_test_single(it);
122 }
123
124 /*
125 * Override final return code in case of failure.
126 *
127 * In this case we suppress any special error codes as
128 * to the outside, there was a failure.
129 */
130 if (ret_code_tmp != PCUT_OUTCOME_PASS) {
131 ret_code = PCUT_OUTCOME_FAIL;
132 }
133
134 total_count++;
135 }
136
137leave_ok:
138 if (total_count > 0) {
139 pcut_report_suite_done(suite);
140 }
141
142leave_no_print:
143 if (last != NULL) {
144 *last = it;
145 }
146
147 return ret_code;
148}
149
150/** Add direct pointers to set-up/tear-down functions to a suites.
151 *
152 * At start-up, set-up and tear-down functions are scattered in the
153 * list as siblings of suites and tests.
154 * This puts them into the structure describing the suite itself.
155 *
156 * @param first First item of the list.
157 */
158static void set_setup_teardown_callbacks(pcut_item_t *first) {
159 pcut_item_t *active_suite = NULL;
160 pcut_item_t *it;
161 for (it = first; it != NULL; it = pcut_get_real_next(it)) {
162 if (it->kind == PCUT_KIND_TESTSUITE) {
163 active_suite = it;
164 } else if (it->kind == PCUT_KIND_SETUP) {
165 if (active_suite != NULL) {
166 active_suite->setup_func = it->setup_func;
167 }
168 it->kind = PCUT_KIND_SKIP;
169 } else if (it->kind == PCUT_KIND_TEARDOWN) {
170 if (active_suite != NULL) {
171 active_suite->teardown_func = it->teardown_func;
172 }
173 it->kind = PCUT_KIND_SKIP;
174 } else {
175 /* Not interesting right now. */
176 }
177 }
178}
179
180/** The main function of PCUT.
181 *
182 * This function is expected to be called as the only function in
183 * normal main().
184 *
185 * @param last Pointer to the last item defined by PCUT_TEST macros.
186 * @param argc Original argc of the program.
187 * @param argv Original argv of the program.
188 * @return Program exit code.
189 */
190int pcut_main(pcut_item_t *last, int argc, char *argv[]) {
191 pcut_item_t *items = pcut_fix_list_get_real_head(last);
192 pcut_item_t *it;
193 pcut_main_extra_t *main_extras = last->main_extras;
194 pcut_main_extra_t *main_extras_it;
195
196 int run_only_suite = -1;
197 int run_only_test = -1;
198
199 int rc, rc_tmp;
200
201 if (main_extras == NULL) {
202 main_extras = empty_main_extra;
203 }
204
205 pcut_report_register_handler(&pcut_report_tap);
206
207 FOR_EACH_MAIN_EXTRA(main_extras, main_extras_it) {
208 if (main_extras_it->type == PCUT_MAIN_EXTRA_REPORT_XML) {
209 pcut_report_register_handler(&pcut_report_xml);
210 }
211 if (main_extras_it->type == PCUT_MAIN_EXTRA_PREINIT_HOOK) {
212 main_extras_it->preinit_hook(&argc, &argv);
213 }
214 }
215
216 if (argc > 1) {
217 int i;
218 for (i = 1; i < argc; i++) {
219 pcut_is_arg_with_number(argv[i], "-s", &run_only_suite);
220 pcut_is_arg_with_number(argv[i], "-t", &run_only_test);
221 if (pcut_str_equals(argv[i], "-l")) {
222 pcut_print_tests(items);
223 return PCUT_OUTCOME_PASS;
224 }
225 if (pcut_str_equals(argv[i], "-x")) {
226 pcut_report_register_handler(&pcut_report_xml);
227 }
228#ifndef PCUT_NO_LONG_JUMP
229 if (pcut_str_equals(argv[i], "-u")) {
230 pcut_run_mode = PCUT_RUN_MODE_SINGLE;
231 }
232#endif
233 }
234 }
235
236 setvbuf(stdout, NULL, _IONBF, 0);
237 set_setup_teardown_callbacks(items);
238
239 FOR_EACH_MAIN_EXTRA(main_extras, main_extras_it) {
240 if (main_extras_it->type == PCUT_MAIN_EXTRA_INIT_HOOK) {
241 main_extras_it->init_hook();
242 }
243 }
244
245 PCUT_DEBUG("run_only_suite = %d run_only_test = %d", run_only_suite, run_only_test);
246
247 if ((run_only_suite >= 0) && (run_only_test >= 0)) {
248 printf("Specify either -s or -t!\n");
249 return PCUT_OUTCOME_BAD_INVOCATION;
250 }
251
252 if (run_only_suite > 0) {
253 pcut_item_t *suite = pcut_find_by_id(items, run_only_suite);
254 if (suite == NULL) {
255 printf("Suite not found, aborting!\n");
256 return PCUT_OUTCOME_BAD_INVOCATION;
257 }
258 if (suite->kind != PCUT_KIND_TESTSUITE) {
259 printf("Invalid suite id!\n");
260 return PCUT_OUTCOME_BAD_INVOCATION;
261 }
262
263 run_suite(suite, NULL, argv[0]);
264 return PCUT_OUTCOME_PASS;
265 }
266
267 if (run_only_test > 0) {
268 pcut_item_t *test = pcut_find_by_id(items, run_only_test);
269 if (test == NULL) {
270 printf("Test not found, aborting!\n");
271 return PCUT_OUTCOME_BAD_INVOCATION;
272 }
273 if (test->kind != PCUT_KIND_TEST) {
274 printf("Invalid test id!\n");
275 return PCUT_OUTCOME_BAD_INVOCATION;
276 }
277
278 if (pcut_run_mode == PCUT_RUN_MODE_SINGLE) {
279 rc = pcut_run_test_single(test);
280 } else {
281 rc = pcut_run_test_forked(test);
282 }
283
284 return rc;
285 }
286
287 /* Otherwise, run the whole thing. */
288 pcut_report_init(items);
289
290 rc = PCUT_OUTCOME_PASS;
291
292 it = items;
293 while (it != NULL) {
294 if (it->kind == PCUT_KIND_TESTSUITE) {
295 pcut_item_t *tmp;
296 rc_tmp = run_suite(it, &tmp, argv[0]);
297 if (rc_tmp != PCUT_OUTCOME_PASS) {
298 rc = rc_tmp;
299 }
300 it = tmp;
301 } else {
302 it = pcut_get_real_next(it);
303 }
304 }
305
306 pcut_report_done();
307
308 return rc;
309}
Note: See TracBrowser for help on using the repository browser.