Browse Source

Set timeouts on test execution

default_compile_flags
vector-of-bool 4 years ago
parent
commit
1d64689ae2
6 changed files with 74 additions and 20 deletions
  1. +1
    -1
      src/dds/build/builder.cpp
  2. +8
    -3
      src/dds/build/plan/exe.cpp
  3. +17
    -1
      src/dds/proc.hpp
  4. +16
    -5
      src/dds/proc.nix.cpp
  5. +27
    -5
      src/dds/proc.win.cpp
  6. +5
    -5
      tools/gcc-9.next.jsonc

+ 1
- 1
src/dds/build/builder.cpp View File

}; };


void log_failure(const test_failure& fail) { void log_failure(const test_failure& fail) {
spdlog::error("Test '{}' failed! [exitted {}]", fail.executable_path.string(), fail.retc);
spdlog::error("Test '{}' failed! [exited {}]", fail.executable_path.string(), fail.retc);
if (fail.signal) { if (fail.signal) {
spdlog::error("Test execution received signal {}", fail.signal); spdlog::error("Test execution received signal {}", fail.signal);
} }

+ 8
- 3
src/dds/build/plan/exe.cpp View File

auto exe_path = calc_executable_path(env); auto exe_path = calc_executable_path(env);
auto msg = fmt::format("Run test: {:30}", fs::relative(exe_path, env.output_root).string()); auto msg = fmt::format("Run test: {:30}", fs::relative(exe_path, env.output_root).string());
spdlog::info(msg); spdlog::info(msg);
auto&& [dur, res]
= timed<std::chrono::microseconds>([&] { return run_proc({exe_path.string()}); });
using namespace std::chrono_literals;
auto&& [dur, res] = timed<std::chrono::microseconds>(
[&] { return run_proc({.command = {exe_path.string()}, .timeout = 10s}); });

if (res.okay()) { if (res.okay()) {
spdlog::info("{} - PASSED - {:>9n}μs", msg, dur.count()); spdlog::info("{} - PASSED - {:>9n}μs", msg, dur.count());
return std::nullopt; return std::nullopt;
} else { } else {
spdlog::error("{} - FAILED - {:>9n}μs [exited {}]", msg, dur.count(), res.retc);
auto exit_msg = fmt::format(res.signal ? "signalled {}" : "exited {}",
res.signal ? res.signal : res.retc);
auto fail_str = res.timed_out ? "TIMEOUT" : "FAILED ";
spdlog::error("{} - {} - {:>9n}μs [{}]", msg, fail_str, dur.count(), exit_msg);
test_failure f; test_failure f;
f.executable_path = exe_path; f.executable_path = exe_path;
f.output = res.output; f.output = res.output;

+ 17
- 1
src/dds/proc.hpp View File

#pragma once #pragma once


#include <chrono>
#include <optional>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
struct proc_result { struct proc_result {
int signal = 0; int signal = 0;
int retc = 0; int retc = 0;
bool timed_out = false;
std::string output; std::string output;


bool okay() const noexcept { return retc == 0 && signal == 0; } bool okay() const noexcept { return retc == 0 && signal == 0; }
}; };


proc_result run_proc(const std::vector<std::string>& args);
struct proc_options {
std::vector<std::string> command;

/**
* Timeout for the subprocess, in milliseconds. If zero, will wait forever
*/
std::optional<std::chrono::milliseconds> timeout = std::nullopt;
};

proc_result run_proc(const proc_options& opts);

inline proc_result run_proc(std::vector<std::string> args) {
return run_proc(proc_options{.command = std::move(args)});
}


} // namespace dds } // namespace dds

+ 16
- 5
src/dds/proc.nix.cpp View File

#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>


#include <poll.h> #include <poll.h>
#include <signal.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>




} // namespace } // namespace


proc_result dds::run_proc(const std::vector<std::string>& command) {
spdlog::debug("Spawning subprocess: {}", quote_command(command));
proc_result dds::run_proc(const proc_options& opts) {
spdlog::debug("Spawning subprocess: {}", quote_command(opts.command));
int stdio_pipe[2] = {}; int stdio_pipe[2] = {};
auto rc = ::pipe(stdio_pipe); auto rc = ::pipe(stdio_pipe);
check_rc(rc == 0, "Create stdio pipe for subprocess"); check_rc(rc == 0, "Create stdio pipe for subprocess");
int read_pipe = stdio_pipe[0]; int read_pipe = stdio_pipe[0];
int write_pipe = stdio_pipe[1]; int write_pipe = stdio_pipe[1];


auto child = spawn_child(command, write_pipe, read_pipe);
auto child = spawn_child(opts.command, write_pipe, read_pipe);


::close(write_pipe); ::close(write_pipe);




proc_result res; proc_result res;


using namespace std::chrono_literals;

auto timeout = opts.timeout;
while (true) { while (true) {
rc = ::poll(&stdio_fd, 1, -1);
rc = ::poll(&stdio_fd, 1, static_cast<int>(timeout.value_or(-1ms).count()));
if (rc && errno == EINTR) { if (rc && errno == EINTR) {
errno = 0; errno = 0;
continue; continue;
} }
check_rc(rc > 0, "Failed in poll()");
if (rc == 0) {
// Timeout!
::kill(child, SIGINT);
timeout = std::nullopt;
res.timed_out = true;
spdlog::debug("Subprocess [{}] timed out", quote_command(opts.command));
continue;
}
std::string buffer; std::string buffer;
buffer.resize(1024); buffer.resize(1024);
auto nread = ::read(stdio_fd.fd, buffer.data(), buffer.size()); auto nread = ::read(stdio_fd.fd, buffer.data(), buffer.size());

+ 27
- 5
src/dds/proc.win.cpp View File

#ifdef _WIN32 #ifdef _WIN32
#include "./proc.hpp" #include "./proc.hpp"


#include <neo/assert.hpp>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <wil/resource.h> #include <wil/resource.h>


#include <stdexcept> #include <stdexcept>


using namespace dds; using namespace dds;
using namespace std::chrono_literals;


namespace { namespace {




} // namespace } // namespace


proc_result dds::run_proc(const std::vector<std::string>& cmd) {
auto cmd_str = quote_command(cmd);
proc_result dds::run_proc(const proc_options& opts) {
auto cmd_str = quote_command(opts.command);


::SECURITY_ATTRIBUTES security = {}; ::SECURITY_ATTRIBUTES security = {};
security.bInheritHandle = TRUE; security.bInheritHandle = TRUE;
} }


::SetHandleInformation(reader.get(), HANDLE_FLAG_INHERIT, 0); ::SetHandleInformation(reader.get(), HANDLE_FLAG_INHERIT, 0);
::COMMTIMEOUTS timeouts;
::GetCommTimeouts(reader.get(), &timeouts);


wil::unique_process_information proc_info; wil::unique_process_information proc_info;


nullptr, nullptr,
nullptr, nullptr,
true, true,
0,
CREATE_NEW_PROCESS_GROUP,
nullptr, nullptr,
nullptr, nullptr,
&startup_info, &startup_info,
writer.reset(); writer.reset();


std::string output; std::string output;
proc_result res;

auto timeout = opts.timeout;
while (true) { while (true) {
const int buffer_size = 256; const int buffer_size = 256;
char buffer[buffer_size]; char buffer[buffer_size];
DWORD nread = 0; DWORD nread = 0;
okay = ::ReadFile(reader.get(), buffer, buffer_size, &nread, nullptr);
// Reload the timeout on the pipe
timeouts.ReadTotalTimeoutConstant = static_cast<DWORD>(timeout.value_or(0ms).count());
::SetCommTimeouts(reader.get(), &timeouts);
// Read some bytes from the process
okay = ::ReadFile(reader.get(), buffer, buffer_size, &nread, nullptr);
if (!okay && ::GetLastError() == ERROR_TIMEOUT) {
// We didn't read any bytes. Hit the timeout
neo_assert_always(invariant,
nread == 0,
"Didn't expect to read bytes when a timeout was reached",
nread,
timeout->count());
res.timed_out = true;
timeout = std::nullopt;
::GenerateConsoleCtrlEvent(CTRL_C_EVENT, proc_info.dwProcessId);
continue;
}
if (!okay && ::GetLastError() != ERROR_BROKEN_PIPE) { if (!okay && ::GetLastError() != ERROR_BROKEN_PIPE) {
throw_system_error("Failed while reading from the stdio pipe"); throw_system_error("Failed while reading from the stdio pipe");
} }
throw_system_error("Failed reading exit code of process"); throw_system_error("Failed reading exit code of process");
} }


proc_result res;
res.retc = rc; res.retc = rc;
res.output = std::move(output); res.output = std::move(output);
return res; return res;

+ 5
- 5
tools/gcc-9.next.jsonc View File

"cxx_compiler": "g++-9", "cxx_compiler": "g++-9",
"flags": [ "flags": [
"-Werror=return-type", "-Werror=return-type",
"-fsanitize=address",
// "-fsanitize=address",
], ],
"cxx_flags": [ "cxx_flags": [
"-fconcepts", "-fconcepts",
"-std=c++2a", "-std=c++2a",
], ],
"link_flags": [ "link_flags": [
// "-static-libgcc",
// "-static-libstdc++"
"-fsanitize=address",
"-fuse-ld=lld",
"-static-libgcc",
"-static-libstdc++"
// "-fsanitize=address",
// "-fuse-ld=lld",
], ],
"debug": true, "debug": true,
// "optimize": true, // "optimize": true,

Loading…
Cancel
Save