Page MenuHomeFreeBSD

D52642.diff
No OneTemporary

D52642.diff

diff --git a/contrib/kyua/cli/cmd_debug.cpp b/contrib/kyua/cli/cmd_debug.cpp
--- a/contrib/kyua/cli/cmd_debug.cpp
+++ b/contrib/kyua/cli/cmd_debug.cpp
@@ -28,6 +28,10 @@
#include "cli/cmd_debug.hpp"
+extern "C" {
+#include <unistd.h>
+}
+
#include <cstdlib>
#include <iostream>
@@ -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 {
@@ -62,6 +73,57 @@
"Pauses right before the test cleanup");
+static const char* DEFAULT_CMD = "$SHELL";
+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;
+ executor::exit_handle& _eh;
+
+public:
+ /// Constructor.
+ ///
+ /// \param program Program binary absolute path.
+ /// \param args Program arguments.
+ execute(
+ const std::string& cmd_,
+ executor::exit_handle& eh_) :
+ _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");
+ 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.
@@ -103,6 +165,21 @@
}
};
+ void upon_test_failure(
+ const model::test_program_ptr&,
+ const model::test_case&,
+ optional< model::test_result >&,
+ executor::exit_handle& eh) const
+ {
+ 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();
+ };
+
};
@@ -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);
}
@@ -151,7 +230,8 @@
engine::debugger_ptr debugger = nullptr;
if (cmdline.has_option(pause_before_cleanup_upon_fail_option.long_name())
- || cmdline.has_option(pause_before_cleanup_option.long_name())) {
+ || cmdline.has_option(pause_before_cleanup_option.long_name())
+ || cmdline.has_option(execute_option.long_name())) {
debugger = std::shared_ptr< engine::debugger >(new dbg(ui, cmdline));
}
diff --git a/contrib/kyua/engine/debugger.hpp b/contrib/kyua/engine/debugger.hpp
--- a/contrib/kyua/engine/debugger.hpp
+++ b/contrib/kyua/engine/debugger.hpp
@@ -58,6 +58,13 @@
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;
};
diff --git a/contrib/kyua/engine/scheduler.cpp b/contrib/kyua/engine/scheduler.cpp
--- a/contrib/kyua/engine/scheduler.cpp
+++ b/contrib/kyua/engine/scheduler.cpp
@@ -1403,6 +1403,9 @@
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) {
diff --git a/contrib/kyua/utils/cmdline/options.hpp b/contrib/kyua/utils/cmdline/options.hpp
--- a/contrib/kyua/utils/cmdline/options.hpp
+++ b/contrib/kyua/utils/cmdline/options.hpp
@@ -91,6 +91,9 @@
/// 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.
@@ -101,7 +104,7 @@
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);
@@ -113,6 +116,7 @@
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;
@@ -219,7 +223,7 @@
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) {}
diff --git a/contrib/kyua/utils/cmdline/options.cpp b/contrib/kyua/utils/cmdline/options.cpp
--- a/contrib/kyua/utils/cmdline/options.cpp
+++ b/contrib/kyua/utils/cmdline/options.cpp
@@ -53,15 +53,18 @@
/// 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_)
{
@@ -164,6 +167,16 @@
}
+/// 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.
@@ -558,9 +571,10 @@
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_)
{
}
diff --git a/contrib/kyua/utils/cmdline/parser.cpp b/contrib/kyua/utils/cmdline/parser.cpp
--- a/contrib/kyua/utils/cmdline/parser.cpp
+++ b/contrib/kyua/utils/cmdline/parser.cpp
@@ -88,7 +88,10 @@
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;
@@ -96,7 +99,7 @@
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++;
@@ -320,9 +323,11 @@
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;
@@ -357,8 +362,13 @@
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("");
}
diff --git a/contrib/kyua/utils/process/child.hpp b/contrib/kyua/utils/process/child.hpp
--- a/contrib/kyua/utils/process/child.hpp
+++ b/contrib/kyua/utils/process/child.hpp
@@ -80,6 +80,8 @@
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&);
@@ -92,6 +94,9 @@
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&);
diff --git a/contrib/kyua/utils/process/child.cpp b/contrib/kyua/utils/process/child.cpp
--- a/contrib/kyua/utils/process/child.cpp
+++ b/contrib/kyua/utils/process/child.cpp
@@ -235,6 +235,30 @@
}
+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
diff --git a/contrib/kyua/utils/process/child.ipp b/contrib/kyua/utils/process/child.ipp
--- a/contrib/kyua/utils/process/child.ipp
+++ b/contrib/kyua/utils/process/child.ipp
@@ -104,6 +104,26 @@
}
+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

File Metadata

Mime Type
text/plain
Expires
Tue, Dec 30, 3:28 PM (21 h, 57 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27386845
Default Alt Text
D52642.diff (12 KB)

Event Timeline