wibo/test/test_time.c

195 lines
6.2 KiB
C

#include <windows.h>
#include <stdint.h>
#include <stdio.h>
#include "test_assert.h"
static uint64_t filetime_to_u64(const FILETIME *ft) {
ULARGE_INTEGER li;
li.LowPart = ft->dwLowDateTime;
li.HighPart = ft->dwHighDateTime;
return li.QuadPart;
}
static FILETIME u64_to_filetime(uint64_t value) {
ULARGE_INTEGER li;
li.QuadPart = value;
FILETIME ft;
ft.dwLowDateTime = li.LowPart;
ft.dwHighDateTime = li.HighPart;
return ft;
}
static uint64_t abs_u64_diff(uint64_t a, uint64_t b) {
return (a > b) ? (a - b) : (b - a);
}
static void test_systemtime_roundtrip(void) {
SYSTEMTIME st = {
.wYear = 2023,
.wMonth = 7,
.wDay = 15,
.wHour = 12,
.wMinute = 34,
.wSecond = 56,
.wMilliseconds = 789
};
FILETIME ft;
TEST_CHECK(SystemTimeToFileTime(&st, &ft));
SYSTEMTIME converted = {0};
TEST_CHECK(FileTimeToSystemTime(&ft, &converted));
TEST_CHECK_EQ(st.wYear, converted.wYear);
TEST_CHECK_EQ(st.wMonth, converted.wMonth);
TEST_CHECK_EQ(st.wDay, converted.wDay);
TEST_CHECK_EQ(st.wHour, converted.wHour);
TEST_CHECK_EQ(st.wMinute, converted.wMinute);
TEST_CHECK_EQ(st.wSecond, converted.wSecond);
TEST_CHECK_EQ(st.wMilliseconds, converted.wMilliseconds);
SetLastError(0);
SYSTEMTIME invalid = st;
invalid.wMonth = 13;
TEST_CHECK(!SystemTimeToFileTime(&invalid, &ft));
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
}
static void test_filetime_known_timestamp(void) {
/* 2023-01-01 00:00:00 UTC */
const uint64_t expected_ticks = 133170048000000000ULL;
FILETIME ft = u64_to_filetime(expected_ticks);
SYSTEMTIME st = {0};
TEST_CHECK(FileTimeToSystemTime(&ft, &st));
TEST_CHECK_EQ(2023, st.wYear);
TEST_CHECK_EQ(1, st.wMonth);
TEST_CHECK_EQ(1, st.wDay);
TEST_CHECK_EQ(0, st.wHour);
TEST_CHECK_EQ(0, st.wMinute);
TEST_CHECK_EQ(0, st.wSecond);
TEST_CHECK_EQ(0, st.wMilliseconds);
FILETIME back;
TEST_CHECK(SystemTimeToFileTime(&st, &back));
TEST_CHECK_U64_EQ(expected_ticks, filetime_to_u64(&back));
}
static void test_getsystemtimeasfiletime(void) {
FILETIME from_api;
GetSystemTimeAsFileTime(&from_api);
SYSTEMTIME sys_now;
GetSystemTime(&sys_now);
FILETIME from_system;
TEST_CHECK(SystemTimeToFileTime(&sys_now, &from_system));
uint64_t delta = abs_u64_diff(filetime_to_u64(&from_api), filetime_to_u64(&from_system));
/* allow 1 second of skew between calls */
TEST_CHECK_MSG(delta < 10000000ULL, "GetSystemTimeAsFileTime skew too large: %llu", (unsigned long long)delta);
}
static void test_gettickcount_progresses(void) {
DWORD start = GetTickCount();
Sleep(60);
DWORD end = GetTickCount();
DWORD diff = end - start;
TEST_CHECK_MSG(diff >= 40, "GetTickCount diff too small: %lu", (unsigned long)diff);
TEST_CHECK_MSG(diff <= 5000, "GetTickCount diff too large: %lu", (unsigned long)diff);
}
static void test_setfiletime_roundtrip(void) {
char temp_path[MAX_PATH];
char temp_file[MAX_PATH];
DWORD path_len = GetTempPathA(sizeof(temp_path), temp_path);
TEST_CHECK(path_len > 0 && path_len < sizeof(temp_path));
UINT unique = GetTempFileNameA(temp_path, "TST", 0, temp_file);
TEST_CHECK(unique != 0);
HANDLE handle = CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
TEST_CHECK(handle != INVALID_HANDLE_VALUE);
FILETIME original_creation = {0}, original_access = {0}, original_write = {0};
TEST_CHECK(GetFileTime(handle, &original_creation, &original_access, &original_write));
SYSTEMTIME desired = {
.wYear = 2022,
.wMonth = 12,
.wDay = 31,
.wHour = 5,
.wMinute = 45,
.wSecond = 12,
.wMilliseconds = 123
};
FILETIME desired_ft;
TEST_CHECK(SystemTimeToFileTime(&desired, &desired_ft));
TEST_CHECK(SetFileTime(handle, NULL, NULL, &desired_ft));
FILETIME updated_creation = {0}, updated_access = {0}, updated_write = {0};
TEST_CHECK(GetFileTime(handle, &updated_creation, &updated_access, &updated_write));
TEST_CHECK_U64_EQ(filetime_to_u64(&desired_ft), filetime_to_u64(&updated_write));
FILETIME zero = {0, 0};
TEST_CHECK(SetFileTime(handle, NULL, &zero, NULL));
FILETIME final_creation = {0}, final_access = {0}, final_write = {0};
TEST_CHECK(GetFileTime(handle, &final_creation, &final_access, &final_write));
TEST_CHECK_U64_EQ(filetime_to_u64(&updated_access), filetime_to_u64(&final_access));
TEST_CHECK_U64_EQ(filetime_to_u64(&updated_write), filetime_to_u64(&final_write));
CloseHandle(handle);
DeleteFileA(temp_file);
}
static void test_local_filetime_conversions(void) {
/* Choose a time likely to be affected by DST in many zones */
SYSTEMTIME utc_time = {
.wYear = 2021,
.wMonth = 6,
.wDay = 15,
.wHour = 18,
.wMinute = 0,
.wSecond = 0,
.wMilliseconds = 0
};
FILETIME utc_ft;
TEST_CHECK(SystemTimeToFileTime(&utc_time, &utc_ft));
FILETIME local_ft;
TEST_CHECK(FileTimeToLocalFileTime(&utc_ft, &local_ft));
FILETIME roundtrip;
TEST_CHECK(LocalFileTimeToFileTime(&local_ft, &roundtrip));
TEST_CHECK_U64_EQ(filetime_to_u64(&utc_ft), filetime_to_u64(&roundtrip));
/* Local filetime should convert back to the original system time when interpreted locally */
SYSTEMTIME local_st = {0};
TEST_CHECK(FileTimeToSystemTime(&local_ft, &local_st));
SYSTEMTIME utc_from_roundtrip = {0};
TEST_CHECK(FileTimeToSystemTime(&roundtrip, &utc_from_roundtrip));
TEST_CHECK_EQ(utc_time.wYear, utc_from_roundtrip.wYear);
TEST_CHECK_EQ(utc_time.wMonth, utc_from_roundtrip.wMonth);
TEST_CHECK_EQ(utc_time.wDay, utc_from_roundtrip.wDay);
TEST_CHECK_EQ(utc_time.wHour, utc_from_roundtrip.wHour);
TEST_CHECK_EQ(utc_time.wMinute, utc_from_roundtrip.wMinute);
TEST_CHECK_EQ(utc_time.wSecond, utc_from_roundtrip.wSecond);
}
int main(void) {
test_systemtime_roundtrip();
test_filetime_known_timestamp();
test_getsystemtimeasfiletime();
test_gettickcount_progresses();
test_setfiletime_roundtrip();
test_local_filetime_conversions();
return 0;
}