PATCH: Unit test for winsock2

Martin Wilck Martin.Wilck at fujitsu-siemens.com
Thu Apr 25 14:24:19 CDT 2002


This patch introduces a unit test for winsock2 functionality.
It is not complete yet, but I have found it useful already.
The most important part (for me), tests for overlapped IO,
have yet to be added.

The test code implements a simple echo protocol over the loopback device.
Servers and clients are started as threads, so that differently
implemented servers/clients can be used.
It should be easy to add more server and client routines to get
extensive testing.

Apart from wine error checks, correct data transmission is also checked
for.

Currently two tests are run:
- synchronous server/synchronous client(s)
- synchronous server/event-driven client(s).

Applies: CVS 2002-04-24.

Only with all patches from my series and today's two bugfixes will all
tests succeed!

Modified files:
	dlls/winsock:	Makefile.in

Added files:
	dlls/winsock: 	sock.c
			testlist.c
			ws2_32_test.spec


diff -ruNX ignore CVS/wine/dlls/winsock/Makefile.in TMP/wine/dlls/winsock/Makefile.in
--- CVS/wine/dlls/winsock/Makefile.in	Tue Apr  2 15:39:30 2002
+++ TMP/wine/dlls/winsock/Makefile.in	Wed Apr 24 11:56:37 2002
@@ -13,6 +13,12 @@
 	async.c \
 	socket.c

+EXTRASUBDIRS = \
+	tests
+
+CTESTS = \
+	tests/sock.c
+
 @MAKE_DLL_RULES@

 ### Dependencies:
diff -ruNX ignore CVS/wine/dlls/winsock/tests/sock.c TMP/wine/dlls/winsock/tests/sock.c
--- CVS/wine/dlls/winsock/tests/sock.c	Thu Jan  1 01:00:00 1970
+++ TMP/wine/dlls/winsock/tests/sock.c	Thu Apr 25 19:27:42 2002
@@ -0,0 +1,683 @@
+/*
+ * Unit test suite for winsock functions
+ *
+ * Copyright 2002 Martin Wilck
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "wine/test.h"
+#include <winbase.h>
+#include <winnt.h>
+#include <winerror.h>
+#undef USE_WS_PREFIX
+#include <winsock2.h>
+
+#define MAX_CLIENTS 4      /* Max number of clients */
+#define NUM_TESTS   2      /* Number of tests performed */
+#define FIRST_CHAR 'A'     /* First character in transferred pattern */
+#define BIND_SLEEP 10      /* seconds to wait between attempts to bind() */
+#define BIND_TRIES 6       /* Number of bind() attempts */
+#define TEST_TIMEOUT 30    /* seconds to wait before killing child threads
+                              after server initialization, if something hangs */
+
+#define wsa_ok(op, cond, msg) \
+   do { \
+        int tmp, err = 0; \
+        tmp = op; \
+        if ( !(cond tmp) ) err = WSAGetLastError(); \
+        ok ( cond tmp, msg, GetCurrentThreadId(), err); \
+   } while (0);
+
+
+/**************** Structs and typedefs ***************/
+
+typedef struct thread_info
+{
+    HANDLE thread;
+    DWORD id;
+} thread_info;
+
+/* Information in the server about open client connections */
+typedef struct sock_info
+{
+    SOCKET                 s;
+    struct sockaddr_in     addr;
+    struct sockaddr_in     peer;
+    char                  *buf;
+    int                    nread;
+} sock_info;
+
+/* Test parameters for both server & client */
+typedef struct test_params
+{
+    int          sock_type;
+    int          sock_prot;
+    char        *inet_addr;
+    int          inet_port;
+    int          chunk_size;
+    int          n_chunks;
+    int          n_clients;
+} test_params;
+
+/* server-specific test parameters */
+typedef struct server_params
+{
+    test_params   *general;
+    DWORD          sock_flags;
+    int            buflen;
+} server_params;
+
+/* client-specific test parameters */
+typedef struct client_params
+{
+    test_params   *general;
+    DWORD          sock_flags;
+    int            buflen;
+} client_params;
+
+/* This type combines all information for setting up a test scenario */
+typedef struct test_setup
+{
+    test_params              general;
+    LPVOID                   srv;
+    server_params            srv_params;
+    LPVOID                   clt;
+    client_params            clt_params;
+} test_setup;
+
+/* Thread local storage for server */
+typedef struct server_memory
+{
+    SOCKET                  s;
+    struct sockaddr_in      addr;
+    sock_info               sock[MAX_CLIENTS];
+} server_memory;
+
+/* Thread local storage for client */
+typedef struct client_memory
+{
+    SOCKET s;
+    struct sockaddr_in      addr;
+    char                   *send_buf;
+    char                   *recv_buf;
+} client_memory;
+
+/**************** Static variables ***************/
+
+static DWORD      tls;              /* Thread local storage index */
+static HANDLE     thread[1+MAX_CLIENTS];
+static DWORD      thread_id[1+MAX_CLIENTS];
+static HANDLE     server_ready;
+static HANDLE     client_ready[MAX_CLIENTS];
+static int        client_id;
+
+/**************** General utility functions ***************/
+
+static void set_so_opentype ( BOOL overlapped )
+{
+    int optval = !overlapped, newval, len = sizeof (int);
+
+    ok ( setsockopt ( INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
+                      (LPVOID) &optval, sizeof (optval) ) == 0,
+         "setting SO_OPENTYPE failed" );
+    ok ( getsockopt ( INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
+                      (LPVOID) &newval, &len ) == 0,
+         "getting SO_OPENTYPE failed" );
+    ok ( optval == newval, "failed to set SO_OPENTYPE" );
+}
+
+static int set_blocking ( SOCKET s, BOOL blocking )
+{
+    u_long val = !blocking;
+    return ioctlsocket ( s, FIONBIO, &val );
+}
+
+static void fill_buffer ( char *buf, int chunk_size, int n_chunks )
+{
+    char c, *p;
+    for ( c = FIRST_CHAR, p = buf; c < FIRST_CHAR + n_chunks; c++, p += chunk_size )
+        memset ( p, c, chunk_size );
+}
+
+static char* test_buffer ( char *buf, int chunk_size, int n_chunks )
+{
+    char c, *p;
+    int i;
+    for ( c = FIRST_CHAR, p = buf; c < FIRST_CHAR + n_chunks; c++, p += chunk_size )
+    {
+        for ( i = 0; i < chunk_size; i++ )
+            if ( p[i] != c ) return p + i;
+    }
+    return NULL;
+}
+
+/*
+ * This routine is called when a client / server does not expect any more data,
+ * but needs to acknowedge the closing of the connection (by reasing 0 bytes).
+ */
+static void read_zero_bytes ( SOCKET s )
+{
+    char buf[256];
+    int tmp, n = 0;
+    while ( ( tmp = recv ( s, buf, 256, 0 ) ) > 0 )
+        n += tmp;
+    ok ( n <= 0, "garbage data received: %d bytes\n", n );
+}
+
+static int do_synchronous_send ( SOCKET s, char *buf, int buflen, int sendlen )
+{
+    char* last = buf + buflen, *p;
+    int n = 1;
+    for ( p = buf; n > 0 && p < last; p += n )
+        n = send ( s, p, min ( sendlen, last - p ), 0 );
+    wsa_ok ( n, 0 <=, "do_synchronous_send (%x): error %d" );
+    return p - buf;
+}
+
+static int do_synchronous_recv ( SOCKET s, char *buf, int buflen, int recvlen )
+{
+    char* last = buf + buflen, *p;
+    int n = 1;
+    for ( p = buf; n > 0 && p < last; p += n )
+        n = recv ( s, p, min ( recvlen, last - p ), 0 );
+    wsa_ok ( n, 0 <=, "do_synchronous_recv (%x): error %d:" );
+    return p - buf;
+}
+
+/*
+ *  Call this routine right after thread startup.
+ *  SO_OPENTYPE must by 0, regardless what the server did.
+ */
+static void check_so_opentype (void)
+{
+    int tmp = 1, len;
+    len = sizeof (tmp);
+    getsockopt ( INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (LPVOID) &tmp, &len );
+    ok ( tmp == 0, "check_so_opentype: wrong startup value of SO_OPENTYPE: %d", tmp );
+}
+
+/**************** Server utility functions ***************/
+
+/*
+ *  Even if we have closed our server socket cleanly,
+ *  the OS may mark the address "in use" for some time -
+ *  this happens with native Linux apps, too.
+ */
+static void do_bind ( SOCKET s, struct sockaddr* addr, int addrlen )
+{
+    int err, wsaerr = 0, n_try = BIND_TRIES;
+
+    while ( ( err = bind ( s, addr, addrlen ) ) != 0 &&
+            ( wsaerr = WSAGetLastError () ) == WSAEADDRINUSE &&
+            n_try-- >= 0)
+    {
+        trace ( "address in use, waiting ...\n" );
+        Sleep ( 1000 * BIND_SLEEP );
+    }
+    ok ( err == 0, "failed to bind: %d\n", wsaerr );
+}
+
+static void server_start ( server_params *par )
+{
+    int i;
+    test_params *gen = par->general;
+    server_memory *mem = (LPVOID) LocalAlloc ( LPTR, sizeof (server_memory));
+
+    TlsSetValue ( tls, mem );
+    mem->s = WSASocketA ( AF_INET, gen->sock_type, gen->sock_prot,
+                          NULL, 0, par->sock_flags );
+    ok ( mem->s != INVALID_SOCKET, "Server: WSASocket failed" );
+
+    mem->addr.sin_family = AF_INET;
+    mem->addr.sin_addr.s_addr = inet_addr ( gen->inet_addr );
+    mem->addr.sin_port = htons ( gen->inet_port );
+
+    for (i = 0; i < MAX_CLIENTS; i++)
+    {
+        mem->sock[i].s = INVALID_SOCKET;
+        mem->sock[i].buf = (LPVOID) LocalAlloc ( LPTR, gen->n_chunks * gen->chunk_size );
+        mem->sock[i].nread = 0;
+    }
+
+    if ( gen->sock_type == SOCK_STREAM )
+        do_bind ( mem->s, (struct sockaddr*) &mem->addr, sizeof (mem->addr) );
+}
+
+static void server_stop (void)
+{
+    int i;
+    server_memory *mem = TlsGetValue ( tls );
+
+    for (i = 0; i < MAX_CLIENTS; i++ )
+    {
+        LocalFree ( (HANDLE) mem->sock[i].buf );
+        if ( mem->sock[i].s != INVALID_SOCKET )
+            closesocket ( mem->sock[i].s );
+    }
+    ok ( closesocket ( mem->s ) == 0, "closesocket failed" );
+    LocalFree ( (HANDLE) mem );
+    ExitThread ( GetCurrentThreadId () );
+}
+
+/**************** Client utilitiy functions ***************/
+
+static void client_start ( client_params *par )
+{
+    test_params *gen = par->general;
+    client_memory *mem = (LPVOID) LocalAlloc (LPTR, sizeof (client_memory));
+
+    TlsSetValue ( tls, mem );
+
+    WaitForSingleObject ( server_ready, INFINITE );
+
+    mem->s = WSASocketA ( AF_INET, gen->sock_type, gen->sock_prot,
+                          NULL, 0, par->sock_flags );
+
+    mem->addr.sin_family = AF_INET;
+    mem->addr.sin_addr.s_addr = inet_addr ( gen->inet_addr );
+    mem->addr.sin_port = htons ( gen->inet_port );
+
+    ok ( mem->s != INVALID_SOCKET, "Client: WSASocket failed" );
+
+    mem->send_buf = (LPVOID) LocalAlloc ( LPTR, 2 * gen->n_chunks * gen->chunk_size );
+    mem->recv_buf = mem->send_buf + gen->n_chunks * gen->chunk_size;
+    fill_buffer ( mem->send_buf, gen->chunk_size, gen->n_chunks );
+
+    SetEvent ( client_ready[client_id] );
+    /* Wait for the other clients to come up */
+    WaitForMultipleObjects ( min ( gen->n_clients, MAX_CLIENTS ), client_ready, TRUE, INFINITE );
+}
+
+static void client_stop (void)
+{
+    client_memory *mem = TlsGetValue ( tls );
+    wsa_ok ( closesocket ( mem->s ), 0 ==, "closesocket error (%x): %d\n" );
+    LocalFree ( (HANDLE) mem->send_buf );
+    LocalFree ( (HANDLE) mem );
+    ExitThread ( GetCurrentThreadId () );
+}
+
+/**************** Servers ***************/
+
+/*
+ * simple_server: A very basic server doing synchronous IO.
+ */
+static VOID WINAPI simple_server ( server_params *par )
+{
+    test_params *gen = par->general;
+    server_memory *mem;
+    int n_recvd, n_sent, n_expected = gen->n_chunks * gen->chunk_size, tmp, i,
+        id = GetCurrentThreadId();
+    char *p;
+
+    trace ( "simple_server (%x) starting\n", id );
+
+    set_so_opentype ( FALSE ); /* non-overlapped */
+    server_start ( par );
+    mem = TlsGetValue ( tls );
+
+    wsa_ok ( set_blocking ( mem->s, TRUE ), 0 ==, "simple_server (%x): failed to set blocking mode: %d");
+    wsa_ok ( listen ( mem->s, SOMAXCONN ), 0 ==, "simple_server (%x): listen failed: %d");
+
+    trace ( "simple_server (%x) ready\n", id );
+    SetEvent ( server_ready ); /* notify clients */
+
+    for ( i = 0; i < min ( gen->n_clients, MAX_CLIENTS ); i++ )
+    {
+        trace ( "simple_server (%x): waiting for client\n", id );
+
+        /* accept a single connection */
+        tmp = sizeof ( mem->sock[0].peer );
+        mem->sock[0].s = accept ( mem->s, (struct sockaddr*) &mem->sock[0].peer, &tmp );
+        wsa_ok ( mem->sock[0].s, INVALID_SOCKET !=, "simple_server (%x): accept failed: %d" );
+
+        ok ( mem->sock[0].peer.sin_addr.s_addr == inet_addr ( gen->inet_addr ),
+             "simple_server (%x): strange peer address", id );
+
+        /* Receive data & check it */
+        n_recvd = do_synchronous_recv ( mem->sock[0].s, mem->sock[0].buf, n_expected, par->buflen );
+        ok ( n_recvd == n_expected,
+             "simple_server (%x): received less data then expected: %d of %d", id, n_recvd, n_expected );
+        p = test_buffer ( mem->sock[0].buf, gen->chunk_size, gen->n_chunks );
+        ok ( p == NULL, "simple_server (%x): test pattern error: %d", p - mem->sock[0].buf);
+
+        /* Echo data back */
+        n_sent = do_synchronous_send ( mem->sock[0].s, mem->sock[0].buf, n_expected, par->buflen );
+        ok ( n_sent == n_expected,
+             "simple_server (%x): sent less data then expected: %d of %d", id, n_sent, n_expected );
+
+        /* cleanup */
+        read_zero_bytes ( mem->sock[0].s );
+        wsa_ok ( closesocket ( mem->sock[0].s ),  0 ==, "simple_server (%x): closesocket error: %d" );
+        mem->sock[0].s = INVALID_SOCKET;
+    }
+
+    trace ( "simple_server (%x) exiting\n", id );
+    server_stop ();
+}
+
+/**************** Clients ***************/
+
+/*
+ * simple_client: A very basic client doing synchronous IO.
+ */
+static VOID WINAPI simple_client ( client_params *par )
+{
+    test_params *gen = par->general;
+    client_memory *mem;
+    int n_sent, n_recvd, n_expected = gen->n_chunks * gen->chunk_size, id;
+    char *p;
+
+    id = GetCurrentThreadId();
+    trace ( "simple_client (%x): starting\n", id );
+    /* wait here because we want to call set_so_opentype before creating a socket */
+    WaitForSingleObject ( server_ready, INFINITE );
+    trace ( "simple_client (%x): server ready\n", id );
+
+    check_so_opentype ();
+    set_so_opentype ( FALSE ); /* non-overlapped */
+    client_start ( par );
+    mem = TlsGetValue ( tls );
+
+    /* Connect */
+    wsa_ok ( connect ( mem->s, (struct sockaddr*) &mem->addr, sizeof ( mem->addr ) ),
+             0 ==, "simple_client (%x): connect error: %d" );
+    ok ( set_blocking ( mem->s, TRUE ) == 0,
+         "simple_client (%x): failed to set blocking mode", id );
+    trace ( "simple_client (%x) connected\n", id );
+
+    /* send data to server */
+    n_sent = do_synchronous_send ( mem->s, mem->send_buf, n_expected, par->buflen );
+    ok ( n_sent == n_expected,
+         "simple_client (%x): sent less data then expected: %d of %d", id, n_sent, n_expected );
+
+    /* shutdown send direction */
+    wsa_ok ( shutdown ( mem->s, SD_SEND ), 0 ==, "simple_client (%x): shutdown failed: %d" );
+
+    /* Receive data echoed back & check it */
+    n_recvd = do_synchronous_recv ( mem->s, mem->recv_buf, n_expected, par->buflen );
+    ok ( n_recvd == n_expected,
+         "simple_client (%x): received less data then expected: %d of %d", id, n_recvd, n_expected );
+
+    /* check data */
+    p = test_buffer ( mem->recv_buf, gen->chunk_size, gen->n_chunks );
+    ok ( p == NULL, "simple_client (%x): test pattern error: %d", id, p - mem->recv_buf);
+
+    /* cleanup */
+    read_zero_bytes ( mem->s );
+    trace ( "simple_client (%x) exiting\n", id );
+    client_stop ();
+}
+
+/*
+ * event_client: An event-driven client
+ */
+static void WINAPI event_client ( client_params *par )
+{
+    test_params *gen = par->general;
+    client_memory *mem;
+    int id = GetCurrentThreadId(), n_expected = gen->n_chunks * gen->chunk_size,
+        tmp, err, n;
+    HANDLE event;
+    WSANETWORKEVENTS wsa_events;
+    char *send_last, *recv_last, *send_p, *recv_p;
+    long mask = FD_READ | FD_WRITE | FD_CLOSE;
+
+    trace ( "event_client (%x): starting\n", id );
+    client_start ( par );
+    trace ( "event_client (%x): server ready\n", id );
+
+    mem = TlsGetValue ( tls );
+
+    /* Prepare event notification for connect, makes socket nonblocking */
+    event = WSACreateEvent ();
+    WSAEventSelect ( mem->s, event, FD_CONNECT );
+    tmp = connect ( mem->s, (struct sockaddr*) &mem->addr, sizeof ( mem->addr ) );
+    if ( tmp != 0 && ( err = WSAGetLastError () ) != WSAEWOULDBLOCK )
+        ok ( 0, "event_client (%x): connect error: %d", id, err );
+
+    tmp = WaitForSingleObject ( event, INFINITE );
+    ok ( tmp == WAIT_OBJECT_0, "event_client (%x): wait for connect event failed: %d", id, tmp );
+    err = WSAEnumNetworkEvents ( mem->s, event, &wsa_events );
+    wsa_ok ( err, 0 ==, "event_client (%x): WSAEnumNetworkEvents error: %d\n" );
+
+    err = wsa_events.iErrorCode[ FD_CONNECT_BIT ];
+    ok ( err == 0, "event_client (%x): connect error: %d", id, err );
+    if ( err ) goto out;
+
+    trace ( "event_client (%x) connected\n", id );
+
+    WSAEventSelect ( mem->s, event, mask );
+
+    recv_p = mem->recv_buf;
+    recv_last = mem->recv_buf + n_expected;
+    send_p = mem->send_buf;
+    send_last = mem->send_buf + n_expected;
+
+    while ( TRUE )
+    {
+        err = WaitForSingleObject ( event, INFINITE );
+        ok ( err == WAIT_OBJECT_0, "event_client (%x): wait failed", id, tmp );
+
+        err = WSAEnumNetworkEvents ( mem->s, event, &wsa_events );
+        wsa_ok ( err, 0 ==, "event_client (%x): WSAEnumNetworkEvents error: %d\n" );
+
+        if ( wsa_events.lNetworkEvents & FD_WRITE )
+        {
+            err = wsa_events.iErrorCode[ FD_WRITE_BIT ];
+            ok ( err == 0, "event_client (%x): FD_WRITE error code: %d\n", id, err );
+
+            if ( err== 0 )
+                do
+                {
+                    n = send ( mem->s, send_p, min ( send_last - send_p, par->buflen ), 0 );
+                    if ( n < 0 )
+                    {
+                        err = WSAGetLastError ();
+                        ok ( err == WSAEWOULDBLOCK, "event_client (%x): send error: %d\n", id, err );
+                    }
+                    else
+                        send_p += n;
+                }
+                while ( n >= 0 && send_p < send_last );
+
+            if ( send_p == send_last )
+            {
+                trace ( "event_client (%x): all data sent - shutdown\n", id );
+                shutdown ( mem->s, SD_SEND );
+                mask &= ~FD_WRITE;
+                WSAEventSelect ( mem->s, event, mask );
+            }
+        }
+        else if ( wsa_events.lNetworkEvents & FD_READ )
+        {
+            err = wsa_events.iErrorCode[ FD_READ_BIT ];
+            ok ( err == 0, "event_client (%x): FD_READ error code: %d\n", id, err );
+
+            n = recv ( mem->s, recv_p, min ( recv_last - recv_p, par->buflen ), 0 );
+            wsa_ok ( n, 0 <=, "event_client (%x): recv error: %d\n" );
+            if ( err != 0 || n < 0 )
+                break;
+            else if ( n == 0 )
+            {
+                ok ( 0, "event_client (%x): empty receive", id );
+                break;
+            }
+
+            recv_p += n;
+            if ( recv_p == recv_last )
+            {
+                trace ( "event_client (%x): all data received\n", id );
+                mask &= ~FD_READ;
+                WSAEventSelect ( mem->s, event, mask );
+            }
+        }
+        else if ( wsa_events.lNetworkEvents & FD_CLOSE )
+        {
+            trace ( "event_client (%x): close event\n", id );
+            err = wsa_events.iErrorCode[ FD_CLOSE_BIT ];
+            ok ( err == 0, "event_client (%x): FD_CLOSE error code: %d\n", id, err );
+            break;
+        }
+    }
+
+    ok ( send_p == send_last,
+         "simple_client (%x): sent less data then expected: %d of %d",
+         id, send_p - mem->send_buf, n_expected );
+    ok ( recv_p == recv_last,
+         "simple_client (%x): received less data then expected: %d of %d",
+         id, recv_p - mem->recv_buf, n_expected );
+    recv_p = test_buffer ( mem->recv_buf, gen->chunk_size, gen->n_chunks );
+    ok ( recv_p == NULL, "event_client (%x): test pattern error: %d", id, recv_p - mem->recv_buf);
+
+out:
+    WSACloseEvent ( event );
+    trace ( "event_client (%x) exiting\n", id );
+    client_stop ();
+}
+
+/**************** Main program utility functions ***************/
+
+static void Init (void)
+{
+    WORD ver = MAKEWORD (2, 2);
+    WSADATA data;
+
+    ok ( WSAStartup ( ver, &data ) == 0, "WSAStartup failed" );
+    tls = TlsAlloc();
+}
+
+static void Exit (void)
+{
+    TlsFree ( tls );
+    ok ( WSACleanup() == 0, "WSACleanup failed" );
+}
+
+static void StartServer (LPTHREAD_START_ROUTINE routine,
+                         test_params *general, server_params *par)
+{
+    par->general = general;
+    thread[0] = CreateThread ( NULL, 0, routine, par, 0, &thread_id[0] );
+    ok ( thread[0] != (HANDLE) NULL, "Failed to create server thread" );
+}
+
+static void StartClients (LPTHREAD_START_ROUTINE routine,
+                          test_params *general, client_params *par)
+{
+    int i;
+    par->general = general;
+    for ( i = 1; i <= min ( general->n_clients, MAX_CLIENTS ); i++ )
+    {
+        client_id = i - 1;
+        thread[i] = CreateThread ( NULL, 0, routine, par, 0, &thread_id[i] );
+        ok ( thread[i] != (HANDLE) NULL, "Failed to create client thread" );
+        /* Make sure the client is up and running */
+        WaitForSingleObject ( client_ready[client_id], INFINITE );
+    };
+}
+
+static void do_test( test_setup *test )
+{
+    int i, n = min (test->general.n_clients, MAX_CLIENTS);
+    int wait;
+
+    server_ready = CreateEventA ( NULL, TRUE, FALSE, NULL );
+    for (i = 0; i <= n; i++)
+        client_ready[i] = CreateEventA ( NULL, TRUE, FALSE, NULL );
+
+    StartServer ( test->srv, &test->general, &test->srv_params );
+    StartClients ( test->clt, &test->general, &test->clt_params );
+    WaitForSingleObject ( server_ready, INFINITE );
+
+    wait = WaitForMultipleObjects ( 1 + n, thread, TRUE, 1000 * TEST_TIMEOUT );
+    ok ( wait == WAIT_OBJECT_0, "some threads have not completed\n" );
+
+    if ( wait == WAIT_TIMEOUT )
+    {
+        for (i = 0; i <= n; i++)
+        {
+            trace ("terminating thread %08x\n", thread_id[i]);
+            if ( WaitForSingleObject ( thread[i], 0 ) != WAIT_OBJECT_0 )
+                TerminateThread ( thread [i], 0 );
+        }
+    }
+    CloseHandle ( server_ready );
+    for (i = 0; i <= n; i++)
+        CloseHandle ( client_ready[i] );
+}
+
+/************* Array containing the tests to run **********/
+
+#define STD_STREAM_SOCKET \
+            sock_type:   SOCK_STREAM, \
+            sock_prot:   0, \
+            inet_addr:   "127.0.0.1", \
+            inet_port:   9374
+
+static test_setup tests [NUM_TESTS] =
+{
+    /* Test 0: synchronous client and server */
+    {
+        general: {
+            STD_STREAM_SOCKET,
+            chunk_size: 2048,
+            n_chunks:  16,
+            n_clients: 2
+        },
+        srv: simple_server,
+        srv_params: {
+            buflen:      64
+        },
+        clt: simple_client,
+        clt_params: {
+            buflen:      128
+        }
+    },
+    /* Test 1: event-driven client, synchronous server */
+    {
+        general: {
+            STD_STREAM_SOCKET,
+            chunk_size: 2048,
+            n_chunks:  16,
+            n_clients: 2
+        },
+        srv: simple_server,
+        srv_params: {
+            buflen:      64
+        },
+        clt: event_client,
+        clt_params: {
+            buflen:      128
+        }
+    }
+};
+
+/**************** Main program  ***************/
+
+START_TEST( sock )
+{
+    int i;
+    Init();
+
+    for (i = 0; i < NUM_TESTS; i++)
+    {
+        trace ( " **** STARTING TEST %d **** \n", i );
+        do_test (  &tests[i] );
+        trace ( " **** TEST %d COMPLETE **** \n", i );
+    }
+
+    Exit();
+}
diff -ruNX ignore CVS/wine/dlls/winsock/tests/testlist.c TMP/wine/dlls/winsock/tests/testlist.c
--- CVS/wine/dlls/winsock/tests/testlist.c	Thu Jan  1 01:00:00 1970
+++ TMP/wine/dlls/winsock/tests/testlist.c	Wed Apr 24 12:04:00 2002
@@ -0,0 +1,13 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+extern void func_sock(void);
+
+const struct
+{
+    const char *name;
+    void (*func)(void);
+} winetest_testlist[] =
+{
+    { "sock", func_sock },
+    { 0, 0 }
+};
diff -ruNX ignore CVS/wine/dlls/winsock/tests/ws2_32_test.spec TMP/wine/dlls/winsock/tests/ws2_32_test.spec
--- CVS/wine/dlls/winsock/tests/ws2_32_test.spec	Thu Jan  1 01:00:00 1970
+++ TMP/wine/dlls/winsock/tests/ws2_32_test.spec	Wed Apr 24 11:53:55 2002
@@ -0,0 +1,7 @@
+name	ws2_32_test
+type	win32
+mode	cuiexe
+
+import ntdll.dll
+import kernel32.dll
+import ws2_32.dll




More information about the wine-devel mailing list