xref: /netbsd-src/external/bsd/atf/dist/atf-sh/atf-check.cpp (revision ee43138c68eeee656501f419f250b4ed76e9d212)
1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2008 The NetBSD Foundation, Inc.
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 // 1. Redistributions of source code must retain the above copyright
11 //    notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 //    notice, this list of conditions and the following disclaimer in the
14 //    documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 
30 extern "C" {
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 
34 #include <limits.h>
35 #include <signal.h>
36 #include <unistd.h>
37 }
38 
39 #include <cerrno>
40 #include <cstdlib>
41 #include <cstring>
42 #include <fstream>
43 #include <ios>
44 #include <iostream>
45 #include <iterator>
46 #include <list>
47 #include <memory>
48 #include <utility>
49 
50 #include "atf-c++/check.hpp"
51 #include "atf-c++/config.hpp"
52 
53 #include "atf-c++/detail/application.hpp"
54 #include "atf-c++/detail/auto_array.hpp"
55 #include "atf-c++/detail/exceptions.hpp"
56 #include "atf-c++/detail/fs.hpp"
57 #include "atf-c++/detail/process.hpp"
58 #include "atf-c++/detail/sanity.hpp"
59 #include "atf-c++/detail/text.hpp"
60 
61 // ------------------------------------------------------------------------
62 // Auxiliary functions.
63 // ------------------------------------------------------------------------
64 
65 namespace {
66 
67 enum status_check_t {
68     sc_exit,
69     sc_ignore,
70     sc_signal,
71 };
72 
73 struct status_check {
74     status_check_t type;
75     bool negated;
76     int value;
77 
status_check__anon9a50e0e80111::status_check78     status_check(const status_check_t& p_type, const bool p_negated,
79                  const int p_value) :
80         type(p_type),
81         negated(p_negated),
82         value(p_value)
83     {
84     }
85 };
86 
87 enum output_check_t {
88     oc_ignore,
89     oc_inline,
90     oc_file,
91     oc_empty,
92     oc_match,
93     oc_save
94 };
95 
96 struct output_check {
97     output_check_t type;
98     bool negated;
99     std::string value;
100 
output_check__anon9a50e0e80111::output_check101     output_check(const output_check_t& p_type, const bool p_negated,
102                  const std::string& p_value) :
103         type(p_type),
104         negated(p_negated),
105         value(p_value)
106     {
107     }
108 };
109 
110 class temp_file : public std::ostream {
111     std::unique_ptr< atf::fs::path > m_path;
112     int m_fd;
113 
114 public:
temp_file(const atf::fs::path & p)115     temp_file(const atf::fs::path& p) :
116         std::ostream(NULL),
117         m_fd(-1)
118     {
119         atf::auto_array< char > buf(new char[p.str().length() + 1]);
120         std::strcpy(buf.get(), p.c_str());
121 
122         m_fd = ::mkstemp(buf.get());
123         if (m_fd == -1)
124             throw atf::system_error("atf_check::temp_file::temp_file(" +
125                                     p.str() + ")", "mkstemp(3) failed",
126                                     errno);
127 
128         m_path.reset(new atf::fs::path(buf.get()));
129     }
130 
~temp_file(void)131     ~temp_file(void)
132     {
133         close();
134         try {
135             remove(*m_path);
136         } catch (const atf::system_error&) {
137             // Ignore deletion errors.
138         }
139     }
140 
141     const atf::fs::path&
get_path(void) const142     get_path(void) const
143     {
144         return *m_path;
145     }
146 
147     void
write(const std::string & text)148     write(const std::string& text)
149     {
150         if (::write(m_fd, text.c_str(), text.size()) == -1)
151             throw atf::system_error("atf_check", "write(2) failed", errno);
152     }
153 
154     void
close(void)155     close(void)
156     {
157         if (m_fd != -1) {
158             flush();
159             ::close(m_fd);
160             m_fd = -1;
161         }
162     }
163 };
164 
165 } // anonymous namespace
166 
167 static int
parse_exit_code(const std::string & str)168 parse_exit_code(const std::string& str)
169 {
170     try {
171         const int value = atf::text::to_type< int >(str);
172         if (value < 0 || value > 255)
173             throw std::runtime_error("Unused reason");
174         return value;
175     } catch (const std::runtime_error&) {
176         throw atf::application::usage_error("Invalid exit code for -s option; "
177             "must be an integer in range 0-255");
178     }
179 }
180 
181 static struct name_number {
182     const char *name;
183     int signo;
184 } signal_names_to_numbers[] = {
185     { "hup", SIGHUP },
186     { "int", SIGINT },
187     { "quit", SIGQUIT },
188     { "trap", SIGTRAP },
189     { "abrt", SIGABRT },
190     { "kill", SIGKILL },
191     { "segv", SIGSEGV },
192     { "pipe", SIGPIPE },
193     { "alrm", SIGALRM },
194     { "term", SIGTERM },
195     { "usr1", SIGUSR1 },
196     { "usr2", SIGUSR2 },
197     { NULL, INT_MIN },
198 };
199 
200 static int
signal_name_to_number(const std::string & str)201 signal_name_to_number(const std::string& str)
202 {
203     struct name_number* iter = signal_names_to_numbers;
204     int signo = INT_MIN;
205     while (signo == INT_MIN && iter->name != NULL) {
206         if (str == iter->name || str == std::string("sig") + iter->name)
207             signo = iter->signo;
208         else
209             iter++;
210     }
211     return signo;
212 }
213 
214 static int
parse_signal(const std::string & str)215 parse_signal(const std::string& str)
216 {
217     const int signo = signal_name_to_number(str);
218     if (signo == INT_MIN) {
219         try {
220             return atf::text::to_type< int >(str);
221         } catch (std::runtime_error &e) {
222             throw atf::application::usage_error("Invalid signal name or number "
223                 "in -s option");
224         }
225     }
226     INV(signo != INT_MIN);
227     return signo;
228 }
229 
230 static status_check
parse_status_check_arg(const std::string & arg)231 parse_status_check_arg(const std::string& arg)
232 {
233     const std::string::size_type delimiter = arg.find(':');
234     bool negated = (arg.compare(0, 4, "not-") == 0);
235     const std::string action_str = arg.substr(0, delimiter);
236     const std::string action = negated ? action_str.substr(4) : action_str;
237     const std::string value_str = (
238         delimiter == std::string::npos ? "" : arg.substr(delimiter + 1));
239     int value;
240 
241     status_check_t type;
242     if (action == "eq") {
243         // Deprecated; use exit instead.  TODO: Remove after 0.10.
244         type = sc_exit;
245         if (negated)
246             throw atf::application::usage_error("Cannot negate eq checker");
247         negated = false;
248         value = parse_exit_code(value_str);
249     } else if (action == "exit") {
250         type = sc_exit;
251         if (value_str.empty())
252             value = INT_MIN;
253         else
254             value = parse_exit_code(value_str);
255     } else if (action == "ignore") {
256         if (negated)
257             throw atf::application::usage_error("Cannot negate ignore checker");
258         type = sc_ignore;
259         value = INT_MIN;
260     } else if (action == "ne") {
261         // Deprecated; use not-exit instead.  TODO: Remove after 0.10.
262         type = sc_exit;
263         if (negated)
264             throw atf::application::usage_error("Cannot negate ne checker");
265         negated = true;
266         value = parse_exit_code(value_str);
267     } else if (action == "signal") {
268         type = sc_signal;
269         if (value_str.empty())
270             value = INT_MIN;
271         else
272             value = parse_signal(value_str);
273     } else
274         throw atf::application::usage_error("Invalid status checker");
275 
276     return status_check(type, negated, value);
277 }
278 
279 static
280 output_check
parse_output_check_arg(const std::string & arg)281 parse_output_check_arg(const std::string& arg)
282 {
283     const std::string::size_type delimiter = arg.find(':');
284     const bool negated = (arg.compare(0, 4, "not-") == 0);
285     const std::string action_str = arg.substr(0, delimiter);
286     const std::string action = negated ? action_str.substr(4) : action_str;
287 
288     output_check_t type;
289     if (action == "empty")
290         type = oc_empty;
291     else if (action == "file")
292         type = oc_file;
293     else if (action == "ignore") {
294         if (negated)
295             throw atf::application::usage_error("Cannot negate ignore checker");
296         type = oc_ignore;
297     } else if (action == "inline")
298         type = oc_inline;
299     else if (action == "match")
300         type = oc_match;
301     else if (action == "save") {
302         if (negated)
303             throw atf::application::usage_error("Cannot negate save checker");
304         type = oc_save;
305     } else
306         throw atf::application::usage_error("Invalid output checker");
307 
308     return output_check(type, negated, arg.substr(delimiter + 1));
309 }
310 
311 static
312 std::string
flatten_argv(char * const * argv)313 flatten_argv(char* const* argv)
314 {
315     std::string cmdline;
316 
317     char* const* arg = &argv[0];
318     while (*arg != NULL) {
319         if (arg != &argv[0])
320             cmdline += ' ';
321 
322         cmdline += *arg;
323 
324         arg++;
325     }
326 
327     return cmdline;
328 }
329 
330 static
331 std::unique_ptr< atf::check::check_result >
execute(const char * const * argv)332 execute(const char* const* argv)
333 {
334     // TODO: This should go to stderr... but fixing it now may be hard as test
335     // cases out there might be relying on stderr being silent.
336     std::cout << "Executing command [ ";
337     for (int i = 0; argv[i] != NULL; ++i)
338         std::cout << argv[i] << " ";
339     std::cout << "]\n";
340     std::cout.flush();
341 
342     atf::process::argv_array argva(argv);
343     return atf::check::exec(argva);
344 }
345 
346 static
347 std::unique_ptr< atf::check::check_result >
execute_with_shell(char * const * argv)348 execute_with_shell(char* const* argv)
349 {
350     const std::string cmd = flatten_argv(argv);
351 
352     const char* sh_argv[4];
353     sh_argv[0] = atf::config::get("atf_shell").c_str();
354     sh_argv[1] = "-c";
355     sh_argv[2] = cmd.c_str();
356     sh_argv[3] = NULL;
357     return execute(sh_argv);
358 }
359 
360 static
361 void
open_error(const atf::fs::path & path)362 open_error(const atf::fs::path& path)
363 {
364     throw std::runtime_error("Failed to open " + path.str() + " "
365 	+ ::strerror(errno));
366 }
367 
368 
369 static
370 void
cat_file(const atf::fs::path & path)371 cat_file(const atf::fs::path& path)
372 {
373     std::ifstream stream(path.c_str());
374     if (!stream)
375 	open_error(path);
376 
377     stream >> std::noskipws;
378     std::istream_iterator< char > begin(stream), end;
379     std::ostream_iterator< char > out(std::cerr);
380     std::copy(begin, end, out);
381 
382     stream.close();
383 }
384 
385 static
386 bool
grep_file(const atf::fs::path & path,const std::string & regexp)387 grep_file(const atf::fs::path& path, const std::string& regexp)
388 {
389     std::ifstream stream(path.c_str());
390     if (!stream)
391 	open_error(path);
392 
393     bool found = false;
394 
395     std::string line;
396     while (!found && !std::getline(stream, line).fail()) {
397         if (atf::text::match(line, regexp))
398             found = true;
399     }
400 
401     stream.close();
402 
403     return found;
404 }
405 
406 static
407 bool
file_empty(const atf::fs::path & p)408 file_empty(const atf::fs::path& p)
409 {
410     atf::fs::file_info f(p);
411 
412     return (f.get_size() == 0);
413 }
414 
415 static bool
compare_files(const atf::fs::path & p1,const atf::fs::path & p2)416 compare_files(const atf::fs::path& p1, const atf::fs::path& p2)
417 {
418     bool equal = false;
419 
420     std::ifstream f1(p1.c_str());
421     if (!f1)
422 	open_error(p1);
423 
424     std::ifstream f2(p2.c_str());
425     if (!f2)
426 	open_error(p2);
427 
428     for (;;) {
429         char buf1[512], buf2[512];
430 
431         f1.read(buf1, sizeof(buf1));
432         if (f1.bad())
433             throw std::runtime_error("Failed to read from " + p1.str());
434 
435         f2.read(buf2, sizeof(buf2));
436         if (f2.bad())
437             throw std::runtime_error("Failed to read from " + p1.str());
438 
439         if ((f1.gcount() == 0) && (f2.gcount() == 0)) {
440             equal = true;
441             break;
442         }
443 
444         if ((f1.gcount() != f2.gcount()) ||
445             (std::memcmp(buf1, buf2, f1.gcount()) != 0)) {
446             break;
447         }
448     }
449 
450     return equal;
451 }
452 
453 static
454 void
print_diff(const atf::fs::path & p1,const atf::fs::path & p2)455 print_diff(const atf::fs::path& p1, const atf::fs::path& p2)
456 {
457     const atf::process::status s =
458         atf::process::exec(atf::fs::path("diff"),
459                            atf::process::argv_array("diff", "-u", p1.c_str(),
460                                                     p2.c_str(), NULL),
461                            atf::process::stream_connect(STDOUT_FILENO,
462                                                         STDERR_FILENO),
463                            atf::process::stream_inherit());
464 
465     if (!s.exited())
466         std::cerr << "Failed to run diff(3)\n";
467 
468     if (s.exitstatus() != 1)
469         std::cerr << "Error while running diff(3)\n";
470 }
471 
472 static
473 std::string
decode(const std::string & s)474 decode(const std::string& s)
475 {
476     size_t i;
477     std::string res;
478 
479     res.reserve(s.length());
480 
481     i = 0;
482     while (i < s.length()) {
483         char c = s[i++];
484 
485         if (c == '\\') {
486             switch (s[i++]) {
487             case 'a': c = '\a'; break;
488             case 'b': c = '\b'; break;
489             case 'c': break;
490             case 'e': c = 033; break;
491             case 'f': c = '\f'; break;
492             case 'n': c = '\n'; break;
493             case 'r': c = '\r'; break;
494             case 't': c = '\t'; break;
495             case 'v': c = '\v'; break;
496             case '\\': break;
497             case '0':
498                 {
499                     int count = 3;
500                     c = 0;
501                     while (--count >= 0 && static_cast<unsigned>(s[i] - '0') < 8)
502                         c = (c << 3) + (s[i++] - '0');
503                     break;
504                 }
505             default:
506                 --i;
507                 break;
508             }
509         }
510 
511         res.push_back(c);
512     }
513 
514     return res;
515 }
516 
517 static
518 bool
run_status_check(const status_check & sc,const atf::check::check_result & cr)519 run_status_check(const status_check& sc, const atf::check::check_result& cr)
520 {
521     bool result;
522 
523     if (sc.type == sc_exit) {
524         if (cr.exited() && sc.value != INT_MIN) {
525             const int status = cr.exitcode();
526 
527             if (!sc.negated && sc.value != status) {
528                 std::cerr << "Fail: incorrect exit status: "
529                           << status << ", expected: "
530                           << sc.value << "\n";
531                 result = false;
532             } else if (sc.negated && sc.value == status) {
533                 std::cerr << "Fail: incorrect exit status: "
534                           << status << ", expected: "
535                           << "anything else\n";
536                 result = false;
537             } else
538                 result = true;
539         } else if (cr.exited() && sc.value == INT_MIN) {
540             result = true;
541         } else {
542             std::cerr << "Fail: program did not exit cleanly\n";
543             result = false;
544         }
545     } else if (sc.type == sc_ignore) {
546         result = true;
547     } else if (sc.type == sc_signal) {
548         if (cr.signaled() && sc.value != INT_MIN) {
549             const int status = cr.termsig();
550 
551             if (!sc.negated && sc.value != status) {
552                 std::cerr << "Fail: incorrect signal received: "
553                           << status << ", expected: " << sc.value << "\n";
554                 result = false;
555             } else if (sc.negated && sc.value == status) {
556                 std::cerr << "Fail: incorrect signal received: "
557                           << status << ", expected: "
558                           << "anything else\n";
559                 result = false;
560             } else
561                 result = true;
562         } else if (cr.signaled() && sc.value == INT_MIN) {
563             result = true;
564         } else {
565             std::cerr << "Fail: program did not receive a signal\n";
566             result = false;
567         }
568     } else {
569         UNREACHABLE;
570         result = false;
571     }
572 
573     if (result == false) {
574         std::cerr << "stdout:\n";
575         cat_file(atf::fs::path(cr.stdout_path()));
576         std::cerr << "\n";
577 
578         std::cerr << "stderr:\n";
579         cat_file(atf::fs::path(cr.stderr_path()));
580         std::cerr << "\n";
581     }
582 
583     return result;
584 }
585 
586 static
587 bool
run_status_checks(const std::vector<status_check> & checks,const atf::check::check_result & result)588 run_status_checks(const std::vector< status_check >& checks,
589                   const atf::check::check_result& result)
590 {
591     bool ok = false;
592 
593     for (std::vector< status_check >::const_iterator iter = checks.begin();
594          !ok && iter != checks.end(); iter++) {
595          ok |= run_status_check(*iter, result);
596     }
597 
598     return ok;
599 }
600 
601 static
602 bool
run_output_check(const output_check oc,const atf::fs::path & path,const std::string & stdxxx)603 run_output_check(const output_check oc, const atf::fs::path& path,
604                  const std::string& stdxxx)
605 {
606     bool result;
607 
608     if (oc.type == oc_empty) {
609         const bool is_empty = file_empty(path);
610         if (!oc.negated && !is_empty) {
611             std::cerr << "Fail: " << stdxxx << " not empty\n";
612             print_diff(atf::fs::path("/dev/null"), path);
613             result = false;
614         } else if (oc.negated && is_empty) {
615             std::cerr << "Fail: " << stdxxx << " is empty\n";
616             result = false;
617         } else
618             result = true;
619     } else if (oc.type == oc_file) {
620         const bool equals = compare_files(path, atf::fs::path(oc.value));
621         if (!oc.negated && !equals) {
622             std::cerr << "Fail: " << stdxxx << " does not match golden "
623                 "output\n";
624             print_diff(atf::fs::path(oc.value), path);
625             result = false;
626         } else if (oc.negated && equals) {
627             std::cerr << "Fail: " << stdxxx << " matches golden output\n";
628             cat_file(atf::fs::path(oc.value));
629             result = false;
630         } else
631             result = true;
632     } else if (oc.type == oc_ignore) {
633         result = true;
634     } else if (oc.type == oc_inline) {
635         atf::fs::path path2 = atf::fs::path(atf::config::get("atf_workdir"))
636                               / "inline.XXXXXX";
637         temp_file temp(path2);
638         temp.write(decode(oc.value));
639         temp.close();
640 
641         const bool equals = compare_files(path, temp.get_path());
642         if (!oc.negated && !equals) {
643             std::cerr << "Fail: " << stdxxx << " does not match expected "
644                 "value\n";
645             print_diff(temp.get_path(), path);
646             result = false;
647         } else if (oc.negated && equals) {
648             std::cerr << "Fail: " << stdxxx << " matches expected value\n";
649             cat_file(temp.get_path());
650             result = false;
651         } else
652             result = true;
653     } else if (oc.type == oc_match) {
654         const bool matches = grep_file(path, oc.value);
655         if (!oc.negated && !matches) {
656             std::cerr << "Fail: regexp " + oc.value + " not in " << stdxxx
657                       << "\n";
658             cat_file(path);
659             result = false;
660         } else if (oc.negated && matches) {
661             std::cerr << "Fail: regexp " + oc.value + " is in " << stdxxx
662                       << "\n";
663             cat_file(path);
664             result = false;
665         } else
666             result = true;
667     } else if (oc.type == oc_save) {
668         INV(!oc.negated);
669         std::ifstream ifs(path.c_str(), std::fstream::binary);
670         ifs >> std::noskipws;
671         std::istream_iterator< char > begin(ifs), end;
672 
673         std::ofstream ofs(oc.value.c_str(), std::fstream::binary
674                                      | std::fstream::trunc);
675         std::ostream_iterator <char> obegin(ofs);
676 
677         std::copy(begin, end, obegin);
678         result = true;
679     } else {
680         UNREACHABLE;
681         result = false;
682     }
683 
684     return result;
685 }
686 
687 static
688 bool
run_output_checks(const std::vector<output_check> & checks,const atf::fs::path & path,const std::string & stdxxx)689 run_output_checks(const std::vector< output_check >& checks,
690                   const atf::fs::path& path, const std::string& stdxxx)
691 {
692     bool ok = true;
693 
694     for (std::vector< output_check >::const_iterator iter = checks.begin();
695          iter != checks.end(); iter++) {
696          ok &= run_output_check(*iter, path, stdxxx);
697     }
698 
699     return ok;
700 }
701 
702 // ------------------------------------------------------------------------
703 // The "atf_check" application.
704 // ------------------------------------------------------------------------
705 
706 namespace {
707 
708 class atf_check : public atf::application::app {
709     bool m_xflag;
710 
711     std::vector< status_check > m_status_checks;
712     std::vector< output_check > m_stdout_checks;
713     std::vector< output_check > m_stderr_checks;
714 
715     static const char* m_description;
716 
717     bool run_output_checks(const atf::check::check_result&,
718                            const std::string&) const;
719 
720     std::string specific_args(void) const;
721     options_set specific_options(void) const;
722     void process_option(int, const char*);
723     void process_option_s(const std::string&);
724 
725 public:
726     atf_check(void);
727     int main(void);
728 };
729 
730 } // anonymous namespace
731 
732 const char* atf_check::m_description =
733     "atf-check executes given command and analyzes its results.";
734 
atf_check(void)735 atf_check::atf_check(void) :
736     app(m_description, "atf-check(1)"),
737     m_xflag(false)
738 {
739 }
740 
741 bool
run_output_checks(const atf::check::check_result & r,const std::string & stdxxx) const742 atf_check::run_output_checks(const atf::check::check_result& r,
743                              const std::string& stdxxx)
744     const
745 {
746     if (stdxxx == "stdout") {
747         return ::run_output_checks(m_stdout_checks,
748             atf::fs::path(r.stdout_path()), "stdout");
749     } else if (stdxxx == "stderr") {
750         return ::run_output_checks(m_stderr_checks,
751             atf::fs::path(r.stderr_path()), "stderr");
752     } else {
753         UNREACHABLE;
754         return false;
755     }
756 }
757 
758 std::string
specific_args(void) const759 atf_check::specific_args(void)
760     const
761 {
762     return "<command>";
763 }
764 
765 atf_check::options_set
specific_options(void) const766 atf_check::specific_options(void)
767     const
768 {
769     using atf::application::option;
770     options_set opts;
771 
772     opts.insert(option('s', "qual:value", "Handle status. Qualifier "
773                 "must be one of: ignore exit:<num> signal:<name|num>"));
774     opts.insert(option('o', "action:arg", "Handle stdout. Action must be "
775                 "one of: empty ignore file:<path> inline:<val> match:regexp "
776                 "save:<path>"));
777     opts.insert(option('e', "action:arg", "Handle stderr. Action must be "
778                 "one of: empty ignore file:<path> inline:<val> match:regexp "
779                 "save:<path>"));
780     opts.insert(option('x', "", "Execute command as a shell command"));
781 
782     return opts;
783 }
784 
785 void
process_option(int ch,const char * arg)786 atf_check::process_option(int ch, const char* arg)
787 {
788     switch (ch) {
789     case 's':
790         m_status_checks.push_back(parse_status_check_arg(arg));
791         break;
792 
793     case 'o':
794         m_stdout_checks.push_back(parse_output_check_arg(arg));
795         break;
796 
797     case 'e':
798         m_stderr_checks.push_back(parse_output_check_arg(arg));
799         break;
800 
801     case 'x':
802         m_xflag = true;
803         break;
804 
805     default:
806         UNREACHABLE;
807     }
808 }
809 
810 int
main(void)811 atf_check::main(void)
812 {
813     if (m_argc < 1)
814         throw atf::application::usage_error("No command specified");
815 
816     int status = EXIT_FAILURE;
817 
818     std::unique_ptr< atf::check::check_result > r =
819         m_xflag ? execute_with_shell(m_argv) : execute(m_argv);
820 
821     if (m_status_checks.empty())
822         m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS));
823     else if (m_status_checks.size() > 1) {
824         // TODO: Remove this restriction.
825         throw atf::application::usage_error("Cannot specify -s more than once");
826     }
827 
828     if (m_stdout_checks.empty())
829         m_stdout_checks.push_back(output_check(oc_empty, false, ""));
830     if (m_stderr_checks.empty())
831         m_stderr_checks.push_back(output_check(oc_empty, false, ""));
832 
833     if ((run_status_checks(m_status_checks, *r) == false) ||
834         (run_output_checks(*r, "stderr") == false) ||
835         (run_output_checks(*r, "stdout") == false))
836         status = EXIT_FAILURE;
837     else
838         status = EXIT_SUCCESS;
839 
840     return status;
841 }
842 
843 int
main(int argc,char * const * argv)844 main(int argc, char* const* argv)
845 {
846     return atf_check().run(argc, argv);
847 }
848