atf-check.cpp revision 258289
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 53240116Smarcel#include "atf-c++/detail/application.hpp" 54258289Sjmmv#include "atf-c++/detail/auto_array.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 { 119258289Sjmmv atf::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{ 334258289Sjmmv // TODO: This should go to stderr... but fixing it now may be hard as test 335258289Sjmmv // cases out there might be relying on stderr being silent. 336240116Smarcel std::cout << "Executing command [ "; 337240116Smarcel for (int i = 0; argv[i] != NULL; ++i) 338240116Smarcel std::cout << argv[i] << " "; 339240116Smarcel std::cout << "]\n"; 340258289Sjmmv std::cout.flush(); 341240116Smarcel 342240116Smarcel atf::process::argv_array argva(argv); 343240116Smarcel return atf::check::exec(argva); 344240116Smarcel} 345240116Smarcel 346240116Smarcelstatic 347240116Smarcelstd::auto_ptr< atf::check::check_result > 348240116Smarcelexecute_with_shell(char* const* argv) 349240116Smarcel{ 350240116Smarcel const std::string cmd = flatten_argv(argv); 351240116Smarcel 352240116Smarcel const char* sh_argv[4]; 353240116Smarcel sh_argv[0] = atf::config::get("atf_shell").c_str(); 354240116Smarcel sh_argv[1] = "-c"; 355240116Smarcel sh_argv[2] = cmd.c_str(); 356240116Smarcel sh_argv[3] = NULL; 357240116Smarcel return execute(sh_argv); 358240116Smarcel} 359240116Smarcel 360240116Smarcelstatic 361240116Smarcelvoid 362240116Smarcelcat_file(const atf::fs::path& path) 363240116Smarcel{ 364240116Smarcel std::ifstream stream(path.c_str()); 365240116Smarcel if (!stream) 366240116Smarcel throw std::runtime_error("Failed to open " + path.str()); 367240116Smarcel 368240116Smarcel stream >> std::noskipws; 369240116Smarcel std::istream_iterator< char > begin(stream), end; 370240116Smarcel std::ostream_iterator< char > out(std::cerr); 371240116Smarcel std::copy(begin, end, out); 372240116Smarcel 373240116Smarcel stream.close(); 374240116Smarcel} 375240116Smarcel 376240116Smarcelstatic 377240116Smarcelbool 378240116Smarcelgrep_file(const atf::fs::path& path, const std::string& regexp) 379240116Smarcel{ 380240116Smarcel std::ifstream stream(path.c_str()); 381240116Smarcel if (!stream) 382240116Smarcel throw std::runtime_error("Failed to open " + path.str()); 383240116Smarcel 384240116Smarcel bool found = false; 385240116Smarcel 386240116Smarcel std::string line; 387240116Smarcel while (!found && !std::getline(stream, line).fail()) { 388240116Smarcel if (atf::text::match(line, regexp)) 389240116Smarcel found = true; 390240116Smarcel } 391240116Smarcel 392240116Smarcel stream.close(); 393240116Smarcel 394240116Smarcel return found; 395240116Smarcel} 396240116Smarcel 397240116Smarcelstatic 398240116Smarcelbool 399240116Smarcelfile_empty(const atf::fs::path& p) 400240116Smarcel{ 401240116Smarcel atf::fs::file_info f(p); 402240116Smarcel 403240116Smarcel return (f.get_size() == 0); 404240116Smarcel} 405240116Smarcel 406240116Smarcelstatic bool 407240116Smarcelcompare_files(const atf::fs::path& p1, const atf::fs::path& p2) 408240116Smarcel{ 409240116Smarcel bool equal = false; 410240116Smarcel 411240116Smarcel std::ifstream f1(p1.c_str()); 412240116Smarcel if (!f1) 413240116Smarcel throw std::runtime_error("Failed to open " + p1.str()); 414240116Smarcel 415240116Smarcel std::ifstream f2(p2.c_str()); 416240116Smarcel if (!f2) 417240116Smarcel throw std::runtime_error("Failed to open " + p1.str()); 418240116Smarcel 419240116Smarcel for (;;) { 420240116Smarcel char buf1[512], buf2[512]; 421240116Smarcel 422240116Smarcel f1.read(buf1, sizeof(buf1)); 423240116Smarcel if (f1.bad()) 424240116Smarcel throw std::runtime_error("Failed to read from " + p1.str()); 425240116Smarcel 426240116Smarcel f2.read(buf2, sizeof(buf2)); 427240116Smarcel if (f2.bad()) 428240116Smarcel throw std::runtime_error("Failed to read from " + p1.str()); 429240116Smarcel 430240116Smarcel if ((f1.gcount() == 0) && (f2.gcount() == 0)) { 431240116Smarcel equal = true; 432240116Smarcel break; 433240116Smarcel } 434240116Smarcel 435240116Smarcel if ((f1.gcount() != f2.gcount()) || 436240116Smarcel (std::memcmp(buf1, buf2, f1.gcount()) != 0)) { 437240116Smarcel break; 438240116Smarcel } 439240116Smarcel } 440240116Smarcel 441240116Smarcel return equal; 442240116Smarcel} 443240116Smarcel 444240116Smarcelstatic 445240116Smarcelvoid 446240116Smarcelprint_diff(const atf::fs::path& p1, const atf::fs::path& p2) 447240116Smarcel{ 448240116Smarcel const atf::process::status s = 449240116Smarcel atf::process::exec(atf::fs::path("diff"), 450240116Smarcel atf::process::argv_array("diff", "-u", p1.c_str(), 451240116Smarcel p2.c_str(), NULL), 452240116Smarcel atf::process::stream_connect(STDOUT_FILENO, 453240116Smarcel STDERR_FILENO), 454240116Smarcel atf::process::stream_inherit()); 455240116Smarcel 456240116Smarcel if (!s.exited()) 457240116Smarcel std::cerr << "Failed to run diff(3)\n"; 458240116Smarcel 459240116Smarcel if (s.exitstatus() != 1) 460240116Smarcel std::cerr << "Error while running diff(3)\n"; 461240116Smarcel} 462240116Smarcel 463240116Smarcelstatic 464240116Smarcelstd::string 465240116Smarceldecode(const std::string& s) 466240116Smarcel{ 467240116Smarcel size_t i; 468240116Smarcel std::string res; 469240116Smarcel 470240116Smarcel res.reserve(s.length()); 471240116Smarcel 472240116Smarcel i = 0; 473240116Smarcel while (i < s.length()) { 474240116Smarcel char c = s[i++]; 475240116Smarcel 476240116Smarcel if (c == '\\') { 477240116Smarcel switch (s[i++]) { 478240116Smarcel case 'a': c = '\a'; break; 479240116Smarcel case 'b': c = '\b'; break; 480240116Smarcel case 'c': break; 481240116Smarcel case 'e': c = 033; break; 482240116Smarcel case 'f': c = '\f'; break; 483240116Smarcel case 'n': c = '\n'; break; 484240116Smarcel case 'r': c = '\r'; break; 485240116Smarcel case 't': c = '\t'; break; 486240116Smarcel case 'v': c = '\v'; break; 487240116Smarcel case '\\': break; 488240116Smarcel case '0': 489240116Smarcel { 490240116Smarcel int count = 3; 491240116Smarcel c = 0; 492240116Smarcel while (--count >= 0 && (unsigned)(s[i] - '0') < 8) 493240116Smarcel c = (c << 3) + (s[i++] - '0'); 494240116Smarcel break; 495240116Smarcel } 496240116Smarcel default: 497240116Smarcel --i; 498240116Smarcel break; 499240116Smarcel } 500240116Smarcel } 501240116Smarcel 502240116Smarcel res.push_back(c); 503240116Smarcel } 504240116Smarcel 505240116Smarcel return res; 506240116Smarcel} 507240116Smarcel 508240116Smarcelstatic 509240116Smarcelbool 510240116Smarcelrun_status_check(const status_check& sc, const atf::check::check_result& cr) 511240116Smarcel{ 512240116Smarcel bool result; 513240116Smarcel 514240116Smarcel if (sc.type == sc_exit) { 515240116Smarcel if (cr.exited() && sc.value != INT_MIN) { 516240116Smarcel const int status = cr.exitcode(); 517240116Smarcel 518240116Smarcel if (!sc.negated && sc.value != status) { 519240116Smarcel std::cerr << "Fail: incorrect exit status: " 520240116Smarcel << status << ", expected: " 521240116Smarcel << sc.value << "\n"; 522240116Smarcel result = false; 523240116Smarcel } else if (sc.negated && sc.value == status) { 524240116Smarcel std::cerr << "Fail: incorrect exit status: " 525240116Smarcel << status << ", expected: " 526240116Smarcel << "anything else\n"; 527240116Smarcel result = false; 528240116Smarcel } else 529240116Smarcel result = true; 530240116Smarcel } else if (cr.exited() && sc.value == INT_MIN) { 531240116Smarcel result = true; 532240116Smarcel } else { 533240116Smarcel std::cerr << "Fail: program did not exit cleanly\n"; 534240116Smarcel result = false; 535240116Smarcel } 536240116Smarcel } else if (sc.type == sc_ignore) { 537240116Smarcel result = true; 538240116Smarcel } else if (sc.type == sc_signal) { 539240116Smarcel if (cr.signaled() && sc.value != INT_MIN) { 540240116Smarcel const int status = cr.termsig(); 541240116Smarcel 542240116Smarcel if (!sc.negated && sc.value != status) { 543240116Smarcel std::cerr << "Fail: incorrect signal received: " 544240116Smarcel << status << ", expected: " << sc.value << "\n"; 545240116Smarcel result = false; 546240116Smarcel } else if (sc.negated && sc.value == status) { 547240116Smarcel std::cerr << "Fail: incorrect signal received: " 548240116Smarcel << status << ", expected: " 549240116Smarcel << "anything else\n"; 550240116Smarcel result = false; 551240116Smarcel } else 552240116Smarcel result = true; 553240116Smarcel } else if (cr.signaled() && sc.value == INT_MIN) { 554240116Smarcel result = true; 555240116Smarcel } else { 556240116Smarcel std::cerr << "Fail: program did not receive a signal\n"; 557240116Smarcel result = false; 558240116Smarcel } 559240116Smarcel } else { 560240116Smarcel UNREACHABLE; 561240116Smarcel result = false; 562240116Smarcel } 563240116Smarcel 564240116Smarcel if (result == false) { 565240116Smarcel std::cerr << "stdout:\n"; 566240116Smarcel cat_file(atf::fs::path(cr.stdout_path())); 567240116Smarcel std::cerr << "\n"; 568240116Smarcel 569240116Smarcel std::cerr << "stderr:\n"; 570240116Smarcel cat_file(atf::fs::path(cr.stderr_path())); 571240116Smarcel std::cerr << "\n"; 572240116Smarcel } 573240116Smarcel 574240116Smarcel return result; 575240116Smarcel} 576240116Smarcel 577240116Smarcelstatic 578240116Smarcelbool 579240116Smarcelrun_status_checks(const std::vector< status_check >& checks, 580240116Smarcel const atf::check::check_result& result) 581240116Smarcel{ 582240116Smarcel bool ok = false; 583240116Smarcel 584240116Smarcel for (std::vector< status_check >::const_iterator iter = checks.begin(); 585240116Smarcel !ok && iter != checks.end(); iter++) { 586240116Smarcel ok |= run_status_check(*iter, result); 587240116Smarcel } 588240116Smarcel 589240116Smarcel return ok; 590240116Smarcel} 591240116Smarcel 592240116Smarcelstatic 593240116Smarcelbool 594240116Smarcelrun_output_check(const output_check oc, const atf::fs::path& path, 595240116Smarcel const std::string& stdxxx) 596240116Smarcel{ 597240116Smarcel bool result; 598240116Smarcel 599240116Smarcel if (oc.type == oc_empty) { 600240116Smarcel const bool is_empty = file_empty(path); 601240116Smarcel if (!oc.negated && !is_empty) { 602240116Smarcel std::cerr << "Fail: " << stdxxx << " not empty\n"; 603240116Smarcel print_diff(atf::fs::path("/dev/null"), path); 604240116Smarcel result = false; 605240116Smarcel } else if (oc.negated && is_empty) { 606240116Smarcel std::cerr << "Fail: " << stdxxx << " is empty\n"; 607240116Smarcel result = false; 608240116Smarcel } else 609240116Smarcel result = true; 610240116Smarcel } else if (oc.type == oc_file) { 611240116Smarcel const bool equals = compare_files(path, atf::fs::path(oc.value)); 612240116Smarcel if (!oc.negated && !equals) { 613240116Smarcel std::cerr << "Fail: " << stdxxx << " does not match golden " 614240116Smarcel "output\n"; 615240116Smarcel print_diff(atf::fs::path(oc.value), path); 616240116Smarcel result = false; 617240116Smarcel } else if (oc.negated && equals) { 618240116Smarcel std::cerr << "Fail: " << stdxxx << " matches golden output\n"; 619240116Smarcel cat_file(atf::fs::path(oc.value)); 620240116Smarcel result = false; 621240116Smarcel } else 622240116Smarcel result = true; 623240116Smarcel } else if (oc.type == oc_ignore) { 624240116Smarcel result = true; 625240116Smarcel } else if (oc.type == oc_inline) { 626240116Smarcel atf::fs::path path2 = atf::fs::path(atf::config::get("atf_workdir")) 627240116Smarcel / "inline.XXXXXX"; 628240116Smarcel temp_file temp(path2); 629240116Smarcel temp.write(decode(oc.value)); 630240116Smarcel temp.close(); 631240116Smarcel 632240116Smarcel const bool equals = compare_files(path, temp.get_path()); 633240116Smarcel if (!oc.negated && !equals) { 634240116Smarcel std::cerr << "Fail: " << stdxxx << " does not match expected " 635240116Smarcel "value\n"; 636240116Smarcel print_diff(temp.get_path(), path); 637240116Smarcel result = false; 638240116Smarcel } else if (oc.negated && equals) { 639240116Smarcel std::cerr << "Fail: " << stdxxx << " matches expected value\n"; 640240116Smarcel cat_file(temp.get_path()); 641240116Smarcel result = false; 642240116Smarcel } else 643240116Smarcel result = true; 644240116Smarcel } else if (oc.type == oc_match) { 645240116Smarcel const bool matches = grep_file(path, oc.value); 646240116Smarcel if (!oc.negated && !matches) { 647240116Smarcel std::cerr << "Fail: regexp " + oc.value + " not in " << stdxxx 648240116Smarcel << "\n"; 649240116Smarcel cat_file(path); 650240116Smarcel result = false; 651240116Smarcel } else if (oc.negated && matches) { 652240116Smarcel std::cerr << "Fail: regexp " + oc.value + " is in " << stdxxx 653240116Smarcel << "\n"; 654240116Smarcel cat_file(path); 655240116Smarcel result = false; 656240116Smarcel } else 657240116Smarcel result = true; 658240116Smarcel } else if (oc.type == oc_save) { 659240116Smarcel INV(!oc.negated); 660240116Smarcel std::ifstream ifs(path.c_str(), std::fstream::binary); 661240116Smarcel ifs >> std::noskipws; 662240116Smarcel std::istream_iterator< char > begin(ifs), end; 663240116Smarcel 664240116Smarcel std::ofstream ofs(oc.value.c_str(), std::fstream::binary 665240116Smarcel | std::fstream::trunc); 666240116Smarcel std::ostream_iterator <char> obegin(ofs); 667240116Smarcel 668240116Smarcel std::copy(begin, end, obegin); 669240116Smarcel result = true; 670240116Smarcel } else { 671240116Smarcel UNREACHABLE; 672240116Smarcel result = false; 673240116Smarcel } 674240116Smarcel 675240116Smarcel return result; 676240116Smarcel} 677240116Smarcel 678240116Smarcelstatic 679240116Smarcelbool 680240116Smarcelrun_output_checks(const std::vector< output_check >& checks, 681240116Smarcel const atf::fs::path& path, const std::string& stdxxx) 682240116Smarcel{ 683240116Smarcel bool ok = true; 684240116Smarcel 685240116Smarcel for (std::vector< output_check >::const_iterator iter = checks.begin(); 686240116Smarcel iter != checks.end(); iter++) { 687240116Smarcel ok &= run_output_check(*iter, path, stdxxx); 688240116Smarcel } 689240116Smarcel 690240116Smarcel return ok; 691240116Smarcel} 692240116Smarcel 693240116Smarcel// ------------------------------------------------------------------------ 694240116Smarcel// The "atf_check" application. 695240116Smarcel// ------------------------------------------------------------------------ 696240116Smarcel 697240116Smarcelnamespace { 698240116Smarcel 699240116Smarcelclass atf_check : public atf::application::app { 700240116Smarcel bool m_xflag; 701240116Smarcel 702240116Smarcel std::vector< status_check > m_status_checks; 703240116Smarcel std::vector< output_check > m_stdout_checks; 704240116Smarcel std::vector< output_check > m_stderr_checks; 705240116Smarcel 706240116Smarcel static const char* m_description; 707240116Smarcel 708240116Smarcel bool run_output_checks(const atf::check::check_result&, 709240116Smarcel const std::string&) const; 710240116Smarcel 711240116Smarcel std::string specific_args(void) const; 712240116Smarcel options_set specific_options(void) const; 713240116Smarcel void process_option(int, const char*); 714240116Smarcel void process_option_s(const std::string&); 715240116Smarcel 716240116Smarcelpublic: 717240116Smarcel atf_check(void); 718240116Smarcel int main(void); 719240116Smarcel}; 720240116Smarcel 721240116Smarcel} // anonymous namespace 722240116Smarcel 723240116Smarcelconst char* atf_check::m_description = 724240116Smarcel "atf-check executes given command and analyzes its results."; 725240116Smarcel 726240116Smarcelatf_check::atf_check(void) : 727240116Smarcel app(m_description, "atf-check(1)", "atf(7)"), 728240116Smarcel m_xflag(false) 729240116Smarcel{ 730240116Smarcel} 731240116Smarcel 732240116Smarcelbool 733240116Smarcelatf_check::run_output_checks(const atf::check::check_result& r, 734240116Smarcel const std::string& stdxxx) 735240116Smarcel const 736240116Smarcel{ 737240116Smarcel if (stdxxx == "stdout") { 738240116Smarcel return ::run_output_checks(m_stdout_checks, 739240116Smarcel atf::fs::path(r.stdout_path()), "stdout"); 740240116Smarcel } else if (stdxxx == "stderr") { 741240116Smarcel return ::run_output_checks(m_stderr_checks, 742240116Smarcel atf::fs::path(r.stderr_path()), "stderr"); 743240116Smarcel } else { 744240116Smarcel UNREACHABLE; 745240116Smarcel return false; 746240116Smarcel } 747240116Smarcel} 748240116Smarcel 749240116Smarcelstd::string 750240116Smarcelatf_check::specific_args(void) 751240116Smarcel const 752240116Smarcel{ 753240116Smarcel return "<command>"; 754240116Smarcel} 755240116Smarcel 756240116Smarcelatf_check::options_set 757240116Smarcelatf_check::specific_options(void) 758240116Smarcel const 759240116Smarcel{ 760240116Smarcel using atf::application::option; 761240116Smarcel options_set opts; 762240116Smarcel 763240116Smarcel opts.insert(option('s', "qual:value", "Handle status. Qualifier " 764240116Smarcel "must be one of: ignore exit:<num> signal:<name|num>")); 765240116Smarcel opts.insert(option('o', "action:arg", "Handle stdout. Action must be " 766240116Smarcel "one of: empty ignore file:<path> inline:<val> match:regexp " 767240116Smarcel "save:<path>")); 768240116Smarcel opts.insert(option('e', "action:arg", "Handle stderr. Action must be " 769240116Smarcel "one of: empty ignore file:<path> inline:<val> match:regexp " 770240116Smarcel "save:<path>")); 771240116Smarcel opts.insert(option('x', "", "Execute command as a shell command")); 772240116Smarcel 773240116Smarcel return opts; 774240116Smarcel} 775240116Smarcel 776240116Smarcelvoid 777240116Smarcelatf_check::process_option(int ch, const char* arg) 778240116Smarcel{ 779240116Smarcel switch (ch) { 780240116Smarcel case 's': 781240116Smarcel m_status_checks.push_back(parse_status_check_arg(arg)); 782240116Smarcel break; 783240116Smarcel 784240116Smarcel case 'o': 785240116Smarcel m_stdout_checks.push_back(parse_output_check_arg(arg)); 786240116Smarcel break; 787240116Smarcel 788240116Smarcel case 'e': 789240116Smarcel m_stderr_checks.push_back(parse_output_check_arg(arg)); 790240116Smarcel break; 791240116Smarcel 792240116Smarcel case 'x': 793240116Smarcel m_xflag = true; 794240116Smarcel break; 795240116Smarcel 796240116Smarcel default: 797240116Smarcel UNREACHABLE; 798240116Smarcel } 799240116Smarcel} 800240116Smarcel 801240116Smarcelint 802240116Smarcelatf_check::main(void) 803240116Smarcel{ 804240116Smarcel if (m_argc < 1) 805240116Smarcel throw atf::application::usage_error("No command specified"); 806240116Smarcel 807240116Smarcel int status = EXIT_FAILURE; 808240116Smarcel 809240116Smarcel std::auto_ptr< atf::check::check_result > r = 810240116Smarcel m_xflag ? execute_with_shell(m_argv) : execute(m_argv); 811240116Smarcel 812240116Smarcel if (m_status_checks.empty()) 813240116Smarcel m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS)); 814240116Smarcel else if (m_status_checks.size() > 1) { 815240116Smarcel // TODO: Remove this restriction. 816240116Smarcel throw atf::application::usage_error("Cannot specify -s more than once"); 817240116Smarcel } 818240116Smarcel 819240116Smarcel if (m_stdout_checks.empty()) 820240116Smarcel m_stdout_checks.push_back(output_check(oc_empty, false, "")); 821240116Smarcel if (m_stderr_checks.empty()) 822240116Smarcel m_stderr_checks.push_back(output_check(oc_empty, false, "")); 823240116Smarcel 824240116Smarcel if ((run_status_checks(m_status_checks, *r) == false) || 825240116Smarcel (run_output_checks(*r, "stderr") == false) || 826240116Smarcel (run_output_checks(*r, "stdout") == false)) 827240116Smarcel status = EXIT_FAILURE; 828240116Smarcel else 829240116Smarcel status = EXIT_SUCCESS; 830240116Smarcel 831240116Smarcel return status; 832240116Smarcel} 833240116Smarcel 834240116Smarcelint 835240116Smarcelmain(int argc, char* const* argv) 836240116Smarcel{ 837240116Smarcel return atf_check().run(argc, argv); 838240116Smarcel} 839