source: mainline/uspace/lib/pcut/src/main.c@ 15d0046

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 15d0046 was 01579ad, checked in by Vojtech Horky <vojtechhorky@…>, 11 years ago

Start work on PCUT integration

PCUT is a simple library for (hopefully) easier unit testing.
See https://github.com/vhotspur/pcut for more details.

  • Property mode set to 100644
File size: 6.7 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
44/** Checks whether the argument is an option followed by a number.
45 *
46 * @param arg Argument from the user.
47 * @param opt Option, including the leading dashes.
48 * @param value Where to store the integer value.
49 * @return Whether @p arg is @p opt followed by a number.
50 */
51int pcut_is_arg_with_number(const char *arg, const char *opt, int *value) {
52 int opt_len = pcut_str_size(opt);
53 if (! pcut_str_start_equals(arg, opt, opt_len)) {
54 return 0;
55 }
56 int val = pcut_str_to_int(arg + opt_len);
57 *value = val;
58 return 1;
59}
60
61
62/** Find item by its id.
63 *
64 * @param first List to search.
65 * @param id Id to find.
66 * @return The item with given id.
67 * @retval NULL No item with such id exists in the list.
68 */
69static pcut_item_t *pcut_find_by_id(pcut_item_t *first, int id) {
70 pcut_item_t *it = pcut_get_real(first);
71 while (it != NULL) {
72 if (it->id == id) {
73 return it;
74 }
75 it = pcut_get_real_next(it);
76 }
77 return NULL;
78}
79
80/** Run the whole test suite.
81 *
82 * @param suite Suite to run.
83 * @param last Pointer to first item after this suite is stored here.
84 * @param prog_path Path to the current binary (used in forked mode).
85 */
86static void run_suite(pcut_item_t *suite, pcut_item_t **last, const char *prog_path) {
87 pcut_item_t *it = pcut_get_real_next(suite);
88 if ((it == NULL) || (it->kind == PCUT_KIND_TESTSUITE)) {
89 goto leave_no_print;
90 }
91
92 int is_first_test = 1;
93 int total_count = 0;
94
95 for (; it != NULL; it = pcut_get_real_next(it)) {
96 if (it->kind == PCUT_KIND_TESTSUITE) {
97 goto leave_ok;
98 }
99 if (it->kind != PCUT_KIND_TEST) {
100 continue;
101 }
102
103 if (is_first_test) {
104 pcut_report_suite_start(suite);
105 is_first_test = 0;
106 }
107
108 if (pcut_run_mode == PCUT_RUN_MODE_FORKING) {
109 pcut_run_test_forking(prog_path, it);
110 } else {
111 pcut_run_test_single(it);
112 }
113 total_count++;
114 }
115
116leave_ok:
117 if (total_count > 0) {
118 pcut_report_suite_done(suite);
119 }
120
121leave_no_print:
122 if (last != NULL) {
123 *last = it;
124 }
125}
126
127/** Add direct pointers to set-up/tear-down functions to a suites.
128 *
129 * At start-up, set-up and tear-down functions are scattered in the
130 * list as siblings of suites and tests.
131 * This puts them into the structure describing the suite itself.
132 *
133 * @param first First item of the list.
134 */
135static void set_setup_teardown_callbacks(pcut_item_t *first) {
136 pcut_item_t *active_suite = NULL;
137 for (pcut_item_t *it = first; it != NULL; it = pcut_get_real_next(it)) {
138 if (it->kind == PCUT_KIND_TESTSUITE) {
139 active_suite = it;
140 } else if (it->kind == PCUT_KIND_SETUP) {
141 if (active_suite != NULL) {
142 active_suite->suite.setup = it->setup.func;
143 }
144 it->kind = PCUT_KIND_SKIP;
145 } else if (it->kind == PCUT_KIND_TEARDOWN) {
146 if (active_suite != NULL) {
147 active_suite->suite.teardown = it->setup.func;
148 }
149 it->kind = PCUT_KIND_SKIP;
150 } else {
151 /* Not interesting right now. */
152 }
153 }
154}
155
156/** The main function of PCUT.
157 *
158 * This function is expected to be called as the only function in
159 * normal main().
160 *
161 * @param last Pointer to the last item defined by PCUT_TEST macros.
162 * @param argc Original argc of the program.
163 * @param argv Original argv of the program.
164 * @return Program exit code.
165 */
166int pcut_main(pcut_item_t *last, int argc, char *argv[]) {
167 pcut_item_t *items = pcut_fix_list_get_real_head(last);
168
169 int run_only_suite = -1;
170 int run_only_test = -1;
171
172 pcut_report_register_handler(&pcut_report_tap);
173
174 if (argc > 1) {
175 int i;
176 for (i = 1; i < argc; i++) {
177 pcut_is_arg_with_number(argv[i], "-s", &run_only_suite);
178 pcut_is_arg_with_number(argv[i], "-t", &run_only_test);
179 if (pcut_str_equals(argv[i], "-l")) {
180 pcut_print_tests(items);
181 return 0;
182 }
183 if (pcut_str_equals(argv[i], "-x")) {
184 pcut_report_register_handler(&pcut_report_xml);
185 }
186#ifndef PCUT_NO_LONG_JUMP
187 if (pcut_str_equals(argv[i], "-u")) {
188 pcut_run_mode = PCUT_RUN_MODE_SINGLE;
189 }
190#endif
191 }
192 }
193
194 setvbuf(stdout, NULL, _IONBF, 0);
195 set_setup_teardown_callbacks(items);
196
197 PCUT_DEBUG("run_only_suite = %d run_only_test = %d", run_only_suite, run_only_test);
198
199 if ((run_only_suite >= 0) && (run_only_test >= 0)) {
200 printf("Specify either -s or -t!\n");
201 return 1;
202 }
203
204 if (run_only_suite > 0) {
205 pcut_item_t *suite = pcut_find_by_id(items, run_only_suite);
206 if (suite == NULL) {
207 printf("Suite not found, aborting!\n");
208 return 2;
209 }
210 if (suite->kind != PCUT_KIND_TESTSUITE) {
211 printf("Invalid suite id!\n");
212 return 3;
213 }
214
215 run_suite(suite, NULL, argv[0]);
216 return 0;
217 }
218
219 if (run_only_test > 0) {
220 pcut_item_t *test = pcut_find_by_id(items, run_only_test);
221 if (test == NULL) {
222 printf("Test not found, aborting!\n");
223 return 2;
224 }
225 if (test->kind != PCUT_KIND_TEST) {
226 printf("Invalid test id!\n");
227 return 3;
228 }
229
230 int rc;
231 if (pcut_run_mode == PCUT_RUN_MODE_SINGLE) {
232 rc = pcut_run_test_single(test);
233 } else {
234 rc = pcut_run_test_forked(test);
235 }
236
237 return rc;
238 }
239
240 /* Otherwise, run the whole thing. */
241 pcut_report_init(items);
242
243 pcut_item_t *it = items;
244 while (it != NULL) {
245 if (it->kind == PCUT_KIND_TESTSUITE) {
246 pcut_item_t *tmp;
247 run_suite(it, &tmp, argv[0]);
248 it = tmp;
249 } else {
250 it = pcut_get_real_next(it);
251 }
252 }
253
254 pcut_report_done();
255
256 return 0;
257}
Note: See TracBrowser for help on using the repository browser.