Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/atf/atf-c++/tests.cpp
39507 views
1
// Copyright (c) 2007 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
#include "atf-c++/tests.hpp"
27
28
#if defined(HAVE_CONFIG_H)
29
#include "config.h"
30
#endif
31
32
extern "C" {
33
#include <sys/types.h>
34
#include <sys/stat.h>
35
#include <sys/time.h>
36
#include <sys/wait.h>
37
#include <signal.h>
38
#include <unistd.h>
39
}
40
41
#include <algorithm>
42
#include <cctype>
43
#include <cerrno>
44
#include <cstdlib>
45
#include <cstring>
46
#include <fstream>
47
#include <iostream>
48
#include <map>
49
#include <memory>
50
#include <sstream>
51
#include <stdexcept>
52
#include <vector>
53
54
extern "C" {
55
#include "atf-c/error.h"
56
#include "atf-c/tc.h"
57
#include "atf-c/utils.h"
58
}
59
60
#include "atf-c++/detail/application.hpp"
61
#include "atf-c++/detail/auto_array.hpp"
62
#include "atf-c++/detail/env.hpp"
63
#include "atf-c++/detail/exceptions.hpp"
64
#include "atf-c++/detail/fs.hpp"
65
#include "atf-c++/detail/sanity.hpp"
66
#include "atf-c++/detail/text.hpp"
67
68
#if defined(HAVE_GNU_GETOPT)
69
# define GETOPT_POSIX "+"
70
#else
71
# define GETOPT_POSIX ""
72
#endif
73
74
namespace impl = atf::tests;
75
namespace detail = atf::tests::detail;
76
#define IMPL_NAME "atf::tests"
77
78
using atf::application::usage_error;
79
80
// ------------------------------------------------------------------------
81
// The "atf_tp_writer" class.
82
// ------------------------------------------------------------------------
83
84
detail::atf_tp_writer::atf_tp_writer(std::ostream& os) :
85
m_os(os),
86
m_is_first(true)
87
{
88
m_os << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
89
}
90
91
void
92
detail::atf_tp_writer::start_tc(const std::string& ident)
93
{
94
if (!m_is_first)
95
m_os << "\n";
96
m_os << "ident: " << ident << "\n";
97
m_os.flush();
98
}
99
100
void
101
detail::atf_tp_writer::end_tc(void)
102
{
103
if (m_is_first)
104
m_is_first = false;
105
}
106
107
void
108
detail::atf_tp_writer::tc_meta_data(const std::string& name,
109
const std::string& value)
110
{
111
PRE(name != "ident");
112
m_os << name << ": " << value << "\n";
113
m_os.flush();
114
}
115
116
// ------------------------------------------------------------------------
117
// Free helper functions.
118
// ------------------------------------------------------------------------
119
120
std::string Program_Name;
121
122
static void
123
set_program_name(const char* argv0)
124
{
125
const std::string program_name = atf::fs::path(argv0).leaf_name();
126
// Libtool workaround: if running from within the source tree (binaries
127
// that are not installed yet), skip the "lt-" prefix added to files in
128
// the ".libs" directory to show the real (not temporary) name.
129
if (program_name.substr(0, 3) == "lt-")
130
Program_Name = program_name.substr(3);
131
else
132
Program_Name = program_name;
133
}
134
135
bool
136
detail::match(const std::string& regexp, const std::string& str)
137
{
138
return atf::text::match(str, regexp);
139
}
140
141
// ------------------------------------------------------------------------
142
// The "tc" class.
143
// ------------------------------------------------------------------------
144
145
static std::map< atf_tc_t*, impl::tc* > wraps;
146
static std::map< const atf_tc_t*, const impl::tc* > cwraps;
147
148
struct impl::tc_impl {
149
private:
150
// Non-copyable.
151
tc_impl(const tc_impl&);
152
tc_impl& operator=(const tc_impl&);
153
154
public:
155
std::string m_ident;
156
atf_tc_t m_tc;
157
bool m_has_cleanup;
158
159
tc_impl(const std::string& ident, const bool has_cleanup) :
160
m_ident(ident),
161
m_has_cleanup(has_cleanup)
162
{
163
}
164
165
static void
166
wrap_head(atf_tc_t *tc)
167
{
168
std::map< atf_tc_t*, impl::tc* >::iterator iter = wraps.find(tc);
169
INV(iter != wraps.end());
170
(*iter).second->head();
171
}
172
173
static void
174
wrap_body(const atf_tc_t *tc)
175
{
176
std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
177
cwraps.find(tc);
178
INV(iter != cwraps.end());
179
(*iter).second->body();
180
}
181
182
static void
183
wrap_cleanup(const atf_tc_t *tc)
184
{
185
std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
186
cwraps.find(tc);
187
INV(iter != cwraps.end());
188
(*iter).second->cleanup();
189
}
190
};
191
192
impl::tc::tc(const std::string& ident, const bool has_cleanup) :
193
pimpl(new tc_impl(ident, has_cleanup))
194
{
195
}
196
197
impl::tc::~tc(void)
198
{
199
cwraps.erase(&pimpl->m_tc);
200
wraps.erase(&pimpl->m_tc);
201
202
atf_tc_fini(&pimpl->m_tc);
203
}
204
205
void
206
impl::tc::init(const vars_map& config)
207
{
208
atf_error_t err;
209
210
auto_array< const char * > array(new const char*[(config.size() * 2) + 1]);
211
const char **ptr = array.get();
212
for (vars_map::const_iterator iter = config.begin();
213
iter != config.end(); iter++) {
214
*ptr = (*iter).first.c_str();
215
*(ptr + 1) = (*iter).second.c_str();
216
ptr += 2;
217
}
218
*ptr = NULL;
219
220
wraps[&pimpl->m_tc] = this;
221
cwraps[&pimpl->m_tc] = this;
222
223
err = atf_tc_init(&pimpl->m_tc, pimpl->m_ident.c_str(), pimpl->wrap_head,
224
pimpl->wrap_body, pimpl->m_has_cleanup ? pimpl->wrap_cleanup : NULL,
225
array.get());
226
if (atf_is_error(err))
227
throw_atf_error(err);
228
}
229
230
bool
231
impl::tc::has_config_var(const std::string& var)
232
const
233
{
234
return atf_tc_has_config_var(&pimpl->m_tc, var.c_str());
235
}
236
237
bool
238
impl::tc::has_md_var(const std::string& var)
239
const
240
{
241
return atf_tc_has_md_var(&pimpl->m_tc, var.c_str());
242
}
243
244
const std::string
245
impl::tc::get_config_var(const std::string& var)
246
const
247
{
248
return atf_tc_get_config_var(&pimpl->m_tc, var.c_str());
249
}
250
251
const std::string
252
impl::tc::get_config_var(const std::string& var, const std::string& defval)
253
const
254
{
255
return atf_tc_get_config_var_wd(&pimpl->m_tc, var.c_str(), defval.c_str());
256
}
257
258
const std::string
259
impl::tc::get_md_var(const std::string& var)
260
const
261
{
262
return atf_tc_get_md_var(&pimpl->m_tc, var.c_str());
263
}
264
265
const impl::vars_map
266
impl::tc::get_md_vars(void)
267
const
268
{
269
vars_map vars;
270
271
char **array = atf_tc_get_md_vars(&pimpl->m_tc);
272
try {
273
char **ptr;
274
for (ptr = array; *ptr != NULL; ptr += 2)
275
vars[*ptr] = *(ptr + 1);
276
} catch (...) {
277
atf_utils_free_charpp(array);
278
throw;
279
}
280
281
return vars;
282
}
283
284
void
285
impl::tc::set_md_var(const std::string& var, const std::string& val)
286
{
287
atf_error_t err = atf_tc_set_md_var(&pimpl->m_tc, var.c_str(), val.c_str());
288
if (atf_is_error(err))
289
throw_atf_error(err);
290
}
291
292
void
293
impl::tc::run(const std::string& resfile)
294
const
295
{
296
atf_error_t err = atf_tc_run(&pimpl->m_tc, resfile.c_str());
297
if (atf_is_error(err))
298
throw_atf_error(err);
299
}
300
301
void
302
impl::tc::run_cleanup(void)
303
const
304
{
305
atf_error_t err = atf_tc_cleanup(&pimpl->m_tc);
306
if (atf_is_error(err))
307
throw_atf_error(err);
308
}
309
310
void
311
impl::tc::head(void)
312
{
313
}
314
315
void
316
impl::tc::cleanup(void)
317
const
318
{
319
}
320
321
void
322
impl::tc::require_kmod(const std::string& kmod)
323
const
324
{
325
atf_tc_require_kmod(kmod.c_str());
326
}
327
328
void
329
impl::tc::require_prog(const std::string& prog)
330
const
331
{
332
atf_tc_require_prog(prog.c_str());
333
}
334
335
void
336
impl::tc::pass(void)
337
{
338
atf_tc_pass();
339
}
340
341
void
342
impl::tc::fail(const std::string& reason)
343
{
344
atf_tc_fail("%s", reason.c_str());
345
}
346
347
void
348
impl::tc::fail_nonfatal(const std::string& reason)
349
{
350
atf_tc_fail_nonfatal("%s", reason.c_str());
351
}
352
353
void
354
impl::tc::skip(const std::string& reason)
355
{
356
atf_tc_skip("%s", reason.c_str());
357
}
358
359
void
360
impl::tc::check_errno(const char* file, const int line, const int exp_errno,
361
const char* expr_str, const bool result)
362
{
363
atf_tc_check_errno(file, line, exp_errno, expr_str, result);
364
}
365
366
void
367
impl::tc::require_errno(const char* file, const int line, const int exp_errno,
368
const char* expr_str, const bool result)
369
{
370
atf_tc_require_errno(file, line, exp_errno, expr_str, result);
371
}
372
373
void
374
impl::tc::expect_pass(void)
375
{
376
atf_tc_expect_pass();
377
}
378
379
void
380
impl::tc::expect_fail(const std::string& reason)
381
{
382
atf_tc_expect_fail("%s", reason.c_str());
383
}
384
385
void
386
impl::tc::expect_exit(const int exitcode, const std::string& reason)
387
{
388
atf_tc_expect_exit(exitcode, "%s", reason.c_str());
389
}
390
391
void
392
impl::tc::expect_signal(const int signo, const std::string& reason)
393
{
394
atf_tc_expect_signal(signo, "%s", reason.c_str());
395
}
396
397
void
398
impl::tc::expect_death(const std::string& reason)
399
{
400
atf_tc_expect_death("%s", reason.c_str());
401
}
402
403
void
404
impl::tc::expect_timeout(const std::string& reason)
405
{
406
atf_tc_expect_timeout("%s", reason.c_str());
407
}
408
409
// ------------------------------------------------------------------------
410
// Test program main code.
411
// ------------------------------------------------------------------------
412
413
namespace {
414
415
typedef std::vector< impl::tc * > tc_vector;
416
417
enum tc_part { BODY, CLEANUP };
418
419
static void
420
parse_vflag(const std::string& str, atf::tests::vars_map& vars)
421
{
422
if (str.empty())
423
throw std::runtime_error("-v requires a non-empty argument");
424
425
std::vector< std::string > ws = atf::text::split(str, "=");
426
if (ws.size() == 1 && str[str.length() - 1] == '=') {
427
vars[ws[0]] = "";
428
} else {
429
if (ws.size() != 2)
430
throw std::runtime_error("-v requires an argument of the form "
431
"var=value");
432
433
vars[ws[0]] = ws[1];
434
}
435
}
436
437
static atf::fs::path
438
handle_srcdir(const char* argv0, const std::string& srcdir_arg)
439
{
440
atf::fs::path srcdir(".");
441
442
if (srcdir_arg.empty()) {
443
srcdir = atf::fs::path(argv0).branch_path();
444
if (srcdir.leaf_name() == ".libs")
445
srcdir = srcdir.branch_path();
446
} else
447
srcdir = atf::fs::path(srcdir_arg);
448
449
if (!atf::fs::exists(srcdir / Program_Name))
450
throw usage_error("Cannot find the test program in the source "
451
"directory `%s'", srcdir.c_str());
452
453
if (!srcdir.is_absolute())
454
srcdir = srcdir.to_absolute();
455
456
return srcdir;
457
}
458
459
static void
460
init_tcs(void (*add_tcs)(tc_vector&), tc_vector& tcs,
461
const atf::tests::vars_map& vars)
462
{
463
add_tcs(tcs);
464
for (tc_vector::iterator iter = tcs.begin(); iter != tcs.end(); iter++) {
465
impl::tc* tc = *iter;
466
467
tc->init(vars);
468
}
469
}
470
471
static int
472
list_tcs(const tc_vector& tcs)
473
{
474
detail::atf_tp_writer writer(std::cout);
475
476
for (tc_vector::const_iterator iter = tcs.begin();
477
iter != tcs.end(); iter++) {
478
const impl::vars_map vars = (*iter)->get_md_vars();
479
480
{
481
impl::vars_map::const_iterator iter2 = vars.find("ident");
482
INV(iter2 != vars.end());
483
writer.start_tc((*iter2).second);
484
}
485
486
for (impl::vars_map::const_iterator iter2 = vars.begin();
487
iter2 != vars.end(); iter2++) {
488
const std::string& key = (*iter2).first;
489
if (key != "ident")
490
writer.tc_meta_data(key, (*iter2).second);
491
}
492
493
writer.end_tc();
494
}
495
496
return EXIT_SUCCESS;
497
}
498
499
static impl::tc*
500
find_tc(tc_vector tcs, const std::string& name)
501
{
502
std::vector< std::string > ids;
503
for (tc_vector::iterator iter = tcs.begin();
504
iter != tcs.end(); iter++) {
505
impl::tc* tc = *iter;
506
507
if (tc->get_md_var("ident") == name)
508
return tc;
509
}
510
throw usage_error("Unknown test case `%s'", name.c_str());
511
}
512
513
static std::pair< std::string, tc_part >
514
process_tcarg(const std::string& tcarg)
515
{
516
const std::string::size_type pos = tcarg.find(':');
517
if (pos == std::string::npos) {
518
return std::make_pair(tcarg, BODY);
519
} else {
520
const std::string tcname = tcarg.substr(0, pos);
521
522
const std::string partname = tcarg.substr(pos + 1);
523
if (partname == "body")
524
return std::make_pair(tcname, BODY);
525
else if (partname == "cleanup")
526
return std::make_pair(tcname, CLEANUP);
527
else {
528
throw usage_error("Invalid test case part `%s'", partname.c_str());
529
}
530
}
531
}
532
533
static int
534
run_tc(tc_vector& tcs, const std::string& tcarg, const atf::fs::path& resfile)
535
{
536
const std::pair< std::string, tc_part > fields = process_tcarg(tcarg);
537
538
impl::tc* tc = find_tc(tcs, fields.first);
539
540
if (!atf::env::has("__RUNNING_INSIDE_ATF_RUN") || atf::env::get(
541
"__RUNNING_INSIDE_ATF_RUN") != "internal-yes-value")
542
{
543
std::cerr << Program_Name << ": WARNING: Running test cases outside "
544
"of kyua(1) is unsupported\n";
545
std::cerr << Program_Name << ": WARNING: No isolation nor timeout "
546
"control is being applied; you may get unexpected failures; see "
547
"atf-test-case(4)\n";
548
}
549
550
switch (fields.second) {
551
case BODY:
552
tc->run(resfile.str());
553
break;
554
case CLEANUP:
555
tc->run_cleanup();
556
break;
557
default:
558
UNREACHABLE;
559
}
560
return EXIT_SUCCESS;
561
}
562
563
static int
564
safe_main(int argc, char** argv, void (*add_tcs)(tc_vector&))
565
{
566
const char* argv0 = argv[0];
567
568
bool lflag = false;
569
atf::fs::path resfile("/dev/stdout");
570
std::string srcdir_arg;
571
atf::tests::vars_map vars;
572
573
int ch;
574
int old_opterr;
575
576
old_opterr = opterr;
577
::opterr = 0;
578
while ((ch = ::getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {
579
switch (ch) {
580
case 'l':
581
lflag = true;
582
break;
583
584
case 'r':
585
resfile = atf::fs::path(::optarg);
586
break;
587
588
case 's':
589
srcdir_arg = ::optarg;
590
break;
591
592
case 'v':
593
parse_vflag(::optarg, vars);
594
break;
595
596
case ':':
597
throw usage_error("Option -%c requires an argument.", ::optopt);
598
break;
599
600
case '?':
601
default:
602
throw usage_error("Unknown option -%c.", ::optopt);
603
}
604
}
605
argc -= optind;
606
argv += optind;
607
608
// Clear getopt state just in case the test wants to use it.
609
::opterr = old_opterr;
610
::optind = 1;
611
#if defined(HAVE_OPTRESET)
612
::optreset = 1;
613
#endif
614
615
vars["srcdir"] = handle_srcdir(argv0, srcdir_arg).str();
616
617
int errcode;
618
619
tc_vector tcs;
620
if (lflag) {
621
if (argc > 0)
622
throw usage_error("Cannot provide test case names with -l");
623
624
init_tcs(add_tcs, tcs, vars);
625
errcode = list_tcs(tcs);
626
} else {
627
if (argc == 0)
628
throw usage_error("Must provide a test case name");
629
else if (argc > 1)
630
throw usage_error("Cannot provide more than one test case name");
631
INV(argc == 1);
632
633
init_tcs(add_tcs, tcs, vars);
634
errcode = run_tc(tcs, argv[0], resfile);
635
}
636
for (tc_vector::iterator iter = tcs.begin(); iter != tcs.end(); iter++) {
637
impl::tc* tc = *iter;
638
639
delete tc;
640
}
641
642
return errcode;
643
}
644
645
} // anonymous namespace
646
647
namespace atf {
648
namespace tests {
649
int run_tp(int, char**, void (*)(tc_vector&));
650
}
651
}
652
653
int
654
impl::run_tp(int argc, char** argv, void (*add_tcs)(tc_vector&))
655
{
656
try {
657
set_program_name(argv[0]);
658
return ::safe_main(argc, argv, add_tcs);
659
} catch (const usage_error& e) {
660
std::cerr
661
<< Program_Name << ": ERROR: " << e.what() << '\n'
662
<< Program_Name << ": See atf-test-program(1) for usage details.\n";
663
return EXIT_FAILURE;
664
}
665
}
666
667