3 sub has_subsecond_file_times {
6 my ($fh, $filename) = File::Temp::tempfile( "Time-HiRes-utime-XXXXXXXXX" );
7 use File::Basename qw[dirname];
8 my $dirname = dirname($filename);
10 $dirname = &Cwd::getcwd if $dirname eq '.';
11 print("\n# Testing for subsecond file timestamps (mtime) in $dirname\n");
15 open $fh, '>', $filename;
18 push @mtimes, (Time::HiRes::stat($filename))[9];
19 Time::HiRes::sleep(.1) if $_ == 1;
21 my $delta = $mtimes[1] - $mtimes[0];
22 # print STDERR "mtimes = @mtimes, delta = $delta\n";
24 my $ok = $delta > 0 && $delta < 1;
25 printf("# Subsecond file timestamps in $dirname: %s\n",
30 sub get_filesys_of_tempfile {
33 my ($fh, $filename) = File::Temp::tempfile( "Time-HiRes-utime-XXXXXXXXX" );
35 if (open(my $df, "df $filename |")) {
38 next if /^Filesystem/;
43 if (defined $fs[0] && length($fs[0])) {
46 printf("# Got empty result from 'df'\n");
49 printf("# Expected one result from 'df', got %d\n", scalar(@fs));
52 # Too noisy to show by default.
53 # Can fail for too many reasons.
54 print "# Failed to run 'df $filename |': $!\n";
59 sub get_mount_of_filesys {
61 # netbsd has /sbin/mount
62 local $ENV{PATH} = "$ENV{PATH}:/sbin" if $^O =~ /^(?:netbsd)$/;
63 if (defined $filesys) {
64 my @fs = split(' ', $filesys);
65 if (open(my $mount, "mount |")) {
69 if ($mnt[0] eq $fs[0]) {
74 # Too noisy to show by default.
75 # The mount(8) might not be in the PATH, for example.
76 # Or this might be a completely non-UNIX system.
77 # print "# Failed to run 'mount |': $!\n";
83 sub get_mount_of_tempfile {
84 return get_mount_of_filesys(get_filesys_of_tempfile());
87 sub tempfile_has_noatime_mount {
88 my ($mount) = get_mount_of_tempfile();
89 return $mount =~ /\bnoatime\b/;
96 unless(&Time::HiRes::d_hires_utime) {
97 Test::More::plan(skip_all => "no hires_utime");
99 unless(&Time::HiRes::d_hires_stat) {
100 # Being able to read subsecond timestamps is a reasonable
101 # prerequisite for being able to write them.
102 Test::More::plan(skip_all => "no hires_stat");
104 unless (&Time::HiRes::d_futimens) {
105 Test::More::plan(skip_all => "no futimens()");
107 unless (&Time::HiRes::d_utimensat) {
108 Test::More::plan(skip_all => "no utimensat()");
110 unless (has_subsecond_file_times()) {
111 Test::More::plan(skip_all => "No subsecond file timestamps");
115 use Test::More tests => 22;
116 BEGIN { push @INC, '.' }
118 use File::Temp qw( tempfile );
121 *done_testing = sub {} unless defined &done_testing;
126 # Hope initially for nanosecond accuracy.
127 my $atime = 1.111111111;
128 my $mtime = 2.222222222;
130 if ($^O eq 'cygwin') {
131 # Cygwin timestamps have less precision.
135 if ($^O eq 'dragonfly') {
136 # Dragonfly (hammer2?) timestamps have less precision.
140 print "# \$^O = $^O, atime = $atime, mtime = $mtime\n";
142 my $skip_atime = $^O eq 'netbsd' && tempfile_has_noatime_mount();
143 $skip_atime = 1 if $^O eq 'dragonfly'; # noatime by default
146 printf("# Skipping atime tests because tempfiles seem to be in a filesystem mounted with 'noatime' ($^O)\n'");
149 print "# utime \$fh\n";
151 my ($fh, $filename) = tempfile( "Time-HiRes-utime-XXXXXXXXX", UNLINK => 1 );
152 is Time::HiRes::utime($atime, $mtime, $fh), 1, "One file changed";
153 my ($got_atime, $got_mtime) = ( Time::HiRes::stat($filename) )[8, 9];
155 skip("noatime mount", 1) if $skip_atime;
156 is $got_atime, $atime, "atime set correctly";
158 is $got_mtime, $mtime, "mtime set correctly";
161 print "#utime \$filename\n";
163 my ($fh, $filename) = tempfile( "Time-HiRes-utime-XXXXXXXXX", UNLINK => 1 );
164 is Time::HiRes::utime($atime, $mtime, $filename), 1, "One file changed";
165 my ($got_atime, $got_mtime) = ( Time::HiRes::stat($fh) )[8, 9];
167 skip("noatime mount", 1) if $skip_atime;
168 is $got_atime, $atime, "atime set correctly";
170 is $got_mtime, $mtime, "mtime set correctly";
173 print "#utime \$filename round-trip\n";
175 my ($fh, $filename) = tempfile( "Time-HiRes-utime-XXXXXXXXX", UNLINK => 1 );
176 # this fractional part is not exactly representable
177 my $t = 1000000000.12345;
178 is Time::HiRes::utime($t, $t, $filename), 1, "One file changed";
179 my ($got_atime, $got_mtime) = ( Time::HiRes::stat($fh) )[8, 9];
180 is Time::HiRes::utime($got_atime, $got_mtime, $filename), 1, "One file changed";
181 my ($got_atime2, $got_mtime2) = ( Time::HiRes::stat($fh) )[8, 9];
182 is $got_atime, $got_atime2, "atime round trip ok";
183 is $got_mtime, $got_mtime2, "mtime round trip ok";
186 print "utime \$filename and \$fh\n";
188 my ($fh1, $filename1) = tempfile( "Time-HiRes-utime-XXXXXXXXX", UNLINK => 1 );
189 my ($fh2, $filename2) = tempfile( "Time-HiRes-utime-XXXXXXXXX", UNLINK => 1 );
190 is Time::HiRes::utime($atime, $mtime, $filename1, $fh2), 2, "Two files changed";
192 my ($got_atime, $got_mtime) = ( Time::HiRes::stat($fh1) )[8, 9];
194 skip("noatime mount", 1) if $skip_atime;
195 is $got_atime, $atime, "File 1 atime set correctly";
197 is $got_mtime, $mtime, "File 1 mtime set correctly";
200 my ($got_atime, $got_mtime) = ( Time::HiRes::stat($filename2) )[8, 9];
202 skip("noatime mount", 1) if $skip_atime;
203 is $got_atime, $atime, "File 2 atime set correctly";
205 is $got_mtime, $mtime, "File 2 mtime set correctly";
209 print "# utime undef sets time to now\n";
211 my ($fh1, $filename1) = tempfile( "Time-HiRes-utime-XXXXXXXXX", UNLINK => 1 );
212 my ($fh2, $filename2) = tempfile( "Time-HiRes-utime-XXXXXXXXX", UNLINK => 1 );
214 my $now = Time::HiRes::time;
216 is Time::HiRes::utime(undef, undef, $filename1, $fh2), 2, "Two files changed";
219 my ($got_atime, $got_mtime) = ( Time::HiRes::stat($fh1) )[8, 9];
221 skip("noatime mount", 1) if $skip_atime;
222 cmp_ok $got_atime, '>=', $now, "File 1 atime set correctly";
224 cmp_ok $got_mtime, '>=', $now, "File 1 mtime set correctly";
227 my ($got_atime, $got_mtime) = ( Time::HiRes::stat($filename2) )[8, 9];
229 skip("noatime mount", 1) if $skip_atime;
230 cmp_ok $got_atime, '>=', $now, "File 2 atime set correctly";
232 cmp_ok $got_mtime, '>=', $now, "File 2 mtime set correctly";
236 print "# negative atime dies\n";
238 eval { Time::HiRes::utime(-4, $mtime) };
239 like $@, qr/::utime\(-4, 2\.22222\): negative time not invented yet/,
240 "negative time error";
243 print "# negative mtime dies;\n";
245 eval { Time::HiRes::utime($atime, -4) };
246 like $@, qr/::utime\(1.11111, -4\): negative time not invented yet/,
247 "negative time error";