Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions contrib/kyua/cli/cmd_debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@

#include "cli/cmd_debug.hpp"

extern "C" {
#include <unistd.h>
}

#include <cstdlib>
#include <iostream>

Expand All @@ -39,13 +43,20 @@
#include "utils/cmdline/parser.ipp"
#include "utils/cmdline/ui.hpp"
#include "utils/format/macros.hpp"
#include "utils/fs/path.hpp"
#include "utils/process/child.ipp"
#include "utils/process/executor.hpp"
#include "utils/process/operations.hpp"
#include "utils/process/status.hpp"

namespace cmdline = utils::cmdline;
namespace config = utils::config;
namespace executor = utils::process::executor;
namespace process = utils::process;

using cli::cmd_debug;
using utils::process::args_vector;
using utils::process::child;


namespace {
Expand All @@ -62,6 +73,57 @@
"Pauses right before the test cleanup");


static const char* DEFAULT_CMD = "$SHELL";

Check failure on line 76 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

"foo* bar" should be "foo *bar"
const cmdline::string_option execute_option(
'x', "execute",
"A command to run upon test failure",
"cmd", DEFAULT_CMD, true);


/// Functor to execute a program.
class execute {
const std::string& _cmd;

Check failure on line 85 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

spaces required around that '&' (ctx:VxW)
executor::exit_handle& _eh;

Check failure on line 86 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

spaces required around that '&' (ctx:VxW)

public:
/// Constructor.
///
/// \param program Program binary absolute path.
/// \param args Program arguments.
execute(
const std::string& cmd_,

Check failure on line 94 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

spaces required around that '&' (ctx:VxW)
executor::exit_handle& eh_) :

Check failure on line 95 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

spaces required around that '&' (ctx:VxW)
_cmd(cmd_),
_eh(eh_)
{
}

/// Body of the subprocess.
void
operator()(void)
{
if (::chdir(_eh.work_directory().c_str()) == -1) {
std::cerr << "execute: chdir() errors: "
<< strerror(errno) << ".\n";
std::exit(EXIT_FAILURE);
}

std::string program_path = "/bin/sh";
const char* shell = std::getenv("SHELL");

Check failure on line 112 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

"foo* bar" should be "foo *bar"
if (shell)
program_path = shell;

args_vector av;
if (!(_cmd.empty() || _cmd == DEFAULT_CMD)) {
av.push_back("-c");
av.push_back(_cmd);
}

process::exec(utils::fs::path(program_path), av);
}
};


/// The debugger interface implementation.
class dbg : public engine::debugger {
/// Object to interact with the I/O of the program.
Expand Down Expand Up @@ -103,6 +165,21 @@
}
};

void upon_test_failure(
const model::test_program_ptr&,

Check failure on line 169 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

spaces required around that '&' (ctx:VxO)
const model::test_case&,

Check failure on line 170 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

spaces required around that '&' (ctx:VxO)
optional< model::test_result >&,

Check failure on line 171 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

space required before that '&' (ctx:OxO)
executor::exit_handle& eh) const

Check failure on line 172 in contrib/kyua/cli/cmd_debug.cpp

View workflow job for this annotation

GitHub Actions / Style Checker

spaces required around that '&' (ctx:VxW)
{
if (!_cmdline.has_option(execute_option.long_name()))
return;
const std::string& cmd = _cmdline.get_option<cmdline::string_option>(
execute_option.long_name());
std::unique_ptr< process::child > child = child::fork_interactive(
execute(cmd, eh));
(void) child->wait();
};

};


Expand All @@ -127,6 +204,8 @@
add_option(cmdline::path_option(
"stderr", "Where to direct the standard error of the test case",
"path", "/dev/stderr"));

add_option(execute_option);
}


Expand Down
7 changes: 7 additions & 0 deletions contrib/kyua/engine/debugger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ class debugger {
const model::test_case&,
optional< model::test_result >&,
executor::exit_handle&) const = 0;

/// Called upon test failure.
virtual void upon_test_failure(
const model::test_program_ptr&,
const model::test_case&,
optional< model::test_result >&,
executor::exit_handle&) const = 0;
};


Expand Down
3 changes: 3 additions & 0 deletions contrib/kyua/engine/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,9 @@ scheduler::scheduler_handle::wait_any(void)
if (debugger) {
debugger->before_cleanup(test_data->test_program, test_case,
result, handle);
if (!result.get().good())
debugger->upon_test_failure(test_data->test_program, test_case,
result, handle);
}

if (test_data->needs_cleanup) {
Expand Down
20 changes: 17 additions & 3 deletions contrib/kyua/utils/cmdline/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,18 @@ namespace text = utils::text;
/// purposes.
/// \param default_value_ If not NULL, specifies that the option has a default
/// value for the mandatory argument.
/// \param arg_is_optional_ Specifies if a value must be provided or not.
cmdline::base_option::base_option(const char short_name_,
const char* long_name_,
const char* description_,
const char* arg_name_,
const char* default_value_) :
const char* default_value_,
bool arg_is_optional_) :
_short_name(short_name_),
_long_name(long_name_),
_description(description_),
_arg_name(arg_name_ == NULL ? "" : arg_name_),
_arg_is_optional(arg_is_optional_),
_has_default_value(default_value_ != NULL),
_default_value(default_value_ == NULL ? "" : default_value_)
{
Expand Down Expand Up @@ -164,6 +167,16 @@ cmdline::base_option::arg_name(void) const
}


/// Returns optionality of the argument.
///
/// \return The optionality.
bool
cmdline::base_option::arg_is_optional(void) const
{
return _arg_is_optional;
}


/// Checks whether the option has a default value for its argument.
///
/// \pre needs_arg() must be true.
Expand Down Expand Up @@ -558,9 +571,10 @@ cmdline::string_option::string_option(const char short_name_,
const char* long_name_,
const char* description_,
const char* arg_name_,
const char* default_value_) :
const char* default_value_,
bool arg_is_optional_) :
base_option(short_name_, long_name_, description_, arg_name_,
default_value_)
default_value_, arg_is_optional_)
{
}

Expand Down
8 changes: 6 additions & 2 deletions contrib/kyua/utils/cmdline/options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class base_option {
/// Descriptive name of the required argument; empty if not allowed.
std::string _arg_name;

/// If the option can be used without an explicit argument provided.
bool _arg_is_optional = false;

/// Whether the option has a default value or not.
///
/// \todo We should probably be using the optional class here.
Expand All @@ -101,7 +104,7 @@ class base_option {

public:
base_option(const char, const char*, const char*, const char* = NULL,
const char* = NULL);
const char* = NULL, bool = false);
base_option(const char*, const char*, const char* = NULL,
const char* = NULL);
virtual ~base_option(void);
Expand All @@ -113,6 +116,7 @@ class base_option {

bool needs_arg(void) const;
const std::string& arg_name(void) const;
bool arg_is_optional(void) const;

bool has_default_value(void) const;
const std::string& default_value(void) const;
Expand Down Expand Up @@ -219,7 +223,7 @@ class property_option : public base_option {
class string_option : public base_option {
public:
string_option(const char, const char*, const char*, const char*,
const char* = NULL);
const char* = NULL, bool = false);
string_option(const char*, const char*, const char*, const char* = NULL);
virtual ~string_option(void) {}

Expand Down
20 changes: 15 additions & 5 deletions contrib/kyua/utils/cmdline/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,18 @@ options_to_getopt_data(const cmdline::options_vector& options,

long_option.name = option->long_name().c_str();
if (option->needs_arg())
long_option.has_arg = required_argument;
if (option->arg_is_optional())
long_option.has_arg = optional_argument;
else
long_option.has_arg = required_argument;
else
long_option.has_arg = no_argument;

int id = -1;
if (option->has_short_name()) {
data.short_options += option->short_name();
if (option->needs_arg())
data.short_options += ':';
data.short_options += option->arg_is_optional() ? "::" : ":";
id = option->short_name();
} else {
id = cur_id++;
Expand Down Expand Up @@ -320,9 +323,11 @@ cmdline::parse(const int argc, const char* const* argv,
for (cmdline::options_vector::const_iterator iter = options.begin();
iter != options.end(); iter++) {
const cmdline::base_option* option = *iter;
if (option->needs_arg() && option->has_default_value())
if (option->needs_arg() && option->has_default_value() &&
!option->arg_is_optional()) {
option_values[option->long_name()].push_back(
option->default_value());
}
}

args_vector args;
Expand Down Expand Up @@ -357,8 +362,13 @@ cmdline::parse(const int argc, const char* const* argv,
if (::optarg != NULL) {
option->validate(::optarg);
option_values[option->long_name()].push_back(::optarg);
} else
INV(option->has_default_value());
} else {
if (option->arg_is_optional())
option_values[option->long_name()].push_back(
option->default_value());
else
INV(option->has_default_value());
}
} else {
option_values[option->long_name()].push_back("");
}
Expand Down
24 changes: 24 additions & 0 deletions contrib/kyua/utils/process/child.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,30 @@ process::child::fork_capture_aux(void)
}


std::unique_ptr< process::child >
process::child::fork_interactive(void)
{
std::cout.flush();
std::cerr.flush();

std::unique_ptr< signals::interrupts_inhibiter > inhibiter(
new signals::interrupts_inhibiter);
pid_t pid = detail::syscall_fork();
if (pid == -1) {
inhibiter.reset(); // Unblock signals.
throw process::system_error("fork(2) failed", errno);
} else if (pid == 0) {
inhibiter.reset(); // Unblock signals.
return {};
} else {
signals::add_pid_to_kill(pid);
inhibiter.reset(NULL); // Unblock signals.
return std::unique_ptr< process::child >(
new process::child(new impl(pid, NULL)));
}
}


/// Helper function for fork().
///
/// Please note: if you update this function to change the return type or to
Expand Down
5 changes: 5 additions & 0 deletions contrib/kyua/utils/process/child.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class child : noncopyable {

static std::unique_ptr< child > fork_capture_aux(void);

static std::unique_ptr< child > fork_interactive(void);

static std::unique_ptr< child > fork_files_aux(const fs::path&,
const fs::path&);

Expand All @@ -92,6 +94,9 @@ class child : noncopyable {
static std::unique_ptr< child > fork_capture(Hook);
std::istream& output(void);

template< typename Hook >
static std::unique_ptr< child > fork_interactive(Hook);

template< typename Hook >
static std::unique_ptr< child > fork_files(Hook, const fs::path&,
const fs::path&);
Expand Down
20 changes: 20 additions & 0 deletions contrib/kyua/utils/process/child.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@ child::fork_capture(Hook hook)
}


template< typename Hook >
std::unique_ptr< child >
child::fork_interactive(Hook hook)
{
std::unique_ptr< child > child = fork_interactive();
if (child.get() == NULL) {
try {
hook();
std::abort();
} catch (const std::runtime_error& e) {
detail::report_error_and_abort(e);
} catch (...) {
detail::report_error_and_abort();
}
}

return child;
}


} // namespace process
} // namespace utils

Expand Down
Loading