diff options
Diffstat (limited to 'tests/builtin')
-rw-r--r-- | tests/builtin/buildfile | 3 | ||||
-rw-r--r-- | tests/builtin/driver.cxx | 184 | ||||
-rw-r--r-- | tests/builtin/find.testscript | 276 | ||||
-rw-r--r-- | tests/builtin/sed.testscript | 55 | ||||
-rw-r--r-- | tests/builtin/timeout.testscript | 30 |
5 files changed, 494 insertions, 54 deletions
diff --git a/tests/builtin/buildfile b/tests/builtin/buildfile index 8341847..8d22fe4 100644 --- a/tests/builtin/buildfile +++ b/tests/builtin/buildfile @@ -6,3 +6,6 @@ import libs = libbutl%lib{butl} ./: exe{driver} file{cp-dir/cp-file} exe{driver}: {hxx cxx}{*} $libs testscript{*} + +if ($cxx.target.class != 'windows') + cxx.libs += -lpthread diff --git a/tests/builtin/driver.cxx b/tests/builtin/driver.cxx index 9fb6d6f..bdf3fa9 100644 --- a/tests/builtin/driver.cxx +++ b/tests/builtin/driver.cxx @@ -1,53 +1,60 @@ // file : tests/builtin/driver.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file -#include <cassert> +#ifdef _WIN32 +# include <libbutl/win32-utility.hxx> +#endif -#ifndef __cpp_lib_modules_ts #include <string> #include <vector> +#include <chrono> #include <utility> // move() +#include <cstdint> // uint8_t #include <ostream> #include <iostream> +#ifndef _WIN32 +# include <thread> // this_thread::sleep_for() #endif -// Other includes. +#include <libbutl/path.hxx> +#include <libbutl/utility.hxx> // eof() +#include <libbutl/builtin.hxx> +#include <libbutl/optional.hxx> +#include <libbutl/timestamp.hxx> // to_stream(duration) -#ifdef __cpp_modules_ts -#ifdef __cpp_lib_modules_ts -import std.core; -import std.io; -#endif -import butl.path; -import butl.utility; // eof() -import butl.builtin; -import butl.optional; -import butl.timestamp; // to_stream(duration) -#else -#include <libbutl/path.mxx> -#include <libbutl/utility.mxx> -#include <libbutl/builtin.mxx> -#include <libbutl/optional.mxx> -#include <libbutl/timestamp.mxx> -#endif +#undef NDEBUG +#include <cassert> using namespace std; using namespace butl; +// Disable arguments globbing that may be enabled by default for MinGW runtime +// (see tests/wildcard/driver.cxx for details). +// +#ifdef __MINGW32__ +int _CRT_glob = 0; +#endif + inline ostream& operator<< (ostream& os, const path& p) { return os << p.representation (); } -// Usage: argv[0] [-d <dir>] [-o <opt>] [-c] [-i] <builtin> <builtin-args> +// Usage: argv[0] [-d <dir>] [-o <opt>] [-c] [-i] [-t <msec>] [-s <sec>] +// <builtin> <builtin-args> // // Execute the builtin and exit with its exit status. // -// -d <dir> use as a current working directory -// -c use callbacks that, in particular, trace calls to stdout -// -o <opt> additional builtin option recognized by the callback -// -i read lines from stdin and append them to the builtin arguments +// -d <dir> use as a current working directory +// -c use callbacks that, in particular, trace calls to stdout +// -o <opt> additional builtin option recognized by the callback +// -i read lines from stdin and append them to the builtin arguments +// -t <msec> print diag if the builtin didn't complete in <msec> milliseconds +// -s <sec> sleep <sec> seconds prior to running the builtin +// +// Note that the 'roundtrip' builtin name is also recognized and results in +// running the pseudo-builtin that just roundtrips stdin to stdout. // int main (int argc, char* argv[]) @@ -62,12 +69,25 @@ main (int argc, char* argv[]) dir_path cwd; string option; builtin_callbacks callbacks; + optional<duration> timeout; + optional<chrono::seconds> sec; string name; vector<string> args; auto flag = [] (bool v) {return v ? "true" : "false";}; + auto num = [] (const string& s) + { + assert (!s.empty ()); + + char* e (nullptr); + errno = 0; // We must clear it according to POSIX. + uint64_t r (strtoull (s.c_str (), &e, 10)); // Can't throw. + assert (errno != ERANGE && e == s.c_str () + s.size ()); + return r; + }; + // Parse the driver options and arguments. // int i (1); @@ -120,7 +140,23 @@ main (int argc, char* argv[]) ); } else if (a == "-i") + { in = true; + } + else if (a == "-t") + { + ++i; + + assert (i != argc); + timeout = chrono::milliseconds (num (argv[i])); + } + else if (a == "-s") + { + ++i; + + assert (i != argc); + sec = chrono::seconds (num (argv[i])); + } else break; } @@ -142,23 +178,95 @@ main (int argc, char* argv[]) args.push_back (move (s)); } - // Execute the builtin. - // - const builtin_info* bi (builtins.find (name)); + auto sleep = [&sec] () + { + if (sec) + { + // MINGW GCC 4.9 doesn't implement this_thread so use Win32 Sleep(). + // +#ifndef _WIN32 + this_thread::sleep_for (*sec); +#else + Sleep (static_cast<DWORD> (sec->count () * 1000)); +#endif + } + }; - if (bi == nullptr) + auto wait = [&timeout] (builtin& b) { - cerr << "unknown builtin '" << name << "'" << endl; - return 1; - } + optional<uint8_t> r; + + if (timeout) + { + r = b.timed_wait (*timeout); + + if (!r) + { + cerr << "timeout expired" << endl; - if (bi->function == nullptr) + b.wait (); + r = 1; + } + } + else + r = b.wait (); + + assert (b.try_wait ()); // While at it, test try_wait(). + + return *r; + }; + + // Execute the builtin. + // + if (name != "roundtrip") { - cerr << "external builtin '" << name << "'" << endl; - return 1; + const builtin_info* bi (builtins.find (name)); + + if (bi == nullptr) + { + cerr << "unknown builtin '" << name << "'" << endl; + return 1; + } + + if (bi->function == nullptr) + { + cerr << "external builtin '" << name << "'" << endl; + return 1; + } + + sleep (); + + uint8_t r; // Storage. + builtin b (bi->function (r, args, nullfd, nullfd, nullfd, cwd, callbacks)); + return wait (b); } + else + { + uint8_t r; // Storage. - uint8_t r; // Storage. - builtin b (bi->function (r, args, nullfd, nullfd, nullfd, cwd, callbacks)); - return b.wait (); + auto run = [&r, &sleep] () + { + // While at it, test that a non-copyable lambda can be used as a + // builtin. + // + auto_fd fd; + + return pseudo_builtin ( + r, + [&sleep, fd = move (fd)] () mutable noexcept + { + fd.reset (); + + sleep (); + + if (cin.peek () != istream::traits_type::eof ()) + cout << cin.rdbuf (); + + return 0; + }); + }; + + builtin b (run ()); + return wait (b); + } } diff --git a/tests/builtin/find.testscript b/tests/builtin/find.testscript new file mode 100644 index 0000000..b09822c --- /dev/null +++ b/tests/builtin/find.testscript @@ -0,0 +1,276 @@ +# file : tests/builtin/find.testscript +# license : MIT; see accompanying LICENSE file + +posix = ($cxx.target.class != 'windows') + +test.arguments = "find" + +: no-paths +: +$* 2>"find: missing start path" == 1 + +: no-paths-primary +: +$* -name foo 2>"find: unknown option '-name'" == 1 + +: unknown-primary +: +$* . -foo 2>"find: unknown primary '-foo'" == 1 + + +: no-primary-value +: +$* . -name 2>"find: missing value for primary '-name'" == 1 + +: empty-primary-value +: +$* . -type '' 2>"find: empty value for primary '-type'" == 1 + +: invalid-type-primary +: +$* . -type foo 2>"find: invalid value 'foo' for primary '-type'" == 1 + +: invalid-mindepth-primary +: +$* . -mindepth 12a 2>"find: invalid value '12a' for primary '-mindepth'" == 1 + +: path-not-exists +: +{ + mkdir d; + $* x d >'d' 2>"find: 'x' doesn't exists" != 0 +} + +: path +: +{ + : relative + : + { + : no-cwd + : + { + mkdir a; + touch a/b; + + $* a >>/EOO + a + a/b + EOO + } + + : absolute-cwd + : + : When cross-testing we cannot guarantee that host absolute paths are + : recognized by the target process. + : + if ($test.target == $build.host) + { + test.options += -d $~/a; + mkdir a; + touch a/b; + + $* b >'b' + } + + : relative-cwd + : + if ($test.target == $build.host) + { + test.options += -d a; + mkdir a; + touch a/b; + + $* b >'b' + } + } + + : non-normalized + : + { + mkdir a; + touch a/b; + + # Note that the path specified on the command line is used unaltered. + # + s = ($posix ? '/' : '\'); + + $* ./a >>"EOO" + ./a + ./a$(s)b + EOO + } + + : absolute + : + { + mkdir a; + touch a/b; + + $* $~/a >>/"EOO" + $~/a + $~/a/b + EOO + } + + : non-existent + : + { + touch a b; + + $* a x b >>EOO 2>"find: 'x' doesn't exists" != 0 + a + b + EOO + } + + : non-directory + : + { + touch a b c; + + $* a b/ c >>EOO 2>"find: 'b' is not a directory" != 0 + a + c + EOO + } + + : trailing-slash + : + { + mkdir -p a/b; + + $* a >>/"EOO"; + a + a/b + EOO + + $* a/ >>"EOO" + a/ + a/b + EOO + } +} + +: name-primary +: +{ + : basic + : + { + mkdir a; + touch a/ab a/ba; + + $* . -name 'a*' >>/EOO; + ./a + ./a/ab + EOO + + $* . -name 'b*' >>/EOO; + ./a/ba + EOO + + $* a -name 'a*' >>/EOO + a + a/ab + EOO + } + + : empty + : + { + touch a; + + $* . -name '' + } +} + +: type-primary +: +{ + : regular + : + { + mkdir -p a/b; + touch a/b/c; + + $* a -type f >>/EOO + a/b/c + EOO + } + + : directory + : + { + mkdir -p a/b; + touch a/b/c; + + $* a -type d >>/EOO + a + a/b + EOO + } + + : symlink + : + if $posix + { + mkdir -p a/b; + touch a/b/c; + ln -s c a/b/d; + + $* a -type l >>/EOO + a/b/d + EOO + } +} + +: mindepth-primary +: +{ + mkdir -p a/b/c; + + $* a -mindepth 0 >>/EOO; + a + a/b + a/b/c + EOO + + $* a -mindepth 1 >>/EOO; + a/b + a/b/c + EOO + + $* a -mindepth 2 >>/EOO; + a/b/c + EOO + + $* a -mindepth 3 +} + +: maxdepth-primary +: +{ + mkdir -p a/b/c; + + $* a -maxdepth 0 >>/EOO; + a + EOO + + $* a -maxdepth 1 >>/EOO; + a + a/b + EOO + + $* a -maxdepth 2 >>/EOO; + a + a/b + a/b/c + EOO + + $* a -maxdepth 3 >>/EOO + a + a/b + a/b/c + EOO +} diff --git a/tests/builtin/sed.testscript b/tests/builtin/sed.testscript index ad26483..2ed3088 100644 --- a/tests/builtin/sed.testscript +++ b/tests/builtin/sed.testscript @@ -89,16 +89,10 @@ test.options += -c sed: empty script EOE - : multiple - : - $* -e 's/a//' -e 's/a//' 2>>EOE != 0 - sed: multiple scripts - EOE - : invalid : $* -e 'z' 2>>EOE != 0 - sed: only 's' command supported + sed: unknown command in 'z': only 's' command supported EOE } @@ -156,13 +150,13 @@ test.options += -c : none : $* -e 's' 2>>EOE != 0 - sed: no delimiter for 's' command + sed: no delimiter for 's' command in 's' EOE : invalid : $* -e 's\\' 2>>EOE != 0 - sed: invalid delimiter for 's' command + sed: invalid delimiter for 's' command in 's\\' EOE } @@ -171,14 +165,14 @@ test.options += -c { : unterminated : - $* -e 's/foo' 2>>/EOE != 0 - sed: unterminated 's' command regex + $* -e 's/foo' 2>>EOE != 0 + sed: invalid 's' command 's/foo': no delimiter after regex EOE : empty : $* -e 's///' 2>>EOE != 0 - sed: empty regex in 's' command + sed: invalid 's' command 's///': empty regex EOE : invalid @@ -187,20 +181,20 @@ test.options += -c : regex errors. For example '*' is parsed successfully. : $* -e 's/foo[/bar/' 2>>~%EOE% != 0 - %sed: invalid regex.*% + %sed: invalid regex 'foo\[' in 's/foo\[/bar/'.*% EOE } : unterminated-replacement : - $* -e 's/foo/bar' 2>>/EOE != 0 - sed: unterminated 's' command replacement + $* -e 's/foo/bar' 2>>EOE != 0 + sed: invalid 's' command 's/foo/bar': no delimiter after replacement EOE : invalid-flags : $* -e 's/foo/bar/a' 2>>EOE != 0 - sed: invalid 's' command flag 'a' + sed: invalid 's' command flag 'a' in 's/foo/bar/a' EOE } @@ -314,6 +308,35 @@ test.options += -c } } } + + : multiple + : + { + $* -e 's/b/x/' -e 's/x/y/' -e 's/c/z/' <'abc' >'ayz' : replace-replacement + + : new-cycle + : + $* -e 's/b/x/p' -e 's/x/y/p' <<EOI >>EOO + abc + klm + dxe + EOI + axc + klm + dye + EOO + + : quiet + : + $* -n -e 's/b/x/p' -e 's/x/y/p' <<EOI >>EOO + abc + klm + dxe + EOI + axc + dye + EOO + } } : in-place diff --git a/tests/builtin/timeout.testscript b/tests/builtin/timeout.testscript new file mode 100644 index 0000000..b8eddc3 --- /dev/null +++ b/tests/builtin/timeout.testscript @@ -0,0 +1,30 @@ +# file : tests/builtin/timeout.testscript +# license : MIT; see accompanying LICENSE file + +: async-builtin +: +{ + : expired + : + $* -s 5 'cat' <'test' | $* -t 1 'cat' >=f 2>'timeout expired' != 0 + + : not-expired + : + echo 'test' | $* -t 10000 'cat' >! +} + +: pseudo-builtin +: +{ + : expired + : + $* -s 5 'cat' <'test' | $* -t 1 'roundtrip' >=f 2>'timeout expired' != 0 + + : not-expired + : + echo 'test' | $* -t 10000 'roundtrip' >! +} + +: sync-builtin +: +$* -t 1 'mkdir' d &d/ |