aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-03-07 14:07:28 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-03-09 14:18:20 +0300
commitdcccba655fe848564e961b3f285ce3a82d3ac73a (patch)
tree598ced3b406d80c23798672930e1a17cfe112b75 /tests
parent63b2988e4f2630cc688ff43b7e5f0d4f977896cd (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.cxx134
-rw-r--r--tests/dir-iterator/testscript100
-rw-r--r--tests/fdstream/driver.cxx38
-rw-r--r--tests/link/driver.cxx93
-rw-r--r--tests/mventry/testscript51
-rw-r--r--tests/path-entry/driver.cxx212
-rw-r--r--tests/path-entry/testscript175
-rw-r--r--tests/wildcard/testscript52
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
: