test-stat-time: speed up execution
[pspp] / tests / test-stat-time.c
1 /* Test of <stat-time.h>.
2    Copyright (C) 2007-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 3 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 /* Written by James Youngman <jay@gnu.org>, 2007.  */
18
19 #include <config.h>
20
21 #include "stat-time.h"
22
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #define ASSERT(expr) \
31   do                                                                         \
32     {                                                                        \
33       if (!(expr))                                                           \
34         {                                                                    \
35           fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
36           fflush (stderr);                                                   \
37           abort ();                                                          \
38         }                                                                    \
39     }                                                                        \
40   while (0)
41
42 enum { NFILES = 4 };
43
44 static void
45 force_unlink (const char *filename)
46 {
47   /* This chmod is necessary on mingw, where unlink() of a read-only file
48      fails with EPERM.  */
49   chmod (filename, 0600);
50   unlink (filename);
51 }
52
53 static void
54 cleanup (int sig)
55 {
56   /* Remove temporary files.  */
57   force_unlink ("t-stt-stamp1");
58   force_unlink ("t-stt-testfile");
59   force_unlink ("t-stt-stamp2");
60   force_unlink ("t-stt-renamed");
61   force_unlink ("t-stt-stamp3");
62
63   if (sig != 0)
64     _exit (1);
65 }
66
67 static int
68 open_file (const char *filename, int flags)
69 {
70   int fd = open (filename, flags | O_WRONLY, 0500);
71   if (fd >= 0)
72     {
73       close (fd);
74       return 1;
75     }
76   else
77     {
78       return 0;
79     }
80 }
81
82 static void
83 create_file (const char *filename)
84 {
85   ASSERT (open_file (filename, O_CREAT | O_EXCL));
86 }
87
88 static void
89 do_stat (const char *filename, struct stat *p)
90 {
91   ASSERT (stat (filename, p) == 0);
92 }
93
94 /* Sleep long enough to notice a timestamp difference on the file
95    system in the current directory.  */
96 static void
97 nap (void)
98 {
99 #if !HAVE_USLEEP
100   /* Assume the worst case file system of FAT, which has a granularity
101      of 2 seconds.  */
102   sleep (2);
103 #else /* HAVE_USLEEP */
104   static long delay;
105   if (!delay)
106     {
107       /* Initialize only once, by sleeping for 1 millisecond.  If that
108          was enough to observe a difference, then we are set;
109          otherwise fall back to 2 seconds.  */
110       struct stat st1;
111       struct stat st2;
112       ASSERT (stat ("t-stt-stamp1", &st1) == 0);
113       ASSERT (unlink ("t-stt-stamp1") == 0);
114       usleep (delay = 1000);
115       create_file ("t-stt-stamp1");
116       ASSERT (stat ("t-stt-stamp1", &st2) == 0);
117       if (! (st1.st_mtime < st2.st_mtime
118              || (st1.st_mtime == st2.st_mtime
119                  && get_stat_mtime_ns (&st1) < get_stat_mtime_ns (&st2))))
120         delay = 2000000;
121     }
122   usleep (delay);
123 #endif /* HAVE_USLEEP */
124 }
125
126 static void
127 prepare_test (struct stat *statinfo, struct timespec *modtimes)
128 {
129   int i;
130
131   create_file ("t-stt-stamp1");
132   nap ();
133   create_file ("t-stt-testfile");
134   nap ();
135   create_file ("t-stt-stamp2");
136   nap ();
137   ASSERT (chmod ("t-stt-testfile", 0400) == 0);
138   nap ();
139   create_file ("t-stt-stamp3");
140
141   do_stat ("t-stt-stamp1",  &statinfo[0]);
142   do_stat ("t-stt-testfile", &statinfo[1]);
143   do_stat ("t-stt-stamp2",  &statinfo[2]);
144   do_stat ("t-stt-stamp3",  &statinfo[3]);
145
146   /* Now use our access functions. */
147   for (i = 0; i < NFILES; ++i)
148     {
149       modtimes[i] = get_stat_mtime (&statinfo[i]);
150     }
151 }
152
153 static void
154 test_mtime (const struct stat *statinfo, struct timespec *modtimes)
155 {
156   int i;
157
158   /* Use the struct stat fields directly. */
159   /* mtime(stamp1) < mtime(stamp2) */
160   ASSERT (statinfo[0].st_mtime < statinfo[2].st_mtime
161           || (statinfo[0].st_mtime == statinfo[2].st_mtime
162               && (get_stat_mtime_ns (&statinfo[0])
163                   < get_stat_mtime_ns (&statinfo[2]))));
164   /* mtime(stamp2) < mtime(stamp3) */
165   ASSERT (statinfo[2].st_mtime < statinfo[3].st_mtime
166           || (statinfo[2].st_mtime == statinfo[3].st_mtime
167               && (get_stat_mtime_ns (&statinfo[2])
168                   < get_stat_mtime_ns (&statinfo[3]))));
169
170   /* Now check the result of the access functions. */
171   /* mtime(stamp1) < mtime(stamp2) */
172   ASSERT (modtimes[0].tv_sec < modtimes[2].tv_sec
173           || (modtimes[0].tv_sec == modtimes[2].tv_sec
174               && modtimes[0].tv_nsec < modtimes[2].tv_nsec));
175   /* mtime(stamp2) < mtime(stamp3) */
176   ASSERT (modtimes[2].tv_sec < modtimes[3].tv_sec
177           || (modtimes[2].tv_sec == modtimes[3].tv_sec
178               && modtimes[2].tv_nsec < modtimes[3].tv_nsec));
179
180   /* verify equivalence */
181   for (i = 0; i < NFILES; ++i)
182     {
183       struct timespec ts;
184       ts = get_stat_mtime (&statinfo[i]);
185       ASSERT (ts.tv_sec == statinfo[i].st_mtime);
186     }
187 }
188
189 #if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
190 static void
191 test_ctime (const struct stat *statinfo)
192 {
193   /* mtime(stamp2) < ctime(renamed) */
194   ASSERT (statinfo[2].st_mtime < statinfo[1].st_ctime
195           || (statinfo[2].st_mtime == statinfo[1].st_ctime
196               && (get_stat_mtime_ns (&statinfo[2])
197                   < get_stat_ctime_ns (&statinfo[1]))));
198 }
199 #endif
200
201 static void
202 test_birthtime (const struct stat *statinfo,
203                 const struct timespec *modtimes,
204                 struct timespec *birthtimes)
205 {
206   int i;
207
208   /* Collect the birth times.. */
209   for (i = 0; i < NFILES; ++i)
210     {
211       birthtimes[i] = get_stat_birthtime (&statinfo[i]);
212       if (birthtimes[i].tv_nsec < 0)
213         return;
214     }
215
216   /* mtime(stamp1) < birthtime(renamed) */
217   ASSERT (modtimes[0].tv_sec < birthtimes[1].tv_sec
218           || (modtimes[0].tv_sec == birthtimes[1].tv_sec
219               && modtimes[0].tv_nsec < birthtimes[1].tv_nsec));
220   /* birthtime(renamed) < mtime(stamp2) */
221   ASSERT (birthtimes[1].tv_sec < modtimes[2].tv_sec
222           || (birthtimes[1].tv_sec == modtimes[2].tv_sec
223               && birthtimes[1].tv_nsec < modtimes[2].tv_nsec));
224 }
225
226 int
227 main ()
228 {
229   struct stat statinfo[NFILES];
230   struct timespec modtimes[NFILES];
231   struct timespec birthtimes[NFILES];
232
233 #ifdef SIGHUP
234   signal (SIGHUP, cleanup);
235 #endif
236 #ifdef SIGINT
237   signal (SIGINT, cleanup);
238 #endif
239 #ifdef SIGQUIT
240   signal (SIGQUIT, cleanup);
241 #endif
242 #ifdef SIGTERM
243   signal (SIGTERM, cleanup);
244 #endif
245
246   cleanup (0);
247   prepare_test (statinfo, modtimes);
248   test_mtime (statinfo, modtimes);
249   /* Skip the ctime tests on native Windows platforms, because there st_ctime
250      is either the same as st_mtime (plus or minus an offset) or set to the
251      file _creation_ time, and is not influenced by rename or chmod.  */
252 #if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
253   test_ctime (statinfo);
254 #endif
255   test_birthtime (statinfo, modtimes, birthtimes);
256
257   cleanup (0);
258   return 0;
259 }