diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2020-03-12 17:39:04 +0000 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2020-08-07 12:42:51 +0100 |
commit | b623cf82022d8837c228c1c9ca6a585318c24e40 (patch) | |
tree | 2c4bdc0942bd6c025c0d23d36591edc5ae74b121 /libstdc++-v3 | |
parent | 3b05a417774d40cf1b9fca61630209de8338ba47 (diff) | |
download | gcc-b623cf82022d8837c228c1c9ca6a585318c24e40.zip gcc-b623cf82022d8837c228c1c9ca6a585318c24e40.tar.gz gcc-b623cf82022d8837c228c1c9ca6a585318c24e40.tar.bz2 |
libstdc++: Fix FS-dependent filesystem tests
These tests were failing on XFS because it doesn't support setting file
timestamps past 2038, so the expected overflow when reading back a huge
timestamp into a file_time_type didn't happen.
Additionally, the std::filesystem::file_time_type::clock has an
epoch that is out of range of 32-bit time_t so testing times around that
epoch may also fail.
This fixes the tests to give up gracefully if the filesystem doesn't
support times that can't be represented in 32-bit time_t.
Backport from mainline
2020-02-28 Jonathan Wakely <jwakely@redhat.com>
* testsuite/27_io/filesystem/operations/last_write_time.cc: Fixes for
filesystems that silently truncate timestamps.
* testsuite/experimental/filesystem/operations/last_write_time.cc:
Likewise.
(cherry picked from commit 2fa3247fef79ede9ec3638605ea137b0e4d76075)
Diffstat (limited to 'libstdc++-v3')
-rw-r--r-- | libstdc++-v3/testsuite/27_io/filesystem/operations/last_write_time.cc | 77 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc | 58 |
2 files changed, 99 insertions, 36 deletions
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/last_write_time.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/last_write_time.cc index c815e01..cedddd1 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/operations/last_write_time.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/last_write_time.cc @@ -32,9 +32,12 @@ #if _GLIBCXX_HAVE_UTIME_H # include <utime.h> #endif +#include <stdio.h> using time_type = std::filesystem::file_time_type; +namespace chrono = std::chrono; + void test01() { @@ -67,10 +70,15 @@ test01() auto end_of_time = time_type::duration::max(); auto last_second - = std::chrono::duration_cast<std::chrono::seconds>(end_of_time).count(); + = chrono::duration_cast<chrono::seconds>(end_of_time).count(); if (last_second > std::numeric_limits<std::time_t>::max()) - return; // can't test overflow + { + puts("Range of time_t is smaller than range of chrono::file_clock, " + "can't test for overflow on this target."); + return; + } + // Set mtime to a date past the maximum possible file_time_type: #if _GLIBCXX_USE_UTIMENSAT struct ::timespec ts[2]; ts[0].tv_sec = 0; @@ -84,25 +92,34 @@ test01() times.actime = std::numeric_limits<std::time_t>::max() - 1; VERIFY( !::utime(p.c_str(), ×) ); #else + puts("No utimensat or utime, giving up."); return; #endif + // Try to read back the impossibly-large mtime: mtime = last_write_time(p, ec); - VERIFY( ec ); - VERIFY( ec == std::make_error_code(std::errc::value_too_large) ); - VERIFY( mtime == time_type::min() ); + // Some filesystems (e.g. XFS) silently truncate distant times to + // the time_t epochalypse, Jan 19 2038, so we won't get an error when + // reading it back: + if (ec) + { + VERIFY( ec == std::make_error_code(std::errc::value_too_large) ); + VERIFY( mtime == time_type::min() ); + } + else + puts("No overflow error, filesystem may not support 64-bit time_t."); #if __cpp_exceptions - caught = false; + // Once more, with exceptions: try { - mtime = last_write_time(p); - } catch (std::system_error const& e) { - caught = true; - ec = e.code(); + auto mtime2 = last_write_time(p); + // If it didn't throw, expect to have read back the same value: + VERIFY( mtime2 == mtime ); + } catch (std::filesystem::filesystem_error const& e) { + // If it did throw, expect the error_code to be the same: + VERIFY( e.code() == ec ); + VERIFY( e.path1() == p ); } - VERIFY( caught ); - VERIFY( ec ); - VERIFY( ec == std::make_error_code(std::errc::value_too_large) ); #endif } @@ -111,7 +128,7 @@ bool approx_equal(time_type file_time, time_type expected) auto delta = expected - file_time; if (delta < delta.zero()) delta = -delta; - return delta < std::chrono::seconds(1); + return delta < chrono::seconds(1); } void @@ -124,20 +141,20 @@ test02() std::error_code ec; time_type time; - time = last_write_time(f.path); ec = bad_ec; + time = last_write_time(f.path); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); ec = bad_ec; - time -= std::chrono::milliseconds(1000 * 60 * 10 + 15); + time -= chrono::milliseconds(1000 * 60 * 10 + 15); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); ec = bad_ec; - time += std::chrono::milliseconds(1000 * 60 * 20 + 15); + time += chrono::milliseconds(1000 * 60 * 20 + 15); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); @@ -146,6 +163,28 @@ test02() < std::numeric_limits<std::int64_t>::max()) return; // file clock's epoch is out of range for 32-bit time_t + using sys_time_32b + = chrono::time_point<chrono::system_clock, chrono::duration<std::int32_t>>; + auto duration_until_2038 = sys_time_32b::max() - sys_time_32b::clock::now(); + auto file_time_2038 = time_type::clock::now() + duration_until_2038; + + ec = bad_ec; + time = file_time_2038 - chrono::seconds(1); + // Assume all filesystems can store times that fit in 32-bit time_t + // (i.e. up to Jan 19 2038) + last_write_time(f.path, time, ec); + VERIFY( !ec ); + VERIFY( approx_equal(last_write_time(f.path), time) ); + + // Check whether the filesystem supports times larger than 32-bit time_t: + time += chrono::seconds(60); + last_write_time(f.path, time, ec); + if (ec || !approx_equal(last_write_time(f.path), time)) + { + puts("Filesystem seems to truncate times past Jan 19 2038, giving up."); + return; // Tests below will fail on this filesystem + } + ec = bad_ec; // The file clock's epoch: time = time_type(); @@ -155,14 +194,14 @@ test02() ec = bad_ec; // A time after the epoch - time += std::chrono::milliseconds(1000 * 60 * 10 + 15); + time += chrono::milliseconds(1000 * 60 * 10 + 15); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); ec = bad_ec; // A time before than the epoch - time -= std::chrono::milliseconds(1000 * 60 * 20 + 15); + time -= chrono::milliseconds(1000 * 60 * 20 + 15); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc index 9d1752f..e43f755 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc @@ -22,6 +22,7 @@ // 15.25 Permissions [fs.op.last_write_time] #include <experimental/filesystem> +#include <limits> #include <testsuite_fs.h> #include <testsuite_hooks.h> @@ -31,9 +32,12 @@ #if _GLIBCXX_HAVE_UTIME_H # include <utime.h> #endif +#include <stdio.h> using time_type = std::experimental::filesystem::file_time_type; +namespace chrono = std::chrono; + void test01() { @@ -66,10 +70,15 @@ test01() auto end_of_time = time_type::duration::max(); auto last_second - = std::chrono::duration_cast<std::chrono::seconds>(end_of_time).count(); + = chrono::duration_cast<chrono::seconds>(end_of_time).count(); if (last_second > std::numeric_limits<std::time_t>::max()) - return; // can't test overflow + { + puts("Range of time_t is smaller than range of chrono::file_clock, " + "can't test for overflow on this target."); + return; + } + // Set mtime to a date past the maximum possible file_time_type: #if _GLIBCXX_USE_UTIMENSAT struct ::timespec ts[2]; ts[0].tv_sec = 0; @@ -83,25 +92,34 @@ test01() times.actime = std::numeric_limits<std::time_t>::max() - 1; VERIFY( !::utime(p.c_str(), ×) ); #else + puts("No utimensat or utime, giving up."); return; #endif + // Try to read back the impossibly-large mtime: mtime = last_write_time(p, ec); - VERIFY( ec ); - VERIFY( ec == std::make_error_code(std::errc::value_too_large) ); - VERIFY( mtime == time_type::min() ); + // Some filesystems (e.g. XFS) silently truncate distant times to + // the time_t epochalypse, Jan 19 2038, so we won't get an error when + // reading it back: + if (ec) + { + VERIFY( ec == std::make_error_code(std::errc::value_too_large) ); + VERIFY( mtime == time_type::min() ); + } + else + puts("No overflow error, filesystem may not support 64-bit time_t."); #if __cpp_exceptions - caught = false; + // Once more, with exceptions: try { - mtime = last_write_time(p); - } catch (std::system_error const& e) { - caught = true; - ec = e.code(); + auto mtime2 = last_write_time(p); + // If it didn't throw, expect to have read back the same value: + VERIFY( mtime2 == mtime ); + } catch (std::experimental::filesystem::filesystem_error const& e) { + // If it did throw, expect the error_code to be the same: + VERIFY( e.code() == ec ); + VERIFY( e.path1() == p ); } - VERIFY( caught ); - VERIFY( ec ); - VERIFY( ec == std::make_error_code(std::errc::value_too_large) ); #endif } @@ -110,7 +128,7 @@ bool approx_equal(time_type file_time, time_type expected) auto delta = expected - file_time; if (delta < delta.zero()) delta = -delta; - return delta < std::chrono::seconds(1); + return delta < chrono::seconds(1); } void @@ -118,31 +136,37 @@ test02() { // write times + const std::error_code bad_ec = make_error_code(std::errc::invalid_argument); __gnu_test::scoped_file f; std::error_code ec; time_type time; + ec = bad_ec; time = last_write_time(f.path); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); - time -= std::chrono::milliseconds(1000 * 60 * 10 + 15); + ec = bad_ec; + time -= chrono::milliseconds(1000 * 60 * 10 + 15); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); - time += std::chrono::milliseconds(1000 * 60 * 20 + 15); + ec = bad_ec; + time += chrono::milliseconds(1000 * 60 * 20 + 15); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); + ec = bad_ec; time = time_type(); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); - time -= std::chrono::milliseconds(1000 * 60 * 10 + 15); + ec = bad_ec; + time -= chrono::milliseconds(1000 * 60 * 10 + 15); last_write_time(f.path, time, ec); VERIFY( !ec ); VERIFY( approx_equal(last_write_time(f.path), time) ); |