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