mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
Tests for handleapi, synchapi, processes
This commit is contained in:
parent
166b9036fd
commit
f52ca2803f
@ -210,6 +210,39 @@ if(BUILD_TESTING)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_threading.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_handleapi.exe
|
||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||
-I${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||
-o test_handleapi.exe
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_handleapi.c
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_handleapi.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_synchapi.exe
|
||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||
-I${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||
-o test_synchapi.exe
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_synchapi.c
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_synchapi.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_processes.exe
|
||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||
-I${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||
-o test_processes.exe
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_processes.c
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_processes.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_heap.exe
|
||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||
@ -342,6 +375,9 @@ if(BUILD_TESTING)
|
||||
${WIBO_TEST_BIN_DIR}/test_bcrypt.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_resources.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_threading.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_handleapi.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_synchapi.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_processes.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_heap.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_actctx.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe
|
||||
@ -388,6 +424,24 @@ if(BUILD_TESTING)
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
|
||||
add_test(NAME wibo.test_handleapi
|
||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_handleapi.exe)
|
||||
set_tests_properties(wibo.test_handleapi PROPERTIES
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
|
||||
add_test(NAME wibo.test_synchapi
|
||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_synchapi.exe)
|
||||
set_tests_properties(wibo.test_synchapi PROPERTIES
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
|
||||
add_test(NAME wibo.test_processes
|
||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_processes.exe)
|
||||
set_tests_properties(wibo.test_processes PROPERTIES
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
|
||||
add_test(NAME wibo.test_heap
|
||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_heap.exe)
|
||||
set_tests_properties(wibo.test_heap PROPERTIES
|
||||
|
@ -189,7 +189,7 @@ HANDLE WIN_FUNC CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LO
|
||||
BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPreviousCount) {
|
||||
HOST_CONTEXT_GUARD();
|
||||
DEBUG_LOG("ReleaseSemaphore(%p, %ld, %p)\n", hSemaphore, lReleaseCount, lpPreviousCount);
|
||||
if (lReleaseCount <= 0) {
|
||||
if (lReleaseCount < 0) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
@ -330,9 +330,8 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
|
||||
pthread_t self = pthread_self();
|
||||
std::unique_lock lk(th->m);
|
||||
if (pthread_equal(th->thread, self)) {
|
||||
// Cannot wait on self
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return WAIT_FAILED;
|
||||
// Windows actually allows you to wait on your own thread, but why bother?
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
bool ok = doWait(lk, th->cv, [&] { return th->signaled.load(std::memory_order_acquire); });
|
||||
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
|
||||
@ -341,9 +340,8 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) {
|
||||
auto po = std::move(obj).downcast<ProcessObject>();
|
||||
std::unique_lock lk(po->m);
|
||||
if (po->pidfd == -1) {
|
||||
// Cannot wait on self
|
||||
wibo::lastError = ERROR_INVALID_HANDLE;
|
||||
return WAIT_FAILED;
|
||||
// Windows actually allows you to wait on your own process, but why bother?
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
bool ok = doWait(lk, po->cv, [&] { return po->signaled.load(std::memory_order_acquire); });
|
||||
return ok ? WAIT_OBJECT_0 : WAIT_TIMEOUT;
|
||||
|
@ -186,6 +186,13 @@ bool Handles::duplicateTo(HANDLE src, Handles &dst, HANDLE &out, uint32_t desire
|
||||
|
||||
uint32_t effAccess = (options & DUPLICATE_SAME_ACCESS) ? meta.grantedAccess : (desiredAccess & meta.grantedAccess);
|
||||
const uint32_t flags = (inherit ? HANDLE_FLAG_INHERIT : 0);
|
||||
|
||||
// Reuse the same handle if duplicating within the same table and no changes
|
||||
if (&dst == this && closeSource && effAccess == meta.grantedAccess && flags == meta.flags) {
|
||||
out = src;
|
||||
return true;
|
||||
}
|
||||
|
||||
out = dst.alloc(std::move(obj), effAccess, flags);
|
||||
|
||||
if (closeSource) {
|
||||
|
@ -51,22 +51,21 @@ static void check_invalid_parameters(void) {
|
||||
data.cbSize = sizeof(data);
|
||||
GUID fakeGuid = {0};
|
||||
SetLastError(0);
|
||||
BOOL ok = FindActCtxSectionStringW(0, &fakeGuid, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &data);
|
||||
TEST_CHECK(!ok);
|
||||
TEST_CHECK(
|
||||
!FindActCtxSectionStringW(0, &fakeGuid, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &data));
|
||||
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
|
||||
|
||||
ACTCTX_SECTION_KEYED_DATA sized = {0};
|
||||
sized.cbSize = sizeof(data) - 4;
|
||||
SetLastError(0);
|
||||
ok = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &sized);
|
||||
TEST_CHECK(!ok);
|
||||
TEST_CHECK(!FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &sized));
|
||||
TEST_CHECK_EQ(ERROR_INSUFFICIENT_BUFFER, GetLastError());
|
||||
|
||||
ACTCTX_SECTION_KEYED_DATA flags = {0};
|
||||
flags.cbSize = sizeof(flags);
|
||||
SetLastError(0);
|
||||
ok = FindActCtxSectionStringW(0x2, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &flags);
|
||||
TEST_CHECK(!ok);
|
||||
TEST_CHECK(
|
||||
!FindActCtxSectionStringW(0x2, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"msvcr80.dll", &flags));
|
||||
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
|
||||
}
|
||||
|
||||
@ -74,17 +73,15 @@ static void check_missing_entries(void) {
|
||||
ACTCTX_SECTION_KEYED_DATA data = {0};
|
||||
data.cbSize = sizeof(data);
|
||||
SetLastError(0);
|
||||
BOOL ok =
|
||||
FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"totally_missing.dll", &data);
|
||||
TEST_CHECK(!ok);
|
||||
TEST_CHECK(
|
||||
!FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION, L"totally_missing.dll", &data));
|
||||
TEST_CHECK_EQ(ERROR_SXS_KEY_NOT_FOUND, GetLastError());
|
||||
|
||||
ACTCTX_SECTION_KEYED_DATA wrongSection = {0};
|
||||
wrongSection.cbSize = sizeof(wrongSection);
|
||||
SetLastError(0);
|
||||
ok = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION, L"msvcr80.dll",
|
||||
&wrongSection);
|
||||
TEST_CHECK(!ok);
|
||||
TEST_CHECK(!FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_ASSEMBLY_INFORMATION, L"msvcr80.dll",
|
||||
&wrongSection));
|
||||
TEST_CHECK_EQ(ERROR_SXS_KEY_NOT_FOUND, GetLastError());
|
||||
}
|
||||
|
||||
|
101
test/test_handleapi.c
Normal file
101
test/test_handleapi.c
Normal file
@ -0,0 +1,101 @@
|
||||
#include "test_assert.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
static void test_duplicate_handle_basic(void) {
|
||||
HANDLE evt = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(evt != NULL);
|
||||
|
||||
HANDLE dup = NULL;
|
||||
BOOL ok = DuplicateHandle(GetCurrentProcess(), evt, GetCurrentProcess(), &dup, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
TEST_CHECK(ok);
|
||||
TEST_CHECK(dup != NULL);
|
||||
TEST_CHECK(dup != evt);
|
||||
|
||||
TEST_CHECK(SetEvent(evt));
|
||||
DWORD waitResult = WaitForSingleObject(dup, 0);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
TEST_CHECK(ResetEvent(evt));
|
||||
|
||||
TEST_CHECK(CloseHandle(dup));
|
||||
TEST_CHECK(CloseHandle(evt));
|
||||
}
|
||||
|
||||
static void test_duplicate_handle_close_source(void) {
|
||||
HANDLE evt = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(evt != NULL);
|
||||
|
||||
HANDLE dup = NULL;
|
||||
BOOL ok = DuplicateHandle(GetCurrentProcess(), evt, GetCurrentProcess(), &dup, 0, FALSE,
|
||||
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
|
||||
TEST_CHECK(ok);
|
||||
TEST_CHECK(dup != NULL);
|
||||
|
||||
// Since we're duplicating within the same process with DUPLICATE_CLOSE_SOURCE,
|
||||
// we should get back the same handle value
|
||||
TEST_CHECK(dup == evt);
|
||||
|
||||
TEST_CHECK(SetEvent(dup));
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(dup, 0));
|
||||
TEST_CHECK(CloseHandle(dup));
|
||||
}
|
||||
|
||||
static void test_duplicate_handle_invalid_source(void) {
|
||||
HANDLE bogus = (HANDLE)(uintptr_t)0x1234;
|
||||
HANDLE out = NULL;
|
||||
SetLastError(0);
|
||||
TEST_CHECK(
|
||||
!DuplicateHandle(GetCurrentProcess(), bogus, GetCurrentProcess(), &out, 0, FALSE, DUPLICATE_SAME_ACCESS));
|
||||
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
|
||||
TEST_CHECK(out == NULL);
|
||||
}
|
||||
|
||||
static void test_duplicate_handle_invalid_target_process(void) {
|
||||
HANDLE evt = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(evt != NULL);
|
||||
|
||||
HANDLE dup = NULL;
|
||||
SetLastError(0xDEADBEEF);
|
||||
TEST_CHECK(!DuplicateHandle(GetCurrentProcess(), evt, NULL, &dup, 0, FALSE, DUPLICATE_SAME_ACCESS));
|
||||
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
|
||||
TEST_CHECK(dup == NULL);
|
||||
|
||||
TEST_CHECK(CloseHandle(evt));
|
||||
}
|
||||
|
||||
static void test_duplicate_pseudo_process_handle(void) {
|
||||
HANDLE pseudo = GetCurrentProcess();
|
||||
HANDLE procHandle = NULL;
|
||||
BOOL ok = DuplicateHandle(pseudo, pseudo, pseudo, &procHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
TEST_CHECK(ok);
|
||||
TEST_CHECK(procHandle != NULL);
|
||||
TEST_CHECK(procHandle != pseudo);
|
||||
|
||||
TEST_CHECK_EQ(WAIT_TIMEOUT, WaitForSingleObject(procHandle, 0));
|
||||
|
||||
TEST_CHECK(CloseHandle(procHandle));
|
||||
}
|
||||
|
||||
static void test_duplicate_handle_after_close(void) {
|
||||
HANDLE evt = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(evt != NULL);
|
||||
|
||||
TEST_CHECK(CloseHandle(evt));
|
||||
|
||||
HANDLE dup = NULL;
|
||||
SetLastError(0);
|
||||
TEST_CHECK(!DuplicateHandle(GetCurrentProcess(), evt, GetCurrentProcess(), &dup, 0, FALSE, DUPLICATE_SAME_ACCESS));
|
||||
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
|
||||
TEST_CHECK(dup == NULL);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_duplicate_handle_basic();
|
||||
test_duplicate_handle_close_source();
|
||||
test_duplicate_handle_invalid_source();
|
||||
test_duplicate_handle_invalid_target_process();
|
||||
test_duplicate_pseudo_process_handle();
|
||||
test_duplicate_handle_after_close();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -63,7 +63,7 @@ static void test_overlapped_read_with_event(void) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
@ -91,7 +91,7 @@ static void test_overlapped_eof(void) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 1234;
|
||||
TEST_CHECK(!GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
@ -127,7 +127,7 @@ static void test_overlapped_write(void) {
|
||||
if (!issued) {
|
||||
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
|
||||
}
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, INFINITE) == WAIT_OBJECT_0);
|
||||
TEST_CHECK(WaitForSingleObject(ov.hEvent, 1000) == WAIT_OBJECT_0);
|
||||
|
||||
DWORD transferred = 0;
|
||||
TEST_CHECK(GetOverlappedResult(file, &ov, &transferred, FALSE));
|
||||
|
@ -75,8 +75,7 @@ int main(void) {
|
||||
|
||||
bytesRead = 123;
|
||||
SetLastError(ERROR_GEN_FAILURE);
|
||||
BOOL ok = ReadFile(readPipe, buffer, sizeof(buffer), &bytesRead, NULL);
|
||||
TEST_CHECK(!ok);
|
||||
TEST_CHECK(!ReadFile(readPipe, buffer, sizeof(buffer), &bytesRead, NULL));
|
||||
TEST_CHECK_EQ(0u, (unsigned int)bytesRead);
|
||||
TEST_CHECK_EQ(ERROR_BROKEN_PIPE, GetLastError());
|
||||
TEST_CHECK(ResetEvent(event));
|
||||
|
116
test/test_processes.c
Normal file
116
test/test_processes.c
Normal file
@ -0,0 +1,116 @@
|
||||
#include "test_assert.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
static DWORD parse_exit_code(const char *value) {
|
||||
TEST_CHECK(value != NULL);
|
||||
DWORD result = 0;
|
||||
for (const char *p = value; *p; ++p) {
|
||||
TEST_CHECK(*p >= '0' && *p <= '9');
|
||||
result = result * 10u + (DWORD)(*p - '0');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int child_main(int argc, char **argv) {
|
||||
TEST_CHECK(argc >= 2);
|
||||
(void)argv;
|
||||
|
||||
char exitBuffer[16];
|
||||
DWORD exitLen = GetEnvironmentVariableA("WIBO_TEST_PROC_EXIT", exitBuffer, sizeof(exitBuffer));
|
||||
TEST_CHECK(exitLen > 0 && exitLen < sizeof(exitBuffer));
|
||||
DWORD desiredExit = parse_exit_code(exitBuffer);
|
||||
|
||||
Sleep(200);
|
||||
return (int)desiredExit;
|
||||
}
|
||||
|
||||
static void test_createprocess_failure(void) {
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
SetLastError(0);
|
||||
char bogusCommandLine[] = "child";
|
||||
TEST_CHECK(
|
||||
!CreateProcessA("Z:/definitely/missing.exe", bogusCommandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
|
||||
DWORD error = GetLastError();
|
||||
TEST_CHECK_MSG(error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND, "CreateProcessA missing file -> %lu",
|
||||
(unsigned long)error);
|
||||
}
|
||||
|
||||
static int parent_main(void) {
|
||||
test_createprocess_failure();
|
||||
|
||||
char modulePath[MAX_PATH];
|
||||
DWORD pathLen = GetModuleFileNameA(NULL, modulePath, (DWORD)sizeof(modulePath));
|
||||
TEST_CHECK(pathLen > 0 && pathLen < sizeof(modulePath));
|
||||
|
||||
const DWORD childExitCode = 0x24u;
|
||||
char commandLine[256];
|
||||
snprintf(commandLine, sizeof(commandLine), "child placeholder %lu", (unsigned long)childExitCode);
|
||||
|
||||
char exitEnv[16];
|
||||
snprintf(exitEnv, sizeof(exitEnv), "%lu", (unsigned long)childExitCode);
|
||||
TEST_CHECK(SetEnvironmentVariableA("WIBO_TEST_PROC_EXIT", exitEnv));
|
||||
TEST_CHECK(SetEnvironmentVariableA("WIBO_TEST_PROC_ROLE", "child"));
|
||||
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
GetStartupInfoA(&si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
TEST_CHECK(CreateProcessA(modulePath, commandLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi));
|
||||
TEST_CHECK(pi.hProcess != NULL);
|
||||
SetEnvironmentVariableA("WIBO_TEST_PROC_EXIT", NULL);
|
||||
SetEnvironmentVariableA("WIBO_TEST_PROC_ROLE", NULL);
|
||||
|
||||
HANDLE processHandle = NULL;
|
||||
TEST_CHECK(DuplicateHandle(GetCurrentProcess(), pi.hProcess, GetCurrentProcess(), &processHandle, 0, FALSE,
|
||||
DUPLICATE_SAME_ACCESS));
|
||||
TEST_CHECK(processHandle != NULL);
|
||||
TEST_CHECK(processHandle != pi.hProcess);
|
||||
|
||||
TEST_CHECK(CloseHandle(pi.hProcess));
|
||||
TEST_CHECK_EQ(WAIT_FAILED, WaitForSingleObject(pi.hProcess, 0));
|
||||
pi.hProcess = NULL;
|
||||
|
||||
Sleep(50);
|
||||
|
||||
TEST_CHECK_EQ(WAIT_TIMEOUT, WaitForSingleObject(processHandle, 0));
|
||||
|
||||
DWORD exitCode = 0;
|
||||
TEST_CHECK(GetExitCodeProcess(processHandle, &exitCode));
|
||||
TEST_CHECK_EQ(STILL_ACTIVE, exitCode);
|
||||
|
||||
TEST_CHECK_EQ(WAIT_TIMEOUT, WaitForSingleObject(processHandle, 10));
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(processHandle, 5000));
|
||||
|
||||
TEST_CHECK(GetExitCodeProcess(processHandle, &exitCode));
|
||||
TEST_CHECK_EQ(childExitCode, exitCode);
|
||||
|
||||
TEST_CHECK(CloseHandle(processHandle));
|
||||
if (pi.hThread) {
|
||||
TEST_CHECK(CloseHandle(pi.hThread));
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char role[16];
|
||||
DWORD roleLen = GetEnvironmentVariableA("WIBO_TEST_PROC_ROLE", role, sizeof(role));
|
||||
if (roleLen > 0 && roleLen < sizeof(role) && strcmp(role, "child") == 0) {
|
||||
return child_main(argc, argv);
|
||||
}
|
||||
if (argc > 1 && strcmp(argv[1], "child") == 0) {
|
||||
return child_main(argc, argv);
|
||||
}
|
||||
return parent_main();
|
||||
}
|
142
test/test_synchapi.c
Normal file
142
test/test_synchapi.c
Normal file
@ -0,0 +1,142 @@
|
||||
#include "test_assert.h"
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
|
||||
typedef struct {
|
||||
HANDLE mutex;
|
||||
HANDLE acquiredEvent;
|
||||
HANDLE releaseEvent;
|
||||
HANDLE doneEvent;
|
||||
} MutexWorkerContext;
|
||||
|
||||
static DWORD WINAPI mutex_worker(LPVOID param) {
|
||||
MutexWorkerContext *ctx = (MutexWorkerContext *)param;
|
||||
DWORD wait = WaitForSingleObject(ctx->mutex, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
|
||||
TEST_CHECK(SetEvent(ctx->acquiredEvent));
|
||||
|
||||
wait = WaitForSingleObject(ctx->releaseEvent, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
|
||||
TEST_CHECK(ReleaseMutex(ctx->mutex));
|
||||
TEST_CHECK(SetEvent(ctx->doneEvent));
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
HANDLE semaphore;
|
||||
HANDLE ackEvent;
|
||||
HANDLE doneEvent;
|
||||
int iterations;
|
||||
} SemaphoreWorkerContext;
|
||||
|
||||
static DWORD WINAPI semaphore_worker(LPVOID param) {
|
||||
SemaphoreWorkerContext *ctx = (SemaphoreWorkerContext *)param;
|
||||
for (int i = 0; i < ctx->iterations; ++i) {
|
||||
DWORD wait = WaitForSingleObject(ctx->semaphore, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
|
||||
TEST_CHECK(SetEvent(ctx->ackEvent));
|
||||
}
|
||||
TEST_CHECK(SetEvent(ctx->doneEvent));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_mutex_contention(void) {
|
||||
HANDLE mutex = CreateMutexA(NULL, FALSE, NULL);
|
||||
TEST_CHECK(mutex != NULL);
|
||||
|
||||
HANDLE acquired = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
HANDLE releaseSignal = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
HANDLE done = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(acquired != NULL && releaseSignal != NULL && done != NULL);
|
||||
|
||||
MutexWorkerContext ctx = {
|
||||
.mutex = mutex,
|
||||
.acquiredEvent = acquired,
|
||||
.releaseEvent = releaseSignal,
|
||||
.doneEvent = done,
|
||||
};
|
||||
|
||||
HANDLE thread = CreateThread(NULL, 0, mutex_worker, &ctx, 0, NULL);
|
||||
TEST_CHECK(thread != NULL);
|
||||
|
||||
DWORD wait = WaitForSingleObject(acquired, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
|
||||
|
||||
wait = WaitForSingleObject(mutex, 10);
|
||||
TEST_CHECK_EQ(WAIT_TIMEOUT, wait);
|
||||
|
||||
TEST_CHECK(SetEvent(releaseSignal));
|
||||
|
||||
wait = WaitForSingleObject(done, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
|
||||
|
||||
wait = WaitForSingleObject(mutex, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
|
||||
|
||||
wait = WaitForSingleObject(mutex, 0);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait);
|
||||
|
||||
TEST_CHECK(ReleaseMutex(mutex));
|
||||
TEST_CHECK(ReleaseMutex(mutex));
|
||||
|
||||
TEST_CHECK(CloseHandle(thread));
|
||||
TEST_CHECK(CloseHandle(acquired));
|
||||
TEST_CHECK(CloseHandle(releaseSignal));
|
||||
TEST_CHECK(CloseHandle(done));
|
||||
TEST_CHECK(CloseHandle(mutex));
|
||||
}
|
||||
|
||||
static void test_semaphore_waits(void) {
|
||||
HANDLE semaphore = CreateSemaphoreA(NULL, 0, 3, NULL);
|
||||
TEST_CHECK(semaphore != NULL);
|
||||
|
||||
DWORD wait = WaitForSingleObject(semaphore, 10);
|
||||
TEST_CHECK_EQ(WAIT_TIMEOUT, wait);
|
||||
|
||||
HANDLE ack = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
HANDLE done = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(ack != NULL && done != NULL);
|
||||
|
||||
SemaphoreWorkerContext ctx = {
|
||||
.semaphore = semaphore,
|
||||
.ackEvent = ack,
|
||||
.doneEvent = done,
|
||||
.iterations = 3,
|
||||
};
|
||||
|
||||
HANDLE thread = CreateThread(NULL, 0, semaphore_worker, &ctx, 0, NULL);
|
||||
TEST_CHECK(thread != NULL);
|
||||
|
||||
for (int i = 0; i < ctx.iterations; ++i) {
|
||||
LONG previous = -1;
|
||||
BOOL ok = ReleaseSemaphore(semaphore, 1, &previous);
|
||||
TEST_CHECK(ok);
|
||||
TEST_CHECK_EQ(0, previous);
|
||||
DWORD ackWait = WaitForSingleObject(ack, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, ackWait);
|
||||
}
|
||||
|
||||
DWORD doneWait = WaitForSingleObject(done, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, doneWait);
|
||||
|
||||
// lReleaseCount = 0 permitted; no effect
|
||||
TEST_CHECK(ReleaseSemaphore(semaphore, 0, NULL));
|
||||
|
||||
HANDLE limited = CreateSemaphoreA(NULL, 1, 1, NULL);
|
||||
TEST_CHECK(limited != NULL);
|
||||
SetLastError(0);
|
||||
TEST_CHECK(!ReleaseSemaphore(limited, 1, NULL));
|
||||
TEST_CHECK_EQ(ERROR_TOO_MANY_POSTS, GetLastError());
|
||||
|
||||
TEST_CHECK(CloseHandle(thread));
|
||||
TEST_CHECK(CloseHandle(ack));
|
||||
TEST_CHECK(CloseHandle(done));
|
||||
TEST_CHECK(CloseHandle(semaphore));
|
||||
TEST_CHECK(CloseHandle(limited));
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_mutex_contention();
|
||||
test_semaphore_waits();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -52,9 +52,9 @@ int main(void) {
|
||||
|
||||
HANDLE thread = CreateThread(NULL, 0, workerProc, NULL, 0, NULL);
|
||||
TEST_CHECK_MSG(thread != NULL, "CreateThread failed: %lu", (unsigned long)GetLastError());
|
||||
DWORD wait_result = WaitForSingleObject(thread, INFINITE);
|
||||
DWORD wait_result = WaitForSingleObject(thread, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait_result);
|
||||
TEST_CHECK_MSG(CloseHandle(thread) != 0, "CloseHandle(thread) failed: %lu", (unsigned long)GetLastError());
|
||||
TEST_CHECK(CloseHandle(thread));
|
||||
|
||||
TEST_CHECK_EQ(1, getAttach());
|
||||
TEST_CHECK_EQ(1, getDetach());
|
||||
@ -68,9 +68,9 @@ int main(void) {
|
||||
|
||||
thread = CreateThread(NULL, 0, workerProc, NULL, 0, NULL);
|
||||
TEST_CHECK_MSG(thread != NULL, "CreateThread after disable failed: %lu", (unsigned long)GetLastError());
|
||||
wait_result = WaitForSingleObject(thread, INFINITE);
|
||||
wait_result = WaitForSingleObject(thread, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, wait_result);
|
||||
TEST_CHECK_MSG(CloseHandle(thread) != 0, "CloseHandle(second thread) failed: %lu", (unsigned long)GetLastError());
|
||||
TEST_CHECK(CloseHandle(thread));
|
||||
|
||||
if (!isRunningUnderWine()) {
|
||||
TEST_CHECK_EQ(0, getAttach());
|
||||
@ -80,7 +80,7 @@ int main(void) {
|
||||
LONG final_attach = getAttach();
|
||||
LONG final_detach = getDetach();
|
||||
|
||||
TEST_CHECK_MSG(FreeLibrary(mod) != 0, "FreeLibrary failed: %lu", (unsigned long)GetLastError());
|
||||
TEST_CHECK(FreeLibrary(mod));
|
||||
|
||||
printf("thread_notifications: attach=%ld detach=%ld\n", (long)final_attach, (long)final_detach);
|
||||
return EXIT_SUCCESS;
|
||||
|
@ -11,7 +11,7 @@ typedef struct {
|
||||
static DWORD WINAPI worker_main(LPVOID param) {
|
||||
WorkerContext *ctx = (WorkerContext *)param;
|
||||
TEST_CHECK(SetEvent(ctx->readyEvent));
|
||||
DWORD waitResult = WaitForSingleObject(ctx->goEvent, INFINITE);
|
||||
DWORD waitResult = WaitForSingleObject(ctx->goEvent, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
return ctx->exitCode;
|
||||
}
|
||||
@ -37,14 +37,14 @@ int main(void) {
|
||||
HANDLE thread = CreateThread(NULL, 0, worker_main, &ctx, 0, NULL);
|
||||
TEST_CHECK(thread != NULL);
|
||||
|
||||
DWORD waitResult = WaitForSingleObject(readyEvent, INFINITE);
|
||||
DWORD waitResult = WaitForSingleObject(readyEvent, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
|
||||
TEST_CHECK(ResetEvent(readyEvent));
|
||||
|
||||
TEST_CHECK(SetEvent(goEvent));
|
||||
|
||||
waitResult = WaitForSingleObject(thread, INFINITE);
|
||||
waitResult = WaitForSingleObject(thread, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
|
||||
DWORD exitCode = 0;
|
||||
@ -56,21 +56,21 @@ int main(void) {
|
||||
HANDLE autoEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
TEST_CHECK(autoEvent != NULL);
|
||||
TEST_CHECK(SetEvent(autoEvent));
|
||||
waitResult = WaitForSingleObject(autoEvent, INFINITE);
|
||||
waitResult = WaitForSingleObject(autoEvent, 0);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
TEST_CHECK(SetEvent(autoEvent));
|
||||
waitResult = WaitForSingleObject(autoEvent, INFINITE);
|
||||
waitResult = WaitForSingleObject(autoEvent, 0);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
TEST_CHECK(CloseHandle(autoEvent));
|
||||
|
||||
HANDLE manualEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
TEST_CHECK(manualEvent != NULL);
|
||||
TEST_CHECK(SetEvent(manualEvent));
|
||||
waitResult = WaitForSingleObject(manualEvent, INFINITE);
|
||||
waitResult = WaitForSingleObject(manualEvent, 0);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
TEST_CHECK(ResetEvent(manualEvent));
|
||||
TEST_CHECK(SetEvent(manualEvent));
|
||||
waitResult = WaitForSingleObject(manualEvent, INFINITE);
|
||||
waitResult = WaitForSingleObject(manualEvent, 0);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
TEST_CHECK(CloseHandle(manualEvent));
|
||||
|
||||
@ -80,7 +80,7 @@ int main(void) {
|
||||
|
||||
HANDLE mutex = CreateMutexA(NULL, FALSE, NULL);
|
||||
TEST_CHECK(mutex != NULL);
|
||||
waitResult = WaitForSingleObject(mutex, INFINITE);
|
||||
waitResult = WaitForSingleObject(mutex, 0);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
TEST_CHECK(ReleaseMutex(mutex));
|
||||
TEST_CHECK(CloseHandle(mutex));
|
||||
@ -88,7 +88,7 @@ int main(void) {
|
||||
DWORD secondExitCode = 0x55AA;
|
||||
HANDLE exitThread = CreateThread(NULL, 0, exit_thread_worker, &secondExitCode, 0, NULL);
|
||||
TEST_CHECK(exitThread != NULL);
|
||||
waitResult = WaitForSingleObject(exitThread, INFINITE);
|
||||
waitResult = WaitForSingleObject(exitThread, 1000);
|
||||
TEST_CHECK_EQ(WAIT_OBJECT_0, waitResult);
|
||||
exitCode = 0;
|
||||
TEST_CHECK(GetExitCodeThread(exitThread, &exitCode));
|
||||
|
Loading…
x
Reference in New Issue
Block a user