/* ** OSSP sio - Stream I/O ** Copyright (c) 2002-2005 Cable & Wireless ** Copyright (c) 2002-2005 The OSSP Project ** Copyright (c) 2002-2005 Ralf S. Engelschall ** ** 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 #include #include #include #include #include #include #include #include #include #include "ts.h" #include "al.h" #include "sio.h" #if ENABLE_BIO #include #include 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 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