summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2009-12-17 12:30:47 -0700
committerEric Blake <ebb9@byu.net>2009-12-19 16:56:38 -0700
commit9963a98120addb9fd80299e5242554d62b002217 (patch)
tree96d8fc1db7fd9462df439ba66b42af3e61025b88 /tests
parente76d5597867e9c247abd383dbb4f518b1c3acbeb (diff)
downloadgnulib-9963a98120addb9fd80299e5242554d62b002217.tar.gz
utimens: check for ctime update
futimens/utimensat on Linux fails to bump ctime if mtime is UTIME_OMIT and atime is specified. * tests/test-utimens-common.h (check_ctime): Define. * tests/test-utimens.h (test_utimens): Expose the Linux bug. * tests/test-futimens.h (test_futimens): Likewise. * tests/test-lutimens.h (test_lutimens): Likewise. * doc/posix-functions/futimens.texi (futimens): Document the bug. * doc/posix-functions/utimensat.texi (utimensat): Likewise. Signed-off-by: Eric Blake <ebb9@byu.net>
Diffstat (limited to 'tests')
-rw-r--r--tests/test-futimens.h35
-rw-r--r--tests/test-lutimens.h48
-rw-r--r--tests/test-utimens-common.h27
-rw-r--r--tests/test-utimens.h37
4 files changed, 122 insertions, 25 deletions
diff --git a/tests/test-futimens.h b/tests/test-futimens.h
index 7c05bbf1ab..795aa9e56a 100644
--- a/tests/test-futimens.h
+++ b/tests/test-futimens.h
@@ -53,6 +53,10 @@ test_futimens (int (*func) (int, struct timespec const *),
UTIMECMP_TRUNCATE_SOURCE to compensate, with st1 as the
source. */
ASSERT (0 <= utimecmp (BASE "file", &st2, &st1, UTIMECMP_TRUNCATE_SOURCE));
+ if (check_ctime)
+ ASSERT (st1.st_ctime < st2.st_ctime
+ || (st1.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
{
/* On some NFS systems, the 'now' timestamp of creat or a NULL
timespec is determined by the server, but the 'now' timestamp
@@ -101,17 +105,40 @@ test_futimens (int (*func) (int, struct timespec const *),
ASSERT (st2.st_mtime == Y2K);
ASSERT (0 <= get_stat_mtime_ns (&st2));
ASSERT (get_stat_mtime_ns (&st2) < BILLION);
+ if (check_ctime)
+ ASSERT (st1.st_ctime < st2.st_ctime
+ || (st1.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
}
/* Play with UTIME_OMIT, UTIME_NOW. */
{
+ struct stat st3;
struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } };
+ nap ();
+ ASSERT (func (fd, ts) == 0);
+ ASSERT (fstat (fd, &st3) == 0);
+ ASSERT (st3.st_atime == Y2K);
+ ASSERT (0 <= get_stat_atime_ns (&st3));
+ ASSERT (get_stat_atime_ns (&st3) <= BILLION / 2);
+ ASSERT (utimecmp (BASE "file", &st1, &st3, 0) <= 0);
+ if (check_ctime)
+ ASSERT (st2.st_ctime < st3.st_ctime
+ || (st2.st_ctime == st3.st_ctime
+ && get_stat_ctime_ns (&st2) < get_stat_ctime_ns (&st3)));
+ nap ();
+ ts[0].tv_nsec = 0;
+ ts[1].tv_nsec = UTIME_OMIT;
ASSERT (func (fd, ts) == 0);
ASSERT (fstat (fd, &st2) == 0);
- ASSERT (st2.st_atime == Y2K);
- ASSERT (0 <= get_stat_atime_ns (&st2));
- ASSERT (get_stat_atime_ns (&st2) <= BILLION / 2);
- ASSERT (utimecmp (BASE "file", &st1, &st2, 0) <= 0);
+ ASSERT (st2.st_atime == BILLION);
+ ASSERT (get_stat_atime_ns (&st2) == 0);
+ ASSERT (st3.st_mtime == st2.st_mtime);
+ ASSERT (get_stat_mtime_ns (&st3) == get_stat_mtime_ns (&st2));
+ if (check_ctime)
+ ASSERT (st3.st_ctime < st2.st_ctime
+ || (st3.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st3) < get_stat_ctime_ns (&st2)));
}
/* Cleanup. */
diff --git a/tests/test-lutimens.h b/tests/test-lutimens.h
index c9302c89e3..f19df80d7e 100644
--- a/tests/test-lutimens.h
+++ b/tests/test-lutimens.h
@@ -54,11 +54,16 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
}
{
struct timespec ts[2] = { { Y2K, 0 }, { Y2K, 0 } };
+ nap ();
ASSERT (func (BASE "file", ts) == 0);
}
- ASSERT (stat (BASE "file", &st1) == 0);
- ASSERT (st1.st_atime == Y2K);
- ASSERT (st1.st_mtime == Y2K);
+ ASSERT (stat (BASE "file", &st2) == 0);
+ ASSERT (st2.st_atime == Y2K);
+ ASSERT (st2.st_mtime == Y2K);
+ if (check_ctime)
+ ASSERT (st1.st_ctime < st2.st_ctime
+ || (st1.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
/* Play with symlink timestamps. */
if (symlink (BASE "file", BASE "link"))
@@ -98,6 +103,8 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
if (st1.st_atime != st2.st_atime
|| get_stat_atime_ns (&st1) != get_stat_atime_ns (&st2))
atime_supported = false;
+ ASSERT (st1.st_ctime == st2.st_ctime);
+ ASSERT (get_stat_ctime_ns (&st1) == get_stat_ctime_ns (&st2));
/* Invalid arguments. */
{
@@ -123,6 +130,7 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
/* Set both times. */
{
struct timespec ts[2] = { { Y2K, BILLION / 2 - 1 }, { Y2K, BILLION - 1 } };
+ nap ();
ASSERT (func (BASE "link", ts) == 0);
ASSERT (lstat (BASE "link", &st2) == 0);
if (atime_supported)
@@ -134,20 +142,46 @@ test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
ASSERT (st2.st_mtime == Y2K);
ASSERT (0 <= get_stat_mtime_ns (&st2));
ASSERT (get_stat_mtime_ns (&st2) < BILLION);
+ if (check_ctime)
+ ASSERT (st1.st_ctime < st2.st_ctime
+ || (st1.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
}
/* Play with UTIME_OMIT, UTIME_NOW. */
{
+ struct stat st3;
struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } };
+ nap ();
+ ASSERT (func (BASE "link", ts) == 0);
+ ASSERT (lstat (BASE "link", &st3) == 0);
+ if (atime_supported)
+ {
+ ASSERT (st3.st_atime == Y2K);
+ ASSERT (0 <= get_stat_atime_ns (&st3));
+ ASSERT (get_stat_atime_ns (&st3) < BILLION / 2);
+ }
+ ASSERT (utimecmp (BASE "link", &st1, &st3, 0) <= 0);
+ if (check_ctime)
+ ASSERT (st2.st_ctime < st3.st_ctime
+ || (st2.st_ctime == st3.st_ctime
+ && get_stat_ctime_ns (&st2) < get_stat_ctime_ns (&st3)));
+ nap ();
+ ts[0].tv_nsec = 0;
+ ts[1].tv_nsec = UTIME_OMIT;
ASSERT (func (BASE "link", ts) == 0);
ASSERT (lstat (BASE "link", &st2) == 0);
if (atime_supported)
{
- ASSERT (st2.st_atime == Y2K);
- ASSERT (0 <= get_stat_atime_ns (&st2));
- ASSERT (get_stat_atime_ns (&st2) < BILLION / 2);
+ ASSERT (st2.st_atime == BILLION);
+ ASSERT (get_stat_atime_ns (&st2) == 0);
}
- ASSERT (utimecmp (BASE "link", &st1, &st2, 0) <= 0);
+ ASSERT (st3.st_mtime == st2.st_mtime);
+ ASSERT (get_stat_mtime_ns (&st3) == get_stat_mtime_ns (&st2));
+ if (check_ctime)
+ ASSERT (st3.st_ctime < st2.st_ctime
+ || (st3.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st3) < get_stat_ctime_ns (&st2)));
}
/* Symlink to directory. */
diff --git a/tests/test-utimens-common.h b/tests/test-utimens-common.h
index 6c404cc5d2..707971abe4 100644
--- a/tests/test-utimens-common.h
+++ b/tests/test-utimens-common.h
@@ -19,14 +19,14 @@
#ifndef GL_TEST_UTIMENS_COMMON
# define GL_TEST_UTIMENS_COMMON
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
+# include <fcntl.h>
+# include <errno.h>
+# include <string.h>
+# include <unistd.h>
-#include "stat-time.h"
-#include "timespec.h"
-#include "utimecmp.h"
+# include "stat-time.h"
+# include "timespec.h"
+# include "utimecmp.h"
enum {
BILLION = 1000 * 1000 * 1000,
@@ -62,9 +62,18 @@ nap (void)
a quantization boundary equal to the resolution. Our usage of
utimecmp allows equality, so no need to waste 980 milliseconds
if the replacement usleep rounds to 1 second. */
-#if HAVE_USLEEP
+# if HAVE_USLEEP
usleep (20 * 1000); /* 20 milliseconds. */
-#endif
+# endif
}
+# if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
+/* Skip ctime tests on native Windows, since it is either a copy of
+ mtime or birth time (depending on the file system), rather than a
+ properly tracked change time. */
+# define check_ctime 0
+# else
+# define check_ctime 1
+# endif
+
#endif /* GL_TEST_UTIMENS_COMMON */
diff --git a/tests/test-utimens.h b/tests/test-utimens.h
index 710741a7c2..b211b41d8f 100644
--- a/tests/test-utimens.h
+++ b/tests/test-utimens.h
@@ -37,6 +37,10 @@ test_utimens (int (*func) (char const *, struct timespec const *), bool print)
ASSERT (func (BASE "file", NULL) == 0);
ASSERT (stat (BASE "file", &st2) == 0);
ASSERT (0 <= utimecmp (BASE "file", &st2, &st1, UTIMECMP_TRUNCATE_SOURCE));
+ if (check_ctime)
+ ASSERT (st1.st_ctime < st2.st_ctime
+ || (st1.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
{
/* On some NFS systems, the 'now' timestamp of creat or a NULL
timespec is determined by the server, but the 'now' timestamp
@@ -97,18 +101,41 @@ test_utimens (int (*func) (char const *, struct timespec const *), bool print)
ASSERT (st2.st_mtime == Y2K);
ASSERT (0 <= get_stat_mtime_ns (&st2));
ASSERT (get_stat_mtime_ns (&st2) < BILLION);
+ if (check_ctime)
+ ASSERT (st1.st_ctime < st2.st_ctime
+ || (st1.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st1) < get_stat_ctime_ns (&st2)));
}
/* Play with UTIME_OMIT, UTIME_NOW. */
{
+ struct stat st3;
struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } };
+ nap ();
ASSERT (func (BASE "file", ts) == 0);
- ASSERT (stat (BASE "file", &st2) == 0);
- ASSERT (st2.st_atime == Y2K);
- ASSERT (0 <= get_stat_atime_ns (&st2));
- ASSERT (get_stat_atime_ns (&st2) < BILLION / 2);
+ ASSERT (stat (BASE "file", &st3) == 0);
+ ASSERT (st3.st_atime == Y2K);
+ ASSERT (0 <= get_stat_atime_ns (&st3));
+ ASSERT (get_stat_atime_ns (&st3) < BILLION / 2);
/* See comment above about this utimecmp call. */
- ASSERT (0 <= utimecmp (BASE "file", &st2, &st1, UTIMECMP_TRUNCATE_SOURCE));
+ ASSERT (0 <= utimecmp (BASE "file", &st3, &st1, UTIMECMP_TRUNCATE_SOURCE));
+ if (check_ctime)
+ ASSERT (st2.st_ctime < st3.st_ctime
+ || (st2.st_ctime == st3.st_ctime
+ && get_stat_ctime_ns (&st2) < get_stat_ctime_ns (&st3)));
+ nap ();
+ ts[0].tv_nsec = 0;
+ ts[1].tv_nsec = UTIME_OMIT;
+ ASSERT (func (BASE "file", ts) == 0);
+ ASSERT (stat (BASE "file", &st2) == 0);
+ ASSERT (st2.st_atime == BILLION);
+ ASSERT (get_stat_atime_ns (&st2) == 0);
+ ASSERT (st3.st_mtime == st2.st_mtime);
+ ASSERT (get_stat_mtime_ns (&st3) == get_stat_mtime_ns (&st2));
+ if (check_ctime)
+ ASSERT (st3.st_ctime < st2.st_ctime
+ || (st3.st_ctime == st2.st_ctime
+ && get_stat_ctime_ns (&st3) < get_stat_ctime_ns (&st2)));
}
/* Make sure this dereferences symlinks. */