From a7efabf301f23364ac2335c80c5e1e712bc43204 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 10 Nov 2016 00:26:54 +0300 Subject: Add cat, false and true builtins --- bootstrap.sh | 2 +- build2/buildfile | 3 + build2/test/script/builtin | 21 ++- build2/test/script/builtin.cxx | 293 ++++++++++++++++++++++++++++++--- build2/test/script/parser | 6 + build2/test/script/parser.cxx | 19 ++- build2/test/script/runner.cxx | 21 ++- build2/types | 3 + build2/utility | 16 +- tests/test/script/buildfile | 2 +- tests/test/script/builtin/buildfile | 5 + tests/test/script/builtin/cat.test | 57 +++++++ tests/test/script/builtin/echo.test | 11 ++ tests/test/script/builtin/mkdir.test | 50 ++++++ tests/test/script/builtin/touch.test | 45 +++++ tests/test/script/runner/buildfile | 5 +- tests/test/script/runner/mkdir.test | 50 ------ tests/test/script/runner/redirect.test | 2 +- tests/test/script/runner/status.test | 4 + tests/test/script/runner/touch.test | 44 ----- 20 files changed, 510 insertions(+), 149 deletions(-) create mode 100644 tests/test/script/builtin/buildfile create mode 100644 tests/test/script/builtin/cat.test create mode 100644 tests/test/script/builtin/echo.test create mode 100644 tests/test/script/builtin/mkdir.test create mode 100644 tests/test/script/builtin/touch.test delete mode 100644 tests/test/script/runner/mkdir.test delete mode 100644 tests/test/script/runner/touch.test diff --git a/bootstrap.sh b/bootstrap.sh index 15410cb..3496a1f 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -131,4 +131,4 @@ src="$src build2/pkgconfig/*.cxx" src="$src $libbutl/butl/*.cxx" set -x -"$cxx" "-I$libbutl" -I. '-DBUILD2_HOST_TRIPLET="'"$host"'"' -std=c++1y "$@" -o build2/b-boot $src +"$cxx" "-I$libbutl" -I. '-DBUILD2_HOST_TRIPLET="'"$host"'"' -std=c++1y "$@" -o build2/b-boot $src -lpthread diff --git a/build2/buildfile b/build2/buildfile index 86b67a9..cd04ce7 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -88,6 +88,9 @@ test/script/{hxx cxx}{ token } \ # obj{b context}: cxx.poptions += -DBUILD2_HOST_TRIPLET=\"$cxx.target\" +if ($cxx.target.class != "windows") + cxx.libs += -lpthread + # Generated options parser. # {hxx ixx cxx}{b-options}: cli{b} diff --git a/build2/test/script/builtin b/build2/test/script/builtin index e3c16b3..bd5fe50 100644 --- a/build2/test/script/builtin +++ b/build2/test/script/builtin @@ -5,11 +5,11 @@ #ifndef BUILD2_TEST_SCRIPT_BUILTIN #define BUILD2_TEST_SCRIPT_BUILTIN +#include + #include #include -#include - namespace build2 { namespace test @@ -18,21 +18,26 @@ namespace build2 { class scope; + // Start builtin command. Throw system_error on failure. + // // Note that unlike argc/argv, our args don't include the program name. // - using builtin = int (*) (scope&, - const strings& args, - auto_fd in, auto_fd out, auto_fd err); + // Also note that the future object being returned doesn't block in dtor + // until the builtin command terminates. + // + using builtin = future (scope&, + const strings& args, + auto_fd in, auto_fd out, auto_fd err); - class builtin_map: public std::map + class builtin_map: public std::map { public: - using base = std::map; + using base = std::map; using base::base; // Return NULL if not a builtin. // - builtin + builtin* find (const string& n) const { auto i (base::find (n)); diff --git a/build2/test/script/builtin.cxx b/build2/test/script/builtin.cxx index 9241447..2dcfde6 100644 --- a/build2/test/script/builtin.cxx +++ b/build2/test/script/builtin.cxx @@ -10,12 +10,22 @@ # include #endif +#include + #include // use default operator<< implementation -#include // fdopen_mode +#include // fdopen_mode, fdstream_mode #include // mkdir_status #include +// Strictly speaking a builtin which reads/writes from/to standard streams +// must be asynchronous so that the caller can communicate with it through +// pipes without being blocked on I/O operations. However, as an optimization, +// we allow builtins that only print diagnostics to STDERR to be synchronous +// assuming that their output will always fit the pipe buffer. Synchronous +// builtins must not read from STDIN and write to STDOUT. Later we may relax +// this rule to allow a "short" output for such builtins. +// using namespace std; using namespace butl; @@ -25,6 +35,18 @@ namespace build2 { namespace script { + using builtin_impl = uint8_t (scope&, + const strings& args, + auto_fd in, auto_fd out, auto_fd err); + static future + to_future (uint8_t status) + { + promise p; + future f (p.get_future ()); + p.set_value (status); + return f; + } + // Operation failed, diagnostics has already been issued. // struct failed {}; @@ -48,13 +70,141 @@ namespace build2 return p; } + // Builtin commands functions. + // + + // cat ... + // + // Read files in sequence and write their contents to STDOUT in the same + // sequence. Read from STDIN if no argumements provided or '-' is + // specified as a file path. STDIN, STDOUT and file streams are set to + // binary mode prior to I/O operations. + // + // Note that POSIX doesn't specify if after I/O operation failure the + // command should proceed with the rest of the arguments. The current + // implementation exits immediatelly in such a case. + // + // @@ Shouldn't we check that we don't print a nonempty regular file to + // itself, as that would merely exhaust the output device? POSIX + // allows (but not requires) such a check and some implementations do + // this. That would require to fstat() file descriptors and complicate + // the code a bit. Was able to reproduce on a big file (should be + // bigger than the stream buffer size) with the test + // 'cat file >>>&file'. + // + // Note: must be executed asynchronously. + // + static uint8_t + cat (scope& sp, + const strings& args, + auto_fd in, auto_fd out, auto_fd err) noexcept + try + { + uint8_t r (1); + ofdstream cerr (move (err)); + + try + { + ifdstream cin (move (in), fdstream_mode::binary); + ofdstream cout (move (out), fdstream_mode::binary); + + // Copy input stream to STDOUT. + // + auto copy = [&cout] (istream& is) + { + if (is.peek () != ifdstream::traits_type::eof ()) + cout << is.rdbuf (); + + is.clear (istream::eofbit); // Sets eofbit. + }; + + // Path of a file being printed to STDOUT. An empty path represents + // STDIN. Used in diagnostics. + // + path p; + + try + { + // Print STDIN. + // + if (args.empty ()) + copy (cin); + + // Print files. + // + for (auto i (args.begin ()); i != args.end (); ++i) + { + if (*i == "-") + { + if (!cin.eof ()) + { + p.clear (); + copy (cin); + } + + continue; + } + + p = parse_path (*i, sp.wd_path); + + ifdstream is (p, ifdstream::binary); + copy (is); + is.close (); + } + } + catch (const io_error& e) + { + cerr << "cat: unable to print "; + + if (p.empty ()) + cerr << "stdin"; + else + cerr << "'" << p << "'"; + + cerr << ": " << e.what () << endl; + throw failed (); + } + + cin.close (); + cout.close (); + r = 0; + } + catch (const invalid_path& e) + { + cerr << "cat: invalid path '" << e.path << "'" << endl; + } + // Can be thrown while closing cin, cout or writing to cerr (that's + // why need to check its state before writing). + // + catch (const io_error& e) + { + if (cerr.good ()) + cerr << "cat: " << e.what () << endl; + } + catch (const failed&) + { + // Diagnostics has already been issued. + } + + cerr.close (); + return r; + } + catch (const std::exception&) + { + return 1; + } + // echo ... // - static int - echo (scope&, const strings& args, auto_fd in, auto_fd out, auto_fd err) + // Note: must be executed asynchronously. + // + static uint8_t + echo (scope&, + const strings& args, + auto_fd in, auto_fd out, auto_fd err) noexcept try { - int r (1); + uint8_t r (1); ofdstream cerr (move (err)); try @@ -83,6 +233,26 @@ namespace build2 return 1; } + // false + // + // Return 1. Failure to close the file descriptors is silently ignored. + // + static future + false_ (scope&, const strings&, auto_fd, auto_fd, auto_fd) + { + return to_future (1); + } + + // true + // + // Return 0. Failure to close the file descriptors is silently ignored. + // + static future + true_ (scope&, const strings&, auto_fd, auto_fd, auto_fd) + { + return to_future (0); + } + // Create a directory if not exist and its parent directories if // necessary. Throw system_error on failure. Register created // directories for cleanup. The directory path must be absolute. @@ -106,20 +276,19 @@ namespace build2 // Create any missing intermediate pathname components. Each argument // that names an existing directory must be ignored without error. // - static int + // Note that POSIX doesn't specify if after a directory creation failure + // the command should proceed with the rest of the arguments. The current + // implementation exits immediatelly in such a case. + // + // Note: can be executed synchronously. + // + static uint8_t mkdir (scope& sp, const strings& args, - auto_fd in, auto_fd out, auto_fd err) + auto_fd in, auto_fd out, auto_fd err) noexcept try { - // @@ Should we set a proper verbosity so paths get printed as - // relative? Can be inconvenient for end-user when build2 runs from - // a testscript. - // - // No, don't think so. If this were an external program, there - // won't be such functionality. - // - int r (1); + uint8_t r (1); ofdstream cerr (move (err)); try @@ -170,7 +339,6 @@ namespace build2 { cerr << "mkdir: unable to create directory '" << p << "': " << e.what () << endl; - throw failed (); } } @@ -181,6 +349,14 @@ namespace build2 { cerr << "mkdir: invalid path '" << e.path << "'" << endl; } + // Can be thrown while closing in, out or writing to cerr (that's why + // need to check its state before writing). + // + catch (const io_error& e) + { + if (cerr.good ()) + cerr << "mkdir: " << e.what () << endl; + } catch (const failed&) { // Diagnostics has already been issued. @@ -194,7 +370,7 @@ namespace build2 return 1; } - // touch ... + // touch ... // // Change file access and modification times to the current time. Create // a file if doesn't exist. Fail if a file system entry other than file @@ -203,13 +379,19 @@ namespace build2 // Note that POSIX doesn't specify the behavior for touching an entry // other than file. // - static int + // Also note that POSIX doesn't specify if after a file touch failure the + // command should proceed with the rest of the arguments. The current + // implementation exits immediatelly in such a case. + // + // Note: can be executed synchronously. + // + static uint8_t touch (scope& sp, const strings& args, - auto_fd in, auto_fd out, auto_fd err) + auto_fd in, auto_fd out, auto_fd err) noexcept try { - int r (1); + uint8_t r (1); ofdstream cerr (move (err)); try @@ -283,6 +465,14 @@ namespace build2 { cerr << "touch: invalid path '" << e.path << "'" << endl; } + // Can be thrown while closing in, out or writing to cerr (that's why + // need to check its state before writing). + // + catch (const io_error& e) + { + if (cerr.good ()) + cerr << "touch: " << e.what () << endl; + } catch (const failed&) { // Diagnostics has already been issued. @@ -296,11 +486,70 @@ namespace build2 return 1; } + static void + thread_thunk (builtin_impl* fn, + scope& sp, + const strings& args, + auto_fd in, auto_fd out, auto_fd err, + promise p) + { + // The use of set_value_at_thread_exit() would be more appropriate but + // the function is not supported by old versions of g++ (e.g., not in + // 4.9). There could also be overhead associated with it. + // + p.set_value (fn (sp, args, move (in), move (out), move (err))); + } + + // Run builtin implementation asynchronously. + // + static future + async_impl (builtin_impl fn, + scope& sp, + const strings& args, + auto_fd in, auto_fd out, auto_fd err) + { + promise p; + future f (p.get_future ()); + + thread t (thread_thunk, + fn, + ref (sp), + cref (args), + move (in), move (out), move (err), + move (p)); + + t.detach (); + return f; + } + + template + static future + async_impl (scope& sp, + const strings& args, + auto_fd in, auto_fd out, auto_fd err) + { + return async_impl (fn, sp, args, move (in), move (out), move (err)); + } + + // Run builtin implementation synchronously. + // + template + static future + sync_impl (scope& sp, + const strings& args, + auto_fd in, auto_fd out, auto_fd err) + { + return to_future (fn (sp, args, move (in), move (out), move (err))); + } + const builtin_map builtins { - {"echo", &echo}, - {"mkdir", &mkdir}, - {"touch", &touch} + {"cat", &async_impl<&cat>}, + {"echo", &async_impl<&echo>}, + {"false", &false_}, + {"mkdir", &sync_impl<&mkdir>}, + {"touch", &sync_impl<&touch>}, + {"true", &true_} }; } } diff --git a/build2/test/script/parser b/build2/test/script/parser index a81ddf0..fdfbe11 100644 --- a/build2/test/script/parser +++ b/build2/test/script/parser @@ -158,6 +158,12 @@ namespace build2 const string& insert_id (string, location); + // Set lexer pointers for both the current and the base classes. + // + protected: + void + set_lexer (lexer* l); + protected: using base_parser = build2::parser; diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx index 10103f3..438e1f2 100644 --- a/build2/test/script/parser.cxx +++ b/build2/test/script/parser.cxx @@ -60,8 +60,7 @@ namespace build2 pre_parse_ = true; lexer l (is, *path_, lexer_mode::script_line); - lexer_ = &l; - base_parser::lexer_ = &l; + set_lexer (&l); id_prefix_.clear (); @@ -948,8 +947,7 @@ namespace build2 path_ = &p; lexer* ol (lexer_); - lexer_ = &l; - base_parser::lexer_ = &l; + set_lexer (&l); string oip (id_prefix_); id_prefix_ += to_string (dl.line); @@ -963,8 +961,7 @@ namespace build2 fail (t) << "stray " << t; id_prefix_ = oip; - base_parser::lexer_ = ol; - lexer_ = ol; + set_lexer (ol); path_ = op; } catch (const io_error& e) @@ -2219,8 +2216,7 @@ namespace build2 pre_parse_ = false; - lexer_ = nullptr; - base_parser::lexer_ = nullptr; + set_lexer (nullptr); script_ = &s; runner_ = &r; @@ -2694,6 +2690,13 @@ namespace build2 return p.first->first; } + + void parser:: + set_lexer (lexer* l) + { + lexer_ = l; + base_parser::lexer_ = l; + } } } } diff --git a/build2/test/script/runner.cxx b/build2/test/script/runner.cxx index 1d6920a..aa35612 100644 --- a/build2/test/script/runner.cxx +++ b/build2/test/script/runner.cxx @@ -406,7 +406,8 @@ namespace build2 ifd.reset (fdnull ()); // @@ Eventually will be throwing. if (ifd.get () == -1) // @@ TMP - throw io_error ("", error_code (errno, system_category ())); + throw io_error ( + error_code (errno, system_category ()).message ()); in = -2; } @@ -512,7 +513,8 @@ namespace build2 fd.reset (fdnull ()); // @@ Eventully will be throwing. if (fd.get () == -1) // @@ TMP - throw io_error ("", error_code (errno, system_category ())); + throw io_error ( + error_code (errno, system_category ()).message ()); } catch (const io_error& e) { @@ -590,13 +592,24 @@ namespace build2 } optional status; - builtin b (builtins.find (c.program.string ())); + builtin* b (builtins.find (c.program.string ())); if (b != nullptr) { // Execute the builtin. // - status = (*b) (sp, c.arguments, move (ifd), move (ofd), move (efd)); + try + { + future f ( + (*b) (sp, c.arguments, move (ifd), move (ofd), move (efd))); + + status = f.get (); + } + catch (const system_error& e) + { + fail (ll) << "unable to execute " << c.program << " builtin: " + << e.what (); + } } else { diff --git a/build2/types b/build2/types index 9b3dbc7..783aff0 100644 --- a/build2/types +++ b/build2/types @@ -8,6 +8,7 @@ #include #include #include +#include #include // unique_ptr, shared_ptr #include // pair, move() #include // size_t, nullptr_t @@ -63,6 +64,8 @@ namespace build2 using std::istream; using std::ostream; + using std::future; + // Exceptions. While is included, there is no using for // std::exception -- use qualified. // diff --git a/build2/utility b/build2/utility index e08ae69..deefc43 100644 --- a/build2/utility +++ b/build2/utility @@ -5,12 +5,13 @@ #ifndef BUILD2_UTILITY #define BUILD2_UTILITY -#include // make_tuple() -#include // make_shared() -#include // to_string() -#include // move(), forward(), declval(), make_pair() -#include // assert() -#include // make_move_iterator() +#include // make_tuple() +#include // make_shared() +#include // to_string() +#include // move(), forward(), declval(), make_pair() +#include // assert() +#include // make_move_iterator() +#include // ref(), cref() #include // combine_hash(), reverse_iterate(), casecmp(), // lcase() @@ -27,6 +28,9 @@ namespace build2 using std::forward; using std::declval; + using std::ref; + using std::cref; + using std::make_pair; using std::make_tuple; using std::make_shared; diff --git a/tests/test/script/buildfile b/tests/test/script/buildfile index e613013..82af4dd 100644 --- a/tests/test/script/buildfile +++ b/tests/test/script/buildfile @@ -2,6 +2,6 @@ # copyright : Copyright (c) 2014-2016 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -d = runner/ integration/ +d = builtin/ runner/ integration/ ./: $d include $d diff --git a/tests/test/script/builtin/buildfile b/tests/test/script/builtin/buildfile new file mode 100644 index 0000000..baa4996 --- /dev/null +++ b/tests/test/script/builtin/buildfile @@ -0,0 +1,5 @@ +# file : tests/test/script/builtin/buildfile +# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +./: test{cat echo mkdir touch} diff --git a/tests/test/script/builtin/cat.test b/tests/test/script/builtin/cat.test new file mode 100644 index 0000000..7797906 --- /dev/null +++ b/tests/test/script/builtin/cat.test @@ -0,0 +1,57 @@ +# file : tests/test/script/runner/cat.test +# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +: in +: +cat <>EOO +foo +bar +EOI +foo +bar +EOO + +: dash +: +cat - <>EOO +foo +bar +EOI +foo +bar +EOO + +: file +: +cat <>>out; +foo +bar +EOI +cat out >>EOO +foo +bar +EOO + +: in-repeat +: +cat - <>EOO +foo +bar +EOI +foo +bar +EOO + +: non-existent +: +cat in 2>- != 0 # @@ REGEX + +: empty-path +: +: Cat an empty path. +: +cat '' 2>"cat: invalid path ''" == 1 + +# @@ When piping is ready test cat on a big file to test it is asynchronous. +# diff --git a/tests/test/script/builtin/echo.test b/tests/test/script/builtin/echo.test new file mode 100644 index 0000000..7f43aac --- /dev/null +++ b/tests/test/script/builtin/echo.test @@ -0,0 +1,11 @@ +# file : tests/test/script/runner/echo.test +# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +: string +: +echo foo >foo + +: strings +: +echo foo bar >"foo bar" diff --git a/tests/test/script/builtin/mkdir.test b/tests/test/script/builtin/mkdir.test new file mode 100644 index 0000000..6b7b5c9 --- /dev/null +++ b/tests/test/script/builtin/mkdir.test @@ -0,0 +1,50 @@ +# file : tests/test/script/runner/mkdir.test +# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +: dirs +: +mkdir a b; +touch a/a b/b + +: parent +: +mkdir -p a/b; +touch a/a a/b/b + +: exists +: +mkdir -p a a a/b a/b + +: double-dash +: +: Make sure '-p' directory is created. +: +mkdir -p -- -p; +touch -p/a + +: no-args +: +: Test passing no arguments. +: +mkdir 2>"mkdir: missing directory" == 1 + +: empty-path +: +: Test creation of empty directory path. +: +mkdir '' 2>"mkdir: invalid path ''" == 1 + +: already-exists +: +: Test creation of an existing directory. Note that error message is +: platform-dependent so is not checked. +: +mkdir a 2>- a == 1 # @@ REGEX + +: not-exists +: +: Test creation of a directory with non-existent parent. Note that error +: message is platform-dependent so is not checked. +: +mkdir a/b 2>- == 1 # @@ REGEX diff --git a/tests/test/script/builtin/touch.test b/tests/test/script/builtin/touch.test new file mode 100644 index 0000000..4d2ff57 --- /dev/null +++ b/tests/test/script/builtin/touch.test @@ -0,0 +1,45 @@ +# file : tests/test/script/runner/touch.test +# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +: file +: +touch a + +: file-create +: +: Test that file is created. If it didn't then 'rm' would fail. +: +touch a &!a; +rm a + +: file-update +: +: Test that existing file touch doesn't fail. +: +cat <"" >>>a; +touch a + +# @@ How we can test that touch of an existing file doesn't register a cleanup? +# + +: no-args +: +: Test passing no arguments. +: +touch 2>"touch: missing file" == 1 + +: empty-path +: +: Test touching an empty path. +: +touch '' 2>"touch: invalid path ''" == 1 + +: dir-update +: +: Test touching an existing directory. +: +a = [path] $~; +a += "a"; +mkdir a; +touch 2>"touch: '$a' exists and is not a file" a == 1 diff --git a/tests/test/script/runner/buildfile b/tests/test/script/runner/buildfile index b9c0e69..e5f2761 100644 --- a/tests/test/script/runner/buildfile +++ b/tests/test/script/runner/buildfile @@ -4,9 +4,6 @@ import libs = libbutl%lib{butl} -exe{driver}: cxx{driver} $libs test{cleanup mkdir redirect status touch} - -if ($cxx.target.class == "windows") # @@ TMP - test{*}: ext = ".exe" +exe{driver}: cxx{driver} $libs test{cleanup redirect status} include ../../../../../build2/ diff --git a/tests/test/script/runner/mkdir.test b/tests/test/script/runner/mkdir.test deleted file mode 100644 index 6b7b5c9..0000000 --- a/tests/test/script/runner/mkdir.test +++ /dev/null @@ -1,50 +0,0 @@ -# file : tests/test/script/runner/mkdir.test -# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file - -: dirs -: -mkdir a b; -touch a/a b/b - -: parent -: -mkdir -p a/b; -touch a/a a/b/b - -: exists -: -mkdir -p a a a/b a/b - -: double-dash -: -: Make sure '-p' directory is created. -: -mkdir -p -- -p; -touch -p/a - -: no-args -: -: Test passing no arguments. -: -mkdir 2>"mkdir: missing directory" == 1 - -: empty-path -: -: Test creation of empty directory path. -: -mkdir '' 2>"mkdir: invalid path ''" == 1 - -: already-exists -: -: Test creation of an existing directory. Note that error message is -: platform-dependent so is not checked. -: -mkdir a 2>- a == 1 # @@ REGEX - -: not-exists -: -: Test creation of a directory with non-existent parent. Note that error -: message is platform-dependent so is not checked. -: -mkdir a/b 2>- == 1 # @@ REGEX diff --git a/tests/test/script/runner/redirect.test b/tests/test/script/runner/redirect.test index 68cc9aa..16c17d8 100644 --- a/tests/test/script/runner/redirect.test +++ b/tests/test/script/runner/redirect.test @@ -166,6 +166,6 @@ EOO echo - : in-str echo "foo" >foo : out-str echo "foo" 2>foo 1>&2 : err-str - cat foo : inout-str # @@ cat is not a builtin yet. + cat foo : inout-str cat foo 1>&2 : inerr-str } diff --git a/tests/test/script/runner/status.test b/tests/test/script/runner/status.test index f1ad5bf..00d7257 100644 --- a/tests/test/script/runner/status.test +++ b/tests/test/script/runner/status.test @@ -15,6 +15,10 @@ b = $build.driver -q --no-column --buildfile - <"./: test{testscript}" \ c = cat >>>testscript test = \'$test\' ++if ($cxx.target.class == "windows") + ext = ".exe" +end + # Successfull tests. # : eq-true diff --git a/tests/test/script/runner/touch.test b/tests/test/script/runner/touch.test deleted file mode 100644 index e9d9f68..0000000 --- a/tests/test/script/runner/touch.test +++ /dev/null @@ -1,44 +0,0 @@ -# file : tests/test/script/runner/touch.test -# copyright : Copyright (c) 2014-2016 Code Synthesis Ltd -# license : MIT; see accompanying LICENSE file - -: file -: -touch a - -: file-create -: -: Test that file is created. If it didn't then 'rm' would fail. -: -touch a &!a; -rm a - -: file-update -: -: Test that existing file touch doesn't register cleanup. If it did then it -: would be left dangling after 'rm' call and so test would fail. -: -$* -f a; -touch a; -rm a - -: no-args -: -: Test passing no arguments. -: -touch 2>"touch: missing file" == 1 - -: empty-path -: -: Test touching an empty path. -: -touch '' 2>"touch: invalid path ''" == 1 - -: dir-update -: -: Test touching an existing directory. -: -a = [path] $~; -a += "a"; -mkdir a; -touch 2>"touch: '$a' exists and is not a file" a == 1 -- cgit v1.1