diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2020-03-07 14:07:28 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2020-03-09 14:18:20 +0300 |
commit | dcccba655fe848564e961b3f285ce3a82d3ac73a (patch) | |
tree | 598ced3b406d80c23798672930e1a17cfe112b75 /tests | |
parent | 63b2988e4f2630cc688ff43b7e5f0d4f977896cd (diff) |
Add more support for symlinks on Windows
See mksymlink() for details of the symlinks support on Windows.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/cpfile/driver.cxx | 134 | ||||
-rw-r--r-- | tests/dir-iterator/testscript | 100 | ||||
-rw-r--r-- | tests/fdstream/driver.cxx | 38 | ||||
-rw-r--r-- | tests/link/driver.cxx | 93 | ||||
-rw-r--r-- | tests/mventry/testscript | 51 | ||||
-rw-r--r-- | tests/path-entry/driver.cxx | 212 | ||||
-rw-r--r-- | tests/path-entry/testscript | 175 | ||||
-rw-r--r-- | tests/wildcard/testscript | 52 |
8 files changed, 676 insertions, 179 deletions
diff --git a/tests/cpfile/driver.cxx b/tests/cpfile/driver.cxx index ae40b5f..c613b49 100644 --- a/tests/cpfile/driver.cxx +++ b/tests/cpfile/driver.cxx @@ -30,10 +30,7 @@ using namespace butl; static const char text1[] = "ABCDEF\nXYZ"; static const char text2[] = "12345\nDEF"; - -#ifndef _WIN32 static const char text3[] = "XAB\r\n9"; -#endif static string from_file (const path& f) @@ -135,72 +132,85 @@ main () assert (from_file (hlink) == text2); -#ifndef _WIN32 - - // Check that 'from' being a symbolic link is properly resolved. + // Note that on Windows regular file symlinks may not be supported (see + // mksymlink() for details), so the following tests are allowed to fail + // with ENOSYS on Windows. // - path fslink (td / path ("fslink")); - mksymlink (from, fslink); - - cpfile (fslink, to, cpflags::overwrite_content); - - // Make sure 'to' is not a symbolic link to 'from' and from_file() just - // follows it. - // - assert (try_rmfile (from) == rmfile_status::success); - assert (from_file (to) == text2); - - // Check that 'to' being a symbolic link is properly resolved. - // - path tslink (td / path ("tslink")); - mksymlink (to, tslink); - - to_file (from, text3); - cpfile (from, tslink, cpflags::overwrite_content); - assert (from_file (to) == text3); - - // Check that permissions are properly overwritten when 'to' is a symbolic - // link. - // - to_file (from, text1); - path_permissions (from, permissions::ru | permissions::xu); - - cpfile ( - from, tslink, cpflags::overwrite_content | cpflags::overwrite_permissions); - - assert (from_file (to) == text1); - assert (path_permissions (to) == path_permissions (from)); - - path_permissions (to, p); - path_permissions (from, p); - - // Check that no-overwrite file copy fails even if 'to' symlink points to - // non-existent file. - // - assert (try_rmfile (to) == rmfile_status::success); - try { - cpfile (from, tslink, cpflags::none); - assert (false); - } - catch (const system_error&) - { + // Check that 'from' being a symbolic link is properly resolved. + // + path fslink (td / path ("fslink")); + mksymlink (from, fslink); + + cpfile (fslink, to, cpflags::overwrite_content); + + // Make sure 'to' is not a symbolic link to 'from' and from_file() just + // follows it. + // + assert (try_rmfile (from) == rmfile_status::success); + assert (from_file (to) == text2); + + // Check that 'to' being a symbolic link is properly resolved. + // + path tslink (td / path ("tslink")); + mksymlink (to, tslink); + + to_file (from, text3); + cpfile (from, tslink, cpflags::overwrite_content); + assert (from_file (to) == text3); + + // Check that permissions are properly overwritten when 'to' is a symbolic + // link. + // + to_file (from, text1); + path_permissions (from, permissions::ru | permissions::xu); + + cpfile ( + from, tslink, cpflags::overwrite_content | cpflags::overwrite_permissions); + + assert (from_file (to) == text1); + assert (path_permissions (to) == path_permissions (from)); + + path_permissions (to, p); + path_permissions (from, p); + + // Check that no-overwrite file copy fails even if 'to' symlink points to + // non-existent file. + // + assert (try_rmfile (to) == rmfile_status::success); + + try + { + cpfile (from, tslink, cpflags::none); + assert (false); + } + catch (const system_error&) + { + } + + // Check that copy fail if 'from' symlink points to non-existent file. The + // std::system_error is thrown as cpfile() fails to obtain permissions for + // the 'from' symlink target. + // + try + { + cpfile (tslink, from, cpflags::none); + assert (false); + } + catch (const system_error&) + { + } } - - // Check that copy fail if 'from' symlink points to non-existent file. The - // std::system_error is thrown as cpfile() fails to obtain permissions for - // the 'from' symlink target. - // - try + catch (const system_error& e) { - cpfile (tslink, from, cpflags::none); +#ifndef _WIN32 assert (false); - } - catch (const system_error&) - { - } +#else + assert (e.code ().category () == generic_category () && + e.code ().value () == ENOSYS); #endif + } rmdir_r (td); } diff --git a/tests/dir-iterator/testscript b/tests/dir-iterator/testscript index b1444ad..720622f 100644 --- a/tests/dir-iterator/testscript +++ b/tests/dir-iterator/testscript @@ -14,42 +14,84 @@ $* a >"reg b" mkdir -p a/b; $* a >"dir b" -# Note that on Windows only directory symlinks are currently supported (see -# mksymlink() for details). -# -: dangling-link +: symlink : -if ($cxx.target.class != 'windows') +: If we are not cross-testing let's test dangling and non-dangling symlynks. +: On Windows that involves mklink command usability test. If we fail to create +: a trial link (say because we are not in the Developer Mode and are running +: non-elevated console), then the test group will be silently skipped. +: +if ($test.target == $build.host) { - +mkdir a - +touch --no-cleanup a/b - +ln -s a/b a/l - +rm a/b + +if ($cxx.target.class != 'windows') + lns = ln -s wd/t wd/l &wd/l + else + echo 'yes' >=t + if cmd /C 'mklink l t' >- 2>- &?l && cat l >'yes' + lns = cmd /C 'mklink wd\l t' &wd/l >- + end - +touch a/c + jnc = cmd /C 'mklink /J wd\l wd\t' &wd/l >- + end - $* ../a >! 2>! != 0 : keep - $* -i ../a >'reg c' : skip -} -else -{ - +mkdir a - +mkdir --no-cleanup a/b - +ln -s a/b a/bl - +rmdir a/b + : symlink + : + if! $empty($lns) + { + : file + : + { + +mkdir wd + +touch --no-cleanup wd/t + +touch wd/f + +$lns + +$* wd >>~%EOO% + %(reg f|reg t|sym reg l)%{3} + EOO + +rm wd/t - +touch a/c + $* ../wd >- 2>! != 0 : keep + $* -i ../wd >'reg f': skip + } - +mkdir a/d - +ln -s a/d a/dl + : dir + : + { + +mkdir wd + +mkdir --no-cleanup wd/t + +mkdir wd/d + +$lns - # On Wine dangling symlinks are not visible (see mksymlink() for details). - # - #$* ../a >! 2>! != 0 : keep + # Note that this test may fail on Windows (see symlinks known issues in + # libbutl/filesystem.mxx). + # + +if ($cxx.target.class != 'windows') + $* wd >>~%EOO% + %(dir d|dir t|sym dir l)%{3} + EOO + end - : skip + +rmdir wd/t + + $* ../wd >- 2>! != 0 : keep + $* -i ../wd >'dir d': skip + } + } + + : junction : - $* -i ../a >>~%EOO% - %(reg c|dir d|sym dir dl)%{3} - EOO + if! $empty($jnc) + { + +mkdir wd + +mkdir --no-cleanup wd/t + +mkdir wd/d + +$jnc + +$* wd >>~%EOO% + %(dir d|dir t|sym dir l)%{3} + EOO + +rmdir wd/t + + $* ../wd >- 2>! != 0 : keep + $* -i ../wd >'dir d': skip + } } diff --git a/tests/fdstream/driver.cxx b/tests/fdstream/driver.cxx index 097383c..3215e02 100644 --- a/tests/fdstream/driver.cxx +++ b/tests/fdstream/driver.cxx @@ -419,26 +419,40 @@ main (int argc, const char* argv[]) ofs.clear (ofdstream::failbit); } -#ifndef _WIN32 - - // Fail for an existing symlink to unexistent file. + // Note that on Windows regular file symlinks may not be supported (see + // mksymlink() for details), so the following tests are allowed to fail + // with ENOSYS on Windows. // - path link (td / path ("link")); - mksymlink (td / path ("unexistent"), link); - try { - fdopen (link, (fdopen_mode::out | - fdopen_mode::create | - fdopen_mode::exclusive)); + // Fail for an existing symlink to unexistent file. + // + path link (td / path ("link")); + mksymlink (td / path ("unexistent"), link); - assert (false); + try + { + fdopen (link, (fdopen_mode::out | + fdopen_mode::create | + fdopen_mode::exclusive)); + + assert (false); + } + catch (const ios::failure&) + { + } } - catch (const ios::failure&) + catch (const system_error& e) { +#ifndef _WIN32 + assert (false); +#else + assert (e.code ().category () == generic_category () && + e.code ().value () == ENOSYS); +#endif } -#else +#ifdef _WIN32 // Check translation modes. // diff --git a/tests/link/driver.cxx b/tests/link/driver.cxx index 96ac880..a77df6f 100644 --- a/tests/link/driver.cxx +++ b/tests/link/driver.cxx @@ -35,18 +35,33 @@ using namespace butl; static const char text[] = "ABCDEF"; +enum class mklink +{ + sym, + hard, + any +}; + static bool -link_file (const path& target, const path& link, bool hard, bool check_content) +link_file (const path& target, const path& link, mklink ml, bool check_content) { try { - if (hard) - mkhardlink (target, link); - else - mksymlink (target, link); + switch (ml) + { + case mklink::sym: mksymlink (target, link); break; + case mklink::hard: mkhardlink (target, link); break; + case mklink::any: mkanylink (target, link, true /* copy */); break; + } } catch (const system_error&) { + //cerr << e << endl; + return false; + } + catch (const pair<entry_type, system_error>&) + { + //cerr << e.second << endl; return false; } @@ -139,22 +154,26 @@ main () // Create the file hard link. // - assert (link_file (fp, td / path ("hlink"), true, true)); + assert (link_file (fp, td / path ("hlink"), mklink::hard, true)); #ifndef _WIN32 // Create the file symlink using an absolute path. // - assert (link_file (fp, td / path ("slink"), false, true)); + assert (link_file (fp, td / path ("slink"), mklink::sym, true)); // Create the file symlink using a relative path. // - assert (link_file (fn, td / path ("rslink"), false, true)); + assert (link_file (fn, td / path ("rslink"), mklink::sym, true)); // Create the file symlink using an unexistent file path. // - assert (link_file (fp + "-a", td / path ("sa"), false, false)); + assert (link_file (fp + "-a", td / path ("sa"), mklink::sym, false)); #endif + // Create the file any link. + // + assert (link_file (fp, td / path ("alink"), mklink::any, true)); + // Prepare the target directory. // dir_path dn ("dir"); @@ -169,8 +188,8 @@ main () } #ifndef _WIN32 - assert (link_file (fp, dp / path ("hlink"), true, true)); - assert (link_file (fp, dp / path ("slink"), false, true)); + assert (link_file (fp, dp / path ("hlink"), mklink::hard, true)); + assert (link_file (fp, dp / path ("slink"), mklink::sym, true)); #endif // Create the directory symlink using an absolute path. @@ -250,24 +269,64 @@ main () assert (link_dir (dp, ld, false /* hard */, true /* check_content */)); rmdir_r (dp); - // On Wine dangling junctions are not visible. That's why we also re-create - // the target before the junction removal. - // -#if 0 { pair<bool, entry_stat> pe (path_entry (ld)); assert (pe.first && pe.second.type == entry_type::symlink); } -#endif { pair<bool, entry_stat> pe (path_entry (ld, true /* follow_symlinks */)); assert (!pe.first); } - assert (try_mkdir (dp) == mkdir_status::success); assert (try_rmsymlink (ld) == rmfile_status::success); + // Try to create a dangling regular file symlink and make sure it is + // properly removed via its parent recursive removal. + // + assert (try_mkdir (dp) == mkdir_status::success); + + // Note that on Windows regular file symlinks may not be supported (see + // mksymlink() for details), so the following tests are allowed to fail + // with ENOSYS on Windows. + // + try + { + mksymlink (dp / "non-existing", dp / "lnk"); + assert (!dir_empty (dp)); + assert (dir_iterator (dp, true /* ignore_dangling */) == dir_iterator ()); + } + catch (const system_error& e) + { +#ifndef _WIN32 + assert (false); +#else + assert (e.code ().category () == generic_category () && + e.code ().value () == ENOSYS); +#endif + } + + rmdir_r (dp); + + // Create a dangling directory symlink and make sure it is properly removed + // via its parent recursive removal. Also make sure that removing directory + // symlink keeps its target intact. + // + assert (try_mkdir (dp) == mkdir_status::success); + + dir_path tgd (td / dir_path ("tdir")); + assert (try_mkdir (tgd) == mkdir_status::success); + + mksymlink (dp / "non-existing", dp / "lnk1", true /* dir */); + assert (!dir_empty (dp)); + assert (dir_iterator (dp, true /* ignore_dangling */) == dir_iterator ()); + + mksymlink (tgd, dp / "lnk2", true /* dir */); + assert (dir_iterator (dp, true /* ignore_dangling */) != dir_iterator ()); + + rmdir_r (dp); + assert (dir_exists (tgd)); + try { rmdir_r (td); diff --git a/tests/mventry/testscript b/tests/mventry/testscript index ecd617a..c6cbf45 100644 --- a/tests/mventry/testscript +++ b/tests/mventry/testscript @@ -92,8 +92,8 @@ : : If we are not cross-testing let's test renaming symlynks from and over. On : Windows that involves mklink command usability test. If we fail to create a -: trial link (say because we are running non-administrative console), then the -: test group will be silently skipped. +: trial link (say because we are not in the Developer Mode and are running +: non-elevated console), then the test group will be silently skipped. : if ($test.target == $build.host) { @@ -104,8 +104,12 @@ if ($test.target == $build.host) if cmd /C 'mklink b a' >- 2>- &?b && cat b >'yes' lns = cmd /C 'mklink b a' >- end + + jnc = cmd /C 'mklink /J b a' >- end + : symlink + : if! $empty($lns) { : file @@ -126,11 +130,11 @@ if ($test.target == $build.host) : to : - : Make sure that if destination is a symlink it is get overwritten and it's - : target stays intact. + : Make sure that if destination is a symlink it is get overwritten and + : it's target stays intact. : echo 'foo' >=a; - $lns; + $lns &b; echo 'bar' >=c &!c; $* c b; cat a >'foo'; @@ -182,6 +186,43 @@ if ($test.target == $build.host) $* b c 2>- == 1 } } + + : junction + : + if! $empty($jnc) + { + : from + : + : Make sure that if source is a junction it refers the same target after + : rename. + : + mkdir -p a; + $jnc &!b; + $* b c &c; + touch a/b; + test -f c/b; + test -d b == 1 + + : to + : + : Make sure that if destination is a junction it is get overwritten and + : it's target stays intact. + : + mkdir -p a; + $jnc; + echo 'foo' >=c &!c; + $* c b &b; + cat b >'foo'; + test -d a; + test -f c == 1 + + : over-existing-dir + : + mkdir a; + $jnc &b; + mkdir c; + $* b c 2>- == 1 + } } : different-fs diff --git a/tests/path-entry/driver.cxx b/tests/path-entry/driver.cxx index d48bf49..51ac04d 100644 --- a/tests/path-entry/driver.cxx +++ b/tests/path-entry/driver.cxx @@ -4,7 +4,9 @@ #include <cassert> #ifndef __cpp_lib_modules_ts +#include <string> #include <iostream> +#include <stdexcept> // invalid_argument #include <system_error> #endif @@ -15,48 +17,210 @@ import std.core; import std.io; #endif +import butl.path; import butl.utility; // operator<<(ostream, exception) +import butl.optional; +import butl.timestamp; import butl.filesystem; #else +#include <libbutl/path.mxx> #include <libbutl/utility.mxx> +#include <libbutl/optional.mxx> +#include <libbutl/timestamp.mxx> #include <libbutl/filesystem.mxx> #endif using namespace std; using namespace butl; -// Usage: argv[0] <path> +// Usage: argv[0] [-l] [-t] [-p <permissions>] [-m <time>] [-a <time>] <path> // -// If path entry exists then print it's type and size (meaningful for the -// regular file only) to STDOUT, and exit with the zero code. Otherwise exit -// with the one code. Don't follow symlink. On failure print the error -// description to STDERR and exit with the two code. +// If path entry exists then optionally modify its meta-information and print +// its type, size (meaningful for the regular file only), permissions, +// modification and access times to STDOUT, one value per line, and exit with +// the zero code. Otherwise exit with the one code. Don't follow symlink by +// default. On failure print the error description to STDERR and exit with +// the two code. +// +// -l +// Follow symlinks. +// +// -t +// Assume the path is a file and touch it. Implies -l. +// +// -p <permissions> +// Set path permissions specified in the chmod utility octal form. Implies +// -l. +// +// -m <time> +// Set path modification time specified in the "%Y-%m-%d %H:%M:%S%[.N]" +// format. Implies -l. +// +// -a <time> +// As -m but set the access time. // int main (int argc, const char* argv[]) -try { - assert (argc == 2); + string stage; + + try + { + using butl::optional; + + bool follow_symlinks (false); + optional<permissions> perms; + optional<timestamp> mtime; + optional<timestamp> atime; + bool touch (false); + + auto time = [] (const char* v) + { + return from_string (v, "%Y-%m-%d %H:%M:%S%[.N]", true /* local */); + }; + + int i (1); + for (; i != argc; ++i) + { + string v (argv[i]); + + if (v == "-l") + follow_symlinks = true; + else if (v == "-t") + { + touch = true; + follow_symlinks = true; + } + else if (v == "-p") + { + assert (++i != argc); + v = argv[i]; + + size_t n; + perms = static_cast<permissions> (stoull (v, &n, 8)); + assert (n == v.size ()); + + follow_symlinks = true; + } + else if (v == "-m") + { + assert (++i != argc); + mtime = time (argv[i]); + + follow_symlinks = true; + } + else if (v == "-a") + { + assert (++i != argc); + atime = time (argv[i]); + + follow_symlinks = true; + } + else + break; + } + + assert (i == argc - 1); + + path p (argv[i]); + + if (touch) + { + stage = "touch"; + touch_file (p); + } + + stage = "stat entry"; + pair<bool, entry_stat> es (path_entry (p, follow_symlinks)); - auto es (path_entry (argv[1])); + if (!es.first) + return 1; - if (!es.first) - return 1; + // The entry is a directory with a symlink followed. + // + bool tdir; - switch (es.second.type) + if (follow_symlinks || es.second.type != entry_type::symlink) + tdir = (es.second.type == entry_type::directory); + else + { + stage = "stat target"; + pair<bool, entry_stat> ts (path_entry (p, true /* follow_symlinks */)); + + if (!ts.first) + return 1; + + tdir = (ts.second.type == entry_type::directory); + } + + if (perms) + { + stage = "set permissions"; + path_permissions (p, *perms); + } + + if (mtime) + { + if (tdir) + { + stage = "set directory mtime"; + dir_mtime (path_cast<dir_path> (p), *mtime); + } + else + { + stage = "set file mtime"; + file_mtime (p, *mtime); + } + } + + if (atime) + { + if (tdir) + { + stage = "set directory atime"; + dir_atime (path_cast<dir_path> (p), *atime); + } + else + { + stage = "set file atime"; + file_atime (p, *atime); + } + } + + cout << "type: "; + + switch (es.second.type) + { + case entry_type::unknown: cout << "unknown"; break; + case entry_type::regular: cout << "regular"; break; + case entry_type::directory: cout << "directory"; break; + case entry_type::symlink: cout << "symlink"; break; + case entry_type::other: cout << "other"; break; + } + + stage = "get permissions"; + + cout << endl + << "size: " << es.second.size << endl + << "permissions: " << oct + << static_cast<size_t> (path_permissions (p)) << endl; + + stage = tdir ? "get directory times" : "get file times"; + + entry_time et (tdir ? dir_time (path_cast<dir_path> (p)) : file_time (p)); + cout << "mtime: " << et.modification << endl + << "atime: " << et.access << endl; + + return 0; + } + catch (const invalid_argument& e) { - case entry_type::unknown: cout << "unknown"; break; - case entry_type::regular: cout << "regular"; break; - case entry_type::directory: cout << "directory"; break; - case entry_type::symlink: cout << "symlink"; break; - case entry_type::other: cout << "other"; break; + cerr << e << endl; + return 2; + } + catch (const system_error& e) + { + cerr << stage << " failed: " << e << endl; + return 2; } - - cout << endl << es.second.size << endl; - return 0; -} -catch (const system_error& e) -{ - cerr << e << endl; - return 2; } diff --git a/tests/path-entry/testscript b/tests/path-entry/testscript index 456f96f..76316bf 100644 --- a/tests/path-entry/testscript +++ b/tests/path-entry/testscript @@ -10,21 +10,176 @@ : printed on Windows. This why we exclude it, to get consistent behavior on : both POSIX and Windows. : - cat <:'abc' >=f; - $* f >>EOO - regular - 3 - EOO + { + cat <:'abc' >=f; + $* f >>~/EOO/ + type: regular + size: 3 + /.+ + EOO + } : dir : : Note that the size value is meaningless for directory entries. : - mkdir -p d; - $* d >>~/EOO/ - directory - /. - EOO + { + mkdir -p d; + $* d >>~/EOO/ + type: directory + /.+ + EOO + } + + : followed-symlink + : + { + cat <:'abc' >=f; + ln -s f l; + $* -l l >>~/EOO/ + type: regular + size: 3 + /.+ + EOO + } + + : symlink + : + : If we are not cross-testing let's test if symlinks are properly followed. + : On Windows that involves mklink command usability test. If we fail to + : create a trial link (say because we are not in the Developer Mode and are + : running non-elevated console), then the test group will be silently + : skipped. + : + if ($test.target == $build.host) + { + +if ($cxx.target.class != 'windows') + lns = ln -s t l &l + else + echo 'yes' >=t + if cmd /C 'mklink l t' >- 2>- &?l && cat l >'yes' + lns = cmd /C 'mklink l t' &l >- + end + + jnc = cmd /C 'mklink /J l t' &l >- + end + + : symlink + : + if! $empty($lns) + { + : file + : + { + : get-info + : + : Test that the target type, size, permissions and file times are + : obtained via a symlink. + : + { + cat <:'abc' >=t; + $lns; + $* -p 400 -m '2020-03-05 00:00:00' -a '2020-03-05 00:00:01' t | set ti; + $* -l l >"$ti" + } + + : set-info + : + : Test that permissions and file times are set via a symlink. + : + { + cat <:'abc' >=t; + $lns; + $* -p 400 -m '2020-03-05 00:00:00' -a '2020-03-05 00:00:01' l | set ti; + sed -n -e 's/permissions: (.+)/\1/p' <"$ti" >~/'4.{2}'/; + sed -n -e 's/mtime: (.+)/\1/p' <"$ti" >'2020-03-05 00:00:00'; + sed -n -e 's/atime: (.+)/\1/p' <"$ti" >'2020-03-05 00:00:01' + } + + : touch + : + : Test that a symlink touch touches the target. + : + { + cat <:'abc' >=t; + $lns; + $* t | set ti; + sleep 2; + $* -t l | set li; + if ("$ti" == "$li") + exit "link touch doesn't change target" + end + } + } + + : dir + : + : Note that the following tests may fail on Windows (see symlinks known + : issues in libbutl/filesystem.mxx). + : + if ($cxx.target.class != 'windows') + { + : get-info + : + : Test that the target type, size, permissions and file times are + : obtained via a symlink. + : + { + mkdir t; + $lns; + $* -p 400 -m '2020-03-05 00:00:00' -a '2020-03-05 00:00:01' t | set ti; + $* -l l >"$ti"; + $* -p 600 t >- # @@ TMP; until build2 is staged. + } + + : set-info + : + : Test that permissions and file times are set via a symlink. + : + { + mkdir t; + $lns; + $* -p 400 -m '2020-03-05 00:00:00' -a '2020-03-05 00:00:01' l | set ti; + sed -n -e 's/permissions: (.+)/\1/p' <"$ti" >~/'4.{2}'/; + sed -n -e 's/mtime: (.+)/\1/p' <"$ti" >'2020-03-05 00:00:00'; + sed -n -e 's/atime: (.+)/\1/p' <"$ti" >'2020-03-05 00:00:01'; + $* -p 600 t >- # @@ TMP; until build2 is staged. + } + } + } + + : junction + : + if! $empty($jnc) + { + : get-info + : + : Test that the target type, size, permissions and file times are + : obtained via a junction. + : + { + mkdir t; + $jnc; + $* -p 400 -m '2020-03-05 00:00:00' -a '2020-03-05 00:00:01' t | set ti; + $* -l l >"$ti"; + $* -p 600 t >- # @@ TMP; until build2 is staged. + } + + : set-info + : + : Test that permissions and file times are set via a junction. + : + { + mkdir t; + $jnc; + $* -p 400 -m '2020-03-05 00:00:00' -a '2020-03-05 00:00:01' l | set ti; + sed -n -e 's/permissions: (.+)/\1/p' <"$ti" >~/'4.{2}'/; + sed -n -e 's/mtime: (.+)/\1/p' <"$ti" >'2020-03-05 00:00:00'; + sed -n -e 's/atime: (.+)/\1/p' <"$ti" >'2020-03-05 00:00:01'; + $* -p 600 t >- # @@ TMP; until build2 is staged. + } + } + } } : non-existent diff --git a/tests/wildcard/testscript b/tests/wildcard/testscript index e1ddd5a..3590aa3 100644 --- a/tests/wildcard/testscript +++ b/tests/wildcard/testscript @@ -149,6 +149,10 @@ : path-directory-search : +: Note that we always need to make sure that no auxiliary files (stdout, etc) +: are created in the directories we search through not to end up with a race +: condition (trying to query type of a removed filesystem entry, etc). +: { test.options = -sd @@ -294,19 +298,21 @@ : file : { - +mkdir -p foo fox fix/bar baz/foo/zab baz/foo/zab/baz - +touch foo/bar foo/fox fox/baz baz/foo/zab/bar + +mkdir -p wd/foo wd/fox wd/fix/bar wd/baz/foo/zab wd/baz/foo/zab/baz + +touch wd/foo/bar wd/foo/fox wd/fox/baz wd/baz/foo/zab/bar + + wd = ../wd : immediate : - $* f*/b* .. >>/EOO + $* f*/b* $wd >>/EOO foo/bar fox/baz EOO : recursive : - $* f**/b** .. >>/EOO + $* f**/b** $wd >>/EOO baz/foo/zab/bar foo/bar fox/baz @@ -315,15 +321,17 @@ : self-recursive : { + wd = ../../wd + : pattern : - $* foo/f*** ../.. >>/EOO + $* foo/f*** $wd >>/EOO foo/fox EOO : start : - $* f*** ../../foo >>/EOO + $* f*** $wd/foo >>/EOO fox EOO @@ -340,19 +348,21 @@ : dir : { - +mkdir -p foo/bar foo/fox/box fox/baz fix baz/foo/zab/baz - +touch fix/bar baz/foo/zab/bar + +mkdir -p wd/foo/bar wd/foo/fox/box wd/fox/baz wd/fix wd/baz/foo/zab/baz + +touch wd/fix/bar wd/baz/foo/zab/bar + + wd = ../wd : immediate : - $* f*/b*/ .. >>/EOO + $* f*/b*/ $wd >>/EOO foo/bar/ fox/baz/ EOO : recursive : - $* f**/b**/ .. >>/EOO + $* f**/b**/ $wd >>/EOO baz/foo/zab/baz/ foo/bar/ foo/fox/box/ @@ -363,9 +373,11 @@ : self-recursive : { + wd = ../../wd + : pattern : - $* foo/f***/b**/ ../.. >>/EOO + $* foo/f***/b**/ $wd >>/EOO foo/bar/ foo/fox/box/ foo/fox/box/ @@ -373,7 +385,7 @@ : start : - $* f***/b**/ ../../foo >>/EOO + $* f***/b**/ $wd/foo >>/EOO bar/ fox/box/ fox/box/ @@ -394,13 +406,13 @@ : fast-forward : { - +mkdir -p foo/bar/baz foo/box - +touch foo/bar/baz/fox + +mkdir -p wd/foo/bar/baz wd/foo/box + +touch wd/foo/bar/baz/fox : partial : { - wd = ../.. + wd = ../../wd : file : @@ -418,7 +430,7 @@ : reduce : { - wd = ../../.. + wd = ../../../wd : exists : @@ -457,11 +469,11 @@ : dot-started : { - +mkdir -p z/.z/.z z/z a/.z .a/.z - +touch z/.z.cxx z/z.cxx z/.z/.z.cxx z/.z/z.cxx z/z/.z.cxx z/z/z.cxx \ - a/z.cxx a/.z.cxx .a/z.cxx .a/.z.cxx + +mkdir -p wd/z/.z/.z wd/z/z wd/a/.z wd/.a/.z + +touch wd/z/.z.cxx wd/z/z.cxx wd/z/.z/.z.cxx wd/z/.z/z.cxx wd/z/z/.z.cxx \ + wd/z/z/z.cxx wd/a/z.cxx wd/a/.z.cxx wd/.a/z.cxx wd/.a/.z.cxx - wd=../../.. + wd=../../../wd : recursive : |