Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/atf/atf-c/detail/tp_main.c
39507 views
1
/* Copyright (c) 2008 The NetBSD Foundation, Inc.
2
* All rights reserved.
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
12
*
13
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17
* IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25
26
#if defined(HAVE_CONFIG_H)
27
#include "config.h"
28
#endif
29
30
#include <ctype.h>
31
#include <stdarg.h>
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#include <unistd.h>
36
37
#include "atf-c/detail/dynstr.h"
38
#include "atf-c/detail/env.h"
39
#include "atf-c/detail/fs.h"
40
#include "atf-c/detail/map.h"
41
#include "atf-c/detail/sanity.h"
42
#include "atf-c/error.h"
43
#include "atf-c/tc.h"
44
#include "atf-c/tp.h"
45
#include "atf-c/utils.h"
46
47
#if defined(HAVE_GNU_GETOPT)
48
# define GETOPT_POSIX "+"
49
#else
50
# define GETOPT_POSIX ""
51
#endif
52
53
static const char *progname = NULL;
54
55
/* This prototype is provided by macros.h during instantiation of the test
56
* program, so it can be kept private. Don't know if that's the best idea
57
* though. */
58
int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *));
59
60
enum tc_part {
61
BODY,
62
CLEANUP,
63
};
64
65
/* ---------------------------------------------------------------------
66
* The "usage" and "user" error types.
67
* --------------------------------------------------------------------- */
68
69
#define FREE_FORM_ERROR(name) \
70
struct name ## _error_data { \
71
char m_what[2048]; \
72
}; \
73
\
74
static \
75
void \
76
name ## _format(const atf_error_t err, char *buf, size_t buflen) \
77
{ \
78
const struct name ## _error_data *data; \
79
\
80
PRE(atf_error_is(err, #name)); \
81
\
82
data = atf_error_data(err); \
83
snprintf(buf, buflen, "%s", data->m_what); \
84
} \
85
\
86
static \
87
atf_error_t \
88
name ## _error(const char *fmt, ...) \
89
{ \
90
atf_error_t err; \
91
struct name ## _error_data data; \
92
va_list ap; \
93
\
94
va_start(ap, fmt); \
95
vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \
96
va_end(ap); \
97
\
98
err = atf_error_new(#name, &data, sizeof(data), name ## _format); \
99
\
100
return err; \
101
}
102
103
FREE_FORM_ERROR(usage);
104
FREE_FORM_ERROR(user);
105
106
/* ---------------------------------------------------------------------
107
* Printing functions.
108
* --------------------------------------------------------------------- */
109
110
static
111
void
112
print_error(const atf_error_t err)
113
{
114
char buf[4096];
115
116
PRE(atf_is_error(err));
117
118
atf_error_format(err, buf, sizeof(buf));
119
fprintf(stderr, "%s: ERROR: %s\n", progname, buf);
120
121
if (atf_error_is(err, "usage"))
122
fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n",
123
progname);
124
}
125
126
static
127
void
128
print_warning(const char *message)
129
{
130
fprintf(stderr, "%s: WARNING: %s\n", progname, message);
131
}
132
133
/* ---------------------------------------------------------------------
134
* Options handling.
135
* --------------------------------------------------------------------- */
136
137
struct params {
138
bool m_do_list;
139
atf_fs_path_t m_srcdir;
140
char *m_tcname;
141
enum tc_part m_tcpart;
142
atf_fs_path_t m_resfile;
143
atf_map_t m_config;
144
};
145
146
static
147
atf_error_t
148
argv0_to_dir(const char *argv0, atf_fs_path_t *dir)
149
{
150
atf_error_t err;
151
atf_fs_path_t temp;
152
153
err = atf_fs_path_init_fmt(&temp, "%s", argv0);
154
if (atf_is_error(err))
155
goto out;
156
157
err = atf_fs_path_branch_path(&temp, dir);
158
159
atf_fs_path_fini(&temp);
160
out:
161
return err;
162
}
163
164
static
165
atf_error_t
166
params_init(struct params *p, const char *argv0)
167
{
168
atf_error_t err;
169
170
p->m_do_list = false;
171
p->m_tcname = NULL;
172
p->m_tcpart = BODY;
173
174
err = argv0_to_dir(argv0, &p->m_srcdir);
175
if (atf_is_error(err))
176
return err;
177
178
err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout");
179
if (atf_is_error(err)) {
180
atf_fs_path_fini(&p->m_srcdir);
181
return err;
182
}
183
184
err = atf_map_init(&p->m_config);
185
if (atf_is_error(err)) {
186
atf_fs_path_fini(&p->m_resfile);
187
atf_fs_path_fini(&p->m_srcdir);
188
return err;
189
}
190
191
return err;
192
}
193
194
static
195
void
196
params_fini(struct params *p)
197
{
198
atf_map_fini(&p->m_config);
199
atf_fs_path_fini(&p->m_resfile);
200
atf_fs_path_fini(&p->m_srcdir);
201
if (p->m_tcname != NULL)
202
free(p->m_tcname);
203
}
204
205
static
206
atf_error_t
207
parse_vflag(char *arg, atf_map_t *config)
208
{
209
atf_error_t err;
210
char *split;
211
212
split = strchr(arg, '=');
213
if (split == NULL) {
214
err = usage_error("-v requires an argument of the form var=value");
215
goto out;
216
}
217
218
*split = '\0';
219
split++;
220
221
err = atf_map_insert(config, arg, split, false);
222
223
out:
224
return err;
225
}
226
227
static
228
atf_error_t
229
replace_path_param(atf_fs_path_t *param, const char *value)
230
{
231
atf_error_t err;
232
atf_fs_path_t temp;
233
234
err = atf_fs_path_init_fmt(&temp, "%s", value);
235
if (!atf_is_error(err)) {
236
atf_fs_path_fini(param);
237
*param = temp;
238
}
239
240
return err;
241
}
242
243
/* ---------------------------------------------------------------------
244
* Test case listing.
245
* --------------------------------------------------------------------- */
246
247
static
248
void
249
list_tcs(const atf_tp_t *tp)
250
{
251
const atf_tc_t *const *tcs;
252
const atf_tc_t *const *tcsptr;
253
254
printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n");
255
256
tcs = atf_tp_get_tcs(tp);
257
INV(tcs != NULL); /* Should be checked. */
258
for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) {
259
const atf_tc_t *tc = *tcsptr;
260
char **vars = atf_tc_get_md_vars(tc);
261
char **ptr;
262
263
INV(vars != NULL); /* Should be checked. */
264
265
if (tcsptr != tcs) /* Not first. */
266
printf("\n");
267
268
for (ptr = vars; *ptr != NULL; ptr += 2) {
269
if (strcmp(*ptr, "ident") == 0) {
270
printf("ident: %s\n", *(ptr + 1));
271
break;
272
}
273
}
274
275
for (ptr = vars; *ptr != NULL; ptr += 2) {
276
if (strcmp(*ptr, "ident") != 0) {
277
printf("%s: %s\n", *ptr, *(ptr + 1));
278
}
279
}
280
281
atf_utils_free_charpp(vars);
282
}
283
}
284
285
/* ---------------------------------------------------------------------
286
* Main.
287
* --------------------------------------------------------------------- */
288
289
static
290
atf_error_t
291
handle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart)
292
{
293
atf_error_t err;
294
295
err = atf_no_error();
296
297
*tcname = strdup(tcarg);
298
if (*tcname == NULL) {
299
err = atf_no_memory_error();
300
goto out;
301
}
302
303
char *delim = strchr(*tcname, ':');
304
if (delim != NULL) {
305
*delim = '\0';
306
307
delim++;
308
if (strcmp(delim, "body") == 0) {
309
*tcpart = BODY;
310
} else if (strcmp(delim, "cleanup") == 0) {
311
*tcpart = CLEANUP;
312
} else {
313
err = usage_error("Invalid test case part `%s'", delim);
314
goto out;
315
}
316
}
317
318
out:
319
return err;
320
}
321
322
static
323
atf_error_t
324
process_params(int argc, char **argv, struct params *p)
325
{
326
atf_error_t err;
327
int ch;
328
int old_opterr;
329
330
err = params_init(p, argv[0]);
331
if (atf_is_error(err))
332
goto out;
333
334
old_opterr = opterr;
335
opterr = 0;
336
while (!atf_is_error(err) &&
337
(ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {
338
switch (ch) {
339
case 'l':
340
p->m_do_list = true;
341
break;
342
343
case 'r':
344
err = replace_path_param(&p->m_resfile, optarg);
345
break;
346
347
case 's':
348
err = replace_path_param(&p->m_srcdir, optarg);
349
break;
350
351
case 'v':
352
err = parse_vflag(optarg, &p->m_config);
353
break;
354
355
case ':':
356
err = usage_error("Option -%c requires an argument.", optopt);
357
break;
358
359
case '?':
360
default:
361
err = usage_error("Unknown option -%c.", optopt);
362
}
363
}
364
argc -= optind;
365
argv += optind;
366
367
/* Clear getopt state just in case the test wants to use it. */
368
opterr = old_opterr;
369
optind = 1;
370
#if defined(HAVE_OPTRESET)
371
optreset = 1;
372
#endif
373
374
if (!atf_is_error(err)) {
375
if (p->m_do_list) {
376
if (argc > 0)
377
err = usage_error("Cannot provide test case names with -l");
378
} else {
379
if (argc == 0)
380
err = usage_error("Must provide a test case name");
381
else if (argc == 1)
382
err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart);
383
else if (argc > 1) {
384
err = usage_error("Cannot provide more than one test case "
385
"name");
386
}
387
}
388
}
389
390
if (atf_is_error(err))
391
params_fini(p);
392
393
out:
394
return err;
395
}
396
397
static
398
atf_error_t
399
srcdir_strip_libtool(atf_fs_path_t *srcdir)
400
{
401
atf_error_t err;
402
atf_fs_path_t parent;
403
404
err = atf_fs_path_branch_path(srcdir, &parent);
405
if (atf_is_error(err))
406
goto out;
407
408
atf_fs_path_fini(srcdir);
409
*srcdir = parent;
410
411
INV(!atf_is_error(err));
412
out:
413
return err;
414
}
415
416
static
417
atf_error_t
418
handle_srcdir(struct params *p)
419
{
420
atf_error_t err;
421
atf_dynstr_t leafname;
422
atf_fs_path_t exe, srcdir;
423
bool b;
424
425
err = atf_fs_path_copy(&srcdir, &p->m_srcdir);
426
if (atf_is_error(err))
427
goto out;
428
429
if (!atf_fs_path_is_absolute(&srcdir)) {
430
atf_fs_path_t srcdirabs;
431
432
err = atf_fs_path_to_absolute(&srcdir, &srcdirabs);
433
if (atf_is_error(err))
434
goto out_srcdir;
435
436
atf_fs_path_fini(&srcdir);
437
srcdir = srcdirabs;
438
}
439
440
err = atf_fs_path_leaf_name(&srcdir, &leafname);
441
if (atf_is_error(err))
442
goto out_srcdir;
443
else {
444
const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs");
445
atf_dynstr_fini(&leafname);
446
447
if (libs) {
448
err = srcdir_strip_libtool(&srcdir);
449
if (atf_is_error(err))
450
goto out;
451
}
452
}
453
454
err = atf_fs_path_copy(&exe, &srcdir);
455
if (atf_is_error(err))
456
goto out_srcdir;
457
458
err = atf_fs_path_append_fmt(&exe, "%s", progname);
459
if (atf_is_error(err))
460
goto out_exe;
461
462
err = atf_fs_exists(&exe, &b);
463
if (!atf_is_error(err)) {
464
if (b) {
465
err = atf_map_insert(&p->m_config, "srcdir",
466
strdup(atf_fs_path_cstring(&srcdir)), true);
467
} else {
468
err = user_error("Cannot find the test program in the source "
469
"directory `%s'", atf_fs_path_cstring(&srcdir));
470
}
471
}
472
473
out_exe:
474
atf_fs_path_fini(&exe);
475
out_srcdir:
476
atf_fs_path_fini(&srcdir);
477
out:
478
return err;
479
}
480
481
static
482
atf_error_t
483
run_tc(const atf_tp_t *tp, struct params *p, int *exitcode)
484
{
485
atf_error_t err;
486
487
err = atf_no_error();
488
489
if (!atf_tp_has_tc(tp, p->m_tcname)) {
490
err = usage_error("Unknown test case `%s'", p->m_tcname);
491
goto out;
492
}
493
494
if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get(
495
"__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0)
496
{
497
print_warning("Running test cases outside of kyua(1) is unsupported");
498
print_warning("No isolation nor timeout control is being applied; you "
499
"may get unexpected failures; see atf-test-case(4)");
500
}
501
502
switch (p->m_tcpart) {
503
case BODY:
504
err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile));
505
if (atf_is_error(err)) {
506
/* TODO: Handle error */
507
*exitcode = EXIT_FAILURE;
508
atf_error_free(err);
509
} else {
510
*exitcode = EXIT_SUCCESS;
511
}
512
513
break;
514
515
case CLEANUP:
516
err = atf_tp_cleanup(tp, p->m_tcname);
517
if (atf_is_error(err)) {
518
/* TODO: Handle error */
519
*exitcode = EXIT_FAILURE;
520
atf_error_free(err);
521
} else {
522
*exitcode = EXIT_SUCCESS;
523
}
524
525
break;
526
527
default:
528
UNREACHABLE;
529
}
530
531
INV(!atf_is_error(err));
532
out:
533
return err;
534
}
535
536
static
537
atf_error_t
538
controlled_main(int argc, char **argv,
539
atf_error_t (*add_tcs_hook)(atf_tp_t *),
540
int *exitcode)
541
{
542
atf_error_t err;
543
struct params p;
544
atf_tp_t tp;
545
char **raw_config;
546
547
err = process_params(argc, argv, &p);
548
if (atf_is_error(err))
549
goto out;
550
551
err = handle_srcdir(&p);
552
if (atf_is_error(err))
553
goto out_p;
554
555
raw_config = atf_map_to_charpp(&p.m_config);
556
if (raw_config == NULL) {
557
err = atf_no_memory_error();
558
goto out_p;
559
}
560
err = atf_tp_init(&tp, (const char* const*)raw_config);
561
atf_utils_free_charpp(raw_config);
562
if (atf_is_error(err))
563
goto out_p;
564
565
err = add_tcs_hook(&tp);
566
if (atf_is_error(err))
567
goto out_tp;
568
569
if (p.m_do_list) {
570
list_tcs(&tp);
571
INV(!atf_is_error(err));
572
*exitcode = EXIT_SUCCESS;
573
} else {
574
err = run_tc(&tp, &p, exitcode);
575
}
576
577
out_tp:
578
atf_tp_fini(&tp);
579
out_p:
580
params_fini(&p);
581
out:
582
return err;
583
}
584
585
int
586
atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *))
587
{
588
atf_error_t err;
589
int exitcode;
590
591
progname = strrchr(argv[0], '/');
592
if (progname == NULL)
593
progname = argv[0];
594
else
595
progname++;
596
597
/* Libtool workaround: if running from within the source tree (binaries
598
* that are not installed yet), skip the "lt-" prefix added to files in
599
* the ".libs" directory to show the real (not temporary) name. */
600
if (strncmp(progname, "lt-", 3) == 0)
601
progname += 3;
602
603
exitcode = EXIT_FAILURE; /* Silence GCC warning. */
604
err = controlled_main(argc, argv, add_tcs_hook, &exitcode);
605
if (atf_is_error(err)) {
606
print_error(err);
607
atf_error_free(err);
608
exitcode = EXIT_FAILURE;
609
}
610
611
return exitcode;
612
}
613
614