OSSP CVS Repository

ossp - ossp-pkg/sio/sio_test.c
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/sio/sio_test.c
/*
**  OSSP sio - Stream I/O
**  Copyright (c) 2002-2005 Cable & Wireless <http://www.cw.com/>
**  Copyright (c) 2002-2005 The OSSP Project <http://www.ossp.org/>
**  Copyright (c) 2002-2005 Ralf S. Engelschall <rse@engelschall.com>
**
**  This file is part of OSSP sio, a layered stream I/O library
**  which can be found at http://www.ossp.org/pkg/lib/sio/.
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
**  SUCH DAMAGE.
**
**  sio_test.c: stream I/O library test suite
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>

#include "ts.h"
#include "al.h"
#include "sio.h"

#if ENABLE_BIO
#include <openssl/ssl.h>
#include <openssl/bio.h>
extern BIO_METHOD *BIO_s_socket();
extern sio_module_t sio_module_bio;
#endif

#if ENABLE_SA
#include "sa.h"
extern sio_module_t sio_module_sa;
#endif

extern sio_module_t sio_module_null;
extern sio_module_t sio_module_hole;
extern sio_module_t sio_module_buffer;
extern sio_module_t sio_module_zlib;
extern sio_module_t sio_module_sio;
extern sio_module_t sio_module_fd;
extern sio_module_t sio_module_hello;
extern sio_module_t sio_module_sillymux;


#define EVAL(name,rc,rc0,block,errf) \
    ts_test_check(TS_CTX, name); \
    block \
    if (rc != rc0) \
        ts_test_fail(TS_CTX, "%s -> %d[%s] (expected %d[%s])\n", \
            name, rc, errf(rc), rc0, errf(rc0))

#define EVAL0(X) EVAL(#X,rc,SIO_OK,{ rc = X; },sio_error)
#define EVALS(X) EVAL(#X,rc,SA_OK,{ rc = X; },sa_error)

static sio_rc_t sreadloop(sio_t *, char *, size_t, size_t *);
static int test_sio_pipe_read(ts_test_t *, int, int);
static int test_sio_pipe_write(ts_test_t *, int, int);

static int test_sio_hello_client(ts_test_t *, int, int);
static int test_sio_hello_server(ts_test_t *, int, int);

typedef int (*func)(ts_test_t *, int, int);
static void session(ts_test_t *, func, func, int);

static
sio_rc_t sreadloop(sio_t *sio, char *buf, size_t len, size_t *actualp)
{
    sio_rc_t rc = SIO_OK;
    size_t actual, total = 0;

    while (len > 0) {
        rc = sio_read(sio, buf, len, &actual);
        if (rc != SIO_OK)
            break;
        if (actual == 0)
            break;
        buf   += actual;
        total += actual;
        len   -= actual;
    }

    *actualp = total;

    return rc;
}

static
void session(ts_test_t *_t, func client, func server, int duplex)
{
    pid_t child;
    int pd[2];
    int wcount = 147;
    int status;

    fflush(stdout);
    fflush(stderr);

    if (duplex) {
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, pd) == -1) {
            ts_test_fail(TS_CTX, "cannot create socketpair (%s)\n",
                strerror(errno));
        }
    } else {
        if (pipe(pd) == -1) {
            ts_test_fail(TS_CTX, "cannot create pipe (%s)\n",
                strerror(errno));
        }
    }

    child = fork();
    if (child == -1) {
        ts_test_fail(TS_CTX, "cannot fork (%s)\n",
            strerror(errno));
        return;
    }

    if (child == 0) {
        int result;
        close(pd[0]);
        result = (*server)(NULL, pd[1], wcount);
        close(pd[1]);
        exit(result ? 1 : 0);
    } else {
        close(pd[1]);
        (*client)(_t, pd[0], wcount);
        close(pd[0]);
        waitpid(child, &status, 0);
        if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
            ts_test_fail(TS_CTX, "child terminated through exit with return code %d\n",
                WEXITSTATUS(status));
        }
        else if (WIFSIGNALED(status)) {
            ts_test_fail(TS_CTX, "child terminated through signal %d%s\n",
                WTERMSIG(status), WCOREDUMP(status) ? " (core dump written)" : "");
        }
        else if (WIFSTOPPED(status)) {
            ts_test_fail(TS_CTX, "child stopped through signal %d%s\n",
                WSTOPSIG(status));
        }
    }
}

TS_TEST(test_sio_buffer)
{
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_buffer, *sios_hole;
    size_t bufsize = 1000;     /* output/input buffer size */
    int i,wcount = 100;
    char S[] = "Hello world\n";
    size_t actual, len = strlen(S);

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(sio, &sio_module_hole, &sios_hole));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(sio, &sio_module_buffer, &sios_buffer));
    if (rc != SIO_OK) return;

    EVAL0(sio_configure_stage(sio, sios_buffer, "outputsize", &bufsize));
    if (rc != SIO_OK) return;

    EVAL0(sio_attach(sio, sios_hole, SIO_MODE_WRITE));
    if (rc != SIO_OK) return;
    EVAL0(sio_attach(sio, sios_buffer, SIO_MODE_WRITE));
    if (rc != SIO_OK) return;

    for (i=0; i<wcount; ++i) {
        EVAL0(sio_write(sio, S, len, &actual));
        if (rc != SIO_OK) break;
        if (actual != len) {
            ts_test_fail(TS_CTX, "sio_write result %d (expected %d)\n",
                (int)actual, (int)len);
            break;
        }
    }

    EVAL0(sio_detach(sio, sios_hole));
    EVAL0(sio_detach(sio, sios_buffer));
    EVAL0(sio_destroy_stage(sio, sios_buffer));
    EVAL0(sio_destroy_stage(sio, sios_hole));
    EVAL0(sio_destroy(sio));
}

TS_TEST(test_sio_fd)
{
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_buffer, *sios_fd;
    size_t bufsize = 1000;     /* output buffer size */
    size_t buflen  =   81;     /* fd input buffer size */
    int fd;
    int i,wcount = 100;
    char S[] = "Hello world\n";
    char buf[sizeof(S)];
    size_t actual, len = strlen(S);
    char tempfile[] = "./sio_test_tmpfile.XXXXXX";
    int do_unlink = 0;

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(sio, &sio_module_fd, &sios_fd));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(sio, &sio_module_buffer, &sios_buffer));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(sio, sios_fd, "buflen", &buflen));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(sio, sios_buffer, "outputsize", &bufsize));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(sio, sios_buffer, "inputsize", &bufsize));
    if (rc != SIO_OK) return;

    /*
     * WRITE phase
     */

    mktemp(tempfile);
    fd = open(tempfile, O_CREAT|O_EXCL|O_WRONLY, 0600);
    if (fd < 0) {
        ts_test_fail(TS_CTX, "cannot create temporary file \"%s\" (%s)\n",
            tempfile, strerror(errno));
    } else {
        do_unlink = 1;
        EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
        if (rc != SIO_OK) goto badwrite;
        EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
        if (rc != SIO_OK) goto badwrite;
        EVAL0(sio_attach(sio, sios_buffer, SIO_MODE_READWRITE));
        if (rc != SIO_OK) goto badwrite2;

        for (i=0; i<wcount; ++i) {
            EVAL0(sio_write(sio, S, len, &actual));
            if (rc != SIO_OK) break;
            if (actual != len) {
                ts_test_fail(TS_CTX, "sio_write result %d (expected %d)\n",
                    (int)actual, (int)len);
                break;
            }
        }

        EVAL0(sio_push(sio));
        EVAL0(sio_detach(sio, sios_buffer));

        badwrite2:
        EVAL0(sio_detach(sio, sios_fd));

        badwrite:
        close(fd);
    }

    fd = open(tempfile, O_RDONLY);
    if (fd < 0) {
        ts_test_fail(TS_CTX, "cannot read temporary file \"%s\" (%s)\n",
            tempfile, strerror(errno));
    } else {
        EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
        if (rc != SIO_OK) goto badread;
        EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
        if (rc != SIO_OK) goto badread;
        EVAL0(sio_attach(sio, sios_buffer, SIO_MODE_READWRITE));
        if (rc != SIO_OK) goto badread2;

        for (i=0; i<wcount; ++i) {
            EVAL0(sreadloop(sio, buf, len, &actual));
            if (rc != SIO_OK) break;
            if (actual != len) {
                ts_test_fail(TS_CTX, "sio_read result %d (expected %d)\n",
                    (int)actual, (int)len);
                break;
            }
            buf[actual] = '\0';
            if (strcmp(buf, S)) {
                ts_test_fail(TS_CTX, "sio_read data mismatch at loop %d\n",
                    i);
                break;
            }
        }

        EVAL0(sio_detach(sio, sios_buffer));

        badread2:
        EVAL0(sio_detach(sio, sios_fd));

        badread:
        close(fd);
    }

    if (do_unlink && unlink(tempfile) < 0) {
        ts_test_fail(TS_CTX, "cannot unlink temporary file \"%s\" (%s)\n",
            tempfile, strerror(errno));
    }

    /*
     * common cleanup
     */

    EVAL0(sio_destroy_stage(sio, sios_buffer));
    EVAL0(sio_destroy_stage(sio, sios_fd));
    EVAL0(sio_destroy(sio));
}

static
int test_sio_pipe_read(ts_test_t *_t, int fd, int wcount)
{
    int error = 0, result = -1;
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_buffer, *sios_fd;
    size_t bufsize =  987;     /* output buffer size */
    size_t buflen  =   64;     /* fd input buffer size */
    int i;
    char S[] = "Hello world\n";
    char buf[sizeof(S)];
    size_t actual, len = strlen(S);

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_fd, &sios_fd));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_buffer, &sios_buffer));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_fd, "buflen", &buflen));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_buffer, "outputsize", &bufsize));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_buffer, "inputsize", &bufsize));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
    if (rc != SIO_OK) goto badread;
    EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badread;
    EVAL0(sio_attach(sio, sios_buffer, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badread2;

    for (i=0; i<wcount; ++i) {
        EVAL0(sreadloop(sio, buf, len, &actual));
        if (rc != SIO_OK) {
            error = 1;
            break;
        }
        if (actual != len) {
            ts_test_fail(TS_CTX, "sio_read result %d (expected %d)\n",
                (int)actual, (int)len);
            error = 1;
            break;
        }
        buf[actual] = '\0';
        if (strcmp(buf, S)) {
            ts_test_fail(TS_CTX, "sio_read data mismatch at loop %d\n",
                i);
            error = 1;
            break;
        }
    }

    EVAL0(sio_detach(sio, sios_buffer));
    if (rc != SIO_OK) error = 1;
    result = error;

    badread2:
    EVAL0(sio_detach(sio, sios_fd));
    if (rc != SIO_OK) result = -1;

    badread:
    EVAL0(sio_destroy_stage(sio, sios_buffer));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy_stage(sio, sios_fd));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy(sio));
    if (rc != SIO_OK) result = -1;

    return result;
}

static
int test_sio_pipe_write(ts_test_t *_t, int fd, int wcount)
{
    int error = 0, result = -1;
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_buffer, *sios_fd;
    size_t bufsize = 1003;     /* output buffer size */
    size_t buflen  =   47;     /* fd input buffer size */
    int i;
    char S[] = "Hello world\n";
    size_t actual, len = strlen(S);

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_fd, &sios_fd));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_buffer, &sios_buffer));
    if (rc != SIO_OK) return -1;

    EVAL0(sio_configure_stage(sio, sios_fd, "buflen", &buflen));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_buffer, "outputsize", &bufsize));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_buffer, "inputsize", &bufsize));
    if (rc != SIO_OK) return -1;

    /*
     * WRITE phase
     */

    EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
    if (rc != SIO_OK) goto badwrite;
    EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badwrite;
    EVAL0(sio_attach(sio, sios_buffer, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badwrite2;

    for (i=0; i<wcount; ++i) {
        EVAL0(sio_write(sio, S, len, &actual));
        if (rc != SIO_OK) {
            error = 1;
            break;
        } 
        if (actual != len) {
            ts_test_fail(TS_CTX, "sio_write result %d (expected %d)\n",
                (int)actual, (int)len);
            error = 1;
            break;
        }
    }

    EVAL0(sio_push(sio));
    if (rc != SIO_OK) error = 1;
    EVAL0(sio_detach(sio, sios_buffer));
    if (rc != SIO_OK) error = 1;
    result = error;

    badwrite2:
    EVAL0(sio_detach(sio, sios_fd));
    if (rc != SIO_OK) result = -1;

    badwrite:
    EVAL0(sio_destroy_stage(sio, sios_buffer));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy_stage(sio, sios_fd));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy(sio));
    if (rc != SIO_OK) result = -1;

    return result;
}

TS_TEST(test_sio_pipe)
{
    session(_t, test_sio_pipe_read, test_sio_pipe_write, 0);
}

TS_TEST(test_sio_sio)
{
    sio_rc_t rc;
    sio_t *A_sio;
    sio_stage_t *A_sios_sillymux;
    sio_stage_t *A_sios_fd;
    sio_t *B_sio;
    sio_stage_t *B_sios_sio;
    sio_t *C_sio;
    sio_stage_t *C_sios_sio;
    size_t buflen  = 53;
    int i,wcount = 100;
    char SO[] = "Hello world\n";
    size_t lenodd = strlen(SO);
    char SE[] = "Goodbye cruel world\n";
    size_t leneven = strlen(SE);
    size_t actual;
    al_label_t ODD  = (al_label_t)1;
    al_label_t EVEN = (al_label_t)2;
    char tempfile[] = "./sio_test_tmpfile.XXXXXX";
    int fd;
    int neven, nodd;
    int ceven, codd;

    mktemp(tempfile);
    fd = open(tempfile, O_CREAT|O_EXCL|O_RDWR, 0600);
    if (fd < 0) {
        ts_test_fail(TS_CTX, "cannot create temporary file \"%s\" (%s)\n",
            tempfile, strerror(errno));
        return;
    }

    for (i=0; i<wcount; ++i) {
        if (write(fd, SO, lenodd) != lenodd ||
            write(fd, SE, leneven) != leneven) {
            ts_test_fail(TS_CTX, "cannot write temporary file \"%s\" (%s)\n",
                tempfile, strerror(errno));
            goto close_and_cleanup;
        }
    }

    if (lseek(fd, (off_t)0, SEEK_SET) == -1) {
            ts_test_fail(TS_CTX, "cannot rewind temporary file \"%s\" (%s)\n",
                tempfile, strerror(errno));
            goto close_and_cleanup;
    }

    /*
     * create MUX stream
     */
    EVAL0(sio_create(&A_sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(A_sio, &sio_module_fd, &A_sios_fd));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(A_sio, A_sios_fd, "fd", &fd));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(A_sio, A_sios_fd, "buflen", &buflen));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(A_sio, &sio_module_sillymux, &A_sios_sillymux));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(A_sio, A_sios_sillymux, "oddlabel", ODD));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(A_sio, A_sios_sillymux, "evenlabel", EVEN));
    if (rc != SIO_OK) return;
    EVAL0(sio_attach(A_sio, A_sios_fd, SIO_MODE_READWRITE));
    if (rc != SIO_OK) return;
    EVAL0(sio_attach(A_sio, A_sios_sillymux, SIO_MODE_READWRITE));
    if (rc != SIO_OK) return;

    /*
     * create ODD stream
     */
    EVAL0(sio_create(&B_sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(B_sio, &sio_module_sio, &B_sios_sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(B_sio, B_sios_sio, "upstream", A_sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(B_sio, B_sios_sio, "mydatalabel", ODD));
    if (rc != SIO_OK) return;
    EVAL0(sio_attach(B_sio, B_sios_sio, SIO_MODE_READWRITE));
    if (rc != SIO_OK) return;

    /*
     * create EVEN stream
     */
    EVAL0(sio_create(&C_sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(C_sio, &sio_module_sio, &C_sios_sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(C_sio, C_sios_sio, "upstream", A_sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(C_sio, C_sios_sio, "mydatalabel", EVEN));
    if (rc != SIO_OK) return;
    EVAL0(sio_attach(C_sio, C_sios_sio, SIO_MODE_READWRITE));
    if (rc != SIO_OK) return;

    /*
     * read from both streams
     *
     * note that sillymux doesn't know how to multiplex
     * signalling chunks. However, since input is blocking
     * we are guaranteed that we have reached EOF as soon
     * as no data is passed downstream.
     *
     * a real mux would might replicate signalling chunks
     * to each listener or synthesize its own error/eof
     * conditions
     *
     */
    nodd = neven = 0;
    codd = ceven = 0;
    for (;;) {
        char buf[17], *p;
        int nstreams = 2;

        EVAL0(
            ( (rc = sreadloop(B_sio, buf, sizeof(buf), &actual)),
              rc == SIO_ERR_EOF ? (--nstreams, SIO_OK) : rc )
        );
        if (rc != SIO_OK)
            break;
        p = buf;
        while (actual > 0) {
            int n = lenodd - codd;
            if (n > actual) n = actual;
            if (memcmp(p, SO + codd, n) != 0) {
                ts_test_fail(TS_CTX, "data mismatch on odd stream\n");
                break;
            }
            actual -= n;
            p      += n;
            codd   += n;
            if (codd >= lenodd) {
                codd = 0;
                ++nodd;
                if (nodd > wcount)
                    ts_test_fail(TS_CTX, "excess data on odd stream\n");
            }
        }
        if (actual > 0)
            break;

        EVAL0(
            ( (rc = sreadloop(C_sio, buf, sizeof(buf), &actual)),
              rc == SIO_ERR_EOF ? (--nstreams, SIO_OK) : rc )
        );
        if (rc != SIO_OK)
            break;
        p = buf;
        while (actual > 0) {
            int n = leneven - ceven;
            if (n > actual) n = actual;
            if (memcmp(p, SE + ceven, n) != 0) {
                ts_test_fail(TS_CTX, "data mismatch on even stream\n");
                break;
            }
            actual -= n;
            p      += n;
            ceven  += n;
            if (ceven >= leneven) {
                ceven = 0;
                ++neven;
                if (neven > wcount)
                    ts_test_fail(TS_CTX, "excess data on even stream\n");
            }
        }
        if (actual > 0)
            break;

        if (nstreams == 0)
            break;
    }
    if (nodd < wcount)
        ts_test_fail(TS_CTX, "missing data on odd stream %d vs %d\n",nodd,wcount);
    if (neven < wcount)
        ts_test_fail(TS_CTX, "missing data on even stream %d vs %d\n",neven,wcount);
    if (codd != 0)
        ts_test_fail(TS_CTX, "extra chars on odd stream\n");
    if (ceven != 0)
        ts_test_fail(TS_CTX, "extra chars on even stream\n");

    /*
     * destroy EVEN stream
     */
    EVAL0(sio_detach(C_sio, C_sios_sio));
    EVAL0(sio_destroy_stage(C_sio, C_sios_sio));
    EVAL0(sio_destroy(C_sio));

    /*
     * destroy ODD stream
     */
    EVAL0(sio_detach(B_sio, B_sios_sio));
    EVAL0(sio_destroy_stage(B_sio, B_sios_sio));
    EVAL0(sio_destroy(B_sio));

    /*
     * destroy MUX stream
     */
    EVAL0(sio_detach(A_sio, A_sios_sillymux));
    EVAL0(sio_detach(A_sio, A_sios_fd));
    EVAL0(sio_destroy_stage(A_sio, A_sios_sillymux));
    EVAL0(sio_destroy_stage(A_sio, A_sios_fd));
    EVAL0(sio_destroy(A_sio));

close_and_cleanup:
    close(fd);
    if (unlink(tempfile) < 0) {
        ts_test_fail(TS_CTX, "cannot unlink temporary file \"%s\" (%s)\n",
            tempfile, strerror(errno));
    }
}

static
int test_sio_hello_client(ts_test_t *_t, int fd, int dummy)
{
    FILE *f;
    char buf[81];
    int c, i;
    int do_login = 1;
    char S[] = "Welcome to the real world\r\n";
    int result = 0;

    f = fdopen(fd, "r+");
    if (f == NULL) {
        ts_test_fail(TS_CTX, "cannot make FILE handle\n");
        return -1;
    }

    i = 0;
    for (;;) {
        c = fgetc(f);
        if (c == EOF) {
            ts_test_fail(TS_CTX, "EOF from server\n");
            result = -1;
            break;
        }
        if (i >= 80) {
            ts_test_fail(TS_CTX, "input buffer overflow\n");
            result = -1;
            break;
        }
        buf[i++] = c;
        buf[i] = '\0';
        if (do_login) {
            if (i > 7) {
                ts_test_fail(TS_CTX, "handshake failure\n");
                result = -1;
                break;
            }
            if (strcmp(buf, "Login: ") == 0) {
                fputs("Geheim\r\n", f);
                fflush(f);
                i = 0;
                do_login = 0;
            }
        } else if (c == '\n')
            break;
    }

    if (strcmp(buf, S)) {
        ts_test_fail(TS_CTX, "data mismatch\n");
        result = -1;
    }

    fclose(f);

    return result;
}

static
int test_sio_hello_server(ts_test_t *_t, int fd, int dummy)
{
    int error = 0, result = -1;
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_hello, *sios_fd;
    size_t buflen  =   47;     /* fd input buffer size */
    char S[] = "Welcome to the real world\r\n";
    size_t actual, len = strlen(S);

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_fd, &sios_fd));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_hello, &sios_hello));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_fd, "buflen", &buflen));
    if (rc != SIO_OK) return -1;

    EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
    if (rc != SIO_OK) goto badwrite;
    EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badwrite;
    EVAL0(sio_attach(sio, sios_hello, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badwrite2;
    EVAL0(sio_write(sio, S, len, &actual));

    if (rc != SIO_OK) error = 1;
    EVAL0(sio_push(sio));
    if (rc != SIO_OK) error = 1;
    EVAL0(sio_detach(sio, sios_hello));
    if (rc != SIO_OK) error = 1;

    result = error;

    badwrite2:
    EVAL0(sio_detach(sio, sios_fd));
    if (rc != SIO_OK) result = -1;

    badwrite:

    /*
     * common cleanup
     */

    EVAL0(sio_destroy_stage(sio, sios_hello));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy_stage(sio, sios_fd));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy(sio));
    if (rc != SIO_OK) result = -1;

    return result;
}

TS_TEST(test_sio_hello)
{
    session(_t, test_sio_hello_server, test_sio_hello_client, 1);
}

#if ENABLE_ZLIB
TS_TEST(test_sio_zlib)
{
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_zlib, *sios_fd;
    size_t bufsize = 1000;     /* input/output buffer size */
    int    zoutlevel =  9;     /* compress best on output */
    int    zinlevel  = -1;     /* decompress input */
    size_t buflen  =   81;     /* fd input buffer size */
    int fd;
    int i,wcount = 100;
    char S[] = "Hello world\n";
    char buf[sizeof(S)];
    size_t actual, len = strlen(S);
    char tempfile[] = "./sio_test_tmpfile.XXXXXX";
    int do_unlink = 0;

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(sio, &sio_module_fd, &sios_fd));
    if (rc != SIO_OK) return;
    EVAL0(sio_create_stage(sio, &sio_module_zlib, &sios_zlib));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(sio, sios_fd, "buflen", &buflen));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(sio, sios_zlib, "outputsize", &bufsize));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(sio, sios_zlib, "inputsize", &bufsize));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(sio, sios_zlib, "outputlevel", &zoutlevel));
    if (rc != SIO_OK) return;
    EVAL0(sio_configure_stage(sio, sios_zlib, "inputlevel", &zinlevel));
    if (rc != SIO_OK) return;

    /*
     * WRITE phase
     */

    mktemp(tempfile);
    fd = open(tempfile, O_CREAT|O_EXCL|O_WRONLY, 0600);
    if (fd < 0) {
        ts_test_fail(TS_CTX, "cannot create temporary file \"%s\" (%s)\n",
            tempfile, strerror(errno));
    } else {
        do_unlink = 1;
        EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
        if (rc != SIO_OK) goto badwrite;
        EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
        if (rc != SIO_OK) goto badwrite;
        EVAL0(sio_attach(sio, sios_zlib, SIO_MODE_READWRITE));
        if (rc != SIO_OK) goto badwrite2;

        for (i=0; i<wcount; ++i) {
            EVAL0(sio_write(sio, S, len, &actual));
            if (rc != SIO_OK) break;
            if (actual != len) {
                ts_test_fail(TS_CTX, "sio_write result %d (expected %d)\n",
                    (int)actual, (int)len);
                break;
            }
        }

        EVAL0(sio_push(sio));
        EVAL0(sio_detach(sio, sios_zlib));

        badwrite2:
        EVAL0(sio_detach(sio, sios_fd));

        badwrite:
        close(fd);
    }

    fd = open(tempfile, O_RDONLY);
    if (fd < 0) {
        ts_test_fail(TS_CTX, "cannot read temporary file \"%s\" (%s)\n",
            tempfile, strerror(errno));
    } else {
        EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
        if (rc != SIO_OK) goto badread;
        EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
        if (rc != SIO_OK) goto badread;
        EVAL0(sio_attach(sio, sios_zlib, SIO_MODE_READWRITE));
        if (rc != SIO_OK) goto badread2;

        for (i=0; i<wcount; ++i) {
            EVAL0(sreadloop(sio, buf, len, &actual));
            if (rc != SIO_OK) break;
            if (actual != len) {
                ts_test_fail(TS_CTX, "sio_read result %d (expected %d)\n",
                    (int)actual, (int)len);
                break;
            }
            buf[actual] = '\0';
            if (strcmp(buf, S)) {
                ts_test_fail(TS_CTX, "sio_read data mismatch at loop %d\n",
                    i);
                break;
            }
        }

        EVAL0(sio_detach(sio, sios_zlib));

        badread2:
        EVAL0(sio_detach(sio, sios_fd));

        badread:
        close(fd);
    }

    if (do_unlink && unlink(tempfile) < 0) {
        ts_test_fail(TS_CTX, "cannot unlink temporary file \"%s\" (%s)\n",
            tempfile, strerror(errno));
    }

    /*
     * common cleanup
     */

    EVAL0(sio_destroy_stage(sio, sios_zlib));
    EVAL0(sio_destroy_stage(sio, sios_fd));
    EVAL0(sio_destroy(sio));
}
#endif

#if ENABLE_SA
static
int test_sio_sa_read(ts_test_t *_t, sa_t *sa, int wcount)
{
    int error = 0, result = -1;
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_buffer, *sios_sa;
    size_t bufsize =  987;     /* output buffer size */
    size_t buflen  =   64;     /* sa input buffer size */
    int i;
    char S[] = "Hello world\n";
    char buf[sizeof(S)];
    size_t actual, len = strlen(S);

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_sa, &sios_sa));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_buffer, &sios_buffer));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_sa, "buflen", &buflen));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_buffer, "outputsize", &bufsize));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_buffer, "inputsize", &bufsize));
    if (rc != SIO_OK) return -1;

    EVAL0(sio_configure_stage(sio, sios_sa, "sa", sa));
    if (rc != SIO_OK) goto badread;
    EVAL0(sio_attach(sio, sios_sa, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badread;
    EVAL0(sio_attach(sio, sios_buffer, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badread2;

    for (i=0; i<wcount; ++i) {
        EVAL0(sreadloop(sio, buf, len, &actual));
        if (rc != SIO_OK) {
            error = 1;
            break;
        }
        if (actual != len) {
            ts_test_fail(TS_CTX, "sio_read result %d (expected %d)\n",
                (int)actual, (int)len);
            error = 1;
            break;
        }
        buf[actual] = '\0';
        if (strcmp(buf, S)) {
            ts_test_fail(TS_CTX, "sio_read data mismatch at loop %d\n",
                i);
            error = 1;
            break;
        }
    }

    EVAL0(sio_detach(sio, sios_buffer));
    if (rc != SIO_OK) error = 1;

    result = error;

    badread2:
    EVAL0(sio_detach(sio, sios_sa));
    if (rc != SIO_OK) result = -1;

    badread:
    EVAL0(sio_destroy_stage(sio, sios_buffer));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy_stage(sio, sios_sa));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy(sio));
    if (rc != SIO_OK) result = -1;

    return result;
}

static
int test_sio_sa_write(ts_test_t *_t, sa_t *sa, int wcount)
{
    int error = 0, result = -1;
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_buffer, *sios_sa;
    size_t bufsize = 1003;     /* output buffer size */
    size_t buflen  =   47;     /* sa input buffer size */
    int i;
    char S[] = "Hello world\n";
    size_t actual, len = strlen(S);

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_sa, &sios_sa));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_buffer, &sios_buffer));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_sa, "buflen", &buflen));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_buffer, "outputsize", &bufsize));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_buffer, "inputsize", &bufsize));
    if (rc != SIO_OK) return -1;

    /*
     * WRITE phase
     */

    EVAL0(sio_configure_stage(sio, sios_sa, "sa", sa));
    if (rc != SIO_OK) goto badwrite;
    EVAL0(sio_attach(sio, sios_sa, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badwrite;
    EVAL0(sio_attach(sio, sios_buffer, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badwrite2;

    for (i=0; i<wcount; ++i) {
        EVAL0(sio_write(sio, S, len, &actual));
        if (rc != SIO_OK) {
           error = 1;
           break;
        }
        if (actual != len) {
            ts_test_fail(TS_CTX, "sio_write result %d (expected %d)\n",
                (int)actual, (int)len);
            error = 1;
            break;
        }
    }

    EVAL0(sio_push(sio));
    if (rc != SIO_OK) error = 1;
    EVAL0(sio_detach(sio, sios_buffer));
    if (rc != SIO_OK) error = 1;
    result = error;

    badwrite2:
    EVAL0(sio_detach(sio, sios_sa));
    if (rc != SIO_OK) result = -1;

    badwrite:

    /*
     * common cleanup
     */

    EVAL0(sio_destroy_stage(sio, sios_buffer));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy_stage(sio, sios_sa));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy(sio));
    if (rc != SIO_OK) result = -1;

    return result;
}

TS_TEST(test_sio_sa)
{
    pid_t child;
    sa_rc_t rc;
    sa_addr_t *saa_b, *saa_p;
    sa_t *sa_client, *sa_server;
    int wcount = 147;
    int status;

    fflush(stdout);
    fflush(stderr);

    EVALS(sa_create(&sa_server));
    if (rc != SA_OK) goto sa_bail;
    EVALS(sa_timeout(sa_server, SA_TIMEOUT_ALL, 5, 0));
    if (rc != SA_OK) goto sa_bail1;
    EVALS(sa_option(sa_server, SA_OPTION_REUSEADDR, 1));
    if (rc != SA_OK) goto sa_bail1;
    EVALS(sa_addr_create(&saa_b));
    if (rc != SA_OK) goto sa_bail1;
    EVALS(sa_addr_u2a(saa_b, "inet://127.0.0.1:0"));
    if (rc != SA_OK) goto sa_bail1;
    EVALS(sa_bind(sa_server, saa_b));
    if (rc != SA_OK) goto sa_bail2;
    EVALS(sa_listen(sa_server, 128));
    if (rc != SA_OK) goto sa_bail2;
    EVALS(sa_getlocal(sa_server, &saa_p));
    if (rc != SA_OK) goto sa_bail2;
    EVALS(sa_create(&sa_client));
    if (rc != SA_OK) goto sa_bail3;
    EVALS(sa_timeout(sa_client, SA_TIMEOUT_ALL, 5, 0));
    if (rc != SA_OK) goto sa_bail4;

    child = fork();
    if (child == -1) {
        ts_test_fail(TS_CTX, "cannot fork (%s)\n",
            strerror(errno));
        return;
    }

    if (child == 0) {
        int result;
        sa_t *sa_conn;
        sa_addr_t *saa_c;
        rc = sa_accept(sa_server, &saa_c, &sa_conn);
        if (rc != SA_OK) exit(2);
        rc = sa_addr_destroy(saa_c);
        rc = sa_shutdown(sa_server, "rw");
        rc = sa_shutdown(sa_conn, "r");
        result = test_sio_sa_write(NULL, sa_conn, wcount);
        rc = sa_shutdown(sa_conn, "w");
        exit(result ? 1 : 0);
    } else {
        EVALS(sa_connect(sa_client, saa_p));
        rc = sa_shutdown(sa_server, "w");
        test_sio_sa_read(_t, sa_client, wcount);
        rc = sa_shutdown(sa_server, "r");
        waitpid(child, &status, 0);
        if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
            ts_test_fail(TS_CTX, "child terminated through exit with return code %d\n",
                WEXITSTATUS(status));
        }
        else if (WIFSIGNALED(status)) {
            ts_test_fail(TS_CTX, "child terminated through signal %d%s\n",
                WTERMSIG(status), WCOREDUMP(status) ? " (core dump written)" : "");
        }
        else if (WIFSTOPPED(status)) {
            ts_test_fail(TS_CTX, "child stopped through signal %d%s\n",
                WSTOPSIG(status));
        }
    }

sa_bail4:
    EVALS(sa_destroy(sa_client));
sa_bail3:
    EVALS(sa_addr_destroy(saa_p));
sa_bail2:
    EVALS(sa_addr_destroy(saa_b));
sa_bail1:
    EVALS(sa_destroy(sa_server));
sa_bail:
    return;
}

#endif

#if ENABLE_BIO
static
BIO *get_server_bio()
{
    SSL_CTX *ctx;
    BIO *bio;

    ERR_load_BIO_strings();
    OpenSSL_add_ssl_algorithms();
    ctx = SSL_CTX_new(SSLv23_server_method());
    if (!SSL_CTX_use_certificate_file(ctx, "sio_test.cer", SSL_FILETYPE_PEM) ||
        !SSL_CTX_use_PrivateKey_file(ctx, "sio_test.key", SSL_FILETYPE_PEM)) {
        SSL_CTX_free(ctx);
        return NULL;
    }
    bio = BIO_new_ssl(ctx,0);
    if (bio == NULL)
        SSL_CTX_free(ctx);
    return bio;
}

static
BIO *get_client_bio()
{
    SSL_CTX *ctx;
    BIO *bio;

    ERR_load_BIO_strings();
    OpenSSL_add_ssl_algorithms();
    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx == NULL)
        return NULL;
    bio = BIO_new_ssl(ctx,1);
    if (bio == NULL)
        SSL_CTX_free(ctx);
    return bio;
}

static
int test_sio_bio_read(ts_test_t *_t, int fd, int wcount)
{
    int error = 0, result = -1;
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_bio, *sios_fd;
    BIO *bio;
    int yes = 1;
    size_t buflen  =   99;     /* fd input buffer size */
    size_t bufsize =  343;     /* BIO input buffer size */
    int i;
    char S[] = "Hello world\n";
    char buf[sizeof(S)];
    size_t actual, len = strlen(S);

    bio = get_client_bio();
    if (bio == NULL) {
            ts_test_fail(TS_CTX, "cannot create client BIO\n");
            return -1;
    }

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_fd, &sios_fd));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_bio, &sios_bio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_fd, "buflen", &buflen));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_bio, "bio", bio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_bio, "freebio", &yes));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_bio, "inputsize", &bufsize));
    if (rc != SIO_OK) return -1;

    EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
    if (rc != SIO_OK) goto badread;
    EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badread;
    EVAL0(sio_attach(sio, sios_bio, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badread2;

    for (i=0; i<wcount; ++i) {
        EVAL0(sreadloop(sio, buf, len, &actual));
        if (rc != SIO_OK) {
            error = 1;
            break;
        }
        if (actual != len) {
            ts_test_fail(TS_CTX, "sio_read result %d (expected %d)\n",
                (int)actual, (int)len);
            error = 1;
            break;
        }
        buf[actual] = '\0';
        if (strcmp(buf, S)) {
            ts_test_fail(TS_CTX, "sio_read data mismatch at loop %d\n",
                i);
            error = 1;
            break;
        }
    }

    EVAL0(sio_detach(sio, sios_bio));
    if (rc != SIO_OK) error = 1;
    result = error;

    badread2:
    EVAL0(sio_detach(sio, sios_fd));
    if (rc != SIO_OK) result = -1;

    badread:
    EVAL0(sio_destroy_stage(sio, sios_bio));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy_stage(sio, sios_fd));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy(sio));
    if (rc != SIO_OK) result = -1;

    return result;
}

static
int test_sio_bio_write(ts_test_t *_t, int fd, int wcount)
{
    int error = 0, result = -1;
    sio_rc_t rc;
    sio_t *sio;
    sio_stage_t *sios_bio, *sios_fd;
    BIO *bio;
    int yes = 1;
    size_t buflen  =   256;     /* fd input buffer size */
    size_t bufsize =    73;     /* BIO input buffer size */
    int i;
    char S[] = "Hello world\n";
    size_t actual, len = strlen(S);

    bio = get_server_bio();
    if (bio == NULL) {
            ts_test_fail(TS_CTX, "cannot create server BIO\n");
            return -1;
    }

    EVAL0(sio_create(&sio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_fd, &sios_fd));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_create_stage(sio, &sio_module_bio, &sios_bio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_fd, "buflen", &buflen));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_bio, "bio", bio));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_bio, "freebio", &yes));
    if (rc != SIO_OK) return -1;
    EVAL0(sio_configure_stage(sio, sios_bio, "inputsize", &bufsize));
    if (rc != SIO_OK) return -1;

    /*
     * WRITE phase
     */

    EVAL0(sio_configure_stage(sio, sios_fd, "fd", &fd));
    if (rc != SIO_OK) goto badwrite;
    EVAL0(sio_attach(sio, sios_fd, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badwrite;
    EVAL0(sio_attach(sio, sios_bio, SIO_MODE_READWRITE));
    if (rc != SIO_OK) goto badwrite2;

    for (i=0; i<wcount; ++i) {
        EVAL0(sio_write(sio, S, len, &actual));
        if (rc != SIO_OK) {
            error = 1;
            break;
        } 
        if (actual != len) {
            ts_test_fail(TS_CTX, "sio_write result %d (expected %d)\n",
                (int)actual, (int)len);
            error = 1;
            break;
        }
    }

    EVAL0(sio_push(sio));
    if (rc != SIO_OK) error = 1;
    EVAL0(sio_detach(sio, sios_bio));
    if (rc != SIO_OK) error = 1;
    result = error;

    badwrite2:
    EVAL0(sio_detach(sio, sios_fd));
    if (rc != SIO_OK) result = -1;

    badwrite:
    EVAL0(sio_destroy_stage(sio, sios_bio));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy_stage(sio, sios_fd));
    if (rc != SIO_OK) result = -1;
    EVAL0(sio_destroy(sio));
    if (rc != SIO_OK) result = -1;

    return result;
}

TS_TEST(test_sio_bio)
{
    session(_t, test_sio_bio_read, test_sio_bio_write, 1);
}
#endif

int main(int argc, char *argv[])
{
    ts_suite_t *ts;
    int n;

    signal(SIGPIPE, SIG_IGN);

    ts = ts_suite_new("OSSP sio (Stream I/O)");
    ts_suite_test(ts, test_sio_buffer, "stream I/O buffering");
    ts_suite_test(ts, test_sio_fd, "stream I/O file");
    ts_suite_test(ts, test_sio_pipe, "stream I/O pipe");
    ts_suite_test(ts, test_sio_sio, "stream I/O multiplexing");
    ts_suite_test(ts, test_sio_hello, "stream I/O hello protocol");
#if ENABLE_ZLIB
    ts_suite_test(ts, test_sio_zlib, "stream I/O zlib compression");
#endif
#if ENABLE_SA
    ts_suite_test(ts, test_sio_sa, "stream I/O socket abstraction");
#endif
#if ENABLE_BIO
    ts_suite_test(ts, test_sio_bio, "stream I/O bio adapter");
#endif
    n = ts_suite_run(ts);
    ts_suite_free(ts);
    return n;
}

CVSTrac 2.0.1