diff --git a/core/libs/tests/GuestUtime.cpp b/core/libs/tests/GuestUtime.cpp new file mode 100644 index 0000000000..27df8f2cb5 --- /dev/null +++ b/core/libs/tests/GuestUtime.cpp @@ -0,0 +1,89 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +struct LibcUtimbuf { + std::int64_t actime; + std::int64_t modtime; +}; +static_assert(sizeof(LibcUtimbuf) == 16); + +extern "C" { +int APS5_VABI utime_nid_postfix(const char*, const LibcUtimbuf*); +int* APS5_VABI __error_nid_postfix(); +} + +static void Require(bool condition, int line) { + if (!condition) { + std::fprintf(stderr, "Utime check failed at line %d\n", line); + std::abort(); + } +} +#define Check(value) Require((value), __LINE__) + +static LibcUtimbuf ReadTimes(const std::filesystem::path& path, bool wholeSeconds = false) { +#ifdef _WIN32 + const HANDLE file = CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, 0, nullptr); + Check(file != INVALID_HANDLE_VALUE); + FILETIME access{}, modified{}; + Check(GetFileTime(file, nullptr, &access, &modified)); + Check(CloseHandle(file)); + const auto decode = [wholeSeconds](FILETIME time) { + const auto ticks = (static_cast(time.dwHighDateTime) << 32) | time.dwLowDateTime; + if (wholeSeconds) Check(ticks % 10000000 == 0); + return static_cast(ticks / 10000000) - 11644473600LL; + }; + return {decode(access), decode(modified)}; +#else + struct stat info{}; + Check(::stat(path.c_str(), &info) == 0); + if (wholeSeconds) { +#ifdef __APPLE__ + Check(info.st_atimespec.tv_nsec == 0 && info.st_mtimespec.tv_nsec == 0); +#else + Check(info.st_atim.tv_nsec == 0 && info.st_mtim.tv_nsec == 0); +#endif + } + return {info.st_atime, info.st_mtime}; +#endif +} + +int main() { + const auto root = std::filesystem::path("anyps5-utime-test-" + + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); + Check(std::filesystem::create_directory(root)); + const auto file = root / "data.bin"; + { std::ofstream stream(file); stream << "timestamps"; Check(stream.good()); } + const auto path = file.string(); + const LibcUtimbuf times{1000000000LL, 1000000123LL}; + *__error_nid_postfix() = 71; + Check(utime_nid_postfix(path.c_str(), ×) == 0); + Check(*__error_nid_postfix() == 71); + const auto stored = ReadTimes(file, true); + Check(stored.actime == times.actime && stored.modtime == times.modtime); + const auto missing = (root / "missing.bin").string(); + Check(utime_nid_postfix(missing.c_str(), ×) == -1 && *__error_nid_postfix() == 2); + Check(utime_nid_postfix("", ×) == -1 && *__error_nid_postfix() == 2); + Check(utime_nid_postfix(nullptr, ×) == -1 && *__error_nid_postfix() == 14); + Check(utime_nid_postfix(nullptr, nullptr) == -1 && *__error_nid_postfix() == 14); + const auto unchanged = ReadTimes(file, true); + Check(unchanged.actime == times.actime && unchanged.modtime == times.modtime); + const auto before = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + Check(utime_nid_postfix(path.c_str(), nullptr) == 0); + const auto after = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + const auto current = ReadTimes(file); + Check(current.actime >= before - 1 && current.actime <= after + 1); + Check(current.modtime >= before - 1 && current.modtime <= after + 1); + Check(std::filesystem::remove_all(root) == 2); +}