[PATCH v3 2/5] ws2_32/tests: Rewrite test_events().

Zebediah Figura z.figura12 at gmail.com
Fri Apr 30 11:38:06 CDT 2021


Separate the tests by event type, make them much more exhaustive, and use a
simpler method of validating event sequences.

Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/ws2_32/tests/sock.c | 1193 ++++++++++++++++++++------------------
 1 file changed, 639 insertions(+), 554 deletions(-)

diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 9cf271d0fea..9c074811a8f 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -3904,628 +3904,714 @@ end:
     HeapFree(GetProcessHeap(), 0, buffer);
 }
 
-typedef struct async_message
-{
-    SOCKET socket;
-    LPARAM lparam;
-    struct async_message *next;
-} async_message;
-
-static struct async_message *messages_received;
-
 #define WM_SOCKET (WM_USER+100)
-static LRESULT CALLBACK ws2_test_WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+
+struct event_test_ctx
 {
-    struct async_message *message;
+    int is_message;
+    SOCKET socket;
+    HANDLE event;
+    HWND window;
+};
 
-    switch (msg)
-    {
-    case WM_SOCKET:
-        message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
-        message->socket = (SOCKET) wparam;
-        message->lparam = lparam;
-        message->next = NULL;
-
-        if (messages_received)
-        {
-            struct async_message *last = messages_received;
-            while (last->next) last = last->next;
-            last->next = message;
-        }
-        else
-            messages_received = message;
-        return 0;
-    }
-
-    return DefWindowProcA(hwnd, msg, wparam, lparam);
-}
-
-static void get_event_details(int event, int *bit, char *name)
+static void select_events(struct event_test_ctx *ctx, SOCKET socket, LONG events)
 {
-    switch (event)
-    {
-        case FD_ACCEPT:
-            if (bit) *bit = FD_ACCEPT_BIT;
-            if (name) strcpy(name, "FD_ACCEPT");
-            break;
-        case FD_CONNECT:
-            if (bit) *bit = FD_CONNECT_BIT;
-            if (name) strcpy(name, "FD_CONNECT");
-            break;
-        case FD_READ:
-            if (bit) *bit = FD_READ_BIT;
-            if (name) strcpy(name, "FD_READ");
-            break;
-        case FD_OOB:
-            if (bit) *bit = FD_OOB_BIT;
-            if (name) strcpy(name, "FD_OOB");
-            break;
-        case FD_WRITE:
-            if (bit) *bit = FD_WRITE_BIT;
-            if (name) strcpy(name, "FD_WRITE");
-            break;
-        case FD_CLOSE:
-            if (bit) *bit = FD_CLOSE_BIT;
-            if (name) strcpy(name, "FD_CLOSE");
-            break;
-        default:
-            if (bit) *bit = -1;
-            if (name) sprintf(name, "bad%x", event);
-    }
-}
-
-static char *dbgstr_event_seq_result(SOCKET s, WSANETWORKEVENTS *netEvents)
-{
-    static char message[1024];
-    struct async_message *curr = messages_received;
-    int index, error, bit = 0;
-    char name[12];
-    int len = 1;
-
-    message[0] = '[';
-    message[1] = 0;
-    while (1)
-    {
-        if (netEvents)
-        {
-            if (bit >= FD_MAX_EVENTS) break;
-            if ( !(netEvents->lNetworkEvents & (1 << bit)) )
-            {
-                bit++;
-                continue;
-            }
-            get_event_details(1 << bit, &index, name);
-            error = netEvents->iErrorCode[index];
-            bit++;
-        }
-        else
-        {
-            if (!curr) break;
-            if (curr->socket != s)
-            {
-                curr = curr->next;
-                continue;
-            }
-            get_event_details(WSAGETSELECTEVENT(curr->lparam), NULL, name);
-            error = WSAGETSELECTERROR(curr->lparam);
-            curr = curr->next;
-        }
-
-        len += sprintf(message + len, "%s(%d) ", name, error);
-    }
-    if (len > 1) len--;
-    strcpy( message + len, "]" );
-    return message;
-}
-
-static void flush_events(SOCKET s, HANDLE hEvent)
-{
-    WSANETWORKEVENTS netEvents;
-    struct async_message *prev = NULL, *curr = messages_received;
     int ret;
-    DWORD dwRet;
 
-    if (hEvent != INVALID_HANDLE_VALUE)
-    {
-        dwRet = WaitForSingleObject(hEvent, 100);
-        if (dwRet == WAIT_OBJECT_0)
-        {
-            ret = WSAEnumNetworkEvents(s, hEvent, &netEvents);
-            ok(!ret, "failed to get network events, error %u\n", WSAGetLastError());
-        }
-    }
+    if (ctx->is_message)
+        ret = WSAAsyncSelect(socket, ctx->window, WM_USER, events);
     else
-    {
-        while (curr)
-        {
-            if (curr->socket == s)
-            {
-                if (prev) prev->next = curr->next;
-                else messages_received = curr->next;
-
-                HeapFree(GetProcessHeap(), 0, curr);
-
-                if (prev) curr = prev->next;
-                else curr = messages_received;
-            }
-            else
-            {
-                prev = curr;
-                curr = curr->next;
-            }
-        }
-    }
+        ret = WSAEventSelect(socket, ctx->event, events);
+    ok(!ret, "failed to select, error %u\n", WSAGetLastError());
+    ctx->socket = socket;
 }
 
-static int match_event_sequence(SOCKET s, WSANETWORKEVENTS *netEvents, const LPARAM *seq)
+#define check_events(a, b, c, d) check_events_(__LINE__, a, b, c, d, FALSE, FALSE)
+#define check_events_todo(a, b, c, d) check_events_(__LINE__, a, b, c, d, TRUE, TRUE)
+#define check_events_todo_event(a, b, c, d) check_events_(__LINE__, a, b, c, d, TRUE, FALSE)
+#define check_events_todo_msg(a, b, c, d) check_events_(__LINE__, a, b, c, d, FALSE, TRUE)
+static void check_events_(int line, struct event_test_ctx *ctx,
+        LONG flag1, LONG flag2, DWORD timeout, BOOL todo_event, BOOL todo_msg)
 {
-    int event, index, error, events;
-    struct async_message *curr;
-
-    if (netEvents)
-    {
-        events = netEvents->lNetworkEvents;
-        while (*seq)
-        {
-            event = WSAGETSELECTEVENT(*seq);
-            error = WSAGETSELECTERROR(*seq);
-            get_event_details(event, &index, NULL);
-
-            if (!(events & event) && index != -1)
-                return 0;
-            if (events & event && index != -1)
-            {
-                if (netEvents->iErrorCode[index] != error)
-                    return 0;
-            }
-            events &= ~event;
-            seq++;
-        }
-        if (events)
-            return 0;
-    }
-    else
-    {
-        curr = messages_received;
-        while (curr)
-        {
-            if (curr->socket == s)
-            {
-                if (!*seq) return 0;
-                if (*seq != curr->lparam) return 0;
-                seq++;
-            }
-            curr = curr->next;
-        }
-        if (*seq)
-            return 0;
-    }
-    return 1;
-}
-
-/* checks for a sequence of events, (order only checked if window is used) */
-static void ok_event_sequence(SOCKET s, HANDLE hEvent, const LPARAM *seq)
-{
-    MSG msg;
-    WSANETWORKEVENTS events, *netEvents = NULL;
     int ret;
-    DWORD dwRet;
 
-    if (hEvent != INVALID_HANDLE_VALUE)
+    if (ctx->is_message)
     {
-        netEvents = &events;
+        BOOL any_fail = FALSE;
+        MSG msg;
 
-        dwRet = WaitForSingleObject(hEvent, 200);
-        if (dwRet == WAIT_OBJECT_0)
+        if (flag1)
         {
-            ret = WSAEnumNetworkEvents(s, hEvent, netEvents);
+            ret = PeekMessageA(&msg, ctx->window, WM_USER, WM_USER, PM_REMOVE);
+            while (!ret && !MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_POSTMESSAGE))
+                ret = PeekMessageA(&msg, ctx->window, WM_USER, WM_USER, PM_REMOVE);
+            todo_wine_if (todo_msg && !ret) ok_(__FILE__, line)(ret, "expected a message\n");
             if (ret)
             {
-                winetest_ok(0, "WSAEnumNetworkEvents failed, error %d\n", ret);
-                return;
+                ok_(__FILE__, line)(msg.wParam == ctx->socket,
+                        "expected wparam %#Ix, got %#Ix\n", ctx->socket, msg.wParam);
+                todo_wine_if (todo_msg && msg.lParam != flag1)
+                    ok_(__FILE__, line)(msg.lParam == flag1, "got first event %#Ix\n", msg.lParam);
+                if (msg.lParam != flag1) any_fail = TRUE;
             }
+            else
+                any_fail = TRUE;
         }
-        else
-            memset(netEvents, 0, sizeof(*netEvents));
+        if (flag2)
+        {
+            ret = PeekMessageA(&msg, ctx->window, WM_USER, WM_USER, PM_REMOVE);
+            while (!ret && !MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_POSTMESSAGE))
+                ret = PeekMessageA(&msg, ctx->window, WM_USER, WM_USER, PM_REMOVE);
+            ok_(__FILE__, line)(ret, "expected a message\n");
+            ok_(__FILE__, line)(msg.wParam == ctx->socket, "got wparam %#Ix\n", msg.wParam);
+            todo_wine_if (todo_msg) ok_(__FILE__, line)(msg.lParam == flag2, "got second event %#Ix\n", msg.lParam);
+        }
+        ret = PeekMessageA(&msg, ctx->window, WM_USER, WM_USER, PM_REMOVE);
+        todo_wine_if (todo_msg && ret) ok_(__FILE__, line)(!ret, "got unexpected event %#Ix\n", msg.lParam);
+        if (ret) any_fail = TRUE;
+
+        /* catch tests which succeed */
+        todo_wine_if (todo_msg) ok_(__FILE__, line)(!any_fail, "event series matches\n");
     }
     else
     {
-        Sleep(200);
-        /* Run the message loop a little */
-        while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE ))
-        {
-            DispatchMessageA(&msg);
-        }
-    }
+        WSANETWORKEVENTS events;
 
-    winetest_ok(match_event_sequence(s, netEvents, seq), "Got sequence %s\n", dbgstr_event_seq_result(s, netEvents));
-    flush_events(s, hEvent);
+        ret = WaitForSingleObject(ctx->event, timeout);
+        if (flag1 | flag2)
+            todo_wine_if (todo_event && ret) ok_(__FILE__, line)(!ret, "event wait timed out\n");
+        else
+            todo_wine_if (todo_event) ok_(__FILE__, line)(ret == WAIT_TIMEOUT, "expected timeout\n");
+        ret = WSAEnumNetworkEvents(ctx->socket, ctx->event, &events);
+        ok_(__FILE__, line)(!ret, "failed to get events, error %u\n", WSAGetLastError());
+        todo_wine_if (todo_event)
+            ok_(__FILE__, line)(events.lNetworkEvents == (flag1 | flag2), "got events %#x\n", events.lNetworkEvents);
+    }
 }
 
-#define ok_event_seq (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : ok_event_sequence
-
-static void test_events(int useMessages)
+static void test_accept_events(struct event_test_ctx *ctx)
 {
-    SOCKET server = INVALID_SOCKET;
-    SOCKET src = INVALID_SOCKET, src2 = INVALID_SOCKET;
-    SOCKET dst = INVALID_SOCKET, dst2 = INVALID_SOCKET;
-    struct sockaddr_in addr;
-    HANDLE hThread = NULL;
-    HANDLE hEvent = INVALID_HANDLE_VALUE, hEvent2 = INVALID_HANDLE_VALUE;
-    WNDCLASSEXA wndclass;
-    HWND hWnd = NULL;
-    char *buffer = NULL;
-    int bufferSize = 1024*1024;
-    WSABUF bufs;
-    OVERLAPPED ov, ov2;
-    DWORD flags = 0;
-    DWORD bytesReturned;
-    DWORD id;
-    int len;
-    int ret;
-    DWORD dwRet;
-    BOOL bret;
-    static char szClassName[] = "wstestclass";
-    static const LPARAM empty_seq[] = { 0 };
-    static const LPARAM write_seq[] = { WSAMAKESELECTREPLY(FD_WRITE, 0), 0 };
-    static const LPARAM read_seq[] = { WSAMAKESELECTREPLY(FD_READ, 0), 0 };
-    static const LPARAM oob_seq[] = { WSAMAKESELECTREPLY(FD_OOB, 0), 0 };
-    static const LPARAM connect_seq[] = { WSAMAKESELECTREPLY(FD_CONNECT, 0),
-                                          WSAMAKESELECTREPLY(FD_WRITE, 0), 0 };
-    static const LPARAM read_close_seq[] = { WSAMAKESELECTREPLY(FD_READ, 0),
-                                             WSAMAKESELECTREPLY(FD_CLOSE, 0), 0 };
+    const struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
+    SOCKET listener, server, client, client2;
+    struct sockaddr_in destaddr;
+    int len, ret;
 
-    memset(&ov, 0, sizeof(ov));
-    memset(&ov2, 0, sizeof(ov2));
+    listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(listener != -1, "failed to create socket, error %u\n", WSAGetLastError());
 
-    /* don't use socketpair, we want connection event */
-    src = socket(AF_INET, SOCK_STREAM, 0);
-    ok(src != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError());
+    select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
 
-    ret = set_blocking(src, TRUE);
-    ok(!ret, "set_blocking failed, error %d\n", WSAGetLastError());
-
-    src2 = socket(AF_INET, SOCK_STREAM, 0);
-    ok(src2 != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError());
-
-    ret = set_blocking(src2, TRUE);
-    ok(!ret, "set_blocking failed, error %d\n", WSAGetLastError());
-
-    len = sizeof(BOOL);
-    ret = getsockopt(src, SOL_SOCKET, SO_OOBINLINE, (void *)&bret, &len);
-    ok(!ret, "failed to get address, error %u\n", WSAGetLastError());
-    ok(bret == FALSE, "OOB not inline\n");
-
-    if (useMessages)
-    {
-        wndclass.cbSize         = sizeof(wndclass);
-        wndclass.style          = CS_HREDRAW | CS_VREDRAW;
-        wndclass.lpfnWndProc    = ws2_test_WndProc;
-        wndclass.cbClsExtra     = 0;
-        wndclass.cbWndExtra     = 0;
-        wndclass.hInstance      = GetModuleHandleA(NULL);
-        wndclass.hIcon          = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
-        wndclass.hIconSm        = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION);
-        wndclass.hCursor        = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
-        wndclass.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1);
-        wndclass.lpszClassName  = szClassName;
-        wndclass.lpszMenuName   = NULL;
-        RegisterClassExA(&wndclass);
-
-        hWnd = CreateWindowA(szClassName, "WS2Test", WS_OVERLAPPEDWINDOW,
-                            0, 0, 500, 500, NULL, NULL, GetModuleHandleA(NULL), NULL);
-        ok(!!hWnd, "failed to create window\n");
-
-        ret = WSAAsyncSelect(src, hWnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_OOB | FD_WRITE | FD_CLOSE);
-        ok(!ret, "got error %u\n", WSAGetLastError());
-
-        ok(set_blocking(src, TRUE) == SOCKET_ERROR, "set_blocking should failed!\n");
-        ok(WSAGetLastError() == WSAEINVAL, "expect WSAEINVAL, returned %x\n", WSAGetLastError());
-
-        ret = WSAAsyncSelect(src2, hWnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_OOB | FD_WRITE | FD_CLOSE);
-        ok(!ret, "got error %u\n", WSAGetLastError());
-
-        ok(set_blocking(src2, TRUE) == SOCKET_ERROR, "set_blocking should failed!\n");
-        ok(WSAGetLastError() == WSAEINVAL, "expect WSAEINVAL, returned %x\n", WSAGetLastError());
-    }
-    else
-    {
-        hEvent = WSACreateEvent();
-        ok(hEvent != WSA_INVALID_EVENT, "failed to create event, error %u\n", WSAGetLastError());
-
-        hEvent2 = WSACreateEvent();
-        ok(hEvent2 != WSA_INVALID_EVENT, "failed to create event, error %u\n", WSAGetLastError());
-
-        ret = WSAEventSelect(src, hEvent, FD_CONNECT | FD_READ | FD_OOB | FD_WRITE | FD_CLOSE);
-        ok(!ret, "got error %u\n", WSAGetLastError());
-
-        ok(set_blocking(src, TRUE) == SOCKET_ERROR, "set_blocking should failed!\n");
-        ok(WSAGetLastError() == WSAEINVAL, "expect WSAEINVAL, returned %x\n", WSAGetLastError());
-
-        ret = WSAEventSelect(src2, hEvent2, FD_CONNECT | FD_READ | FD_OOB | FD_WRITE | FD_CLOSE);
-        ok(!ret, "got error %u\n", WSAGetLastError());
-
-        ok(set_blocking(src2, TRUE) == SOCKET_ERROR, "set_blocking should failed!\n");
-        ok(WSAGetLastError() == WSAEINVAL, "expect WSAEINVAL, returned %x\n", WSAGetLastError());
-    }
-
-    server = socket(AF_INET, SOCK_STREAM, 0);
-    ok(server != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError());
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin_family = AF_INET;
-    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
-    ret = bind(server, (struct sockaddr*)&addr, sizeof(addr));
+    ret = bind(listener, (const struct sockaddr *)&addr, sizeof(addr));
     ok(!ret, "failed to bind, error %u\n", WSAGetLastError());
-
-    len = sizeof(addr);
-    ret = getsockname(server, (struct sockaddr*)&addr, &len);
+    len = sizeof(destaddr);
+    ret = getsockname(listener, (struct sockaddr *)&destaddr, &len);
     ok(!ret, "failed to get address, error %u\n", WSAGetLastError());
-
-    ret = listen(server, 2);
+    ret = listen(listener, 2);
     ok(!ret, "failed to listen, error %u\n", WSAGetLastError());
 
-    SetLastError(0xdeadbeef);
-    ret = connect(src, NULL, 0);
-    ok(ret == SOCKET_ERROR, "expected -1, got %d\n", ret);
-    ok(GetLastError() == WSAEFAULT, "expected 10014, got %d\n", GetLastError());
+    check_events(ctx, 0, 0, 0);
 
-    ret = connect(src, (struct sockaddr*)&addr, sizeof(addr));
-    ok(!ret || WSAGetLastError() == WSAEWOULDBLOCK, "failed to connect, error %u\n", WSAGetLastError());
+    client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client != -1, "failed to create socket, error %u\n", WSAGetLastError());
+    ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret, "failed to connect, error %u\n", WSAGetLastError());
 
-    ret = connect(src2, (struct sockaddr*)&addr, sizeof(addr));
-    ok(!ret || WSAGetLastError() == WSAEWOULDBLOCK, "failed to connect, error %u\n", WSAGetLastError());
+    check_events(ctx, FD_ACCEPT, 0, 200);
+    check_events(ctx, 0, 0, 0);
+    select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
+    if (ctx->is_message)
+        check_events(ctx, FD_ACCEPT, 0, 200);
+    check_events_todo_event(ctx, 0, 0, 0);
+    select_events(ctx, listener, 0);
+    select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
+    if (ctx->is_message)
+        check_events(ctx, FD_ACCEPT, 0, 200);
+    check_events_todo_event(ctx, 0, 0, 0);
 
-    len = sizeof(addr);
-    dst = accept(server, (struct sockaddr*)&addr, &len);
-    ok(dst != INVALID_SOCKET, "failed to accept socket, error %u\n", WSAGetLastError());
+    client2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client2 != -1, "failed to create socket, error %u\n", WSAGetLastError());
+    ret = connect(client2, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret, "failed to connect, error %u\n", WSAGetLastError());
 
-    len = sizeof(addr);
-    dst2 = accept(server, (struct sockaddr*)&addr, &len);
-    ok(dst2 != INVALID_SOCKET, "failed to accept socket, error %u\n", WSAGetLastError());
+    if (!ctx->is_message)
+        check_events_todo(ctx, FD_ACCEPT, 0, 200);
+    check_events(ctx, 0, 0, 0);
+
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
+    closesocket(server);
+
+    check_events(ctx, FD_ACCEPT, 0, 200);
+    check_events(ctx, 0, 0, 0);
+
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
+    closesocket(server);
+
+    check_events(ctx, 0, 0, 0);
+
+    closesocket(client2);
+    closesocket(client);
+
+    client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client != -1, "failed to create socket, error %u\n", WSAGetLastError());
+    ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret, "failed to connect, error %u\n", WSAGetLastError());
+
+    check_events(ctx, FD_ACCEPT, 0, 200);
+
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
+    closesocket(server);
+    closesocket(client);
+
+    check_events(ctx, 0, 0, 200);
+
+    closesocket(listener);
+
+    /* Connect and then select. */
+
+    listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(listener != -1, "failed to create socket, error %u\n", WSAGetLastError());
+    ret = bind(listener, (const struct sockaddr *)&addr, sizeof(addr));
+    ok(!ret, "failed to bind, error %u\n", WSAGetLastError());
+    len = sizeof(destaddr);
+    ret = getsockname(listener, (struct sockaddr *)&destaddr, &len);
+    ok(!ret, "failed to get address, error %u\n", WSAGetLastError());
+    ret = listen(listener, 2);
+    ok(!ret, "failed to listen, error %u\n", WSAGetLastError());
+
+    client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client != -1, "failed to create socket, error %u\n", WSAGetLastError());
+    ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret, "failed to connect, error %u\n", WSAGetLastError());
+
+    select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
+
+    check_events(ctx, FD_ACCEPT, 0, 200);
+
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
+    closesocket(server);
+    closesocket(client);
+
+    /* As above, but select on a subset not containing FD_ACCEPT first. */
+
+    select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB);
+
+    client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client != -1, "failed to create socket, error %u\n", WSAGetLastError());
+    ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret, "failed to connect, error %u\n", WSAGetLastError());
+
+    select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
+    check_events(ctx, FD_ACCEPT, 0, 200);
+
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
+    closesocket(server);
+    closesocket(client);
+
+    /* As above, but call accept() before selecting. */
+
+    select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB);
+
+    client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client != -1, "failed to create socket, error %u\n", WSAGetLastError());
+    ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret, "failed to connect, error %u\n", WSAGetLastError());
+    Sleep(200);
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
+
+    select_events(ctx, listener, FD_CONNECT | FD_READ | FD_OOB | FD_ACCEPT);
+    check_events(ctx, 0, 0, 200);
 
     closesocket(server);
-    server = INVALID_SOCKET;
+    closesocket(client);
 
-    /* On Windows it seems when a non-blocking socket sends to a
-       blocking socket on the same host, the send() is BLOCKING,
-       so make both sockets non-blocking. src is already non-blocking
-       from the async select */
+    closesocket(listener);
+}
 
-    ret = set_blocking(dst, FALSE);
-    ok(!ret, "failed to set nonblocking, error %u\n", WSAGetLastError());
+static void test_connect_events(struct event_test_ctx *ctx)
+{
+    const struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
+    SOCKET listener, server, client;
+    struct sockaddr_in destaddr;
+    int len, ret;
 
-    buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufferSize);
-    ov.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
-    ov2.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+    listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(listener != -1, "failed to create socket, error %u\n", WSAGetLastError());
+    ret = bind(listener, (const struct sockaddr *)&addr, sizeof(addr));
+    ok(!ret, "failed to bind, error %u\n", WSAGetLastError());
+    len = sizeof(destaddr);
+    ret = getsockname(listener, (struct sockaddr *)&destaddr, &len);
+    ok(!ret, "failed to get address, error %u\n", WSAGetLastError());
+    ret = listen(listener, 2);
+    ok(!ret, "failed to listen, error %u\n", WSAGetLastError());
 
-    /* FD_WRITE should be set initially, and allow us to send at least 1 byte */
-    ok_event_seq(src, hEvent, connect_seq);
-    ok_event_seq(src2, hEvent2, connect_seq);
+    client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client != -1, "failed to create socket, error %u\n", WSAGetLastError());
 
-    /* Test simple send/recv */
-    SetLastError(0xdeadbeef);
-    ret = send(dst, buffer, 100, 0);
-    ok(ret == 100, "Failed to send buffer %d err %d\n", ret, GetLastError());
-    ok(GetLastError() == ERROR_SUCCESS, "Expected 0, got %d\n", GetLastError());
-    ok_event_seq(src, hEvent, read_seq);
+    select_events(ctx, client, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
+    check_events(ctx, 0, 0, 0);
 
-    SetLastError(0xdeadbeef);
-    ret = recv(src, buffer, 1, MSG_PEEK);
-    ok(ret == 1, "Failed to peek at recv buffer %d err %d\n", ret, GetLastError());
-    ok(GetLastError() == ERROR_SUCCESS, "Expected 0, got %d\n", GetLastError());
-    ok_event_seq(src, hEvent, read_seq);
+    ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret || WSAGetLastError() == WSAEWOULDBLOCK, "failed to connect, error %u\n", WSAGetLastError());
 
-    SetLastError(0xdeadbeef);
-    ret = recv(src, buffer, 50, 0);
-    ok(ret == 50, "Failed to recv buffer %d err %d\n", ret, GetLastError());
-    ok(GetLastError() == ERROR_SUCCESS, "Expected 0, got %d\n", GetLastError());
-    ok_event_seq(src, hEvent, read_seq);
+    check_events(ctx, FD_CONNECT, FD_WRITE, 200);
+    check_events(ctx, 0, 0, 0);
+    select_events(ctx, client, 0);
+    select_events(ctx, client, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
+    if (ctx->is_message)
+        check_events(ctx, FD_WRITE, 0, 200);
+    check_events_todo_event(ctx, 0, 0, 0);
 
-    ret = recv(src, buffer, 50, 0);
-    ok(ret == 50, "Failed to recv buffer %d err %d\n", ret, GetLastError());
-    ok_event_seq(src, hEvent, empty_seq);
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
 
-    /* fun fact - events are re-enabled even on failure, but only for messages */
-    ret = send(dst, "1", 1, 0);
-    ok(ret == 1, "Failed to send buffer %d err %d\n", ret, GetLastError());
-    ok_event_seq(src, hEvent, read_seq);
+    closesocket(client);
+    closesocket(server);
 
-    ret = recv(src, buffer, -1, 0);
-    ok(ret == SOCKET_ERROR, "expected failure\n");
-    ok(WSAGetLastError() == WSAEFAULT || WSAGetLastError() == WSAENOBUFS /* < 7 */,
-            "got error %u\n", WSAGetLastError());
-    if (useMessages)
-        todo_wine ok_event_seq(src, hEvent, read_seq);
+    /* Connect and then select. */
+
+    client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client != -1, "failed to create socket, error %u\n", WSAGetLastError());
+
+    ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret, "failed to connect, error %u\n", WSAGetLastError());
+
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
+
+    ret = send(client, "data", 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+
+    select_events(ctx, client, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
+    if (ctx->is_message)
+        check_events(ctx, FD_WRITE, 0, 200);
     else
-        ok_event_seq(src, hEvent, empty_seq);
+        check_events_todo(ctx, FD_CONNECT, FD_WRITE, 200);
 
-    SetLastError(0xdeadbeef);
-    ret = recv(src, buffer, 1, 0);
-    ok(ret == 1, "Failed to recv buffer %d err %d\n", ret, GetLastError());
-    ok(GetLastError() == ERROR_SUCCESS, "Expected 0, got %d\n", GetLastError());
-    ok_event_seq(src, hEvent, empty_seq);
+    closesocket(client);
+    closesocket(server);
 
-    /* Interaction with overlapped */
-    bufs.len = sizeof(char);
-    bufs.buf = buffer;
-    ret = WSARecv(src, &bufs, 1, &bytesReturned, &flags, &ov, NULL);
-    ok(ret == SOCKET_ERROR && GetLastError() == ERROR_IO_PENDING,
-       "WSARecv failed - %d error %d\n", ret, GetLastError());
+    /* As above, but select on a subset not containing FD_CONNECT first. */
 
-    bufs.len = sizeof(char);
-    bufs.buf = buffer+1;
-    ret = WSARecv(src, &bufs, 1, &bytesReturned, &flags, &ov2, NULL);
-    ok(ret == SOCKET_ERROR && GetLastError() == ERROR_IO_PENDING,
-       "WSARecv failed - %d error %d\n", ret, GetLastError());
+    client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(client != -1, "failed to create socket, error %u\n", WSAGetLastError());
 
-    ret = send(dst, "12", 2, 0);
-    ok(ret == 2, "Failed to send buffer %d err %d\n", ret, GetLastError());
-    ok_event_seq(src, hEvent, empty_seq);
+    select_events(ctx, client, FD_ACCEPT | FD_CLOSE | FD_OOB | FD_READ | FD_WRITE);
 
-    dwRet = WaitForSingleObject(ov.hEvent, 100);
-    ok(!dwRet, "got %u\n", dwRet);
-    bret = GetOverlappedResult((HANDLE)src, &ov, &bytesReturned, FALSE);
-    ok(bret, "got error %u\n", GetLastError());
-    ok(bytesReturned == 1, "got %u bytes\n", bytesReturned);
-    ok(buffer[0] == '1', "Got %c instead of 2\n", buffer[0]);
+    ret = connect(client, (struct sockaddr *)&destaddr, sizeof(destaddr));
+    ok(!ret || WSAGetLastError() == WSAEWOULDBLOCK, "failed to connect, error %u\n", WSAGetLastError());
 
-    dwRet = WaitForSingleObject(ov2.hEvent, 100);
-    ok(!dwRet, "got %u\n", dwRet);
-    bret = GetOverlappedResult((HANDLE)src, &ov2, &bytesReturned, FALSE);
-    ok(bret, "got error %u\n", GetLastError());
-    ok(bytesReturned == 1, "got %u bytes\n", bytesReturned);
-    ok(buffer[1] == '2', "Got %c instead of 2\n", buffer[0]);
+    server = accept(listener, NULL, NULL);
+    ok(server != -1, "failed to accept, error %u\n", WSAGetLastError());
 
-    SetLastError(0xdeadbeef);
-    ret = send(dst, "1", 1, 0);
-    ok(ret == 1, "Failed to send buffer %d err %d\n", ret, GetLastError());
-    ok(GetLastError() == ERROR_SUCCESS, "Expected 0, got %d\n", GetLastError());
-    ok_event_seq(src, hEvent, read_seq);
+    check_events_todo_msg(ctx, FD_WRITE, 0, 200);
 
-    ret = recv(src, buffer, 1, 0);
-    ok(ret == 1, "Failed to empty buffer: %d - %d\n", ret, GetLastError());
-    ok_event_seq(src, hEvent, empty_seq);
+    select_events(ctx, client, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
 
-    /* Notifications are delivered as soon as possible, blocked only on
-     * async requests on the same type */
-    bufs.len = sizeof(char);
-    bufs.buf = buffer;
-    ret = WSARecv(src, &bufs, 1, &bytesReturned, &flags, &ov, NULL);
-    ok(ret == SOCKET_ERROR && GetLastError() == ERROR_IO_PENDING,
-       "WSARecv failed - %d error %d\n", ret, GetLastError());
+    if (ctx->is_message)
+        check_events(ctx, FD_WRITE, 0, 200);
+    else
+        check_events_todo(ctx, FD_CONNECT, 0, 200);
 
-    ret = send(dst, "1", 1, MSG_OOB);
-    ok(ret == 1, "Failed to send buffer %d err %d\n", ret, GetLastError());
-    todo_wine ok_event_seq(src, hEvent, oob_seq);
+    closesocket(client);
+    closesocket(server);
 
-    dwRet = WaitForSingleObject(ov.hEvent, 100);
-    ok(dwRet == WAIT_TIMEOUT, "OOB message activated read?: %d - %d\n", dwRet, GetLastError());
+    closesocket(listener);
+}
 
-    ret = send(dst, "2", 1, 0);
-    ok(ret == 1, "Failed to send buffer %d err %d\n", ret, GetLastError());
-    ok_event_seq(src, hEvent, empty_seq);
+/* perform a blocking recv() even on a nonblocking socket */
+static int sync_recv(SOCKET s, void *buffer, int len, DWORD flags)
+{
+    OVERLAPPED overlapped = {0};
+    WSABUF wsabuf;
+    DWORD ret_len;
+    int ret;
 
-    dwRet = WaitForSingleObject(ov.hEvent, 100);
-    ok(!dwRet, "got %u\n", dwRet);
-    bret = GetOverlappedResult((HANDLE)src, &ov, &bytesReturned, FALSE);
-    ok(bret, "got error %u\n", GetLastError());
-    ok(bytesReturned == 1, "got %u bytes\n", bytesReturned);
-    ok(buffer[0] == '2', "Got %c instead of 2\n", buffer[0]);
-
-    ret = recv(src, buffer, 1, MSG_OOB);
-    todo_wine ok(ret == 1, "Failed to empty buffer: %d - %d\n", ret, GetLastError());
-    ok_event_seq(src, hEvent, empty_seq);
-
-    /* Flood the send queue */
-    hThread = CreateThread(NULL, 0, drain_socket_thread, &dst, 0, &id);
-
-    /* Now FD_WRITE should not be set, because the socket send buffer isn't full yet */
-    ok_event_seq(src, hEvent, empty_seq);
-
-    /* Now if we send a ton of data and the 'server' does not drain it fast
-     * enough (set drain_pause to be sure), the socket send buffer will only
-     * take some of it, and we will get a short write. This will trigger
-     * another FD_WRITE event as soon as data is sent and more space becomes
-     * available, but not any earlier. */
-    drain_pause = TRUE;
-    do
+    overlapped.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+    wsabuf.buf = buffer;
+    wsabuf.len = len;
+    ret = WSARecv(s, &wsabuf, 1, &ret_len, &flags, &overlapped, NULL);
+    if (ret == -1 && WSAGetLastError() == ERROR_IO_PENDING)
     {
-        ret = send(src, buffer, bufferSize, 0);
-    } while (ret == bufferSize);
-    drain_pause = FALSE;
-    ok(ret >= 0 || WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError());
-    ok_event_seq(src, hEvent, write_seq);
-
-    /* Test how FD_CLOSE is handled */
-    ret = send(dst, "12", 2, 0);
-    ok(ret == 2, "Failed to send buffer %d err %d\n", ret, GetLastError());
-
-    /* Wait a little and let the send complete */
-    Sleep(100);
-    closesocket(dst);
-    Sleep(100);
-
-    /* We can never implement this in wine, best we can hope for is
-       sending FD_CLOSE after the reads complete */
-    todo_wine ok_event_seq(src, hEvent, read_close_seq);
-
-    ret = recv(src, buffer, 1, 0);
-    ok(ret == 1, "Failed to empty buffer: %d - %d\n", ret, GetLastError());
-    ok_event_seq(src, hEvent, read_seq);
-
-    ret = recv(src, buffer, 1, 0);
-    ok(ret == 1, "Failed to empty buffer: %d - %d\n", ret, GetLastError());
-    /* want it? it's here, but you can't have it */
-    todo_wine ok_event_seq(src, hEvent, empty_seq);
-
-    /* Test how FD_CLOSE is handled */
-    ret = send(dst2, "12", 2, 0);
-    ok(ret == 2, "Failed to send buffer %d err %d\n", ret, GetLastError());
-
-    Sleep(200);
-    shutdown(dst2, SD_SEND);
-    Sleep(200);
-
-    todo_wine ok_event_seq(src2, hEvent2, read_close_seq);
-
-    ret = recv(src2, buffer, 1, 0);
-    ok(ret == 1, "got ret %d, error %u\n", ret, WSAGetLastError());
-    ok_event_seq(src2, hEvent2, read_seq);
-
-    ret = recv(src2, buffer, 1, 0);
-    ok(ret == 1, "got ret %d, error %u\n", ret, WSAGetLastError());
-    todo_wine ok_event_seq(src2, hEvent2, empty_seq);
-
-    ret = send(src2, "1", 1, 0);
-    ok(ret == 1, "Sending to half-closed socket failed %d err %d\n", ret, GetLastError());
-    ok_event_seq(src2, hEvent2, empty_seq);
-
-    ret = send(src2, "1", 1, 0);
-    ok(ret == 1, "Sending to half-closed socket failed %d err %d\n", ret, GetLastError());
-    ok_event_seq(src2, hEvent2, empty_seq);
-
-    if (useMessages)
-    {
-        ret = WSAAsyncSelect(src, hWnd, WM_SOCKET, 0);
-        ok(!ret, "got error %u\n", WSAGetLastError());
-
-        ret = set_blocking(src, TRUE);
-        ok(!ret, "set_blocking failed, error %d\n", WSAGetLastError());
-
-        ret = WSAAsyncSelect(src2, hWnd, WM_SOCKET, 0);
-        ok(!ret, "got error %u\n", WSAGetLastError());
-
-        ret = set_blocking(src2, TRUE);
-        ok(!ret, "set_blocking failed, error %d\n", WSAGetLastError());
+        ret = WaitForSingleObject(overlapped.hEvent, 1000);
+        ok(!ret, "wait timed out\n");
+        ret = WSAGetOverlappedResult(s, &overlapped, &ret_len, FALSE, &flags);
+        ret = (ret ? 0 : -1);
     }
-    else
+    CloseHandle(overlapped.hEvent);
+    if (!ret) return ret_len;
+    return -1;
+}
+
+static void test_write_events(struct event_test_ctx *ctx)
+{
+    static const int buffer_size = 1024 * 1024;
+    SOCKET server, client;
+    char *buffer;
+    int ret;
+
+    buffer = malloc(buffer_size);
+
+    tcp_socketpair(&client, &server);
+    set_blocking(client, FALSE);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
+    check_events(ctx, FD_WRITE, 0, 200);
+    check_events(ctx, 0, 0, 0);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
+    if (ctx->is_message)
+        check_events(ctx, FD_WRITE, 0, 200);
+    check_events_todo_event(ctx, 0, 0, 0);
+    select_events(ctx, server, 0);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
+    if (ctx->is_message)
+        check_events(ctx, FD_WRITE, 0, 200);
+    check_events_todo_event(ctx, 0, 0, 0);
+
+    ret = send(server, "data", 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+
+    check_events(ctx, 0, 0, 0);
+
+    ret = sync_recv(client, buffer, buffer_size, 0);
+    ok(ret == 5, "got %d\n", ret);
+
+    check_events(ctx, 0, 0, 0);
+
+    if (!broken(1))
     {
-        ret = WSAEventSelect(src, hEvent2, 0);
-        ok(!ret, "got error %u\n", WSAGetLastError());
+        while (send(server, buffer, buffer_size, 0) == buffer_size);
+        todo_wine ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError());
 
-        ret = set_blocking(src, TRUE);
-        ok(!ret, "set_blocking failed, error %d\n", WSAGetLastError());
+        while (recv(client, buffer, buffer_size, 0) > 0);
+        ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError());
 
-        ret = WSAEventSelect(src2, hEvent2, 0);
-        ok(!ret, "got error %u\n", WSAGetLastError());
-
-        ret = set_blocking(src2, TRUE);
-        ok(!ret, "set_blocking failed, error %d\n", WSAGetLastError());
+        /* Broken on Windows versions older than win10v1607 (though sometimes
+         * works regardless, for unclear reasons. */
+        check_events(ctx, FD_WRITE, 0, 200);
+        check_events(ctx, 0, 0, 0);
+        select_events(ctx, server, 0);
+        select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
+        if (ctx->is_message)
+            check_events(ctx, FD_WRITE, 0, 200);
+        check_events_todo_event(ctx, 0, 0, 0);
     }
 
-    flush_events(src, hEvent);
-    closesocket(src);
-    flush_events(src2, hEvent2);
-    closesocket(src2);
-    HeapFree(GetProcessHeap(), 0, buffer);
-    closesocket(dst2);
-    CloseHandle(hThread);
-    DestroyWindow(hWnd);
-    CloseHandle(hEvent);
-    CloseHandle(hEvent2);
-    CloseHandle(ov.hEvent);
-    CloseHandle(ov2.hEvent);
+    closesocket(server);
+    closesocket(client);
+
+    /* Despite the documentation, and unlike FD_ACCEPT and FD_RECV, calling
+     * send() doesn't clear the FD_WRITE bit. */
+
+    tcp_socketpair(&client, &server);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CONNECT | FD_OOB | FD_READ | FD_WRITE);
+
+    ret = send(server, "data", 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+
+    check_events(ctx, FD_WRITE, 0, 200);
+
+    closesocket(server);
+    closesocket(client);
+
+    free(buffer);
+}
+
+static void test_read_events(struct event_test_ctx *ctx)
+{
+    SOCKET server, client;
+    unsigned int i;
+    char buffer[8];
+    int ret;
+
+    tcp_socketpair(&client, &server);
+    set_blocking(client, FALSE);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    check_events(ctx, 0, 0, 0);
+
+    ret = send(client, "data", 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+
+    check_events(ctx, FD_READ, 0, 200);
+    check_events(ctx, 0, 0, 0);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    if (ctx->is_message)
+        check_events(ctx, FD_READ, 0, 200);
+    check_events_todo_event(ctx, 0, 0, 0);
+    select_events(ctx, server, 0);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    if (ctx->is_message)
+        check_events(ctx, FD_READ, 0, 200);
+    check_events_todo_event(ctx, 0, 0, 0);
+
+    ret = send(client, "data", 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+
+    if (!ctx->is_message)
+        check_events_todo(ctx, FD_READ, 0, 200);
+    check_events(ctx, 0, 0, 0);
+
+    ret = recv(server, buffer, 2, 0);
+    ok(ret == 2, "got %d\n", ret);
+
+    check_events(ctx, FD_READ, 0, 200);
+    check_events(ctx, 0, 0, 0);
+
+    ret = recv(server, buffer, -1, 0);
+    ok(ret == -1, "got %d\n", ret);
+    ok(WSAGetLastError() == WSAEFAULT || WSAGetLastError() == WSAENOBUFS /* < Windows 7 */,
+             "got error %u\n", WSAGetLastError());
+
+    if (ctx->is_message)
+        check_events_todo_msg(ctx, FD_READ, 0, 200);
+    check_events(ctx, 0, 0, 0);
+
+    for (i = 0; i < 8; ++i)
+    {
+        ret = sync_recv(server, buffer, 1, 0);
+        ok(ret == 1, "got %d\n", ret);
+
+        if (i < 7)
+            check_events(ctx, FD_READ, 0, 200);
+        check_events(ctx, 0, 0, 0);
+    }
+
+    /* Send data while we're not selecting. */
+
+    select_events(ctx, server, 0);
+    ret = send(client, "data", 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+
+    check_events(ctx, FD_READ, 0, 200);
+
+    ret = recv(server, buffer, 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+
+    select_events(ctx, server, 0);
+    ret = send(client, "data", 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+    ret = sync_recv(server, buffer, 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+    select_events(ctx, server, FD_ACCEPT | FD_CONNECT | FD_OOB | FD_READ);
+
+    check_events(ctx, 0, 0, 200);
+
+    closesocket(server);
+    closesocket(client);
+}
+
+static void test_oob_events(struct event_test_ctx *ctx)
+{
+    SOCKET server, client;
+    char buffer[1];
+    int ret;
+
+    tcp_socketpair(&client, &server);
+    set_blocking(client, FALSE);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    check_events(ctx, 0, 0, 0);
+
+    ret = send(client, "a", 1, MSG_OOB);
+    ok(ret == 1, "got %d\n", ret);
+
+    check_events_todo_msg(ctx, FD_OOB, 0, 200);
+    check_events_todo(ctx, 0, 0, 0);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    if (ctx->is_message)
+        check_events_todo_msg(ctx, FD_OOB, 0, 200);
+    check_events_todo(ctx, 0, 0, 0);
+    select_events(ctx, server, 0);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    if (ctx->is_message)
+        check_events_todo_msg(ctx, FD_OOB, 0, 200);
+    check_events_todo(ctx, 0, 0, 0);
+
+    ret = send(client, "b", 1, MSG_OOB);
+    ok(ret == 1, "got %d\n", ret);
+
+    if (!ctx->is_message)
+        check_events(ctx, FD_OOB, 0, 200);
+    check_events_todo(ctx, 0, 0, 0);
+
+    ret = recv(server, buffer, 1, MSG_OOB);
+    ok(ret == 1, "got %d\n", ret);
+
+    check_events_todo_msg(ctx, FD_OOB, 0, 200);
+    check_events_todo_msg(ctx, 0, 0, 0);
+
+    ret = recv(server, buffer, 1, MSG_OOB);
+    todo_wine ok(ret == 1, "got %d\n", ret);
+
+    check_events_todo_msg(ctx, 0, 0, 0);
+
+    /* Send data while we're not selecting. */
+
+    select_events(ctx, server, 0);
+    ret = send(client, "a", 1, MSG_OOB);
+    ok(ret == 1, "got %d\n", ret);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+
+    check_events_todo_msg(ctx, FD_OOB, 0, 200);
+
+    ret = recv(server, buffer, 1, MSG_OOB);
+    ok(ret == 1, "got %d\n", ret);
+
+    closesocket(server);
+    closesocket(client);
+}
+
+static void test_close_events(struct event_test_ctx *ctx)
+{
+    SOCKET server, client;
+    char buffer[5];
+    int ret;
+
+    /* Test closesocket(). */
+
+    tcp_socketpair(&client, &server);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+
+    closesocket(client);
+
+    check_events(ctx, FD_CLOSE, 0, 200);
+    check_events(ctx, 0, 0, 0);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    if (ctx->is_message)
+        check_events_todo_msg(ctx, FD_CLOSE, 0, 200);
+    check_events(ctx, 0, 0, 0);
+    select_events(ctx, server, 0);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    if (ctx->is_message)
+        check_events_todo_msg(ctx, FD_CLOSE, 0, 200);
+    check_events(ctx, 0, 0, 0);
+
+    closesocket(server);
+
+    /* Test shutdown(remote end, SD_SEND). */
+
+    tcp_socketpair(&client, &server);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+
+    shutdown(client, SD_SEND);
+
+    check_events(ctx, FD_CLOSE, 0, 200);
+    check_events(ctx, 0, 0, 0);
+
+    closesocket(client);
+
+    check_events(ctx, 0, 0, 0);
+
+    closesocket(server);
+
+    /* No other shutdown() call generates an event. */
+
+    tcp_socketpair(&client, &server);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+
+    shutdown(client, SD_RECEIVE);
+    shutdown(server, SD_BOTH);
+
+    check_events(ctx, 0, 0, 200);
+
+    shutdown(client, SD_SEND);
+
+    check_events_todo(ctx, FD_CLOSE, 0, 200);
+    check_events(ctx, 0, 0, 0);
+
+    closesocket(server);
+    closesocket(client);
+
+    /* Test sending data before calling closesocket(). */
+
+    tcp_socketpair(&client, &server);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+
+    ret = send(client, "data", 5, 0);
+    ok(ret == 5, "got %d\n", ret);
+
+    check_events(ctx, FD_READ, 0, 200);
+
+    closesocket(client);
+
+    check_events_todo(ctx, FD_CLOSE, 0, 200);
+
+    ret = recv(server, buffer, 3, 0);
+    ok(ret == 3, "got %d\n", ret);
+
+    check_events(ctx, FD_READ, 0, 200);
+
+    ret = recv(server, buffer, 5, 0);
+    ok(ret == 2, "got %d\n", ret);
+
+    check_events_todo(ctx, 0, 0, 0);
+
+    closesocket(server);
+
+    /* Close and then select. */
+
+    tcp_socketpair(&client, &server);
+    closesocket(client);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    check_events(ctx, FD_CLOSE, 0, 200);
+
+    closesocket(server);
+
+    /* As above, but select on a subset not containing FD_CLOSE first. */
+
+    tcp_socketpair(&client, &server);
+
+    select_events(ctx, server, FD_ACCEPT | FD_CONNECT | FD_OOB | FD_READ);
+
+    closesocket(client);
+
+    check_events(ctx, 0, 0, 200);
+    select_events(ctx, server, FD_ACCEPT | FD_CLOSE | FD_CONNECT | FD_OOB | FD_READ);
+    check_events_todo_event(ctx, FD_CLOSE, 0, 200);
+
+    closesocket(server);
+}
+
+static void test_events(void)
+{
+    struct event_test_ctx ctx;
+
+    ctx.is_message = FALSE;
+    ctx.event = CreateEventW(NULL, TRUE, FALSE, NULL);
+
+    test_accept_events(&ctx);
+    test_connect_events(&ctx);
+    test_write_events(&ctx);
+    test_read_events(&ctx);
+    test_close_events(&ctx);
+    test_oob_events(&ctx);
+
+    CloseHandle(ctx.event);
+
+    ctx.is_message = TRUE;
+    ctx.window = CreateWindowA("Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
+
+    test_accept_events(&ctx);
+    test_connect_events(&ctx);
+    test_write_events(&ctx);
+    test_read_events(&ctx);
+    test_close_events(&ctx);
+    test_oob_events(&ctx);
+
+    DestroyWindow(ctx.window);
 }
 
 static void test_ipv6only(void)
@@ -8478,8 +8564,7 @@ START_TEST( sock )
     test_write_watch();
     test_iocp();
 
-    test_events(0);
-    test_events(1);
+    test_events();
 
     test_ipv6only();
     test_TransmitFile();
-- 
2.30.2




More information about the wine-devel mailing list