//
// Automated Testing Framework (atf)
//
// Copyright (c) 2008 The NetBSD Foundation, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
static int
parse_exit_code(const std::string& str)
{
try {
const int value = atf::text::to_type< int >(str);
if (value < 0 || value > 255)
throw std::runtime_error("Unused reason");
return value;
} catch (const std::runtime_error&) {
throw atf::application::usage_error("Invalid exit code for -s option; "
"must be an integer in range 0-255");
}
}
static int
signal_name_to_number(const std::string& str)
{
struct name_number* iter = signal_names_to_numbers;
int signo = INT_MIN;
while (signo == INT_MIN && iter->name != NULL) {
if (str == iter->name || str == std::string("sig") + iter->name)
signo = iter->signo;
else
iter++;
}
return signo;
}
static int
parse_signal(const std::string& str)
{
const int signo = signal_name_to_number(str);
if (signo == INT_MIN) {
try {
return atf::text::to_type< int >(str);
} catch (std::runtime_error &e) {
throw atf::application::usage_error("Invalid signal name or number "
"in -s option");
}
}
INV(signo != INT_MIN);
return signo;
}
status_check_t type;
if (action == "eq") {
// Deprecated; use exit instead. TODO: Remove after 0.10.
type = sc_exit;
if (negated)
throw atf::application::usage_error("Cannot negate eq checker");
negated = false;
value = parse_exit_code(value_str);
} else if (action == "exit") {
type = sc_exit;
if (value_str.empty())
value = INT_MIN;
else
value = parse_exit_code(value_str);
} else if (action == "ignore") {
if (negated)
throw atf::application::usage_error("Cannot negate ignore checker");
type = sc_ignore;
value = INT_MIN;
} else if (action == "ne") {
// Deprecated; use not-exit instead. TODO: Remove after 0.10.
type = sc_exit;
if (negated)
throw atf::application::usage_error("Cannot negate ne checker");
negated = true;
value = parse_exit_code(value_str);
} else if (action == "signal") {
type = sc_signal;
if (value_str.empty())
value = INT_MIN;
else
value = parse_signal(value_str);
} else
throw atf::application::usage_error("Invalid status checker");
output_check_t type;
if (action == "empty")
type = oc_empty;
else if (action == "file")
type = oc_file;
else if (action == "ignore") {
if (negated)
throw atf::application::usage_error("Cannot negate ignore checker");
type = oc_ignore;
} else if (action == "inline")
type = oc_inline;
else if (action == "match")
type = oc_match;
else if (action == "save") {
if (negated)
throw atf::application::usage_error("Cannot negate save checker");
type = oc_save;
} else
throw atf::application::usage_error("Invalid output checker");
char* const* arg = &argv[0];
while (*arg != NULL) {
if (arg != &argv[0])
cmdline += ' ';
cmdline += *arg;
arg++;
}
return cmdline;
}
static
std::unique_ptr< atf::check::check_result >
execute(const char* const* argv)
{
// TODO: This should go to stderr... but fixing it now may be hard as test
// cases out there might be relying on stderr being silent.
std::cout << "Executing command [ ";
for (int i = 0; argv[i] != NULL; ++i)
std::cout << argv[i] << " ";
std::cout << "]\n";
std::cout.flush();
if (c == '\\') {
switch (s[i++]) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'c': break;
case 'e': c = 033; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case '\\': break;
case '0':
{
int count = 3;
c = 0;
while (--count >= 0 && static_cast<unsigned>(s[i] - '0') < 8)
c = (c << 3) + (s[i++] - '0');
break;
}
default:
--i;
break;
}
}
for (std::vector< status_check >::const_iterator iter = checks.begin();
!ok && iter != checks.end(); iter++) {
ok |= run_status_check(*iter, result);
}
case 'o':
m_stdout_checks.push_back(parse_output_check_arg(arg));
break;
case 'e':
m_stderr_checks.push_back(parse_output_check_arg(arg));
break;
case 'x':
m_xflag = true;
break;
default:
UNREACHABLE;
}
}
int
atf_check::main(void)
{
if (m_argc < 1)
throw atf::application::usage_error("No command specified");
int status = EXIT_FAILURE;
std::unique_ptr< atf::check::check_result > r =
m_xflag ? execute_with_shell(m_argv) : execute(m_argv);
if (m_status_checks.empty())
m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS));
else if (m_status_checks.size() > 1) {
// TODO: Remove this restriction.
throw atf::application::usage_error("Cannot specify -s more than once");
}
if (m_stdout_checks.empty())
m_stdout_checks.push_back(output_check(oc_empty, false, ""));
if (m_stderr_checks.empty())
m_stderr_checks.push_back(output_check(oc_empty, false, ""));
if ((run_status_checks(m_status_checks, *r) == false) ||
(run_output_checks(*r, "stderr") == false) ||
(run_output_checks(*r, "stdout") == false))
status = EXIT_FAILURE;
else
status = EXIT_SUCCESS;