ws2_32/tests: Add some socket event tests.

This commit is contained in:
Mike Kaplinskiy 2010-05-12 13:27:32 -04:00 committed by Alexandre Julliard
parent 00d5f87d30
commit 688b94b43d
2 changed files with 709 additions and 85 deletions

View File

@ -3,7 +3,7 @@ TOPOBJDIR = ../../..
SRCDIR = @srcdir@
VPATH = @srcdir@
TESTDLL = ws2_32.dll
IMPORTS = ws2_32 kernel32
IMPORTS = ws2_32 kernel32 user32
C_SRCS = \
protocol.c \

View File

@ -20,6 +20,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdio.h>
#include <stdarg.h>
#include "ntstatus.h"
@ -32,6 +33,7 @@
#include "wine/test.h"
#include <winnt.h>
#include <winerror.h>
#include <winuser.h>
#define MAX_CLIENTS 4 /* Max number of clients */
#define NUM_TESTS 4 /* Number of tests performed */
@ -2562,37 +2564,479 @@ end:
HeapFree(GetProcessHeap(), 0, buffer);
}
static void test_write_events(void)
typedef struct async_message
{
SOCKET socket;
LPARAM lparam;
struct async_message *next;
} async_message;
static struct async_message *messages_recieved;
#define WM_SOCKET (WM_USER+100)
static LRESULT CALLBACK ws2_test_WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
struct async_message *message;
switch (msg)
{
case WM_SOCKET:
message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
message->socket = (SOCKET) wparam;
message->lparam = lparam;
message->next = NULL;
if (messages_recieved)
{
struct async_message *last = messages_recieved;
while (last->next) last = last->next;
last->next = message;
}
else
messages_recieved = message;
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
static void get_event_details(int event, int *bit, char *name)
{
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(const LPARAM *seq)
{
static char message[1024];
char name[10];
message[0] = '[';
message[1] = 0;
while (*seq)
{
get_event_details(WSAGETSELECTEVENT(*seq), NULL, name);
sprintf(message, "%s%s%s(%d)", message, message[1] == 0 ? "" : " ",
name, WSAGETSELECTERROR(*seq));
seq++;
}
strcat(message, "]");
return message;
}
static char *dbgstr_event_seq_result(SOCKET s, WSANETWORKEVENTS *netEvents)
{
static char message[1024];
struct async_message *curr = messages_recieved;
int index, error, bit = 0;
char name[10];
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;
}
sprintf(message, "%s%s%s(%d)", message, message[1] == 0 ? "" : " ",
name, error);
}
strcat(message, "]");
return message;
}
static void flush_events(SOCKET s, HANDLE hEvent)
{
SOCKET src = INVALID_SOCKET;
SOCKET dst = INVALID_SOCKET;
HANDLE hThread = NULL;
HANDLE hEvent = INVALID_HANDLE_VALUE;
char *buffer = NULL;
int bufferSize = 1024*1024;
u_long one = 1;
int ret;
DWORD id;
WSANETWORKEVENTS netEvents;
struct async_message *prev = NULL, *curr = messages_recieved;
int ret;
DWORD dwRet;
if (tcp_socketpair(&src, &dst) != 0)
if (hEvent != INVALID_HANDLE_VALUE)
{
ok(0, "creating socket pair failed, skipping test\n");
dwRet = WaitForSingleObject(hEvent, 100);
if (dwRet == WAIT_OBJECT_0)
{
ret = WSAEnumNetworkEvents(s, hEvent, &netEvents);
if (ret)
ok(0, "WSAEnumNetworkEvents failed, error %d\n", ret);
}
}
else
{
while (curr)
{
if (curr->socket == s)
{
if (prev) prev->next = curr->next;
else messages_recieved = curr->next;
HeapFree(GetProcessHeap(), 0, curr);
if (prev) curr = prev->next;
else curr = messages_recieved;
}
else
{
prev = curr;
curr = curr->next;
}
}
}
}
static int match_event_sequence(SOCKET s, WSANETWORKEVENTS *netEvents, const LPARAM *seq)
{
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_recieved;
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, const LPARAM **broken_seqs, int completelyBroken)
{
MSG msg;
WSANETWORKEVENTS events, *netEvents = NULL;
int ret;
DWORD dwRet;
if (hEvent != INVALID_HANDLE_VALUE)
{
netEvents = &events;
dwRet = WaitForSingleObject(hEvent, 200);
if (dwRet == WAIT_OBJECT_0)
{
ret = WSAEnumNetworkEvents(s, hEvent, netEvents);
if (ret)
{
winetest_ok(0, "WSAEnumNetworkEvents failed, error %d\n", ret);
return;
}
}
else
memset(netEvents, 0, sizeof(*netEvents));
}
else
{
Sleep(200);
/* Run the message loop a little */
while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE ))
{
DispatchMessageA(&msg);
}
}
if (match_event_sequence(s, netEvents, seq))
{
winetest_ok(1, "Sequence matches expected: %s\n", dbgstr_event_seq(seq));
flush_events(s, hEvent);
return;
}
/* 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 */
ret = ioctlsocket(src, FIONBIO, &one);
if (ret)
if (broken_seqs)
{
ok(0, "ioctlsocket failed, error %d\n", WSAGetLastError());
for (; *broken_seqs; broken_seqs++)
{
if (match_event_sequence(s, netEvents, *broken_seqs))
{
winetest_ok(broken(1), "Sequence matches broken: %s, expected %s\n", dbgstr_event_seq_result(s, netEvents), dbgstr_event_seq(seq));
flush_events(s, hEvent);
return;
}
}
}
winetest_ok(broken(completelyBroken), "Expected event sequence %s, got %s\n", dbgstr_event_seq(seq),
dbgstr_event_seq_result(s, netEvents));
flush_events(s, hEvent);
}
#define ok_event_seq (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : ok_event_sequence
static void test_events(int useMessages)
{
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;
WNDCLASSEX 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";
memset(&ov, 0, sizeof(ov));
memset(&ov2, 0, sizeof(ov2));
/* don't use socketpair, we want connection event */
src = socket(AF_INET, SOCK_STREAM, 0);
if (src == INVALID_SOCKET)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
ret = ioctlsocket(dst, FIONBIO, &one);
src2 = socket(AF_INET, SOCK_STREAM, 0);
if (src2 == INVALID_SOCKET)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
len = sizeof(BOOL);
if (getsockopt(src, SOL_SOCKET, SO_OOBINLINE, (void *)&bret, &len) == SOCKET_ERROR)
{
ok(0, "failed to get oobinline status, %d\n", GetLastError());
goto end;
}
ok(bret == FALSE, "OOB not inline\n");
if (useMessages)
{
trace("Event test using messages\n");
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = ws2_test_WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = GetModuleHandle(NULL);
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszClassName = szClassName;
wndclass.lpszMenuName = NULL;
RegisterClassEx(&wndclass);
hWnd = CreateWindow(szClassName, "WS2Test", WS_OVERLAPPEDWINDOW, 0, 0, 500, 500, NULL, NULL, GetModuleHandle(NULL), NULL);
if (!hWnd)
{
ok(0, "failed to create window: %d\n", GetLastError());
return;
}
ret = WSAAsyncSelect(src, hWnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_OOB | FD_WRITE | FD_CLOSE);
if (ret)
{
ok(0, "WSAAsyncSelect failed, error %d\n", ret);
goto end;
}
ret = WSAAsyncSelect(src2, hWnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_OOB | FD_WRITE | FD_CLOSE);
if (ret)
{
ok(0, "WSAAsyncSelect failed, error %d\n", ret);
goto end;
}
}
else
{
trace("Event test using events\n");
hEvent = WSACreateEvent();
if (hEvent == INVALID_HANDLE_VALUE)
{
ok(0, "WSACreateEvent failed, error %d\n", GetLastError());
goto end;
}
hEvent2 = WSACreateEvent();
if (hEvent2 == INVALID_HANDLE_VALUE)
{
ok(0, "WSACreateEvent failed, error %d\n", GetLastError());
goto end;
}
ret = WSAEventSelect(src, hEvent, FD_CONNECT | FD_READ | FD_OOB | FD_WRITE | FD_CLOSE);
if (ret)
{
ok(0, "WSAEventSelect failed, error %d\n", ret);
goto end;
}
ret = WSAEventSelect(src2, hEvent2, FD_CONNECT | FD_READ | FD_OOB | FD_WRITE | FD_CLOSE);
if (ret)
{
ok(0, "WSAEventSelect failed, error %d\n", ret);
goto end;
}
}
server = socket(AF_INET, SOCK_STREAM, 0);
if (server == INVALID_SOCKET)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
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));
if (ret != 0)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
len = sizeof(addr);
ret = getsockname(server, (struct sockaddr*)&addr, &len);
if (ret != 0)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
ret = listen(server, 2);
if (ret != 0)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
ret = connect(src, (struct sockaddr*)&addr, sizeof(addr));
if (ret == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
ret = connect(src2, (struct sockaddr*)&addr, sizeof(addr));
if (ret == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
len = sizeof(addr);
dst = accept(server, (struct sockaddr*)&addr, &len);
if (dst == INVALID_SOCKET)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
len = sizeof(addr);
dst2 = accept(server, (struct sockaddr*)&addr, &len);
if (dst2 == INVALID_SOCKET)
{
ok(0, "creating socket pair failed (%d), skipping test\n", GetLastError());
goto end;
}
closesocket(server);
server = INVALID_SOCKET;
/* 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 */
if (set_blocking(dst, FALSE))
{
ok(0, "ioctlsocket failed, error %d\n", WSAGetLastError());
goto end;
@ -2605,6 +3049,162 @@ static void test_write_events(void)
goto end;
}
ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (ov.hEvent == NULL)
{
ok(0, "could not create event object, errno = %d\n", GetLastError());
goto end;
}
ov2.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (ov2.hEvent == NULL)
{
ok(0, "could not create event object, errno = %d\n", GetLastError());
goto end;
}
/* FD_WRITE should be set initially, and allow us to send at least 1 byte */
ok_event_seq(src, hEvent, (LPARAM[]){ WSAMAKESELECTREPLY(FD_CONNECT, 0), WSAMAKESELECTREPLY(FD_WRITE, 0), 0 }, 0, 1);
ok_event_seq(src2, hEvent2, (LPARAM[]){ WSAMAKESELECTREPLY(FD_CONNECT, 0), WSAMAKESELECTREPLY(FD_WRITE, 0), 0 }, 0, 1);
/* broken on all windows - FD_CONNECT error is garbage */
/* Test simple send/recv */
ret = send(dst, buffer, 100, 0);
ok(ret == 100, "Failed to send buffer %d err %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 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, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 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, (LPARAM[]){ 0 }, 0, 0);
/* fun fact - events are reenabled 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, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 }, 0, 0);
ret = recv(src, buffer, -1, 0);
ok(ret == SOCKET_ERROR && (GetLastError() == WSAEFAULT || GetLastError() == WSAENOBUFS),
"Failed to recv buffer %d err %d\n", ret, GetLastError());
if (useMessages)
todo_wine ok_event_seq(src, hEvent, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 },
(const LPARAM*[]){ (LPARAM[]){ 0 } /* win9x */, 0 }, 0);
else
ok_event_seq(src, hEvent, (LPARAM[]){ 0 }, 0, 0);
ret = recv(src, buffer, 1, 0);
ok(ret == 1, "Failed to recv buffer %d err %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ 0 }, 0, 0);
/* 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());
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());
ret = send(dst, "12", 2, 0);
ok(ret == 2, "Failed to send buffer %d err %d\n", ret, GetLastError());
if (useMessages) /* we like to erase pmask in server, so we have varying behavior here */
todo_wine ok_event_seq(src, hEvent, (LPARAM[]){ 0 },
(const LPARAM*[]){ (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), WSAMAKESELECTREPLY(FD_READ, 0), 0 } /* win9x */, 0 }, 0);
else
ok_event_seq(src, hEvent, (LPARAM[]){ 0 },
(const LPARAM*[]){ (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), WSAMAKESELECTREPLY(FD_READ, 0), 0 } /* win9x */, 0 }, 0);
dwRet = WaitForSingleObject(ov.hEvent, 100);
ok(dwRet == WAIT_OBJECT_0, "Failed to wait for recv message: %d - %d\n", dwRet, GetLastError());
if (dwRet == WAIT_OBJECT_0)
{
bret = GetOverlappedResult((HANDLE)src, &ov, &bytesReturned, FALSE);
ok((bret && bytesReturned == 1) || broken(!bret && GetLastError() == ERROR_IO_INCOMPLETE) /* win9x */,
"Got %d instead of 1 (%d - %d)\n", bytesReturned, bret, GetLastError());
ok(buffer[0] == '1', "Got %c instead of 1\n", buffer[0]);
}
dwRet = WaitForSingleObject(ov2.hEvent, 100);
ok(dwRet == WAIT_OBJECT_0, "Failed to wait for recv message: %d - %d\n", dwRet, GetLastError());
if (dwRet == WAIT_OBJECT_0)
{
bret = GetOverlappedResult((HANDLE)src, &ov2, &bytesReturned, FALSE);
ok((bret && bytesReturned == 1) || broken(!bret && GetLastError() == ERROR_IO_INCOMPLETE) /* win9x */,
"Got %d instead of 1 (%d - %d)\n", bytesReturned, bret, GetLastError());
ok(buffer[1] == '2', "Got %c instead of 2\n", buffer[1]);
}
ret = send(dst, "1", 1, 0);
ok(ret == 1, "Failed to send buffer %d err %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 }, 0, 0);
ret = recv(src, buffer, 1, 0);
ok(ret == 1, "Failed to empty buffer: %d - %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ 0 }, 0, 0);
/* 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 (0) {
ret = send(dst, "1", 1, MSG_OOB);
ok(ret == 1, "Failed to send buffer %d err %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ WSAMAKESELECTREPLY(FD_OOB, 0), 0 }, 0, 0);
}
dwRet = WaitForSingleObject(ov.hEvent, 100);
ok(dwRet == WAIT_TIMEOUT, "OOB message activated read?: %d - %d\n", dwRet, GetLastError());
ret = send(dst, "2", 1, 0);
ok(ret == 1, "Failed to send buffer %d err %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ 0 },
(const LPARAM*[]){ (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 } /* win98 */, 0 }, 0);
dwRet = WaitForSingleObject(ov.hEvent, 100);
ok(dwRet == WAIT_OBJECT_0 || broken(dwRet == WAIT_TIMEOUT),
"Failed to wait for recv message: %d - %d\n", dwRet, GetLastError());
if (dwRet == WAIT_OBJECT_0)
{
bret = GetOverlappedResult((HANDLE)src, &ov, &bytesReturned, FALSE);
ok((bret && bytesReturned == 1) || broken(!bret && GetLastError() == ERROR_IO_INCOMPLETE) /* win9x */,
"Got %d instead of 1 (%d - %d)\n", bytesReturned, bret, GetLastError());
ok(buffer[0] == '2', "Got %c instead of 2\n", buffer[1]);
}
else if (dwRet == WAIT_TIMEOUT)
{
/* this happens on win98. We get an FD_READ later on the next test */
CancelIo((HANDLE) src);
}
if (0) {
ret = recv(src, buffer, 1, MSG_OOB);
todo_wine ok(ret == 1, "Failed to empty buffer: %d - %d\n", ret, GetLastError());
/* We get OOB notification, but no data on wine */
ok_event_seq(src, hEvent, (LPARAM[]){ 0 }, 0, 0);
}
/* wine gets a stale notifications because of the async ops, clear them.
* remove when sending messages during pending asyncs is fixed */
ret = send(dst, "2", 1, 0);
ok(ret == 1, "Failed to send buffer %d err %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 }, 0, 0);
ret = recv(src, buffer, 1, 0);
ok(ret == 1, "Failed to empty buffer: %d - %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ 0 }, 0, 0);
/* Flood the send queue */
hThread = CreateThread(NULL, 0, drain_socket_thread, &dst, 0, &id);
if (hThread == NULL)
{
@ -2612,53 +3212,8 @@ static void test_write_events(void)
goto end;
}
hEvent = CreateEventA(NULL, FALSE, TRUE, NULL);
if (hEvent == INVALID_HANDLE_VALUE)
{
ok(0, "CreateEventA failed, error %d\n", GetLastError());
goto end;
}
ret = WSAEventSelect(src, hEvent, FD_WRITE | FD_CLOSE);
if (ret)
{
ok(0, "WSAEventSelect failed, error %d\n", ret);
goto end;
}
/* FD_WRITE should be set initially, and allow us to send at least 1 byte */
dwRet = WaitForSingleObject(hEvent, 5000);
if (dwRet != WAIT_OBJECT_0)
{
ok(0, "Initial WaitForSingleObject failed, error %d\n", dwRet);
goto end;
}
ret = WSAEnumNetworkEvents(src, NULL, &netEvents);
if (ret)
{
ok(0, "WSAEnumNetworkEvents failed, error %d\n", ret);
goto end;
}
if (netEvents.lNetworkEvents & FD_WRITE)
{
ret = send(src, "a", 1, 0);
ok(ret == 1, "sending 1 byte failed, error %d\n", WSAGetLastError());
if (ret != 1)
goto end;
}
else
{
ok(0, "FD_WRITE not among initial events\n");
goto end;
}
/* Now FD_WRITE should not be set, because the socket send buffer isn't full yet */
dwRet = WaitForSingleObject(hEvent, 2000);
if (dwRet == WAIT_OBJECT_0)
{
ok(0, "WaitForSingleObject should have timed out, but succeeded!\n");
goto end;
}
ok_event_seq(src, hEvent, (LPARAM[]){ 0 }, 0, 0);
/* 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
@ -2673,35 +3228,103 @@ static void test_write_events(void)
drain_pause=0;
if (ret >= 0 || WSAGetLastError() == WSAEWOULDBLOCK)
{
dwRet = WaitForSingleObject(hEvent, 5000);
ok(dwRet == WAIT_OBJECT_0, "Waiting failed with %d\n", dwRet);
if (dwRet == WAIT_OBJECT_0)
{
ret = WSAEnumNetworkEvents(src, NULL, &netEvents);
ok(ret == 0, "WSAEnumNetworkEvents failed, error %d\n", ret);
if (ret == 0)
goto end;
ok(netEvents.lNetworkEvents & FD_WRITE,
"FD_WRITE event not set as expected, events are 0x%x\n", netEvents.lNetworkEvents);
}
else
goto end;
Sleep(400); /* win9x */
ok_event_seq(src, hEvent, (LPARAM[]){ WSAMAKESELECTREPLY(FD_WRITE, 0), 0 },
(const LPARAM*[]){ (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), WSAMAKESELECTREPLY(FD_WRITE, 0), 0 }, 0 }, 0);
}
else
{
ok(0, "sending a lot of data failed with error %d\n", WSAGetLastError());
goto end;
}
/* 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);
dst = INVALID_SOCKET;
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, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), WSAMAKESELECTREPLY(FD_CLOSE, 0), 0 },
(const LPARAM*[]){ (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 } /* win9x */, 0 }, 0);
ret = recv(src, buffer, 1, 0);
ok(ret == 1, "Failed to empty buffer: %d - %d\n", ret, GetLastError());
ok_event_seq(src, hEvent, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 }, 0, 0);
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, (LPARAM[]){ 0 }, /* wine sends FD_CLOSE here */
(const LPARAM*[]){ (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 },
(LPARAM[]){ WSAMAKESELECTREPLY(FD_CLOSE, 0), 0 }, 0 } /* win9x */, 0);
/* 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);
/* Some of the below are technically todo_wine, but our event sequence is still valid, so to prevent
regressions, don't mark them as todo_wine, and mark windows as broken */
ok_event_seq(src2, hEvent2, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 },
(const LPARAM*[]){ (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), WSAMAKESELECTREPLY(FD_CLOSE, 0), 0 },
(LPARAM[]){ WSAMAKESELECTREPLY(FD_CLOSE, 0), 0 }, 0 }, 0);
ret = recv(src2, buffer, 1, 0);
ok(ret == 1 || broken(!ret), "Failed to empty buffer: %d - %d\n", ret, GetLastError());
ok_event_seq(src2, hEvent2, (LPARAM[]){ WSAMAKESELECTREPLY(FD_READ, 0), 0 },
(const LPARAM*[]){ (LPARAM[]){ 0 }, (LPARAM[]){ WSAMAKESELECTREPLY(FD_CLOSE, 0), 0 } /* win98 */, 0 }, 0);
ret = recv(src2, buffer, 1, 0);
ok(ret == 1 || broken(!ret), "Failed to empty buffer: %d - %d\n", ret, GetLastError());
ok_event_seq(src2, hEvent2, (LPARAM[]){ WSAMAKESELECTREPLY(FD_CLOSE, 0), 0 },
(const LPARAM*[]){ (LPARAM[]){ 0 }, 0 }, 0);
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, (LPARAM[]){ 0 }, 0, 0);
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, (LPARAM[]){ 0 }, 0, 0);
end:
HeapFree(GetProcessHeap(), 0, buffer);
if (src != INVALID_SOCKET)
{
flush_events(src, hEvent);
closesocket(src);
}
if (src2 != INVALID_SOCKET)
{
flush_events(src2, hEvent2);
closesocket(src2);
}
HeapFree(GetProcessHeap(), 0, buffer);
if (server != INVALID_SOCKET)
closesocket(server);
if (dst != INVALID_SOCKET)
closesocket(dst);
if (dst2 != INVALID_SOCKET)
closesocket(dst2);
if (hThread != NULL)
CloseHandle(hThread);
if (hWnd != NULL)
CloseHandle(hWnd);
if (hEvent != NULL)
CloseHandle(hEvent);
if (hEvent2 != NULL)
CloseHandle(hEvent2);
if (ov.hEvent != NULL)
CloseHandle(ov.hEvent);
if (ov2.hEvent != NULL)
CloseHandle(ov2.hEvent);
}
static void test_ipv6only(void)
@ -3446,11 +4069,12 @@ START_TEST( sock )
test_gethostbyname_hack();
test_send();
test_write_events();
test_WSASendTo();
test_WSARecv();
test_events(0);
test_events(1);
test_ipv6only();
test_GetAddrInfoW();