Mike McCormack wrote:
Well, I'm curious to know what happens when you:
* put a pipe into message mode, then do partial reads.
* do TransactNamedPipe, and there is already read data
available in the pipe.
* DisconnectNamedPipe on a pipe that still has messages in it
* try reading from a pipe that is not connected (before and after connecting)
* switch pipes between message mode and byte mode with data in the pipe

Er, looks like it'll be a while before I test those, but I do have a just-barely-nontrivial test; wine fails it in several ways. Attached for your testing pleasure. - Dan

--
Dan Kegel
http://www.kegel.com
http://counter.li.org/cgi-bin/runscript/display-person.cgi?user=78045
/*
 * Unit tests for named pipe functions in Wine
 *
 * Copyright (c) 2002 Dan Kegel
 *
 * 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 <stdlib.h>
#include <stdio.h>
#include <time.h>

#ifndef STANDALONE
#include "wine/test.h"
#else
#include <assert.h>
#define START_TEST(name) main(int argc, char **argv)
#define ok(condition, msg) assert(condition)
#define todo_wine
#endif

#include <wtypes.h>
#include <windef.h>
#include <winbase.h>
#include <winerror.h>

#define PIPENAME "\\\\.\\PiPe\\tests_" __FILE__

static void msg(const char *s)
{
        DWORD cbWritten;
        WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
                   s, strlen(s), &cbWritten, NULL);
}

void test_CreateNamedPipeA(void)
{
    HANDLE hnp;
    HANDLE hFile;
    const char obuf[] = "Bit Bucket";
    char ibuf[32];
    DWORD written;
    DWORD gelesen;

    /* Bad parameter checks */
    hnp = CreateNamedPipeA("not a named pipe", 
        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_WAIT, 
        /* nMaxInstances */ 1,
        /* nOutBufSize */ 1024,
        /* nInBufSize */ 1024,
        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
        /* lpSecurityAttrib */ NULL);

    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.");
        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, 
        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");

    /* Functional checks */

    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), &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.
         */
    }

    ok(CloseHandle(hnp), "CloseHandle");

}

/** implementation of alarm() */
static DWORD CALLBACK alarmThreadMain(LPVOID arg)
{
        DWORD timeout = (DWORD) arg;
        msg("alarmThreadMain\n");
        Sleep(timeout);
        ok(FALSE, "alarm");
        ExitProcess(1);
        return 1;
}

/** Trivial byte echo server */
static DWORD CALLBACK serverThreadMain(LPVOID arg)
{
        HANDLE hnp = (HANDLE) arg;
        int i;
    for (i=0; ; i++) {
                char buf[512];
                DWORD written;
                DWORD gelesen;
                DWORD success;

                /* Wait for client to connect */
                msg("Server calling ConnectNamedPipe...\n");
                ok(ConnectNamedPipe(hnp, NULL) || GetLastError() == 
ERROR_PIPE_CONNECTED, "ConnectNamedPipe");
                msg("ConnectNamedPipe returned.\n");

                /* Echo bytes once */
                memset(buf, 0, sizeof(buf));

                /* Hmm.  This read seems to completely hose wine.  Total lockup. */
                msg("Server reading...\n");
                success = ReadFile(hnp, buf, sizeof(buf), &gelesen, NULL);
                msg("Server done reading.\n");
                ok(success, "ReadFile");

                msg("Server writing...\n");
                ok(WriteFile(hnp, buf, gelesen, &written, NULL), "WriteFile");
                msg("Server done writing.\n");
                ok(written == gelesen, "write file len");

                /* finish this connection, wait for next one */
                ok(FlushFileBuffers(hnp), "FlushFileBuffers");
                ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe");
        }
}

void test_NamedPipe_2(void)
{
        HANDLE hnp;
        HANDLE serverThread;
        HANDLE alarmThread;
        DWORD serverThreadId;
        DWORD alarmThreadId;
        int i;

        /* Set up a ten second timeout */
    alarmThread = CreateThread(NULL,0,alarmThreadMain,(void *)10000,0,&alarmThreadId);

        /* Set up a simple echo server */
    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");
    serverThread = CreateThread(NULL,0,serverThreadMain,hnp,0,&serverThreadId);
        ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread");

        for (i=0; i<3; i++) {
                HANDLE hFile;
                const char obuf[] = "Bit Bucket";
                char ibuf[32];
                DWORD written;
                DWORD gelesen;
                int loop;

                for (loop=0; loop<3; loop++) {
                        msg("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;
                        /* must be the second time around the loop, and server hasn't 
called ConnectNamedPipe yet */
                        ok(GetLastError() == ERROR_PIPE_BUSY, "connecting to pipe");
                        msg("connect failed, retrying\n");
                        Sleep(10);
                }
                ok(hFile != INVALID_HANDLE_VALUE, "client opening named pipe");

                /* Make sure it can echo */
                memset(ibuf, 0, sizeof(ibuf));
                msg("Client writing...\n");
                ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile");
                ok(written == sizeof(obuf), "write file len");
                msg("Client reading...\n");
                ok(ReadFile(hFile, ibuf, sizeof(obuf), &gelesen, NULL), "ReadFile");
                ok(gelesen == sizeof(obuf), "read file len");
                ok(memcmp(obuf, ibuf, written) == 0, "content check");

                msg("Client closing...\n");
                ok(CloseHandle(hFile), "CloseHandle");
        }

        ok(TerminateThread(alarmThread, 0), "TerminateThread");
        ok(TerminateThread(serverThread, 0), "TerminateThread");
        ok(WaitForSingleObject(serverThread,5000)==WAIT_OBJECT_0,
     "TerminateThread didn't work");
        ok(CloseHandle(hnp), "CloseHandle");
        msg("test_NamedPipe_2 done\n");
}

START_TEST(pipe)
{
        test_NamedPipe_2();
    test_CreateNamedPipeA();
}

Reply via email to