Files
wibo/test/test_namedpipe.c

184 lines
6.5 KiB
C

#include "test_assert.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string.h>
static const char *kPipeName = "\\\\.\\pipe\\wibo_test_namedpipe";
static const char *kPipeNameConnect = "\\\\.\\pipe\\wibo_test_namedpipe_connect";
static const char *kPipeNameNowait = "\\\\.\\pipe\\wibo_test_namedpipe_nowait";
static const char *kPipeNameOverlapped = "\\\\.\\pipe\\wibo_test_namedpipe_overlapped";
struct ConnectThreadArgs {
const char *pipeName;
const char *message;
DWORD delayMs;
HANDLE client;
DWORD error;
};
static DWORD WINAPI client_connect_thread(LPVOID parameter) {
struct ConnectThreadArgs *args = (struct ConnectThreadArgs *)parameter;
args->client = INVALID_HANDLE_VALUE;
args->error = ERROR_SUCCESS;
if (args->delayMs) {
Sleep(args->delayMs);
}
HANDLE client =
CreateFileA(args->pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
args->client = client;
if (client == INVALID_HANDLE_VALUE) {
args->error = GetLastError();
return 0;
}
if (args->message) {
DWORD written = 0;
WriteFile(client, args->message, (DWORD)strlen(args->message), &written, NULL);
}
return 0;
}
static void write_checked(HANDLE handle, const char *msg) {
DWORD written = 0;
TEST_CHECK(WriteFile(handle, msg, (DWORD)strlen(msg), &written, NULL));
TEST_CHECK_EQ((DWORD)strlen(msg), written);
}
static void read_checked(HANDLE handle, const char *expected) {
char buffer[64] = {0};
DWORD read = 0;
TEST_CHECK(ReadFile(handle, buffer, sizeof(buffer), &read, NULL));
TEST_CHECK_EQ((DWORD)strlen(expected), read);
TEST_CHECK(memcmp(buffer, expected, read) == 0);
}
int main(void) {
SetLastError(0xdeadbeefu);
HANDLE missing = CreateFileA(kPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
TEST_CHECK(missing == INVALID_HANDLE_VALUE);
TEST_CHECK_EQ(ERROR_FILE_NOT_FOUND, GetLastError());
HANDLE pipe = CreateNamedPipeA(kPipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1,
1024, 1024, 0, NULL);
TEST_CHECK(pipe != INVALID_HANDLE_VALUE);
HANDLE client =
CreateFileA(kPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
TEST_CHECK(client != INVALID_HANDLE_VALUE);
write_checked(client, "ping");
read_checked(pipe, "ping");
write_checked(pipe, "pong");
read_checked(client, "pong");
HANDLE invalidPrefix = CreateNamedPipeA("\\\\.\\pipe\\", PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
TEST_CHECK(invalidPrefix == INVALID_HANDLE_VALUE);
TEST_CHECK_EQ(ERROR_INVALID_HANDLE, GetLastError());
SetLastError(0xdeadbeefu);
HANDLE busy = CreateFileA(kPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
TEST_CHECK(busy == INVALID_HANDLE_VALUE);
TEST_CHECK_EQ(ERROR_PIPE_BUSY, GetLastError());
SetLastError(0xdeadbeefu);
HANDLE invalidName = CreateNamedPipeA("invalid", PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
TEST_CHECK(invalidName == INVALID_HANDLE_VALUE);
TEST_CHECK_EQ(ERROR_INVALID_NAME, GetLastError());
HANDLE badMode = CreateNamedPipeA(kPipeName, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1, 1024, 1024, 0, NULL);
TEST_CHECK(badMode == INVALID_HANDLE_VALUE);
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
HANDLE nullName = CreateNamedPipeA(NULL, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1,
1024, 1024, 0, NULL);
TEST_CHECK(nullName == INVALID_HANDLE_VALUE);
TEST_CHECK_EQ(ERROR_PATH_NOT_FOUND, GetLastError());
TEST_CHECK(CloseHandle(client));
TEST_CHECK(CloseHandle(pipe));
HANDLE connectPipe = CreateNamedPipeA(kPipeNameConnect, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
TEST_CHECK(connectPipe != INVALID_HANDLE_VALUE);
struct ConnectThreadArgs connectArgs = {
.pipeName = kPipeNameConnect,
.message = "hello",
.delayMs = 50,
.client = INVALID_HANDLE_VALUE,
.error = ERROR_SUCCESS,
};
HANDLE connectThread = CreateThread(NULL, 0, client_connect_thread, &connectArgs, 0, NULL);
TEST_CHECK(connectThread != NULL);
TEST_CHECK(ConnectNamedPipe(connectPipe, NULL));
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(connectThread, 5000));
TEST_CHECK(CloseHandle(connectThread));
TEST_CHECK(connectArgs.client != INVALID_HANDLE_VALUE);
TEST_CHECK_EQ(ERROR_SUCCESS, connectArgs.error);
read_checked(connectPipe, "hello");
write_checked(connectPipe, "world");
read_checked(connectArgs.client, "world");
SetLastError(0xdeadbeefu);
TEST_CHECK(!ConnectNamedPipe(connectPipe, NULL));
TEST_CHECK_EQ(ERROR_PIPE_CONNECTED, GetLastError());
TEST_CHECK(CloseHandle(connectArgs.client));
TEST_CHECK(CloseHandle(connectPipe));
HANDLE nowaitPipe = CreateNamedPipeA(kPipeNameNowait, PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 1024, 1024, 0, NULL);
TEST_CHECK(nowaitPipe != INVALID_HANDLE_VALUE);
SetLastError(0xdeadbeefu);
TEST_CHECK(!ConnectNamedPipe(nowaitPipe, NULL));
TEST_CHECK_EQ(ERROR_PIPE_LISTENING, GetLastError());
TEST_CHECK(CloseHandle(nowaitPipe));
HANDLE overlappedPipe = CreateNamedPipeA(kPipeNameOverlapped, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, NULL);
TEST_CHECK(overlappedPipe != INVALID_HANDLE_VALUE);
struct ConnectThreadArgs overlappedArgs = {
.pipeName = kPipeNameOverlapped,
.message = NULL,
.delayMs = 50,
.client = INVALID_HANDLE_VALUE,
.error = ERROR_SUCCESS,
};
OVERLAPPED ov = {0};
ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
TEST_CHECK(ov.hEvent != NULL);
HANDLE overlappedThread = CreateThread(NULL, 0, client_connect_thread, &overlappedArgs, 0, NULL);
TEST_CHECK(overlappedThread != NULL);
SetLastError(0xdeadbeefu);
TEST_CHECK(!ConnectNamedPipe(overlappedPipe, &ov));
TEST_CHECK_EQ(ERROR_IO_PENDING, GetLastError());
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(overlappedThread, 5000));
TEST_CHECK(CloseHandle(overlappedThread));
TEST_CHECK(overlappedArgs.client != INVALID_HANDLE_VALUE);
TEST_CHECK_EQ(ERROR_SUCCESS, overlappedArgs.error);
TEST_CHECK_EQ(WAIT_OBJECT_0, WaitForSingleObject(ov.hEvent, 5000));
DWORD transferred = 0;
TEST_CHECK(GetOverlappedResult(overlappedPipe, &ov, &transferred, FALSE));
TEST_CHECK_EQ((DWORD)0, transferred);
TEST_CHECK(CloseHandle(overlappedArgs.client));
TEST_CHECK(CloseHandle(ov.hEvent));
TEST_CHECK(CloseHandle(overlappedPipe));
return 0;
}