1240116Smarcel//
2240116Smarcel// Automated Testing Framework (atf)
3240116Smarcel//
4240116Smarcel// Copyright (c) 2008 The NetBSD Foundation, Inc.
5240116Smarcel// All rights reserved.
6240116Smarcel//
7240116Smarcel// Redistribution and use in source and binary forms, with or without
8240116Smarcel// modification, are permitted provided that the following conditions
9240116Smarcel// are met:
10240116Smarcel// 1. Redistributions of source code must retain the above copyright
11240116Smarcel//    notice, this list of conditions and the following disclaimer.
12240116Smarcel// 2. Redistributions in binary form must reproduce the above copyright
13240116Smarcel//    notice, this list of conditions and the following disclaimer in the
14240116Smarcel//    documentation and/or other materials provided with the distribution.
15240116Smarcel//
16240116Smarcel// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17240116Smarcel// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18240116Smarcel// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19240116Smarcel// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20240116Smarcel// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21240116Smarcel// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22240116Smarcel// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23240116Smarcel// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24240116Smarcel// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25240116Smarcel// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26240116Smarcel// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27240116Smarcel// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28240116Smarcel//
29240116Smarcel
30240116Smarcelextern "C" {
31240116Smarcel#include <sys/types.h>
32240116Smarcel#include <sys/wait.h>
33240116Smarcel
34240116Smarcel#include <limits.h>
35240116Smarcel#include <signal.h>
36240116Smarcel#include <unistd.h>
37240116Smarcel}
38240116Smarcel
39240116Smarcel#include <cerrno>
40240116Smarcel#include <cstdlib>
41240116Smarcel#include <cstring>
42240116Smarcel#include <fstream>
43240116Smarcel#include <ios>
44240116Smarcel#include <iostream>
45240116Smarcel#include <iterator>
46240116Smarcel#include <list>
47240116Smarcel#include <memory>
48240116Smarcel#include <utility>
49240116Smarcel
50240116Smarcel#include "atf-c++/check.hpp"
51240116Smarcel#include "atf-c++/config.hpp"
52240116Smarcel#include "atf-c++/utils.hpp"
53240116Smarcel
54240116Smarcel#include "atf-c++/detail/application.hpp"
55240116Smarcel#include "atf-c++/detail/exceptions.hpp"
56240116Smarcel#include "atf-c++/detail/fs.hpp"
57240116Smarcel#include "atf-c++/detail/process.hpp"
58240116Smarcel#include "atf-c++/detail/sanity.hpp"
59240116Smarcel#include "atf-c++/detail/text.hpp"
60240116Smarcel
61240116Smarcel// ------------------------------------------------------------------------
62240116Smarcel// Auxiliary functions.
63240116Smarcel// ------------------------------------------------------------------------
64240116Smarcel
65240116Smarcelnamespace {
66240116Smarcel
67240116Smarcelenum status_check_t {
68240116Smarcel    sc_exit,
69240116Smarcel    sc_ignore,
70240116Smarcel    sc_signal,
71240116Smarcel};
72240116Smarcel
73240116Smarcelstruct status_check {
74240116Smarcel    status_check_t type;
75240116Smarcel    bool negated;
76240116Smarcel    int value;
77240116Smarcel
78240116Smarcel    status_check(const status_check_t& p_type, const bool p_negated,
79240116Smarcel                 const int p_value) :
80240116Smarcel        type(p_type),
81240116Smarcel        negated(p_negated),
82240116Smarcel        value(p_value)
83240116Smarcel    {
84240116Smarcel    }
85240116Smarcel};
86240116Smarcel
87240116Smarcelenum output_check_t {
88240116Smarcel    oc_ignore,
89240116Smarcel    oc_inline,
90240116Smarcel    oc_file,
91240116Smarcel    oc_empty,
92240116Smarcel    oc_match,
93240116Smarcel    oc_save
94240116Smarcel};
95240116Smarcel
96240116Smarcelstruct output_check {
97240116Smarcel    output_check_t type;
98240116Smarcel    bool negated;
99240116Smarcel    std::string value;
100240116Smarcel
101240116Smarcel    output_check(const output_check_t& p_type, const bool p_negated,
102240116Smarcel                 const std::string& p_value) :
103240116Smarcel        type(p_type),
104240116Smarcel        negated(p_negated),
105240116Smarcel        value(p_value)
106240116Smarcel    {
107240116Smarcel    }
108240116Smarcel};
109240116Smarcel
110240116Smarcelclass temp_file : public std::ostream {
111240116Smarcel    std::auto_ptr< atf::fs::path > m_path;
112240116Smarcel    int m_fd;
113240116Smarcel
114240116Smarcelpublic:
115240116Smarcel    temp_file(const atf::fs::path& p) :
116240116Smarcel        std::ostream(NULL),
117240116Smarcel        m_fd(-1)
118240116Smarcel    {
119240116Smarcel        atf::utils::auto_array< char > buf(new char[p.str().length() + 1]);
120240116Smarcel        std::strcpy(buf.get(), p.c_str());
121240116Smarcel
122240116Smarcel        m_fd = ::mkstemp(buf.get());
123240116Smarcel        if (m_fd == -1)
124240116Smarcel            throw atf::system_error("atf_check::temp_file::temp_file(" +
125240116Smarcel                                    p.str() + ")", "mkstemp(3) failed",
126240116Smarcel                                    errno);
127240116Smarcel
128240116Smarcel        m_path.reset(new atf::fs::path(buf.get()));
129240116Smarcel    }
130240116Smarcel
131240116Smarcel    ~temp_file(void)
132240116Smarcel    {
133240116Smarcel        close();
134240116Smarcel        try {
135240116Smarcel            remove(*m_path);
136240116Smarcel        } catch (const atf::system_error&) {
137240116Smarcel            // Ignore deletion errors.
138240116Smarcel        }
139240116Smarcel    }
140240116Smarcel
141240116Smarcel    const atf::fs::path&
142240116Smarcel    get_path(void) const
143240116Smarcel    {
144240116Smarcel        return *m_path;
145240116Smarcel    }
146240116Smarcel
147240116Smarcel    void
148240116Smarcel    write(const std::string& text)
149240116Smarcel    {
150240116Smarcel        if (::write(m_fd, text.c_str(), text.size()) == -1)
151240116Smarcel            throw atf::system_error("atf_check", "write(2) failed", errno);
152240116Smarcel    }
153240116Smarcel
154240116Smarcel    void
155240116Smarcel    close(void)
156240116Smarcel    {
157240116Smarcel        if (m_fd != -1) {
158240116Smarcel            flush();
159240116Smarcel            ::close(m_fd);
160240116Smarcel            m_fd = -1;
161240116Smarcel        }
162240116Smarcel    }
163240116Smarcel};
164240116Smarcel
165240116Smarcel} // anonymous namespace
166240116Smarcel
167240116Smarcelstatic int
168240116Smarcelparse_exit_code(const std::string& str)
169240116Smarcel{
170240116Smarcel    try {
171240116Smarcel        const int value = atf::text::to_type< int >(str);
172240116Smarcel        if (value < 0 || value > 255)
173240116Smarcel            throw std::runtime_error("Unused reason");
174240116Smarcel        return value;
175240116Smarcel    } catch (const std::runtime_error&) {
176240116Smarcel        throw atf::application::usage_error("Invalid exit code for -s option; "
177240116Smarcel            "must be an integer in range 0-255");
178240116Smarcel    }
179240116Smarcel}
180240116Smarcel
181240116Smarcelstatic struct name_number {
182240116Smarcel    const char *name;
183240116Smarcel    int signo;
184240116Smarcel} signal_names_to_numbers[] = {
185240116Smarcel    { "hup", SIGHUP },
186240116Smarcel    { "int", SIGINT },
187240116Smarcel    { "quit", SIGQUIT },
188240116Smarcel    { "trap", SIGTRAP },
189240116Smarcel    { "abrt", SIGABRT },
190240116Smarcel    { "kill", SIGKILL },
191240116Smarcel    { "segv", SIGSEGV },
192240116Smarcel    { "pipe", SIGPIPE },
193240116Smarcel    { "alrm", SIGALRM },
194240116Smarcel    { "term", SIGTERM },
195240116Smarcel    { "usr1", SIGUSR1 },
196240116Smarcel    { "usr2", SIGUSR2 },
197240116Smarcel    { NULL, INT_MIN },
198240116Smarcel};
199240116Smarcel
200240116Smarcelstatic int
201240116Smarcelsignal_name_to_number(const std::string& str)
202240116Smarcel{
203240116Smarcel    struct name_number* iter = signal_names_to_numbers;
204240116Smarcel    int signo = INT_MIN;
205240116Smarcel    while (signo == INT_MIN && iter->name != NULL) {
206240116Smarcel        if (str == iter->name || str == std::string("sig") + iter->name)
207240116Smarcel            signo = iter->signo;
208240116Smarcel        else
209240116Smarcel            iter++;
210240116Smarcel    }
211240116Smarcel    return signo;
212240116Smarcel}
213240116Smarcel
214240116Smarcelstatic int
215240116Smarcelparse_signal(const std::string& str)
216240116Smarcel{
217240116Smarcel    const int signo = signal_name_to_number(str);
218240116Smarcel    if (signo == INT_MIN) {
219240116Smarcel        try {
220240116Smarcel            return atf::text::to_type< int >(str);
221240116Smarcel        } catch (std::runtime_error) {
222240116Smarcel            throw atf::application::usage_error("Invalid signal name or number "
223240116Smarcel                "in -s option");
224240116Smarcel        }
225240116Smarcel    }
226240116Smarcel    INV(signo != INT_MIN);
227240116Smarcel    return signo;
228240116Smarcel}
229240116Smarcel
230240116Smarcelstatic status_check
231240116Smarcelparse_status_check_arg(const std::string& arg)
232240116Smarcel{
233240116Smarcel    const std::string::size_type delimiter = arg.find(':');
234240116Smarcel    bool negated = (arg.compare(0, 4, "not-") == 0);
235240116Smarcel    const std::string action_str = arg.substr(0, delimiter);
236240116Smarcel    const std::string action = negated ? action_str.substr(4) : action_str;
237240116Smarcel    const std::string value_str = (
238240116Smarcel        delimiter == std::string::npos ? "" : arg.substr(delimiter + 1));
239240116Smarcel    int value;
240240116Smarcel
241240116Smarcel    status_check_t type;
242240116Smarcel    if (action == "eq") {
243240116Smarcel        // Deprecated; use exit instead.  TODO: Remove after 0.10.
244240116Smarcel        type = sc_exit;
245240116Smarcel        if (negated)
246240116Smarcel            throw atf::application::usage_error("Cannot negate eq checker");
247240116Smarcel        negated = false;
248240116Smarcel        value = parse_exit_code(value_str);
249240116Smarcel    } else if (action == "exit") {
250240116Smarcel        type = sc_exit;
251240116Smarcel        if (value_str.empty())
252240116Smarcel            value = INT_MIN;
253240116Smarcel        else
254240116Smarcel            value = parse_exit_code(value_str);
255240116Smarcel    } else if (action == "ignore") {
256240116Smarcel        if (negated)
257240116Smarcel            throw atf::application::usage_error("Cannot negate ignore checker");
258240116Smarcel        type = sc_ignore;
259240116Smarcel        value = INT_MIN;
260240116Smarcel    } else if (action == "ne") {
261240116Smarcel        // Deprecated; use not-exit instead.  TODO: Remove after 0.10.
262240116Smarcel        type = sc_exit;
263240116Smarcel        if (negated)
264240116Smarcel            throw atf::application::usage_error("Cannot negate ne checker");
265240116Smarcel        negated = true;
266240116Smarcel        value = parse_exit_code(value_str);
267240116Smarcel    } else if (action == "signal") {
268240116Smarcel        type = sc_signal;
269240116Smarcel        if (value_str.empty())
270240116Smarcel            value = INT_MIN;
271240116Smarcel        else
272240116Smarcel            value = parse_signal(value_str);
273240116Smarcel    } else
274240116Smarcel        throw atf::application::usage_error("Invalid status checker");
275240116Smarcel
276240116Smarcel    return status_check(type, negated, value);
277240116Smarcel}
278240116Smarcel
279240116Smarcelstatic
280240116Smarceloutput_check
281240116Smarcelparse_output_check_arg(const std::string& arg)
282240116Smarcel{
283240116Smarcel    const std::string::size_type delimiter = arg.find(':');
284240116Smarcel    const bool negated = (arg.compare(0, 4, "not-") == 0);
285240116Smarcel    const std::string action_str = arg.substr(0, delimiter);
286240116Smarcel    const std::string action = negated ? action_str.substr(4) : action_str;
287240116Smarcel
288240116Smarcel    output_check_t type;
289240116Smarcel    if (action == "empty")
290240116Smarcel        type = oc_empty;
291240116Smarcel    else if (action == "file")
292240116Smarcel        type = oc_file;
293240116Smarcel    else if (action == "ignore") {
294240116Smarcel        if (negated)
295240116Smarcel            throw atf::application::usage_error("Cannot negate ignore checker");
296240116Smarcel        type = oc_ignore;
297240116Smarcel    } else if (action == "inline")
298240116Smarcel        type = oc_inline;
299240116Smarcel    else if (action == "match")
300240116Smarcel        type = oc_match;
301240116Smarcel    else if (action == "save") {
302240116Smarcel        if (negated)
303240116Smarcel            throw atf::application::usage_error("Cannot negate save checker");
304240116Smarcel        type = oc_save;
305240116Smarcel    } else
306240116Smarcel        throw atf::application::usage_error("Invalid output checker");
307240116Smarcel
308240116Smarcel    return output_check(type, negated, arg.substr(delimiter + 1));
309240116Smarcel}
310240116Smarcel
311240116Smarcelstatic
312240116Smarcelstd::string
313240116Smarcelflatten_argv(char* const* argv)
314240116Smarcel{
315240116Smarcel    std::string cmdline;
316240116Smarcel
317240116Smarcel    char* const* arg = &argv[0];
318240116Smarcel    while (*arg != NULL) {
319240116Smarcel        if (arg != &argv[0])
320240116Smarcel            cmdline += ' ';
321240116Smarcel
322240116Smarcel        cmdline += *arg;
323240116Smarcel
324240116Smarcel        arg++;
325240116Smarcel    }
326240116Smarcel
327240116Smarcel    return cmdline;
328240116Smarcel}
329240116Smarcel
330240116Smarcelstatic
331240116Smarcelstd::auto_ptr< atf::check::check_result >
332240116Smarcelexecute(const char* const* argv)
333240116Smarcel{
334240116Smarcel    std::cout << "Executing command [ ";
335240116Smarcel    for (int i = 0; argv[i] != NULL; ++i)
336240116Smarcel        std::cout << argv[i] << " ";
337240116Smarcel    std::cout << "]\n";
338240116Smarcel
339240116Smarcel    atf::process::argv_array argva(argv);
340240116Smarcel    return atf::check::exec(argva);
341240116Smarcel}
342240116Smarcel
343240116Smarcelstatic
344240116Smarcelstd::auto_ptr< atf::check::check_result >
345240116Smarcelexecute_with_shell(char* const* argv)
346240116Smarcel{
347240116Smarcel    const std::string cmd = flatten_argv(argv);
348240116Smarcel
349240116Smarcel    const char* sh_argv[4];
350240116Smarcel    sh_argv[0] = atf::config::get("atf_shell").c_str();
351240116Smarcel    sh_argv[1] = "-c";
352240116Smarcel    sh_argv[2] = cmd.c_str();
353240116Smarcel    sh_argv[3] = NULL;
354240116Smarcel    return execute(sh_argv);
355240116Smarcel}
356240116Smarcel
357240116Smarcelstatic
358240116Smarcelvoid
359240116Smarcelcat_file(const atf::fs::path& path)
360240116Smarcel{
361240116Smarcel    std::ifstream stream(path.c_str());
362240116Smarcel    if (!stream)
363240116Smarcel        throw std::runtime_error("Failed to open " + path.str());
364240116Smarcel
365240116Smarcel    stream >> std::noskipws;
366240116Smarcel    std::istream_iterator< char > begin(stream), end;
367240116Smarcel    std::ostream_iterator< char > out(std::cerr);
368240116Smarcel    std::copy(begin, end, out);
369240116Smarcel
370240116Smarcel    stream.close();
371240116Smarcel}
372240116Smarcel
373240116Smarcelstatic
374240116Smarcelbool
375240116Smarcelgrep_file(const atf::fs::path& path, const std::string& regexp)
376240116Smarcel{
377240116Smarcel    std::ifstream stream(path.c_str());
378240116Smarcel    if (!stream)
379240116Smarcel        throw std::runtime_error("Failed to open " + path.str());
380240116Smarcel
381240116Smarcel    bool found = false;
382240116Smarcel
383240116Smarcel    std::string line;
384240116Smarcel    while (!found && !std::getline(stream, line).fail()) {
385240116Smarcel        if (atf::text::match(line, regexp))
386240116Smarcel            found = true;
387240116Smarcel    }
388240116Smarcel
389240116Smarcel    stream.close();
390240116Smarcel
391240116Smarcel    return found;
392240116Smarcel}
393240116Smarcel
394240116Smarcelstatic
395240116Smarcelbool
396240116Smarcelfile_empty(const atf::fs::path& p)
397240116Smarcel{
398240116Smarcel    atf::fs::file_info f(p);
399240116Smarcel
400240116Smarcel    return (f.get_size() == 0);
401240116Smarcel}
402240116Smarcel
403240116Smarcelstatic bool
404240116Smarcelcompare_files(const atf::fs::path& p1, const atf::fs::path& p2)
405240116Smarcel{
406240116Smarcel    bool equal = false;
407240116Smarcel
408240116Smarcel    std::ifstream f1(p1.c_str());
409240116Smarcel    if (!f1)
410240116Smarcel        throw std::runtime_error("Failed to open " + p1.str());
411240116Smarcel
412240116Smarcel    std::ifstream f2(p2.c_str());
413240116Smarcel    if (!f2)
414240116Smarcel        throw std::runtime_error("Failed to open " + p1.str());
415240116Smarcel
416240116Smarcel    for (;;) {
417240116Smarcel        char buf1[512], buf2[512];
418240116Smarcel
419240116Smarcel        f1.read(buf1, sizeof(buf1));
420240116Smarcel        if (f1.bad())
421240116Smarcel            throw std::runtime_error("Failed to read from " + p1.str());
422240116Smarcel
423240116Smarcel        f2.read(buf2, sizeof(buf2));
424240116Smarcel        if (f2.bad())
425240116Smarcel            throw std::runtime_error("Failed to read from " + p1.str());
426240116Smarcel
427240116Smarcel        if ((f1.gcount() == 0) && (f2.gcount() == 0)) {
428240116Smarcel            equal = true;
429240116Smarcel            break;
430240116Smarcel        }
431240116Smarcel
432240116Smarcel        if ((f1.gcount() != f2.gcount()) ||
433240116Smarcel            (std::memcmp(buf1, buf2, f1.gcount()) != 0)) {
434240116Smarcel            break;
435240116Smarcel        }
436240116Smarcel    }
437240116Smarcel
438240116Smarcel    return equal;
439240116Smarcel}
440240116Smarcel
441240116Smarcelstatic
442240116Smarcelvoid
443240116Smarcelprint_diff(const atf::fs::path& p1, const atf::fs::path& p2)
444240116Smarcel{
445240116Smarcel    const atf::process::status s =
446240116Smarcel        atf::process::exec(atf::fs::path("diff"),
447240116Smarcel                           atf::process::argv_array("diff", "-u", p1.c_str(),
448240116Smarcel                                                    p2.c_str(), NULL),
449240116Smarcel                           atf::process::stream_connect(STDOUT_FILENO,
450240116Smarcel                                                        STDERR_FILENO),
451240116Smarcel                           atf::process::stream_inherit());
452240116Smarcel
453240116Smarcel    if (!s.exited())
454240116Smarcel        std::cerr << "Failed to run diff(3)\n";
455240116Smarcel
456240116Smarcel    if (s.exitstatus() != 1)
457240116Smarcel        std::cerr << "Error while running diff(3)\n";
458240116Smarcel}
459240116Smarcel
460240116Smarcelstatic
461240116Smarcelstd::string
462240116Smarceldecode(const std::string& s)
463240116Smarcel{
464240116Smarcel    size_t i;
465240116Smarcel    std::string res;
466240116Smarcel
467240116Smarcel    res.reserve(s.length());
468240116Smarcel
469240116Smarcel    i = 0;
470240116Smarcel    while (i < s.length()) {
471240116Smarcel        char c = s[i++];
472240116Smarcel
473240116Smarcel        if (c == '\\') {
474240116Smarcel            switch (s[i++]) {
475240116Smarcel            case 'a': c = '\a'; break;
476240116Smarcel            case 'b': c = '\b'; break;
477240116Smarcel            case 'c': break;
478240116Smarcel            case 'e': c = 033; break;
479240116Smarcel            case 'f': c = '\f'; break;
480240116Smarcel            case 'n': c = '\n'; break;
481240116Smarcel            case 'r': c = '\r'; break;
482240116Smarcel            case 't': c = '\t'; break;
483240116Smarcel            case 'v': c = '\v'; break;
484240116Smarcel            case '\\': break;
485240116Smarcel            case '0':
486240116Smarcel                {
487240116Smarcel                    int count = 3;
488240116Smarcel                    c = 0;
489240116Smarcel                    while (--count >= 0 && (unsigned)(s[i] - '0') < 8)
490240116Smarcel                        c = (c << 3) + (s[i++] - '0');
491240116Smarcel                    break;
492240116Smarcel                }
493240116Smarcel            default:
494240116Smarcel                --i;
495240116Smarcel                break;
496240116Smarcel            }
497240116Smarcel        }
498240116Smarcel
499240116Smarcel        res.push_back(c);
500240116Smarcel    }
501240116Smarcel
502240116Smarcel    return res;
503240116Smarcel}
504240116Smarcel
505240116Smarcelstatic
506240116Smarcelbool
507240116Smarcelrun_status_check(const status_check& sc, const atf::check::check_result& cr)
508240116Smarcel{
509240116Smarcel    bool result;
510240116Smarcel
511240116Smarcel    if (sc.type == sc_exit) {
512240116Smarcel        if (cr.exited() && sc.value != INT_MIN) {
513240116Smarcel            const int status = cr.exitcode();
514240116Smarcel
515240116Smarcel            if (!sc.negated && sc.value != status) {
516240116Smarcel                std::cerr << "Fail: incorrect exit status: "
517240116Smarcel                          << status << ", expected: "
518240116Smarcel                          << sc.value << "\n";
519240116Smarcel                result = false;
520240116Smarcel            } else if (sc.negated && sc.value == status) {
521240116Smarcel                std::cerr << "Fail: incorrect exit status: "
522240116Smarcel                          << status << ", expected: "
523240116Smarcel                          << "anything else\n";
524240116Smarcel                result = false;
525240116Smarcel            } else
526240116Smarcel                result = true;
527240116Smarcel        } else if (cr.exited() && sc.value == INT_MIN) {
528240116Smarcel            result = true;
529240116Smarcel        } else {
530240116Smarcel            std::cerr << "Fail: program did not exit cleanly\n";
531240116Smarcel            result = false;
532240116Smarcel        }
533240116Smarcel    } else if (sc.type == sc_ignore) {
534240116Smarcel        result = true;
535240116Smarcel    } else if (sc.type == sc_signal) {
536240116Smarcel        if (cr.signaled() && sc.value != INT_MIN) {
537240116Smarcel            const int status = cr.termsig();
538240116Smarcel
539240116Smarcel            if (!sc.negated && sc.value != status) {
540240116Smarcel                std::cerr << "Fail: incorrect signal received: "
541240116Smarcel                          << status << ", expected: " << sc.value << "\n";
542240116Smarcel                result = false;
543240116Smarcel            } else if (sc.negated && sc.value == status) {
544240116Smarcel                std::cerr << "Fail: incorrect signal received: "
545240116Smarcel                          << status << ", expected: "
546240116Smarcel                          << "anything else\n";
547240116Smarcel                result = false;
548240116Smarcel            } else
549240116Smarcel                result = true;
550240116Smarcel        } else if (cr.signaled() && sc.value == INT_MIN) {
551240116Smarcel            result = true;
552240116Smarcel        } else {
553240116Smarcel            std::cerr << "Fail: program did not receive a signal\n";
554240116Smarcel            result = false;
555240116Smarcel        }
556240116Smarcel    } else {
557240116Smarcel        UNREACHABLE;
558240116Smarcel        result = false;
559240116Smarcel    }
560240116Smarcel
561240116Smarcel    if (result == false) {
562240116Smarcel        std::cerr << "stdout:\n";
563240116Smarcel        cat_file(atf::fs::path(cr.stdout_path()));
564240116Smarcel        std::cerr << "\n";
565240116Smarcel
566240116Smarcel        std::cerr << "stderr:\n";
567240116Smarcel        cat_file(atf::fs::path(cr.stderr_path()));
568240116Smarcel        std::cerr << "\n";
569240116Smarcel    }
570240116Smarcel
571240116Smarcel    return result;
572240116Smarcel}
573240116Smarcel
574240116Smarcelstatic
575240116Smarcelbool
576240116Smarcelrun_status_checks(const std::vector< status_check >& checks,
577240116Smarcel                  const atf::check::check_result& result)
578240116Smarcel{
579240116Smarcel    bool ok = false;
580240116Smarcel
581240116Smarcel    for (std::vector< status_check >::const_iterator iter = checks.begin();
582240116Smarcel         !ok && iter != checks.end(); iter++) {
583240116Smarcel         ok |= run_status_check(*iter, result);
584240116Smarcel    }
585240116Smarcel
586240116Smarcel    return ok;
587240116Smarcel}
588240116Smarcel
589240116Smarcelstatic
590240116Smarcelbool
591240116Smarcelrun_output_check(const output_check oc, const atf::fs::path& path,
592240116Smarcel                 const std::string& stdxxx)
593240116Smarcel{
594240116Smarcel    bool result;
595240116Smarcel
596240116Smarcel    if (oc.type == oc_empty) {
597240116Smarcel        const bool is_empty = file_empty(path);
598240116Smarcel        if (!oc.negated && !is_empty) {
599240116Smarcel            std::cerr << "Fail: " << stdxxx << " not empty\n";
600240116Smarcel            print_diff(atf::fs::path("/dev/null"), path);
601240116Smarcel            result = false;
602240116Smarcel        } else if (oc.negated && is_empty) {
603240116Smarcel            std::cerr << "Fail: " << stdxxx << " is empty\n";
604240116Smarcel            result = false;
605240116Smarcel        } else
606240116Smarcel            result = true;
607240116Smarcel    } else if (oc.type == oc_file) {
608240116Smarcel        const bool equals = compare_files(path, atf::fs::path(oc.value));
609240116Smarcel        if (!oc.negated && !equals) {
610240116Smarcel            std::cerr << "Fail: " << stdxxx << " does not match golden "
611240116Smarcel                "output\n";
612240116Smarcel            print_diff(atf::fs::path(oc.value), path);
613240116Smarcel            result = false;
614240116Smarcel        } else if (oc.negated && equals) {
615240116Smarcel            std::cerr << "Fail: " << stdxxx << " matches golden output\n";
616240116Smarcel            cat_file(atf::fs::path(oc.value));
617240116Smarcel            result = false;
618240116Smarcel        } else
619240116Smarcel            result = true;
620240116Smarcel    } else if (oc.type == oc_ignore) {
621240116Smarcel        result = true;
622240116Smarcel    } else if (oc.type == oc_inline) {
623240116Smarcel        atf::fs::path path2 = atf::fs::path(atf::config::get("atf_workdir"))
624240116Smarcel                              / "inline.XXXXXX";
625240116Smarcel        temp_file temp(path2);
626240116Smarcel        temp.write(decode(oc.value));
627240116Smarcel        temp.close();
628240116Smarcel
629240116Smarcel        const bool equals = compare_files(path, temp.get_path());
630240116Smarcel        if (!oc.negated && !equals) {
631240116Smarcel            std::cerr << "Fail: " << stdxxx << " does not match expected "
632240116Smarcel                "value\n";
633240116Smarcel            print_diff(temp.get_path(), path);
634240116Smarcel            result = false;
635240116Smarcel        } else if (oc.negated && equals) {
636240116Smarcel            std::cerr << "Fail: " << stdxxx << " matches expected value\n";
637240116Smarcel            cat_file(temp.get_path());
638240116Smarcel            result = false;
639240116Smarcel        } else
640240116Smarcel            result = true;
641240116Smarcel    } else if (oc.type == oc_match) {
642240116Smarcel        const bool matches = grep_file(path, oc.value);
643240116Smarcel        if (!oc.negated && !matches) {
644240116Smarcel            std::cerr << "Fail: regexp " + oc.value + " not in " << stdxxx
645240116Smarcel                      << "\n";
646240116Smarcel            cat_file(path);
647240116Smarcel            result = false;
648240116Smarcel        } else if (oc.negated && matches) {
649240116Smarcel            std::cerr << "Fail: regexp " + oc.value + " is in " << stdxxx
650240116Smarcel                      << "\n";
651240116Smarcel            cat_file(path);
652240116Smarcel            result = false;
653240116Smarcel        } else
654240116Smarcel            result = true;
655240116Smarcel    } else if (oc.type == oc_save) {
656240116Smarcel        INV(!oc.negated);
657240116Smarcel        std::ifstream ifs(path.c_str(), std::fstream::binary);
658240116Smarcel        ifs >> std::noskipws;
659240116Smarcel        std::istream_iterator< char > begin(ifs), end;
660240116Smarcel
661240116Smarcel        std::ofstream ofs(oc.value.c_str(), std::fstream::binary
662240116Smarcel                                     | std::fstream::trunc);
663240116Smarcel        std::ostream_iterator <char> obegin(ofs);
664240116Smarcel
665240116Smarcel        std::copy(begin, end, obegin);
666240116Smarcel        result = true;
667240116Smarcel    } else {
668240116Smarcel        UNREACHABLE;
669240116Smarcel        result = false;
670240116Smarcel    }
671240116Smarcel
672240116Smarcel    return result;
673240116Smarcel}
674240116Smarcel
675240116Smarcelstatic
676240116Smarcelbool
677240116Smarcelrun_output_checks(const std::vector< output_check >& checks,
678240116Smarcel                  const atf::fs::path& path, const std::string& stdxxx)
679240116Smarcel{
680240116Smarcel    bool ok = true;
681240116Smarcel
682240116Smarcel    for (std::vector< output_check >::const_iterator iter = checks.begin();
683240116Smarcel         iter != checks.end(); iter++) {
684240116Smarcel         ok &= run_output_check(*iter, path, stdxxx);
685240116Smarcel    }
686240116Smarcel
687240116Smarcel    return ok;
688240116Smarcel}
689240116Smarcel
690240116Smarcel// ------------------------------------------------------------------------
691240116Smarcel// The "atf_check" application.
692240116Smarcel// ------------------------------------------------------------------------
693240116Smarcel
694240116Smarcelnamespace {
695240116Smarcel
696240116Smarcelclass atf_check : public atf::application::app {
697240116Smarcel    bool m_xflag;
698240116Smarcel
699240116Smarcel    std::vector< status_check > m_status_checks;
700240116Smarcel    std::vector< output_check > m_stdout_checks;
701240116Smarcel    std::vector< output_check > m_stderr_checks;
702240116Smarcel
703240116Smarcel    static const char* m_description;
704240116Smarcel
705240116Smarcel    bool run_output_checks(const atf::check::check_result&,
706240116Smarcel                           const std::string&) const;
707240116Smarcel
708240116Smarcel    std::string specific_args(void) const;
709240116Smarcel    options_set specific_options(void) const;
710240116Smarcel    void process_option(int, const char*);
711240116Smarcel    void process_option_s(const std::string&);
712240116Smarcel
713240116Smarcelpublic:
714240116Smarcel    atf_check(void);
715240116Smarcel    int main(void);
716240116Smarcel};
717240116Smarcel
718240116Smarcel} // anonymous namespace
719240116Smarcel
720240116Smarcelconst char* atf_check::m_description =
721240116Smarcel    "atf-check executes given command and analyzes its results.";
722240116Smarcel
723240116Smarcelatf_check::atf_check(void) :
724240116Smarcel    app(m_description, "atf-check(1)", "atf(7)"),
725240116Smarcel    m_xflag(false)
726240116Smarcel{
727240116Smarcel}
728240116Smarcel
729240116Smarcelbool
730240116Smarcelatf_check::run_output_checks(const atf::check::check_result& r,
731240116Smarcel                             const std::string& stdxxx)
732240116Smarcel    const
733240116Smarcel{
734240116Smarcel    if (stdxxx == "stdout") {
735240116Smarcel        return ::run_output_checks(m_stdout_checks,
736240116Smarcel            atf::fs::path(r.stdout_path()), "stdout");
737240116Smarcel    } else if (stdxxx == "stderr") {
738240116Smarcel        return ::run_output_checks(m_stderr_checks,
739240116Smarcel            atf::fs::path(r.stderr_path()), "stderr");
740240116Smarcel    } else {
741240116Smarcel        UNREACHABLE;
742240116Smarcel        return false;
743240116Smarcel    }
744240116Smarcel}
745240116Smarcel
746240116Smarcelstd::string
747240116Smarcelatf_check::specific_args(void)
748240116Smarcel    const
749240116Smarcel{
750240116Smarcel    return "<command>";
751240116Smarcel}
752240116Smarcel
753240116Smarcelatf_check::options_set
754240116Smarcelatf_check::specific_options(void)
755240116Smarcel    const
756240116Smarcel{
757240116Smarcel    using atf::application::option;
758240116Smarcel    options_set opts;
759240116Smarcel
760240116Smarcel    opts.insert(option('s', "qual:value", "Handle status. Qualifier "
761240116Smarcel                "must be one of: ignore exit:<num> signal:<name|num>"));
762240116Smarcel    opts.insert(option('o', "action:arg", "Handle stdout. Action must be "
763240116Smarcel                "one of: empty ignore file:<path> inline:<val> match:regexp "
764240116Smarcel                "save:<path>"));
765240116Smarcel    opts.insert(option('e', "action:arg", "Handle stderr. Action must be "
766240116Smarcel                "one of: empty ignore file:<path> inline:<val> match:regexp "
767240116Smarcel                "save:<path>"));
768240116Smarcel    opts.insert(option('x', "", "Execute command as a shell command"));
769240116Smarcel
770240116Smarcel    return opts;
771240116Smarcel}
772240116Smarcel
773240116Smarcelvoid
774240116Smarcelatf_check::process_option(int ch, const char* arg)
775240116Smarcel{
776240116Smarcel    switch (ch) {
777240116Smarcel    case 's':
778240116Smarcel        m_status_checks.push_back(parse_status_check_arg(arg));
779240116Smarcel        break;
780240116Smarcel
781240116Smarcel    case 'o':
782240116Smarcel        m_stdout_checks.push_back(parse_output_check_arg(arg));
783240116Smarcel        break;
784240116Smarcel
785240116Smarcel    case 'e':
786240116Smarcel        m_stderr_checks.push_back(parse_output_check_arg(arg));
787240116Smarcel        break;
788240116Smarcel
789240116Smarcel    case 'x':
790240116Smarcel        m_xflag = true;
791240116Smarcel        break;
792240116Smarcel
793240116Smarcel    default:
794240116Smarcel        UNREACHABLE;
795240116Smarcel    }
796240116Smarcel}
797240116Smarcel
798240116Smarcelint
799240116Smarcelatf_check::main(void)
800240116Smarcel{
801240116Smarcel    if (m_argc < 1)
802240116Smarcel        throw atf::application::usage_error("No command specified");
803240116Smarcel
804240116Smarcel    int status = EXIT_FAILURE;
805240116Smarcel
806240116Smarcel    std::auto_ptr< atf::check::check_result > r =
807240116Smarcel        m_xflag ? execute_with_shell(m_argv) : execute(m_argv);
808240116Smarcel
809240116Smarcel    if (m_status_checks.empty())
810240116Smarcel        m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS));
811240116Smarcel    else if (m_status_checks.size() > 1) {
812240116Smarcel        // TODO: Remove this restriction.
813240116Smarcel        throw atf::application::usage_error("Cannot specify -s more than once");
814240116Smarcel    }
815240116Smarcel
816240116Smarcel    if (m_stdout_checks.empty())
817240116Smarcel        m_stdout_checks.push_back(output_check(oc_empty, false, ""));
818240116Smarcel    if (m_stderr_checks.empty())
819240116Smarcel        m_stderr_checks.push_back(output_check(oc_empty, false, ""));
820240116Smarcel
821240116Smarcel    if ((run_status_checks(m_status_checks, *r) == false) ||
822240116Smarcel        (run_output_checks(*r, "stderr") == false) ||
823240116Smarcel        (run_output_checks(*r, "stdout") == false))
824240116Smarcel        status = EXIT_FAILURE;
825240116Smarcel    else
826240116Smarcel        status = EXIT_SUCCESS;
827240116Smarcel
828240116Smarcel    return status;
829240116Smarcel}
830240116Smarcel
831240116Smarcelint
832240116Smarcelmain(int argc, char* const* argv)
833240116Smarcel{
834240116Smarcel    return atf_check().run(argc, argv);
835240116Smarcel}
836