From 1c6758009e82c47b5b341d418be2be401ef31482 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 6 Sep 2019 22:20:46 +0300 Subject: Add builtins support --- tests/builtin/buildfile | 9 + tests/builtin/cat.testscript | 82 ++++++++ tests/builtin/cp-dir/cp-file | 0 tests/builtin/cp.testscript | 447 +++++++++++++++++++++++++++++++++++++++++ tests/builtin/driver.cxx | 159 +++++++++++++++ tests/builtin/echo.testscript | 26 +++ tests/builtin/ln.testscript | 217 ++++++++++++++++++++ tests/builtin/mkdir.testscript | 102 ++++++++++ tests/builtin/mv.testscript | 182 +++++++++++++++++ tests/builtin/rm.testscript | 105 ++++++++++ tests/builtin/rmdir.testscript | 95 +++++++++ tests/builtin/sed.testscript | 350 ++++++++++++++++++++++++++++++++ tests/builtin/sleep.testscript | 42 ++++ tests/builtin/test.testscript | 84 ++++++++ tests/builtin/touch.testscript | 92 +++++++++ 15 files changed, 1992 insertions(+) create mode 100644 tests/builtin/buildfile create mode 100644 tests/builtin/cat.testscript create mode 100644 tests/builtin/cp-dir/cp-file create mode 100644 tests/builtin/cp.testscript create mode 100644 tests/builtin/driver.cxx create mode 100644 tests/builtin/echo.testscript create mode 100644 tests/builtin/ln.testscript create mode 100644 tests/builtin/mkdir.testscript create mode 100644 tests/builtin/mv.testscript create mode 100644 tests/builtin/rm.testscript create mode 100644 tests/builtin/rmdir.testscript create mode 100644 tests/builtin/sed.testscript create mode 100644 tests/builtin/sleep.testscript create mode 100644 tests/builtin/test.testscript create mode 100644 tests/builtin/touch.testscript (limited to 'tests') diff --git a/tests/builtin/buildfile b/tests/builtin/buildfile new file mode 100644 index 0000000..26acc13 --- /dev/null +++ b/tests/builtin/buildfile @@ -0,0 +1,9 @@ +# file : tests/builtin/buildfile +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +import libs = libbutl%lib{butl} + +./: exe{driver} file{cp-dir/cp-file} + +exe{driver}: {hxx cxx}{*} $libs testscript{*} diff --git a/tests/builtin/cat.testscript b/tests/builtin/cat.testscript new file mode 100644 index 0000000..336bb03 --- /dev/null +++ b/tests/builtin/cat.testscript @@ -0,0 +1,82 @@ +# file : tests/builtin/cat.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "cat" + +: unknown-option +: +$* -u 2>"cat: unknown option '-u'" == 1 + +: in +: +$* <>EOF + foo + bar + EOF + +: dash +: +$* - <>EOF + foo + bar + EOF + +: file +: +{ + $* <=out; + foo + bar + EOF + + cat out >>EOO + foo + bar + EOO +} + +: in-repeat +: +$* - - <>EOF + foo + bar + EOF + +: non-existent +: +$* in 2>>/~%EOE% != 0 + %cat: unable to print '.+/in': .+% + EOE + +: empty-path +: +: Cat an empty path. +: +$* '' 2>"cat: invalid path ''" == 1 + +: big +: +: Cat a big file (about 100K) to test that the builtin is asynchronous. +: +{ + s="--------------------------------"; + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s"; + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s"; + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s"; + $* <"$s" | $* >"$s" +} + +: 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; + echo 'foo' >=a/b; + + $* b >'foo' +} diff --git a/tests/builtin/cp-dir/cp-file b/tests/builtin/cp-dir/cp-file new file mode 100644 index 0000000..e69de29 diff --git a/tests/builtin/cp.testscript b/tests/builtin/cp.testscript new file mode 100644 index 0000000..4f83586 --- /dev/null +++ b/tests/builtin/cp.testscript @@ -0,0 +1,447 @@ +# file : tests/builtin/cp.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "cp" +test.options += -c + +: unknown-option +: +$* -u >'option -u' 2>"cp: unknown option '-u'" == 1 + +: args +: +{ + : none + : + $* 2>"cp: missing arguments" == 1 + + : no-source + : + $* -R a 2>"cp: missing source path" == 1 + + : no-trailing-sep + : + $* a b c 2>"cp: multiple source paths without trailing separator for destination directory" == 1 + + : empty + : + { + : dest + : + $* '' 2>"cp: invalid path ''" == 1 + + : src1 + : + $* '' a 2>"cp: invalid path ''" == 1 + + : src2 + : + $* '' a b/ 2>"cp: invalid path ''" == 1 + } +} + +: file +: +: Test synopsis 1: make a file copy at the specified path. +: +{ + : existing + : + { + : to-non-existing + : + { + touch a; + + $* a b >>/~%EOO% &b; + %create .+/b true% + %create .+/b false% + EOO + + test -f b + } + + : to-existing + : + { + touch a b; + + $* a b >>/~%EOO% + %create .+/b true% + %create .+/b false% + EOO + } + + : to-dir + : + { + touch a; + mkdir b; + + $* a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b true% + EOO + %cp: unable to copy file '.+/a' to '.+/b': .+% + EOE + } + } + + : non-existing + : + { + $* a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b true% + EOO + %cp: unable to copy file '.+/a' to '.+/b': .+% + EOE + } + + : non-file + : + { + mkdir a; + + $* a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b true% + EOO + %cp: unable to copy file '.+/a' to '.+/b': .+% + EOE + } +} + +: dir +: +: Test synopsis 2: make a directory copy at the specified path. +: +{ + : existing + : + { + : to-non-existing + : + { + mkdir a; + + $* -r a b >>/~%EOO% &b/; + %create .+/b/ true% + %create .+/b/ false% + EOO + + test -d b + } + + : to-existing + : + { + mkdir a b; + + $* -R a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/ true% + EOO + %cp: unable to copy directory '.+/a' to '.+/b': .+% + EOE + } + + : to-file + : + { + mkdir a; + touch b; + + $* -r a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/ true% + EOO + %cp: unable to copy directory '.+/a' to '.+/b': .+% + EOE + } + + : recursively + : + { + mkdir -p a/b/c; + touch a/x a/b/y; + + $* -r a d >>/~%EOO% &d/ &d/x &d/b/ &d/b/y &d/b/c/; + %create .+/d/ true% + %create .+/d/ false% + %( + %create .+/d/.+ true% + %create .+/d/.+ false% + %){4} + EOO + + test -d d/b/c && test -f d/x && test -f d/b/y + } + } + + : non-existing + : + { + $* -r a b >>/~%EOO% 2>>/~%EOE% &b/ != 0 + %create .+/b/ true% + %create .+/b/ false% + EOO + %cp: unable to copy directory '.+/a' to '.+/b': .+% + EOE + } + + : non-dir + : + { + touch a; + + $* -r a b >>/~%EOO% 2>>/~%EOE% &b/ != 0 + %create .+/b/ true% + %create .+/b/ false% + EOO + %cp: unable to copy directory '.+/a' to '.+/b': .+% + EOE + } +} + +: files +: +: Test synopsis 3: copy files into the specified directory. +: +{ + : existing + : + { + : into-dir + : + { + : over-non-existing + : + { + mkdir b; + touch a; + + $* a b/ >>/~%EOO% &b/a; + %create .+/b/a true% + %create .+/b/a false% + EOO + + test -f b/a + } + + : over-dir + : + { + mkdir -p b/a; + touch a; + + $* a b/ >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/a true% + EOO + %cp: unable to copy file '.+/a' to '.+/b/a': .+% + EOE + } + + : multiple + : + { + touch a b; + mkdir c; + + $* a b c/ >>/~%EOO% &c/a &c/b &c/; + %create .+/c/a true% + %create .+/c/a false% + %create .+/c/b true% + %create .+/c/b false% + EOO + + test -f c/a && test -f c/b + } + } + + : into-non-existing-dir + : + { + touch a; + + $* a b/ >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/a true% + EOO + %cp: unable to copy file '.+/a' to '.+/b/a': .+% + EOE + } + + : into-non-dir + : + { + touch a b; + + $* a b/ >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/a true% + EOO + %cp: unable to copy file '.+/a' to '.+/b/a': .+% + EOE + } + } + + : non-existing + : + { + mkdir b; + + $* a b/ >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/a true% + EOO + %cp: unable to copy file '.+/a' to '.+/b/a': .+% + EOE + } + + : non-file + : + { + mkdir a b; + + $* a b/ >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/a true% + EOO + %cp: unable to copy file '.+/a' to '.+/b/a': .+% + EOE + } +} + +: filesystem-entries +: +: Test synopsis 4: copy filesystem entries into the specified directory. +: +{ + : file + : + { + mkdir b; + touch a; + + $* -R a b/ >>/~%EOO% &b/a; + %create .+/b/a true% + %create .+/b/a false% + EOO + + test -f b/a + } + + : dir + : + { + : over-non-existing + : + { + mkdir a b; + touch a/c; + + $* -R a b/ >>/~%EOO% &b/a/ &b/a/c; + %create .+/b/a/ true% + %create .+/b/a/ false% + %create .+/b/a/c true% + %create .+/b/a/c false% + EOO + + test -f b/a/c + } + + : over-existing + : + { + mkdir -p a b/a; + + $* -R a b/ >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/a/ true% + EOO + %cp: unable to copy directory '.+/a' to '.+/b/a': .+% + EOE + } + } +} + +: attrs +: +if ($cxx.target.class != 'windows') +{ + fs = 's/.+ (\S+\s+\S+\s+\S+)\s+cp-file/\1/p' + ds = 's/.+ (\S+\s+\S+\s+\S+)\s+cp-dir/\1/p' + + : copy + : + { + : file + : + { + ls -l $src_base/cp-dir | sed -n -e "$fs" | set t; + + $* -p $src_base/cp-dir/cp-file ./ >! &cp-file; + + ls -l | sed -n -e "$fs" >"$t" + } + + : dir + : + { + ls -l $src_base | sed -n -e "$ds" | set t; + + $* -pr $src_base/cp-dir ./ >! &cp-dir/ &cp-dir/cp-file; + + ls -l | sed -n -e "$ds" >"$t" + } + } + + : no-copy + : + : Note that the `ls -l` command by default displays the filesystem entry + : modification time with the minute resolution and building from git + : repository may not preserve the filesystem entry original modification + : times. That is why we also pass --full-time and enable the test for only + : platforms where ls supports this option. + : + if ($cxx.target.class == 'linux') + { + : file + : + { + ls -l --full-time $src_base/cp-dir | sed -n -e "$fs" | set t; + + $* $src_base/cp-dir/cp-file ./ >! &cp-file; + + ls -l --full-time | sed -n -e "$fs" | set tn; + + if ("$tn" == "$t") + exit "unexpectedly copied timestamp \($t\)" + end + } + + : dir + : + { + ls -l --full-time $src_base | sed -n -e "$ds" | set t; + + $* -r $src_base/cp-dir ./ >! &cp-dir/ &cp-dir/cp-file; + + ls -l --full-time | sed -n -e "$ds" | set tn; + + if ("$tn" == "$t") + exit "unexpectedly copied timestamp \($t\)" + end + } + } +} + +: 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 -p a/b; + + $* -R b c >>/~%EOO% &a/c/; + %create .+/a/c/ true% + %create .+/a/c/ false% + EOO + + test -d a/c +} diff --git a/tests/builtin/driver.cxx b/tests/builtin/driver.cxx new file mode 100644 index 0000000..bf171cb --- /dev/null +++ b/tests/builtin/driver.cxx @@ -0,0 +1,159 @@ +// file : tests/builtin/driver.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#ifndef __cpp_lib_modules_ts +#include +#include +#include // move() +#include +#include +#endif + +// Other includes. + +#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 +#include +#include +#include +#include +#endif + +using namespace std; +using namespace butl; + +inline ostream& +operator<< (ostream& os, const path& p) +{ + return os << p.representation (); +} + +// Usage: argv[0] [-d ] [-o ] [-c] [-i] +// +// Execute the builtin and exit with its exit status. +// +// -d use as a current working directory +// -c use callbacks that, in particular, trace calls to stdout +// -o additional builtin option recognized by the callback +// -i read lines from stdin and append them to the builtin arguments +// +int +main (int argc, char* argv[]) +{ + using butl::optional; + + cin.exceptions (ios::badbit); + cout.exceptions (ios::failbit | ios::badbit); + cerr.exceptions (ios::failbit | ios::badbit); + + bool in (false); + dir_path cwd; + string option; + builtin_callbacks callbacks; + + string name; + vector args; + + auto flag = [] (bool v) {return v ? "true" : "false";}; + + // Parse the driver options and arguments. + // + int i (1); + for (; i != argc; ++i) + { + string a (argv[i]); + + if (a == "-d") + { + ++i; + + assert (i != argc); + cwd = dir_path (argv[i]); + } + else if (a == "-o") + { + ++i; + + assert (i != argc); + option = argv[i]; + } + else if (a == "-c") + { + callbacks = builtin_callbacks ( + [&flag] (const path& p, bool pre) + { + cout << "create " << p << ' ' << flag (pre) << endl; + }, + [&flag] (const path& from, const path& to, bool force, bool pre) + { + cout << "move " << from << ' ' << to << ' ' << flag (force) << ' ' + << flag (pre) << endl; + }, + [&flag] (const path& p, bool force, bool pre) + { + cout << "remove " << p << ' ' << flag (force) << ' ' << flag (pre) + << endl; + }, + [&option] (const vector& args, size_t i) + { + cout << "option " << args[i] << endl; + return !option.empty () && args[i] == option ? 1 : 0; + }, + [] (const duration& d) + { + cout << "sleep "; + to_stream (cout, d, false /* nanoseconds */); + cout << endl; + } + ); + } + else if (a == "-i") + in = true; + else + break; + } + + // Parse the builtin name and arguments. + // + assert (i != argc); + name = argv[i++]; + + for (; i != argc; ++i) + args.push_back (argv[i]); + + // Read out additional arguments from stdin. + // + if (in) + { + string s; + while (!eof (getline (cin, s))) + args.push_back (move (s)); + } + + // Execute the builtin. + // + builtin_function* bf (builtins.find (name)); + + if (bf == nullptr) + { + cerr << "unknown builtin '" << name << "'" << endl; + return 1; + } + + uint8_t r; // Storage. + builtin b (bf (r, args, nullfd, nullfd, nullfd, cwd, callbacks)); + return b.wait (); +} diff --git a/tests/builtin/echo.testscript b/tests/builtin/echo.testscript new file mode 100644 index 0000000..562a14c --- /dev/null +++ b/tests/builtin/echo.testscript @@ -0,0 +1,26 @@ +# file : tests/builtin/echo.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "echo" + +: string +: +$* foo >foo + +: strings +: +$* foo bar >"foo bar" + +: big +: +: Echo a big string (about 100K) to test that the builtin is asynchronous. +: +{ + s="--------------------------------"; + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s"; + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s"; + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s"; + test.options += -i; # Pass the echo argument via the driver's stdin. + $* <"$s" | cat >"$s" +} diff --git a/tests/builtin/ln.testscript b/tests/builtin/ln.testscript new file mode 100644 index 0000000..ed4ca67 --- /dev/null +++ b/tests/builtin/ln.testscript @@ -0,0 +1,217 @@ +# file : tests/builtin/ln.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "ln" +test.options += -c + +: unknown-option +: +$* -u >'option -u' 2>"ln: unknown option '-u'" == 1 + +: args +: +{ + : -s-option + : + $* 2>"ln: missing -s|--symbolic option" == 1 + + : none + : + $* -s 2>"ln: missing arguments" == 1 + + : no-target + : + $* -s a 2>"ln: missing target path" == 1 + + : no-trailing-sep + : + $* -s a b c 2>"ln: multiple target paths with non-directory link path" == 1 + + : empty + : + { + : link + : + $* -s '' 2>"ln: invalid path ''" == 1 + + : target1 + : + $* -s '' a 2>"ln: invalid path ''" == 1 + + : target2 + : + $* -s '' a b/ 2>"ln: invalid path ''" == 1 + } +} + +: file +: +: Test creating a file symlink. +: +{ + : non-existing-link-path + : + { + touch a; + + $* -s a b >>/~%EOO% &b; + %create .+/b true% + %create .+/b false% + EOO + + test -f b + } + + : existing-link + : + { + : file + : + { + touch a b; + + $* -s a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b true% + EOO + %( + %ln: unable to create .+link '.+/b' to '.+/a': .+%| + %ln: unable to copy file '.+/a' to '.+/b': .+% + %) + EOE + } + + : dir + : + { + touch a; + mkdir b; + + $* -s a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b true% + EOO + %( + %ln: unable to create .+link '.+/b' to '.+/a': .+%| + %ln: unable to copy file '.+/a' to '.+/b': .+% + %) + EOE + } + } + + : non-existing + { + : target + : + { + $* -s a b 2>>/~%EOE% != 0 + %ln: unable to create symlink to '.+/a': no such file or directory% + EOE + } + + : link-dir + : + { + touch a; + + $* -s a b/c >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/c true% + EOO + %( + %ln: unable to create .+link '.+/b/c' to '.+/a': .+%| + %ln: unable to copy file '.+/a' to '.+/b/c': .+% + %) + EOE + } + } +} + +: dir +: +: Test creating a directory symlink. +: +{ + : non-existing-link-path + : + { + mkdir a; + touch a/b; + + $* -s a c >>/~%EOO% &c; + %create .+/c true% + %create .+/c false% + EOO + + test -f c/b + } + + : existing-link + : + { + : dir + : + { + mkdir a b; + + $* -s a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b true% + EOO + %( + %ln: unable to create .+link '.+/b' to '.+/a': .+%| + %ln: unable to copy directory '.+/a' to '.+/b': .+% + %) + EOE + } + + : file + : + { + mkdir a; + touch b; + + $* -s a b >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b true% + EOO + %( + %ln: unable to create .+link '.+/b' to '.+/a': .+%| + %ln: unable to copy directory '.+/a' to '.+/b': .+% + %) + EOE + } + } + + : non-existing + { + : link-dir + : + { + mkdir a; + + $* -s a b/c >>/~%EOO% 2>>/~%EOE% != 0 + %create .+/b/c true% + EOO + %( + %ln: unable to create .+link '.+/b/c' to '.+/a': .+%| + %ln: unable to copy directory '.+/a' to '.+/b/c': .+% + %) + EOE + } + } +} + +: multiple-targets +: +: Test creating links for multiple targets in the specified directory. +: +{ + touch a; + mkdir b c; + + $* -s a b c/ >>/~%EOO% &c/a &c/b; + %create .+/c/a true% + %create .+/c/a false% + %create .+/c/b true% + %create .+/c/b false% + EOO + + test -f c/a && test -d c/b +} diff --git a/tests/builtin/mkdir.testscript b/tests/builtin/mkdir.testscript new file mode 100644 index 0000000..5225caa --- /dev/null +++ b/tests/builtin/mkdir.testscript @@ -0,0 +1,102 @@ +# file : tests/builtin/mkdir.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "mkdir" +test.options += -c + +: unknown-option +: +$* -u >'option -u' 2>"mkdir: unknown option '-u'" == 1 + +: parent +: +{ + $* -p a/b >>/~%EOO% &a/ &a/b/; + %create .+/a/ true% + %create .+/a/ false% + %create .+/a/b/ true% + %create .+/a/b/ false% + EOO + + touch a/a a/b/b +} + +: exists +: +{ + $* -p a a a/b a/b >>/~%EOO% &a/ &a/b/ + %create .+/a/ true% + %create .+/a/ false% + %create .+/a/b/ true% + %create .+/a/b/ false% + EOO +} + +: dirs +: +{ + $* a b >>/~%EOO% &a/ &b/; + %create .+/a/ true% + %create .+/a/ false% + %create .+/b/ true% + %create .+/b/ false% + EOO + + touch a/a b/b +} + +: double-dash +: +: Make sure '-p' directory is created. +: +{ + $* -p -- -p >>/~%EOO% &-p/; + %create .+/-p/ true% + %create .+/-p/ false% + EOO + + touch -- -p/a +} + +: no-args +: +: Test passing no arguments. +: +{ + $* 2>"mkdir: missing directory" == 1 +} + +: empty-path +: +: Test creation of empty directory path. +: +{ + $* '' 2>"mkdir: invalid path ''" == 1 +} + +: already-exists +: +: Test creation of an existing directory. +: +{ + $* a a >>/~%EOO% 2>>/~%EOE% &a/ == 1 + %create .+/a/ true% + %create .+/a/ false% + %create .+/a/ true% + EOO + %mkdir: unable to create directory '.+/a': .+% + EOE +} + +: not-exists +: +: Test creation of a directory with non-existent parent. +: +{ + $* a/b >>/~%EOO% 2>>/~%EOE% == 1 + %create .+/a/b/ true% + EOO + %mkdir: unable to create directory '.+/a/b': .+% + EOE +} diff --git a/tests/builtin/mv.testscript b/tests/builtin/mv.testscript new file mode 100644 index 0000000..2647d0f --- /dev/null +++ b/tests/builtin/mv.testscript @@ -0,0 +1,182 @@ +# file : tests/builtin/mv.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "mv" +test.options += -c + +: unknown-option +: +$* -u >'option -u' 2>"mv: unknown option '-u'" == 1 + +: args +: +{ + : none + : + $* 2>"mv: missing arguments" == 1 + + : no-source + : + $* a 2>"mv: missing source path" == 1 + + : no-trailing-sep + : + $* a b c 2>"mv: multiple source paths without trailing separator for destination directory" == 1 + + : empty + : + { + : dest + : + $* '' 2>"mv: invalid path ''" == 1 + + : src1 + : + $* '' a 2>"mv: invalid path ''" == 1 + + : src2 + : + $* '' a b/ 2>"mv: invalid path ''" == 1 + } +} + +: synopsis-1 +: +: Move an entity to the specified path. +: +{ + : file + : + { + : existing + : + { + : to-non-existing + : + { + touch a &!a; + + $* a b >>/~%EOO% &b; + %move .+/a .+/b false true% + %move .+/a .+/b false false% + EOO + + test -f b && test -f a == 1 + } + + : to-existing + : + { + touch a b &!a; + + $* a b >>/~%EOO%; + %move .+/a .+/b false true% + %move .+/a .+/b false false% + EOO + + test -f b && test -f a == 1 + } + + : to-self + : + { + touch a; + + $* a a >/~'%move .+/a .+/a false true%' 2>>/~%EOE% != 0 + %mv: unable to move entity '.+/a' to itself% + EOE + } + + : to-dir + : + { + touch a; + mkdir b; + + $* a b >/~'%move .+/a .+/b false true%' 2>>/~%EOE% != 0 + %mv: unable to move entity '.+/a' to '.+/b': .+% + EOE + } + } + } + + : dir + : + { + : existing + : + { + : to-non-existing + : + { + mkdir a &!a/; + + $* a b &b/ >>/~%EOO%; + %move .+/a .+/b false true% + %move .+/a .+/b false false% + EOO + + test -d b && test -d a == 1 + } + + : to-non-empty + : + { + mkdir a b; + touch b/c; + + $* a b >/~'%move .+/a .+/b false true%' 2>>/~%EOE% != 0 + %mv: unable to move entity '.+/a' to '.+/b': .+% + EOE + } + + : to-non-dir + : + { + mkdir a; + touch b; + + $* a b >/~'%move .+/a .+/b false true%' 2>>/~%EOE% != 0 + %mv: unable to move entity '.+/a' to '.+/b': .+% + EOE + } + } + + : overlap + : + { + mkdir a; + + $* a a/b >/~'%move .+/a .+/a/b false true%' 2>>/~%EOE% != 0 + %mv: unable to move entity '.+/a' to '.+/a/b': .+% + EOE + } + } + + : non-existing + : + { + $* a b >/~'%move .+/a .+/b false true%' 2>>/~%EOE% != 0 + %mv: unable to move entity '.+/a' to '.+/b': .+% + EOE + } +} + +: synopsis-2 +: +: Move entities into the specified directory. +: +{ + mkdir a c &!a/; + touch a/b b &!a/b &!b; + + $* a b c/ >>/~%EOO% &c/a/ &c/a/b &c/b; + %move .+/a .+/c/a false true% + %move .+/a .+/c/a false false% + %move .+/b .+/c/b false true% + %move .+/b .+/c/b false false% + EOO + + test -d c/a && test -f c/a/b && test -f c/b +} diff --git a/tests/builtin/rm.testscript b/tests/builtin/rm.testscript new file mode 100644 index 0000000..991b0f6 --- /dev/null +++ b/tests/builtin/rm.testscript @@ -0,0 +1,105 @@ +# file : tests/builtin/rm.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "rm" +test.options += -c + +: unknown-option +: +$* -u >'option -u' 2>"rm: unknown option '-u'" == 1 + +: no-args +: +{ + : fail + : + : Removing with no arguments fails. + : + $* 2>"rm: missing file" == 1 + + : force + : + : Removing with no arguments succeeds with -f option. + : + $* -f +} + +: file +: +{ + : exists + : + : Removing existing file succeeds. + : + { + touch a &!a; + + $* a >>/~%EOO% + %remove .+/a false true% + %remove .+/a false false% + EOO + } + + : not-exists + : + { + : fail + : + : Removing non-existing file fails. + : + $* a >/~'%remove .+/a false true%' 2>>/~%EOE% == 1 + %rm: unable to remove '.+/a': .+% + EOE + + : force + : + : Removing non-existing file succeeds with -f option. + : + $* -f a >>/~%EOO% + %remove .+/a true true% + %remove .+/a true false% + EOO + } +} + +: dir +: +{ + : default + : + : Removing directory fails by default. + : + { + mkdir a; + + $* a >/~'%remove .+/a false true%' 2>>/~%EOE% == 1 + %rm: '.+/a' is a directory% + EOE + } + + : recursive + : + : Removing directory succeeds with -r option. + : + { + mkdir -p a/b &!a &!a/b; + + $* -r a >>/~%EOO% + %remove .+/a false true% + %remove .+/a false false% + EOO + } +} + +: path +: +{ + : empty + : + : Removing an empty path fails. + : + { + $* '' 2>"rm: invalid path ''" == 1 + } +} diff --git a/tests/builtin/rmdir.testscript b/tests/builtin/rmdir.testscript new file mode 100644 index 0000000..a63f701 --- /dev/null +++ b/tests/builtin/rmdir.testscript @@ -0,0 +1,95 @@ +# file : tests/builtin/rmdir.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "rmdir" +test.options += -c + +: unknown-option +: +rmdir -u 2>"rmdir: unknown option '-u'" == 1 + +: no-args +: +{ + : fail + : + : Removing with no arguments fails. + : + $* 2>"rmdir: missing directory" == 1 + + : force + : + : Removing with no arguments succeeds with -f option. + : + $* -f +} + +: dir +: +{ + : empty-path + : + : Removing an empty path fails. + : + $* '' 2>"rmdir: invalid path ''" == 1 + + : exists + : + : Removing existing directory succeeds. + : + { + mkdir a &!a; + + $* a >>/~%EOO% + %remove .+/a/ false true% + %remove .+/a/ false false% + EOO + } + + : not-exists + : + { + : fail + : Removing non-existing directory fails. + : + { + $* a >/~'%remove .+/a/ false true%' 2>>/~%EOE% == 1 + %rmdir: unable to remove '.+/a': .+% + EOE + } + + : force + : + : Removing non-existing directory succeeds with -f option. + : + $* -f a >>/~%EOO% + %remove .+/a/ true true% + %remove .+/a/ true false% + EOO + } + + : not-empty + : + : Removing non-empty directory fails. + : + { + mkdir -p a/b; + + $* a >/~'%remove .+/a/ false true%' 2>>/~%EOE% == 1 + %rmdir: unable to remove '.+/a': .+% + EOE + } + + : not-dir + : + : Removing not a directory path fails. + : + { + touch a; + + $* a >/~'%remove .+/a/ false true%' 2>>/~%EOE% == 1 + %rmdir: unable to remove '.+/a': .+% + EOE + } +} diff --git a/tests/builtin/sed.testscript b/tests/builtin/sed.testscript new file mode 100644 index 0000000..0d032bc --- /dev/null +++ b/tests/builtin/sed.testscript @@ -0,0 +1,350 @@ +# file : tests/builtin/sed.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "sed" +test.options += -c + +: unknown-option +: +{ + : unparsed + : + $* -u >'option -u' 2>"sed: unknown option '-u'" == 1 + + : parsed + : + { + test.options += -o -u + + : start + : + $* -u -n -e 's/a/b/p' <'a' >>EOO + option -u + b + EOO + + : middle + : + $* -n -u -e 's/a/b/p' <'a' >>EOO + option -u + b + EOO + + : end + : + $* -n -e 's/a/b/p' -u <'a' >>EOO + option -u + b + EOO + + : before-args + : + { + echo 'a' >=f; + + $* -n -e 's/a/b/p' -u f >>EOO + option -u + b + EOO + } + } + + : arg + : + { + echo 'a' >=-u; + + $* -n -e 's/a/b/p' -- -u >'b' + } +} + +: arg +: +{ + : auto-prn + : + { + $* -n -e 's/fox/bar/' <'foo' : on + $* -e 's/fox/bar/' <'foo' >'foo' : off + } + + : script + : + { + : missed + : + $* 2>>EOE != 0 + sed: missing script + EOE + + : missed-val + : + $* -e 2>>EOE != 0 + sed: missing value for option '-e' + EOE + + : empty + : + $* -e '' 2>>EOE != 0 + 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 + EOE + } + + : file + : + { + : exist + : + { + cat <'foo' >=f; + + $* -e 's/foo/bar/' f >'bar' + } + + : none + : + $* -e 's/foo/bar/' <'foo' >'bar' + + : dash + : + $* -e 's/foo/bar/' - <'foo' >'bar' + + : not-exist + : + $* -e 's/foo/bar/' f 2>>/~%EOE% != 0 + %sed: unable to edit '.+/f': .+% + EOE + + : empty + : + $* -e 's/foo/bar/' '' 2>>EOE != 0 + sed: invalid path '' + EOE + } + + : unexpected + : + $* -e 's/a//' a b 2>>EOE != 0 + sed: unexpected argument 'b' + EOE +} + +: command +: +{ + : subst + : + { + : parsing + : + { + : delim + : + { + : none + : + $* -e 's' 2>>EOE != 0 + sed: no delimiter for 's' command + EOE + + : invalid + : + $* -e 's\\' 2>>EOE != 0 + sed: invalid delimiter for 's' command + EOE + } + + : regex + : + { + : unterminated + : + $* -e 's/foo' 2>>/EOE != 0 + sed: unterminated 's' command regex + EOE + + : empty + : + $* -e 's///' 2>>EOE != 0 + sed: empty regex in 's' command + EOE + + : invalid + : + : Note that old versions of libc++ (for example 1.1) do not detect some + : regex errors. For example '*' is parsed successfully. + : + $* -e 's/foo[/bar/' 2>>~%EOE% != 0 + %sed: invalid regex.*% + EOE + } + + : unterminated-replacement + : + $* -e 's/foo/bar' 2>>/EOE != 0 + sed: unterminated 's' command replacement + EOE + + : invalid-flags + : + $* -e 's/foo/bar/a' 2>>EOE != 0 + sed: invalid 's' command flag 'a' + EOE + } + + : exec + : + { + : flags + : + { + : global + : + { + $* -e 's/o/a/g' <'foo' >'faa' : on + $* -e 's/o/a/' <'foo' >'fao' : off + } + + : icase + : + { + $* -e 's/O/a/i' <'foo' >'fao' : on + $* -e 's/O/a/' <'foo' >'foo' : off + } + + : print + : + { + $* -n -e 's/o/a/p' <'foo' >'fao' : on-match + $* -n -e 's/o/a/' <'foo' : off-match + $* -n -e 's/u/a/p' <'foo' : on-no-match + } + } + + : search + { + : anchor + : + { + $* -n -e 's/^o/a/gp' <'oof' >'aof' : begin + $* -n -e 's/o$/a/gp' <'foo' >'foa' : end + } + + : match + : Match corner cases + : + { + $* -n -e 's/a/b/p' <'a' >'b' : full + $* -n -e 's/a/b/p' <'ac' >'bc' : left + $* -n -e 's/a/b/p' <'ca' >'cb' : right + $* -n -e 's/a/b/pg' <'xaax' >'xbbx' : adjacent + } + } + + : replacement + : + { + : ecma-escape + : + { + $* <'xay' -e 's/a/$b/' >'x$by' : none + $* <'xay' -e 's/a/$/' >'x$y' : none-term + $* <'xay' -e 's/a/$$/' >'x$y' : self + $* <'xay' -e 's/a/b$&c/' >'xbacy' : match + $* <'xay' -e 's/a/b$`c/' >'xbxcy' : match-precede + $* <'xay' -e "s/a/b\$'c/" >'xbycy' : match-follow + + : capture + : + $* <'abcdefghij' -e 's/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)/$1$10/' >'aj' + } + + : perl-escape + : + { + $* <'xay' -e 's/a/\b/' >'xby' : none + $* <'xay' -e 's/a/\/' >'xy' : none-term + $* <'xay' -e 's/a/\\/' >'x\y' : self + + : capture + : + $* <'abcdefghij' -e 's/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)/\1\10/' >'aa0' + + : upper + : + { + $* <'xay' -e 's/a/\U/' >'xy' : none + $* <'xay' -e 's/a/\Uvz/' >'xVZy' : repl + $* <'xay' -e 's/a/\Uv\Ez/' >'xVzy' : end + $* <'aa' -e 's/a/v\Uz/g' >'vZvZ' : locality + $* <'xay' -e 's/(a)/\U\1/' >'xAy' : capt + $* <'x-y' -e 's/(a?)-/\U\1z/' >'xZy' : capt-empty + $* <'xay' -e 's/a/\uvz/' >'xVzy' : once + } + + : lower + : + { + $* <'xay' -e 's/a/\lVZ/' >'xvZy' : once + } + } + } + + $* -e 's/a//' <:'b' >'b' : no-newline + $* -e 's/a//' <:'' : empty-stdin + + : empty-file + : + { + touch f; + + $* -e 's/a//' f + } + } + } +} + +: in-place +: +{ + : no-file + : + $* -i -e 's/a/b/' 2>>EOE != 0 + sed: -i|--in-place option specified while reading from stdin + EOE + + : edit + : + { + cat <'foo' >=f; + + $* -i -e 's/foo/bar/' f; + + cat f >'bar' + } +} + +: big +: +: Sed a big file (about 100K) to test that the builtin is asynchronous. +: +{ + s="--------------------------------" + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" + s="$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s" + cat <"$s" | $* -e 's/^x//' | cat >"$s" +} diff --git a/tests/builtin/sleep.testscript b/tests/builtin/sleep.testscript new file mode 100644 index 0000000..a334cba --- /dev/null +++ b/tests/builtin/sleep.testscript @@ -0,0 +1,42 @@ +# file : tests/builtin/sleep.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "sleep" + +: unknown-option +: +$* -u 2>"sleep: unknown option '-u'" == 1 + +: success +: +{ + : custom + : + { + test.options += -c; + $* 1 >'sleep 01 seconds' + } + + : own + : + $* 1 +} + +: no-time +: +: Test passing no time interval. +: +$* 2>"sleep: missing time interval" != 0 + +: invalid-time +: +: Test passing invalid time interval. +: +$* 1a 2>"sleep: invalid time interval '1a'" != 0 + +: unexpected-arg +: +: Test passing extra argument. +: +$* 1 1 2>"sleep: unexpected argument '1'" != 0 diff --git a/tests/builtin/test.testscript b/tests/builtin/test.testscript new file mode 100644 index 0000000..3e132d9 --- /dev/null +++ b/tests/builtin/test.testscript @@ -0,0 +1,84 @@ +# file : tests/builtin/test.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "test" + +: file +: +{ + : exists + : + touch a; + $* -f a + + : not-exists + : + $* -f a == 1 + + : not-file + : + $* -f . == 1 +} + +: dir +: +{ + : exists + : + $* -d . + + : not-exists + : + $* -d a == 1 + + : not-dir + : + touch a; + $* -d a == 1 +} + +: options +: +{ + : unknown + : + $* -u 2>"test: unknown option '-u'" == 2 + + : none + : + $* 2>"test: either -f|--file or -d|--directory must be specified" == 2 + + : both-file-dir + : + $* -fd 2>"test: both -f|--file and -d|--directory specified" == 2 +} + +: args +: +{ + : none + : + $* -f 2>"test: missing path" == 2 + + : unexpected + : + $* -f a b 2>"test: unexpected argument 'b'" == 2 + + : empty-path + : + $* -d '' 2>"test: invalid path ''" == 2 +} + +: 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 -p a/b; + + $* -d b +} diff --git a/tests/builtin/touch.testscript b/tests/builtin/touch.testscript new file mode 100644 index 0000000..aa0ea10 --- /dev/null +++ b/tests/builtin/touch.testscript @@ -0,0 +1,92 @@ +# file : tests/builtin/touch.testscript +# copyright : Copyright (c) 2014-2019 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +test.arguments = "touch" + +: file +: +$* a &a + +: file-create +: +: Test that file is created. If it didn't then 'rm' would fail. +: +{ + $* a &!a; + + rm a +} + +: file-update +: +: Test that existing file touch doesn't fail. +: +{ + cat <'' >=a; + + $* a +} + +: callback +: +: Test that the callback is not called for touching an existing file. +: +{ + test.options += -c; + + $* a >>/~%EOO% &a; + %create .+/a true% + %create .+/a false% + EOO + + $* >>/~%EOO% a + %create .+/a true% + %create .+/a false% + EOO +} + +: unknown-option +: +$* -u 2>"touch: unknown option '-u'" == 1 + +: no-args +: +: Test passing no arguments. +: +$* 2>"touch: missing file" != 0 + +: empty-path +: +: Test touching an empty path. +: +$* '' 2>"touch: invalid path ''" != 0 + +: dir-update +: +: Test touching an existing directory. +: +{ + mkdir a; + + $* a 2>~'%touch: cannot create/update .+: .+%' != 0 +} + +: after +: +{ + : success + : + { + $* a &a; + $* --after a b &b + } + + : no-value + : + $* --after 2>"touch: missing value for option '--after'" != 0 + + : not-exists + : + touch --after a b 2>~"%touch: cannot obtain file '.+a' modification time: .+%" != 0 +} -- cgit v1.1