forked from OSchip/llvm-project
[libcxx] [test] Fix fs.op.last_write_time for Windows
Don't use stat and lstat on Windows; lstat is missing, stat only provides the modification times with second granularity (and does the wrong thing regarding symlinks). Instead do a minimal reimplementation using the native windows APIs. Differential Revision: https://reviews.llvm.org/D101731
This commit is contained in:
parent
77997f28d5
commit
6bd3d8a17c
|
@ -8,8 +8,6 @@
|
|||
|
||||
// UNSUPPORTED: c++03
|
||||
|
||||
// XFAIL: LIBCXX-WINDOWS-FIXME
|
||||
|
||||
// The string reported on errors changed, which makes those tests fail when run
|
||||
// against already-released libc++'s.
|
||||
// XFAIL: use_system_cxx_lib && x86_64-apple-macosx10.15
|
||||
|
@ -34,21 +32,69 @@
|
|||
#include "filesystem_test_helper.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
using namespace fs;
|
||||
|
||||
using TimeSpec = timespec;
|
||||
using StatT = struct stat;
|
||||
|
||||
using Sec = std::chrono::duration<file_time_type::rep>;
|
||||
using Hours = std::chrono::hours;
|
||||
using Minutes = std::chrono::minutes;
|
||||
using MilliSec = std::chrono::duration<file_time_type::rep, std::milli>;
|
||||
using MicroSec = std::chrono::duration<file_time_type::rep, std::micro>;
|
||||
using NanoSec = std::chrono::duration<file_time_type::rep, std::nano>;
|
||||
using std::chrono::duration_cast;
|
||||
|
||||
#ifdef _WIN32
|
||||
struct TimeSpec {
|
||||
int64_t tv_sec;
|
||||
int64_t tv_nsec;
|
||||
};
|
||||
struct StatT {
|
||||
TimeSpec st_atim;
|
||||
TimeSpec st_mtim;
|
||||
};
|
||||
// There were 369 years and 89 leap days from the Windows epoch
|
||||
// (1601) to the Unix epoch (1970).
|
||||
#define FILE_TIME_OFFSET_SECS (uint64_t(369 * 365 + 89) * (24 * 60 * 60))
|
||||
static TimeSpec filetime_to_timespec(LARGE_INTEGER li) {
|
||||
TimeSpec ret;
|
||||
ret.tv_sec = li.QuadPart / 10000000 - FILE_TIME_OFFSET_SECS;
|
||||
ret.tv_nsec = (li.QuadPart % 10000000) * 100;
|
||||
return ret;
|
||||
}
|
||||
static int stat_file(const char *path, StatT *buf, int flags) {
|
||||
HANDLE h = CreateFileA(path, FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | flags, nullptr);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
int ret = -1;
|
||||
FILE_BASIC_INFO basic;
|
||||
if (GetFileInformationByHandleEx(h, FileBasicInfo, &basic, sizeof(basic))) {
|
||||
buf->st_mtim = filetime_to_timespec(basic.LastWriteTime);
|
||||
buf->st_atim = filetime_to_timespec(basic.LastAccessTime);
|
||||
ret = 0;
|
||||
}
|
||||
CloseHandle(h);
|
||||
return ret;
|
||||
}
|
||||
static int stat(const char *path, StatT *buf) {
|
||||
return stat_file(path, buf, 0);
|
||||
}
|
||||
static int lstat(const char *path, StatT *buf) {
|
||||
return stat_file(path, buf, FILE_FLAG_OPEN_REPARSE_POINT);
|
||||
}
|
||||
#else
|
||||
using TimeSpec = timespec;
|
||||
using StatT = struct stat;
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
|
||||
TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
|
||||
|
@ -355,6 +401,11 @@ TEST_CASE(read_last_write_time_static_env_test)
|
|||
static_test_env static_env;
|
||||
using C = file_time_type::clock;
|
||||
file_time_type min = file_time_type::min();
|
||||
// Sleep a little to make sure that static_env.File created above is
|
||||
// strictly older than C::now() even with a coarser clock granularity
|
||||
// in C::now(). (GetSystemTimeAsFileTime on windows has a fairly coarse
|
||||
// granularity.)
|
||||
SleepFor(MilliSec(30));
|
||||
{
|
||||
file_time_type ret = last_write_time(static_env.File);
|
||||
TEST_CHECK(ret != min);
|
||||
|
|
|
@ -578,7 +578,7 @@ inline bool ErrorIs(const std::error_code& ec, std::errc First, ErrcT... Rest) {
|
|||
|
||||
// Provide our own Sleep routine since std::this_thread::sleep_for is not
|
||||
// available in single-threaded mode.
|
||||
void SleepFor(std::chrono::seconds dur) {
|
||||
template <class Dur> void SleepFor(Dur dur) {
|
||||
using namespace std::chrono;
|
||||
#if defined(_LIBCPP_HAS_NO_MONOTONIC_CLOCK)
|
||||
using Clock = system_clock;
|
||||
|
|
Loading…
Reference in New Issue