aboutsummaryrefslogtreecommitdiff
path: root/tests/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'tests/builtin')
-rw-r--r--tests/builtin/buildfile3
-rw-r--r--tests/builtin/driver.cxx184
-rw-r--r--tests/builtin/find.testscript276
-rw-r--r--tests/builtin/sed.testscript55
-rw-r--r--tests/builtin/timeout.testscript30
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/