named pipe tests... make wine fall down go boom

Dan Kegel dank at kegel.com
Tue Mar 4 04:42:19 CST 2003


Changelog:
     * dlls/kernel/tests/pipe.c: exercize named pipes more thoroughly
       Copyright 2003, Dan Kegel.  LGPL.

Wine doesn't handle these tests well at all.  It silently
exits with status zero halfway through the tests.
No idea why.
- Dan

-- 
Dan Kegel
http://www.kegel.com
http://counter.li.org/cgi-bin/runscript/display-person.cgi?user=78045
-------------- next part --------------
Index: dlls/kernel/tests/pipe.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/tests/pipe.c,v
retrieving revision 1.2
diff -u -p -u -r1.2 pipe.c
--- dlls/kernel/tests/pipe.c	25 Feb 2003 03:56:43 -0000	1.2
+++ dlls/kernel/tests/pipe.c	4 Mar 2003 10:37:55 -0000
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
+#include <assert.h>
 
 #ifndef STANDALONE
 #include "wine/test.h"
@@ -29,6 +30,11 @@
 #define START_TEST(name) main(int argc, char **argv)
 #define ok(condition, msg) assert(condition)
 #define todo_wine
+static void trace(const char *s)
+{
+    DWORD cbWritten;
+    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), s, strlen(s), &cbWritten, NULL);
+}
 #endif
 
 #include <wtypes.h>
@@ -38,6 +44,7 @@
 
 #define PIPENAME "\\\\.\\PiPe\\tests_" __FILE__
 
+
 void test_CreateNamedPipeA(void)
 {
     HANDLE hnp;
@@ -45,11 +52,11 @@ void test_CreateNamedPipeA(void)
     const char obuf[] = "Bit Bucket";
     char ibuf[32];
     DWORD written;
-    DWORD gelesen;
+    DWORD readden;
 
+    trace("test_CreateNamedPipe starting\n");
     /* Bad parameter checks */
-    hnp = CreateNamedPipeA("not a named pipe", 
-        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, 
+    hnp = CreateNamedPipeA("not a named pipe", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
         /* nMaxInstances */ 1,
         /* nOutBufSize */ 1024,
         /* nInBufSize */ 1024,
@@ -58,27 +65,305 @@ void test_CreateNamedPipeA(void)
 
     if (hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
         /* Is this the right way to notify user of skipped tests? */
-        ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, 
-	    "CreateNamedPipe not supported on this platform, skipping tests.");
+        ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
+            "CreateNamedPipe not supported on this platform, skipping tests.");
         return;
     }
     ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME,
         "CreateNamedPipe should fail if name doesn't start with \\\\.\\pipe");
 
-    hnp = CreateNamedPipeA(NULL, 
-        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, 
+    hnp = CreateNamedPipeA(NULL,
+        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
         1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, NULL);
     ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PATH_NOT_FOUND,
         "CreateNamedPipe should fail if name is NULL");
 
-    hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
-	NULL, OPEN_EXISTING, 0, 0);
-    ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND, "connecting to nonexistent named pipe should fail with ERROR_FILE_NOT_FOUND");
+    hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(hFile == INVALID_HANDLE_VALUE
+        && GetLastError() == ERROR_FILE_NOT_FOUND,
+        "connecting to nonexistent named pipe should fail with ERROR_FILE_NOT_FOUND");
 
     /* Functional checks */
 
-    hnp = CreateNamedPipeA(PIPENAME,
-        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, 
+    hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+
+    hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    todo_wine {
+        ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed");
+    }
+
+    /* don't try to do i/o if one side couldn't be opened, as it hangs */
+    if (hFile != INVALID_HANDLE_VALUE) {
+        HANDLE hFile2;
+
+        /* Make sure we can read and write a few bytes in both directions */
+        memset(ibuf, 0, sizeof(ibuf));
+        ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile");
+        ok(written == sizeof(obuf), "write file len");
+        ok(ReadFile(hFile, ibuf, sizeof(obuf), &readden, NULL), "ReadFile");
+        ok(readden == sizeof(obuf), "read file len");
+        ok(memcmp(obuf, ibuf, written) == 0, "content check");
+
+        memset(ibuf, 0, sizeof(ibuf));
+        ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile");
+        ok(written == sizeof(obuf), "write file len");
+        ok(ReadFile(hnp, ibuf, sizeof(obuf), &readden, NULL), "ReadFile");
+        ok(readden == sizeof(obuf), "read file len");
+        ok(memcmp(obuf, ibuf, written) == 0, "content check");
+
+        /* Picky conformance tests */
+
+        /* Verify that you can't connect to pipe again
+         * until server calls DisconnectNamedPipe+ConnectNamedPipe
+         * or creates a new pipe
+         * case 1: other client not yet closed
+         */
+        hFile2 = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+        ok(hFile2 == INVALID_HANDLE_VALUE,
+            "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
+        ok(GetLastError() == ERROR_PIPE_BUSY,
+            "connecting to named pipe before other client closes should fail with ERROR_PIPE_BUSY");
+
+        ok(CloseHandle(hFile), "CloseHandle");
+
+        /* case 2: other client already closed */
+        hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+        ok(hFile == INVALID_HANDLE_VALUE,
+            "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
+        ok(GetLastError() == ERROR_PIPE_BUSY,
+            "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail with ERROR_PIPE_BUSY");
+
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe");
+
+        /* case 3: server has called DisconnectNamedPipe but not ConnectNamed Pipe */
+        hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+        ok(hFile == INVALID_HANDLE_VALUE,
+            "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
+        ok(GetLastError() == ERROR_PIPE_BUSY,
+            "connecting to named pipe after other client closes but before ConnectNamedPipe should fail with ERROR_PIPE_BUSY");
+
+        /* to be complete, we'd call ConnectNamedPipe here and loop,
+         * but by default that's blocking, so we'd either have
+         * to turn on the uncommon nonblocking mode, or
+         * use another thread.
+         */
+    }
+
+    ok(CloseHandle(hnp), "CloseHandle");
+
+    trace("test_CreateNamedPipe returning\n");
+}
+
+void test_CreateNamedPipe_instances_must_match(void)
+{
+    HANDLE hnp, hnp2;
+
+    /* Check no mismatch */
+    hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 2,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+
+    hnp2 = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 2,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp2 != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+
+    ok(CloseHandle(hnp), "CloseHandle");
+    ok(CloseHandle(hnp2), "CloseHandle");
+
+    /* Check nMaxInstances */
+    hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+
+    todo_wine {
+        hnp2 = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+            /* nMaxInstances */ 1,
+            /* nOutBufSize */ 1024,
+            /* nInBufSize */ 1024,
+            /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+            /* lpSecurityAttrib */ NULL);
+        ok(hnp2 == INVALID_HANDLE_VALUE
+            && GetLastError() == ERROR_PIPE_BUSY, "nMaxInstances not obeyed");
+    }
+
+    ok(CloseHandle(hnp), "CloseHandle");
+
+    /* Check PIPE_ACCESS_* */
+    hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 2,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+
+    todo_wine {
+        hnp2 = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_WAIT,
+            /* nMaxInstances */ 1,
+            /* nOutBufSize */ 1024,
+            /* nInBufSize */ 1024,
+            /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+            /* lpSecurityAttrib */ NULL);
+        ok(hnp2 == INVALID_HANDLE_VALUE
+            && GetLastError() == ERROR_ACCESS_DENIED, "PIPE_ACCESS_* mismatch allowed");
+    }
+
+    ok(CloseHandle(hnp), "CloseHandle");
+
+    /* etc, etc */
+}
+
+/** implementation of alarm() */
+static DWORD CALLBACK alarmThreadMain(LPVOID arg)
+{
+    DWORD timeout = (DWORD) arg;
+    trace("alarmThreadMain\n");
+    Sleep(timeout);
+    ok(FALSE, "alarm");
+    ExitProcess(1);
+    return 1;
+}
+
+HANDLE hnp = INVALID_HANDLE_VALUE;
+
+/** Trivial byte echo server - disconnects after each session */
+static DWORD CALLBACK serverThreadMain1(LPVOID arg)
+{
+    int i;
+
+    trace("serverThreadMain1 start\n");
+    /* Set up a simple echo server */
+    hnp = CreateNamedPipeA(PIPENAME "serverThreadMain1", PIPE_ACCESS_DUPLEX,
+        PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+    for (i = 0; ; i++) {
+        char buf[512];
+        DWORD written;
+        DWORD readden;
+        DWORD success;
+
+        /* Wait for client to connect */
+        trace("Server calling ConnectNamedPipe...\n");
+        ok(ConnectNamedPipe(hnp, NULL)
+            || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe");
+        trace("ConnectNamedPipe returned.\n");
+
+        /* Echo bytes once */
+        memset(buf, 0, sizeof(buf));
+
+        trace("Server reading...\n");
+        success = ReadFile(hnp, buf, sizeof(buf), &readden, NULL);
+        trace("Server done reading.\n");
+        ok(success, "ReadFile");
+
+        trace("Server writing...\n");
+        ok(WriteFile(hnp, buf, readden, &written, NULL), "WriteFile");
+        trace("Server done writing.\n");
+        ok(written == readden, "write file len");
+
+        /* finish this connection, wait for next one */
+        ok(FlushFileBuffers(hnp), "FlushFileBuffers");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe");
+    }
+}
+
+/** Trivial byte echo server - closes after each connection */
+static DWORD CALLBACK serverThreadMain2(LPVOID arg)
+{
+    int i;
+    HANDLE hnpNext = 0;
+
+    trace("serverThreadMain2\n");
+    /* Set up a simple echo server */
+    hnp = CreateNamedPipeA(PIPENAME "serverThreadMain2", PIPE_ACCESS_DUPLEX,
+        PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 2,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+
+    for (i = 0; ; i++) {
+        char buf[512];
+        DWORD written;
+        DWORD readden;
+        DWORD success;
+
+        /* Wait for client to connect */
+        trace("Server calling ConnectNamedPipe...\n");
+        ok(ConnectNamedPipe(hnp, NULL)
+            || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe");
+        trace("ConnectNamedPipe returned.\n");
+
+        /* Echo bytes once */
+        memset(buf, 0, sizeof(buf));
+
+        trace("Server reading...\n");
+        success = ReadFile(hnp, buf, sizeof(buf), &readden, NULL);
+        trace("Server done reading.\n");
+        ok(success, "ReadFile");
+
+        trace("Server writing...\n");
+        ok(WriteFile(hnp, buf, readden, &written, NULL), "WriteFile");
+        trace("Server done writing.\n");
+        ok(written == readden, "write file len");
+
+        /* finish this connection, wait for next one */
+        ok(FlushFileBuffers(hnp), "FlushFileBuffers");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe");
+
+        /* Set up next echo server */
+        hnpNext =
+            CreateNamedPipeA(PIPENAME "serverThreadMain2", PIPE_ACCESS_DUPLEX,
+            PIPE_TYPE_BYTE | PIPE_WAIT,
+            /* nMaxInstances */ 2,
+            /* nOutBufSize */ 1024,
+            /* nInBufSize */ 1024,
+            /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+            /* lpSecurityAttrib */ NULL);
+
+        ok(hnpNext != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+
+        ok(CloseHandle(hnp), "CloseHandle");
+        hnp = hnpNext;
+    }
+}
+
+/** Trivial byte echo server - uses overlapped named pipe calls */
+static DWORD CALLBACK serverThreadMain3(LPVOID arg)
+{
+    int i;
+    HANDLE hEvent;
+
+    trace("serverThreadMain3\n");
+    /* Set up a simple echo server */
+    hnp = CreateNamedPipeA(PIPENAME "serverThreadMain3", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+        PIPE_TYPE_BYTE | PIPE_WAIT,
         /* nMaxInstances */ 1,
         /* nOutBufSize */ 1024,
         /* nInBufSize */ 1024,
@@ -86,71 +371,228 @@ void test_CreateNamedPipeA(void)
         /* lpSecurityAttrib */ NULL);
     ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
 
-    hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
-            NULL, OPEN_EXISTING, 0, 0);
-    todo_wine
-    {
+    hEvent = CreateEvent(NULL,  // security attribute
+        TRUE,                   // manual reset event 
+        FALSE,                  // initial state 
+        NULL);                  // name
+    ok(hEvent != NULL, "CreateEvent");
+
+    for (i = 0; ; i++) {
+        char buf[512];
+        DWORD written;
+        DWORD readden;
+        DWORD dummy;
+        DWORD success;
+        OVERLAPPED oOverlap;
+        int letWFSOEwait = (i & 2);
+        int letGORwait = (i & 1);
+        DWORD err;
+
+        memset(&oOverlap, 0, sizeof(oOverlap));
+        oOverlap.hEvent = hEvent;
+
+        /* Wait for client to connect */
+        trace("Server calling overlapped ConnectNamedPipe...\n");
+        success = ConnectNamedPipe(hnp, &oOverlap);
+        err = GetLastError();
+        ok(success || err == ERROR_IO_PENDING
+            || err == ERROR_PIPE_CONNECTED, "overlapped ConnectNamedPipe");
+        trace("overlapped ConnectNamedPipe returned.\n");
+        if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+            ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait ConnectNamedPipe");
+        success = GetOverlappedResult(hnp, &oOverlap, &dummy, letGORwait);
+        if (!letGORwait && !letWFSOEwait && !success) {
+            ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult");
+            success = GetOverlappedResult(hnp, &oOverlap, &dummy, TRUE);
+        }
+        ok(success, "GetOverlappedResult ConnectNamedPipe");
+        trace("overlapped ConnectNamedPipe operation complete.\n");
+
+        /* Echo bytes once */
+        memset(buf, 0, sizeof(buf));
+
+        trace("Server reading...\n");
+        success = ReadFile(hnp, buf, sizeof(buf), NULL, &oOverlap);
+        trace("Server ReadFile returned...\n");
+        err = GetLastError();
+        ok(success || err == ERROR_IO_PENDING, "overlapped ReadFile");
+        trace("overlapped ReadFile returned.\n");
+        if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+            ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait ReadFile");
+        success = GetOverlappedResult(hnp, &oOverlap, &readden, letGORwait);
+        if (!letGORwait && !letWFSOEwait && !success) {
+            ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult");
+            success = GetOverlappedResult(hnp, &oOverlap, &readden, TRUE);
+        }
+        trace("Server done reading.\n");
+        ok(success, "overlapped ReadFile");
+
+        trace("Server writing...\n");
+        success = WriteFile(hnp, buf, readden, NULL, &oOverlap);
+        trace("Server WriteFile returned...\n");
+        err = GetLastError();
+        ok(success || err == ERROR_IO_PENDING, "overlapped WriteFile");
+        trace("overlapped WriteFile returned.\n");
+        if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+            ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait WriteFile");
+        success = GetOverlappedResult(hnp, &oOverlap, &written, letGORwait);
+        if (!letGORwait && !letWFSOEwait && !success) {
+            ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult");
+            success = GetOverlappedResult(hnp, &oOverlap, &written, TRUE);
+        }
+        trace("Server done writing.\n");
+        ok(success, "overlapped WriteFile");
+        ok(written == readden, "write file len");
+
+        /* finish this connection, wait for next one */
+        ok(FlushFileBuffers(hnp), "FlushFileBuffers");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe");
+    }
+}
+
+static void exercizeServer(const char *pipename, HANDLE serverThread)
+{
+    int i;
+
+    trace("exercizeServer starting\n");
+    for (i = 0; i < 8; i++) {
+        HANDLE hFile;
+        const char obuf[] = "Bit Bucket";
+        char ibuf[32];
+        DWORD written;
+        DWORD readden;
+        int loop;
+
+        for (loop = 0; loop < 3; loop++) {
+            DWORD err;
+            trace("Client connecting...\n");
+            /* Connect to the server */
+            hFile = CreateFileA(pipename, GENERIC_READ | GENERIC_WRITE, 0,
+                NULL, OPEN_EXISTING, 0, 0);
+            if (hFile != INVALID_HANDLE_VALUE)
+                break;
+            err = GetLastError();
+            if (loop == 0)
+                ok(err == ERROR_PIPE_BUSY || err == ERROR_FILE_NOT_FOUND, "connecting to pipe");
+            else
+                ok(err == ERROR_PIPE_BUSY, "connecting to pipe");
+            trace("connect failed, retrying\n");
+            Sleep(200);
+        }
+        ok(hFile != INVALID_HANDLE_VALUE, "client opening named pipe");
+
+        /* Make sure it can echo */
+        memset(ibuf, 0, sizeof(ibuf));
+        trace("Client writing...\n");
+        ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile to client end of pipe");
+        ok(written == sizeof(obuf), "write file len");
+        trace("Client reading...\n");
+        ok(ReadFile(hFile, ibuf, sizeof(obuf), &readden, NULL), "ReadFile from client end of pipe");
+        ok(readden == sizeof(obuf), "read file len");
+        ok(memcmp(obuf, ibuf, written) == 0, "content check");
+
+        trace("Client closing...\n");
+        ok(CloseHandle(hFile), "CloseHandle");
+    }
+
+    ok(TerminateThread(serverThread, 0), "TerminateThread");
+    CloseHandle(hnp);
+    trace("exercizeServer returning\n");
+}
+
+void test_NamedPipe_2(void)
+{
+    HANDLE serverThread;
+    DWORD serverThreadId;
+    HANDLE alarmThread;
+    DWORD alarmThreadId;
+
+    trace("test_NamedPipe_2 starting\n");
+    /* Set up a ten second timeout */
+    alarmThread = CreateThread(NULL, 0, alarmThreadMain, (void *) 10000, 0, &alarmThreadId);
+
+    /* The servers we're about to exercize do try to clean up carefully,
+     * but to reduce the change of a test failure due to a pipe handle
+     * leak in the test code, we'll use a different pipe name for each server.
+     */
+
+    /* Try server #1 */
+    serverThread = CreateThread(NULL, 0, serverThreadMain1, 0, 0, &serverThreadId);
+    ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread");
+    exercizeServer(PIPENAME "serverThreadMain1", serverThread);
+
+    /* Try server #2 */
+    serverThread = CreateThread(NULL, 0, serverThreadMain2, 0, 0, &serverThreadId);
+    ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread");
+    exercizeServer(PIPENAME "serverThreadMain2", serverThread);
+
+    /* Try server #3 */
+    serverThread = CreateThread(NULL, 0, serverThreadMain3, 0, 0, &serverThreadId);
+    ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread");
+    exercizeServer(PIPENAME "serverThreadMain3", serverThread);
+
+    ok(TerminateThread(alarmThread, 0), "TerminateThread");
+    trace("test_NamedPipe_2 returning\n");
+}
+
+void test_DisconnectNamedPipe(void)
+{
+    HANDLE hnp;
+    HANDLE hFile;
+    const char obuf[] = "Bit Bucket";
+    char ibuf[32];
+    DWORD written;
+    DWORD readden;
+
+    hnp = CreateNamedPipeA(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed");
+
+    ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL) == 0
+        && GetLastError() == ERROR_PIPE_LISTENING, "WriteFile to not-yet-connected pipe");
+    ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL) == 0
+        && GetLastError() == ERROR_PIPE_LISTENING, "ReadFile from not-yet-connected pipe");
+
+    hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    todo_wine {
         ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed");
     }
 
     /* don't try to do i/o if one side couldn't be opened, as it hangs */
     if (hFile != INVALID_HANDLE_VALUE) {
-	HANDLE hFile2;
 
-	/* Make sure we can read and write a few bytes in both directions*/
-	memset(ibuf, 0, sizeof(ibuf));
-	ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile");
-	ok(written == sizeof(obuf), "write file len");
-	ok(ReadFile(hFile, ibuf, sizeof(obuf), &gelesen, NULL), "ReadFile");
-	ok(gelesen == sizeof(obuf), "read file len");
-	ok(memcmp(obuf, ibuf, written) == 0, "content check");
-
-	memset(ibuf, 0, sizeof(ibuf));
-	ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile");
-	ok(written == sizeof(obuf), "write file len");
-	ok(ReadFile(hnp, ibuf, sizeof(obuf), &gelesen, NULL), "ReadFile");
-	ok(gelesen == sizeof(obuf), "read file len");
-	ok(memcmp(obuf, ibuf, written) == 0, "content check");
-
-	/* Picky conformance tests */
-
-	/* Verify that you can't connect to pipe again
-	 * until server calls DisconnectNamedPipe+ConnectNamedPipe
-	 * or creates a new pipe
-	 * case 1: other client not yet closed
-	 */
-	hFile2 = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
-	    NULL, OPEN_EXISTING, 0, 0);
-	ok(hFile2 == INVALID_HANDLE_VALUE, "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
-	ok(GetLastError() == ERROR_PIPE_BUSY, "connecting to named pipe before other client closes should fail with ERROR_PIPE_BUSY");
-
-	ok(CloseHandle(hFile), "CloseHandle");
-
-	/* case 2: other client already closed */
-	hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
-	    NULL, OPEN_EXISTING, 0, 0);
-	ok(hFile == INVALID_HANDLE_VALUE, "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
-	ok(GetLastError() == ERROR_PIPE_BUSY, "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail with ERROR_PIPE_BUSY");
-
-	ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe");
-
-	/* case 3: server has called DisconnectNamedPipe but not ConnectNamed Pipe */
-	hFile = CreateFileA(PIPENAME, GENERIC_READ|GENERIC_WRITE, 0, 
-	    NULL, OPEN_EXISTING, 0, 0);
-	ok(hFile == INVALID_HANDLE_VALUE, "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail");
-	ok(GetLastError() == ERROR_PIPE_BUSY, "connecting to named pipe after other client closes but before ConnectNamedPipe should fail with ERROR_PIPE_BUSY");
-
-	/* to be complete, we'd call ConnectNamedPipe here and loop,
-	 * but by default that's blocking, so we'd either have
-	 * to turn on the uncommon nonblocking mode, or
-	 * use another thread.
-	 */
+        /* see what happens if server calls DisconnectNamedPipe
+         * when there are bytes in the pipe
+         */
+
+        ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile");
+        ok(written == sizeof(obuf), "write file len");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe while messages waiting");
+        ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL) == 0
+            && GetLastError() == ERROR_PIPE_NOT_CONNECTED, "WriteFile to disconnected pipe");
+        ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL) == 0
+            && GetLastError() == ERROR_PIPE_NOT_CONNECTED,
+            "ReadFile from disconnected pipe with bytes waiting");
+        ok(CloseHandle(hFile), "CloseHandle");
     }
 
     ok(CloseHandle(hnp), "CloseHandle");
+
 }
 
 START_TEST(pipe)
 {
+    trace("test 1 of 4:\n");
     test_CreateNamedPipeA();
+    trace("test 2 of 4:\n");
+    test_CreateNamedPipe_instances_must_match();
+    trace("test 3 of 4:\n");
+    test_NamedPipe_2();
+    trace("test 4 of 4:\n");
+    test_DisconnectNamedPipe();
+    trace("all tests done\n");
 }


More information about the wine-patches mailing list