utimens: add lutimens interface
[gnulib.git] / tests / test-lutimens.h
1 /* Test of file timestamp modification functions.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* This file assumes that BASE and ASSERT are already defined.  */
18
19 #ifndef GL_TEST_UTIMENS
20 # define GL_TEST_UTIMENS
21
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "stat-time.h"
28 #include "timespec.h"
29 #include "utimecmp.h"
30
31 enum {
32   BILLION = 1000 * 1000 * 1000,
33
34   Y2K = 946684800, /* Jan 1, 2000, in seconds since epoch.  */
35
36   /* Bogus positive and negative tv_nsec values closest to valid
37      range, but without colliding with UTIME_NOW or UTIME_OMIT.  */
38   UTIME_BOGUS_POS = BILLION + ((UTIME_NOW == BILLION || UTIME_OMIT == BILLION)
39                                ? (1 + (UTIME_NOW == BILLION + 1)
40                                   + (UTIME_OMIT == BILLION + 1))
41                                : 0),
42   UTIME_BOGUS_NEG = -1 - ((UTIME_NOW == -1 || UTIME_OMIT == -1)
43                           ? (1 + (UTIME_NOW == -2) + (UTIME_OMIT == -2))
44                           : 0)
45 };
46
47 #endif /* GL_TEST_UTIMENS */
48
49 /* This function is designed to test both lutimens(a,b) and
50    utimensat(AT_FDCWD,a,b,AT_SYMLINK_NOFOLLOW).  FUNC is the function
51    to test.  If PRINT, warn before skipping tests with status 77.  */
52 static int
53 test_lutimens (int (*func) (char const *, struct timespec const *), bool print)
54 {
55   int result;
56   struct stat st1;
57   struct stat st2;
58   bool atime_supported = true;
59
60   if (symlink ("nowhere", BASE "link"))
61     {
62       if (print)
63         fputs ("skipping test: symlinks not supported on this file system\n",
64                stderr);
65       return 77;
66     }
67   errno = 0;
68   result = func (BASE "link", NULL);
69   if (result == -1 && errno == ENOSYS)
70     {
71       ASSERT (unlink (BASE "link") == 0);
72       if (print)
73         fputs ("skipping test: "
74                "setting symlink time not supported on this file system\n",
75                stderr);
76       return 77;
77     }
78   ASSERT (!result);
79   ASSERT (lstat (BASE "link", &st1) == 0);
80 #if HAVE_USLEEP
81   /* On Cygwin, the mere act of lstat changes symlink atime, even
82      though POSIX says that only readlink is allowed to do that.
83      Sleeping for one millisecond is enough to expose this.  Platforms
84      without usleep either don't have symlinks, or are immune.  */
85   usleep (1000);
86 #endif
87   ASSERT (lstat (BASE "link", &st2) == 0);
88   if (st1.st_atime != st2.st_atime
89       || get_stat_atime_ns (&st1) != get_stat_atime_ns (&st2))
90      atime_supported = false;
91
92   /* Invalid arguments.  */
93   errno = 0;
94   ASSERT (func ("no_such", NULL) == -1);
95   ASSERT (errno == ENOENT);
96   errno = 0;
97   ASSERT (func ("", NULL) == -1);
98   ASSERT (errno == ENOENT);
99   {
100     struct timespec ts[2] = { { Y2K, UTIME_BOGUS_POS }, { Y2K, 0 } };
101     errno = 0;
102     ASSERT (func (BASE "link", ts) == -1);
103     ASSERT (errno == EINVAL);
104   }
105   {
106     struct timespec ts[2] = { { Y2K, 0 }, { Y2K, UTIME_BOGUS_NEG } };
107     errno = 0;
108     ASSERT (func (BASE "link", ts) == -1);
109     ASSERT (errno == EINVAL);
110   }
111   ASSERT (lstat (BASE "link", &st2) == 0);
112   if (atime_supported)
113     {
114       ASSERT (st1.st_atime == st2.st_atime);
115       ASSERT (get_stat_atime_ns (&st1) == get_stat_atime_ns (&st2));
116     }
117   ASSERT (utimecmp (BASE "link", &st1, &st2, 0) == 0);
118
119   /* Set both times.  */
120   {
121     struct timespec ts[2] = { { Y2K, BILLION / 2 - 1 }, { Y2K, BILLION - 1 } };
122     ASSERT (func (BASE "link", ts) == 0);
123     ASSERT (lstat (BASE "link", &st2) == 0);
124     if (atime_supported)
125       {
126         ASSERT (st2.st_atime == Y2K);
127         ASSERT (0 <= get_stat_atime_ns (&st2));
128         ASSERT (get_stat_atime_ns (&st2) < BILLION / 2);
129       }
130     ASSERT (st2.st_mtime == Y2K);
131     ASSERT (0 <= get_stat_mtime_ns (&st2));
132     ASSERT (get_stat_mtime_ns (&st2) < BILLION);
133   }
134
135   /* Play with UTIME_OMIT, UTIME_NOW.  */
136   {
137     struct timespec ts[2] = { { BILLION, UTIME_OMIT }, { 0, UTIME_NOW } };
138     ASSERT (func (BASE "link", ts) == 0);
139     ASSERT (lstat (BASE "link", &st2) == 0);
140     if (atime_supported)
141       {
142         ASSERT (st2.st_atime == Y2K);
143         ASSERT (0 <= get_stat_atime_ns (&st2));
144         ASSERT (get_stat_atime_ns (&st2) < BILLION / 2);
145       }
146     ASSERT (utimecmp (BASE "link", &st1, &st2, 0) <= 0);
147   }
148
149   /* Cleanup.  */
150   ASSERT (unlink (BASE "link") == 0);
151   return 0;
152 }