Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F140918415
D52642.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D52642.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D52642: kyua: Add "debug -x|--execute cmd" option
Attached
Detach File
Event Timeline
Log In to Comment