diff options
| author | Alex Richardson <arichardson@FreeBSD.org> | 2021-02-04 14:48:10 +0000 |
|---|---|---|
| committer | Alex Richardson <arichardson@FreeBSD.org> | 2021-02-04 15:03:05 +0000 |
| commit | c203bd70b5957f85616424b6fa374479372d06e3 (patch) | |
| tree | 5a810500e4cf44b327516db0553157bd3ea1f0a6 /contrib/atf/atf-sh/atf-check.cpp | |
| parent | cb7cc72c546e0f87598961c3860e17391f42866c (diff) | |
| parent | a3330ae736606c1812b9e9c4b9dcfdfb1a150dde (diff) | |
Diffstat (limited to 'contrib/atf/atf-sh/atf-check.cpp')
| -rw-r--r-- | contrib/atf/atf-sh/atf-check.cpp | 128 |
1 files changed, 118 insertions, 10 deletions
diff --git a/contrib/atf/atf-sh/atf-check.cpp b/contrib/atf/atf-sh/atf-check.cpp index 414b64ea91f0..38ab527aab54 100644 --- a/contrib/atf/atf-sh/atf-check.cpp +++ b/contrib/atf/atf-sh/atf-check.cpp @@ -29,6 +29,7 @@ extern "C" { #include <limits.h> #include <signal.h> +#include <stdint.h> #include <unistd.h> } @@ -53,6 +54,10 @@ extern "C" { #include "atf-c++/detail/sanity.hpp" #include "atf-c++/detail/text.hpp" +static const useconds_t seconds_in_useconds = (1000 * 1000); +static const useconds_t mseconds_in_useconds = 1000; +static const useconds_t useconds_in_nseconds = 1000; + // ------------------------------------------------------------------------ // Auxiliary functions. // ------------------------------------------------------------------------ @@ -162,6 +167,33 @@ public: } // anonymous namespace +static useconds_t +get_monotonic_useconds(void) +{ + struct timespec ts; + useconds_t res; + int rc; + + rc = clock_gettime(CLOCK_MONOTONIC, &ts); + if (rc != 0) + throw std::runtime_error("clock_gettime: " + + std::string(strerror(errno))); + + res = ts.tv_sec * seconds_in_useconds; + res += ts.tv_nsec / useconds_in_nseconds; + return res; +} + +static bool +timo_expired(useconds_t timeout) +{ + + if (get_monotonic_useconds() >= timeout) + return true; + return false; +} + + static int parse_exit_code(const std::string& str) { @@ -216,7 +248,7 @@ parse_signal(const std::string& str) if (signo == INT_MIN) { try { return atf::text::to_type< int >(str); - } catch (std::runtime_error) { + } catch (const std::runtime_error&) { throw atf::application::usage_error("Invalid signal name or number " "in -s option"); } @@ -306,6 +338,62 @@ parse_output_check_arg(const std::string& arg) return output_check(type, negated, arg.substr(delimiter + 1)); } +static void +parse_repeat_check_arg(const std::string& arg, useconds_t *m_timo, + useconds_t *m_interval) +{ + const std::string::size_type delimiter = arg.find(':'); + const bool has_interval = (delimiter != std::string::npos); + const std::string timo_str = arg.substr(0, delimiter); + + long l; + char *end; + + // There is no reason this couldn't be a non-integer number of seconds, + // this was just easy to do for now. + errno = 0; + l = strtol(timo_str.c_str(), &end, 10); + if (errno == ERANGE) + throw atf::application::usage_error("Bogus timeout in seconds"); + else if (errno != 0) + throw atf::application::usage_error("Timeout must be a number"); + + if (*end != 0) + throw atf::application::usage_error("Timeout must be a number"); + + *m_timo = get_monotonic_useconds() + (l * seconds_in_useconds); + // 50 milliseconds is chosen arbitrarily. There is a tradeoff between + // longer and shorter poll times. A shorter poll time makes for faster + // tests. A longer poll time makes for lower CPU overhead for the polled + // operation. 50ms is chosen with these tradeoffs in mind: on + // microcontrollers, the hope is that we can still avoid meaningful CPU use + // with a small test every 50ms. And on typical fast x86 hardware, our + // tests can be much more precise with time wasted than they typically are + // without this feature. + *m_interval = 50 * mseconds_in_useconds; + + if (!has_interval) + return; + + const std::string intv_str = arg.substr(delimiter + 1, std::string::npos); + + // Same -- this could be non-integer milliseconds. + errno = 0; + l = strtol(intv_str.c_str(), &end, 10); + if (errno == ERANGE) + throw atf::application::usage_error( + "Bogus repeat interval in milliseconds"); + else if (errno != 0) + throw atf::application::usage_error( + "Repeat interval must be a number"); + + if (*end != 0) + throw atf::application::usage_error( + "Repeat interval must be a number"); + + *m_interval = l * mseconds_in_useconds; +} + static std::string flatten_argv(char* const* argv) @@ -694,8 +782,12 @@ run_output_checks(const std::vector< output_check >& checks, namespace { class atf_check : public atf::application::app { + bool m_rflag; bool m_xflag; + useconds_t m_timo; + useconds_t m_interval; + std::vector< status_check > m_status_checks; std::vector< output_check > m_stdout_checks; std::vector< output_check > m_stderr_checks; @@ -722,6 +814,7 @@ const char* atf_check::m_description = atf_check::atf_check(void) : app(m_description, "atf-check(1)"), + m_rflag(false), m_xflag(false) { } @@ -765,6 +858,8 @@ atf_check::specific_options(void) opts.insert(option('e', "action:arg", "Handle stderr. Action must be " "one of: empty ignore file:<path> inline:<val> match:regexp " "save:<path>")); + opts.insert(option('r', "timeout[:interval]", "Repeat failed check until " + "the timeout expires.")); opts.insert(option('x', "", "Execute command as a shell command")); return opts; @@ -786,6 +881,11 @@ atf_check::process_option(int ch, const char* arg) m_stderr_checks.push_back(parse_output_check_arg(arg)); break; + case 'r': + m_rflag = true; + parse_repeat_check_arg(arg, &m_timo, &m_interval); + break; + case 'x': m_xflag = true; break; @@ -803,9 +903,6 @@ atf_check::main(void) int status = EXIT_FAILURE; - std::auto_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) { @@ -818,12 +915,23 @@ atf_check::main(void) 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; + do { + std::auto_ptr< atf::check::check_result > r = + m_xflag ? execute_with_shell(m_argv) : execute(m_argv); + + 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; + + if (m_rflag && status == EXIT_FAILURE) { + if (timo_expired(m_timo)) + break; + usleep(m_interval); + } + } while (m_rflag && status == EXIT_FAILURE); return status; } |
