From cc6280af7eca660c916dc652066216acd474979d Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 23 Jan 2019 21:47:38 +0300 Subject: Add testscript sleep builtin --- build2/scheduler.cxx | 26 +++++++++ build2/scheduler.hxx | 7 +++ build2/test/script/builtin.cxx | 84 +++++++++++++++++++++++++++++- build2/test/script/runner.cxx | 4 +- doc/testscript.cli | 11 ++++ tests/test/script/builtin/sed.testscript | 2 +- tests/test/script/builtin/sleep.testscript | 31 +++++++++++ tests/test/script/builtin/test.testscript | 4 +- tests/test/script/runner/exit.testscript | 2 +- tests/test/script/runner/set.testscript | 2 +- 10 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 tests/test/script/builtin/sleep.testscript diff --git a/build2/scheduler.cxx b/build2/scheduler.cxx index 2438d0bb..053532ef 100644 --- a/build2/scheduler.cxx +++ b/build2/scheduler.cxx @@ -11,6 +11,14 @@ # endif #endif +#ifndef _WIN32 +# include // this_thread::sleep_for() +#else +# include + +# include +#endif + #include #include // std::terminate() @@ -175,6 +183,24 @@ namespace build2 throw_generic_error (ECANCELED); } + void scheduler:: + sleep (const duration& d) + { + deactivate (); + + // MINGW GCC 4.9 doesn't implement this_thread so use Win32 Sleep(). + // +#ifndef _WIN32 + this_thread::sleep_for (d); +#else + using namespace chrono; + + Sleep (static_cast (duration_cast (d).count ())); +#endif + + activate (); + } + size_t scheduler:: suspend (size_t start_count, const atomic_count& task_count) { diff --git a/build2/scheduler.hxx b/build2/scheduler.hxx index 388fa61a..cd3693be 100644 --- a/build2/scheduler.hxx +++ b/build2/scheduler.hxx @@ -140,6 +140,13 @@ namespace build2 void activate (bool collision = false); + // Sleep for the specified duration, deactivating the thread before going + // to sleep and re-activating it after waking up (which means this + // function may sleep potentially significantly longer than requested). + // + void + sleep (const duration&); + // Startup and shutdown. // public: diff --git a/build2/test/script/builtin.cxx b/build2/test/script/builtin.cxx index cf8f20d2..657d8d42 100644 --- a/build2/test/script/builtin.cxx +++ b/build2/test/script/builtin.cxx @@ -4,15 +4,19 @@ #include +#include #include #include #include +#include // strtoull() #include #include // use default operator<< implementation #include // fdopen_mode, fdstream_mode #include +#include // sched + #include // Strictly speaking a builtin which reads/writes from/to standard streams @@ -1498,7 +1502,7 @@ namespace build2 } if (i != e) - error () << "unexpected argument"; + error () << "unexpected argument '" << *i << "'"; // If we edit file in place make sure that the file path is specified // and obtain a temporary file path. We will be writing to the @@ -1634,6 +1638,81 @@ namespace build2 return 1; } + // sleep + // + // Note: can be executed synchronously. + // + static uint8_t + sleep (scope&, + const strings& args, + auto_fd in, auto_fd out, auto_fd err) noexcept + try + { + uint8_t r (1); + ofdstream cerr (move (err)); + + auto error = [&cerr] (bool fail = true) + { + return error_record (cerr, fail, "sleep"); + }; + + try + { + in.close (); + out.close (); + + if (args.empty ()) + error () << "missing time interval"; + + if (args.size () > 1) + error () << "unexpected argument '" << args[1] << "'"; + + uint64_t n; + + for (;;) // Breakout loop. + { + const string& a (args[0]); + + // Note: strtoull() allows these. + // + if (!a.empty () && a[0] != '-' && a[0] != '+') + { + char* e (nullptr); + n = strtoull (a.c_str (), &e, 10); // Can't throw. + + if (errno != ERANGE && e == a.c_str () + a.size ()) + break; + } + + error () << "invalid time interval '" << a << "'"; + } + + // If/when required we could probably support the precise sleep mode + // (e.g., via an option). + // + sched.sleep (chrono::seconds (n)); + + r = 0; + } + // Can be thrown while closing in, out or writing to cerr. + // + catch (const io_error& e) + { + error (false) << e; + } + catch (const failed&) + { + // Diagnostics has already been issued. + } + + cerr.close (); + return r; + } + catch (const std::exception&) + { + return 1; + } + // test -f|-d // // Note: can be executed synchronously. @@ -1666,7 +1745,7 @@ namespace build2 error () << "invalid option"; if (args.size () > 2) - error () << "unexpected argument"; + error () << "unexpected argument '" << args[2] << "'"; path p (parse_path (args[1], sp.wd_path)); @@ -1890,6 +1969,7 @@ namespace build2 {"rm", &sync_impl<&rm>}, {"rmdir", &sync_impl<&rmdir>}, {"sed", &async_impl<&sed>}, + {"sleep", &sync_impl<&sleep>}, {"test", &sync_impl<&test>}, {"touch", &sync_impl<&touch>}, {"true", &true_} diff --git a/build2/test/script/runner.cxx b/build2/test/script/runner.cxx index 2f4b0a28..c8810316 100644 --- a/build2/test/script/runner.cxx +++ b/build2/test/script/runner.cxx @@ -978,7 +978,7 @@ namespace build2 const string& s (*i++); if (i != e) - fail (ll) << "unexpected argument"; + fail (ll) << "unexpected argument '" << *i << "'"; error (ll) << s; throw exit_scope (false); @@ -1040,7 +1040,7 @@ namespace build2 const string& vname (i == e ? a : *i++); if (i != e) - fail (ll) << "unexpected argument"; + fail (ll) << "unexpected argument '" << *i << "'"; if (ats != nullptr && ats->empty ()) fail (ll) << "empty variable attributes"; diff --git a/doc/testscript.cli b/doc/testscript.cli index 1ef6fc7d..e84dffd7 100644 --- a/doc/testscript.cli +++ b/doc/testscript.cli @@ -2657,6 +2657,17 @@ set [null] $foo <- \ +\h#builtins-sleep|\c{sleep}| + +\ +sleep +\ + +Suspend the current test or test group execution for at least the specified +number of seconds. Note that in order to improve resource utilization, the +implementation may sleep longer than requested, potentially significantly. + + \h#builtins-test|\c{test}| \ diff --git a/tests/test/script/builtin/sed.testscript b/tests/test/script/builtin/sed.testscript index 1665de2b..e3c0965d 100644 --- a/tests/test/script/builtin/sed.testscript +++ b/tests/test/script/builtin/sed.testscript @@ -109,7 +109,7 @@ $c <"sed -e 's/a//' a b" && $b 2>>/EOE != 0 testscript:1:1: error: sed exit code 1 != 0 info: stderr: test/1/stderr - sed: unexpected argument + sed: unexpected argument 'b' info: test id: 1 EOE } diff --git a/tests/test/script/builtin/sleep.testscript b/tests/test/script/builtin/sleep.testscript new file mode 100644 index 00000000..c43418d4 --- /dev/null +++ b/tests/test/script/builtin/sleep.testscript @@ -0,0 +1,31 @@ +# file : tests/test/script/builtin/sleep.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +.include ../common.testscript + +: success +: +$c <'sleep 1' && $b + +: no-time +: +: Test passing no time interval. +: +$c <'sleep 2>"sleep: missing time interval" != 0' && $b + +: invalid-time +: +: Test passing invalid time interval. +: +$c <"sleep: invalid time interval '1a'" != 0 +EOI + +: unexpected-arg +: +: Test passing extra argument. +: +$c <"sleep: unexpected argument '1'" != 0 +EOI diff --git a/tests/test/script/builtin/test.testscript b/tests/test/script/builtin/test.testscript index c17b3b0b..650f9ef7 100644 --- a/tests/test/script/builtin/test.testscript +++ b/tests/test/script/builtin/test.testscript @@ -58,7 +58,9 @@ $c <'test -c a 2>"test: invalid option" == 2' && $b : : Test passing extra argument. : -$c <'test -f a b 2>"test: unexpected argument" == 2' && $b +$c <"test: unexpected argument 'b'" == 2 +EOI : empty-path : diff --git a/tests/test/script/runner/exit.testscript b/tests/test/script/runner/exit.testscript index 897cf655..261b02dd 100644 --- a/tests/test/script/runner/exit.testscript +++ b/tests/test/script/runner/exit.testscript @@ -77,7 +77,7 @@ empty_id = '' : unexpected : $c <'exit "foo" "bar"' && $b 2>>EOE != 0 - testscript:1:1: error: unexpected argument + testscript:1:1: error: unexpected argument 'bar' info: test id: 1 EOE } diff --git a/tests/test/script/runner/set.testscript b/tests/test/script/runner/set.testscript index a7959636..28d66869 100644 --- a/tests/test/script/runner/set.testscript +++ b/tests/test/script/runner/set.testscript @@ -53,7 +53,7 @@ : unexpected : $c <'set foo bar baz' && $b 2>>EOE != 0 - testscript:1:1: error: unexpected argument + testscript:1:1: error: unexpected argument 'baz' info: test id: 1 EOE -- cgit