/* ** OSSP sio - Stream I/O ** Copyright (c) 2002-2003 Cable & Wireless Deutschland ** Copyright (c) 2002-2003 The OSSP Project ** Copyright (c) 2002-2003 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 "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) \ ts_test_check(TS_CTX, name); \ block \ if (rc != rc0) \ ts_test_fail(TS_CTX, "%s -> %d[%s] (expected %d[%s])\n", \ name, rc, sio_error(rc), rc0, sio_error(rc0)) #define EVAL0(name,block) EVAL(name,rc,SIO_OK,block) 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); static int test_sio_hello_server(ts_test_t *, int); 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; buf += actual; total += actual; len -= actual; } *actualp = total; return rc; } 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", { rc = sio_create(&sio); }); if (rc != SIO_OK) return; EVAL0("sio_create_stage(&sios_hole)", { rc = sio_create_stage(sio, &sio_module_hole, &sios_hole); }); if (rc != SIO_OK) return; EVAL0("sio_create_stage(&sios_buffer)", { rc = sio_create_stage(sio, &sio_module_buffer, &sios_buffer); }); if (rc != SIO_OK) return; EVAL0("sio_configure_stage(sios_buffer, outputsize)", { rc = sio_configure_stage(sio, sios_buffer, "outputsize", &bufsize); }); if (rc != SIO_OK) return; EVAL0("sio_attach(sios_hole)", { rc = sio_attach(sio, sios_hole, SIO_MODE_WRITE); }); if (rc != SIO_OK) return; EVAL0("sio_attach(sios_buffer)", { rc = 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("sio_read(C_sio)", { rc = sreadloop(C_sio, buf, sizeof(buf), &actual); if (rc == SIO_ERR_EOF) { --nstreams; rc = SIO_OK; } }); 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_sios_sio)", { rc = sio_detach(C_sio, C_sios_sio); }); EVAL0("sio_destroy_stage(C_sios_sio)", { rc = sio_destroy_stage(C_sio, C_sios_sio); }); EVAL0("sio_destroy(C_sio)", { rc = sio_destroy(C_sio); }); /* * destroy ODD stream */ EVAL0("sio_detach(B_sios_sio)", { rc = sio_detach(B_sio, B_sios_sio); }); EVAL0("sio_destroy_stage(B_sios_sio)", { rc = sio_destroy_stage(B_sio, B_sios_sio); }); EVAL0("sio_destroy(B_sio)", { rc = sio_destroy(B_sio); }); /* * destroy MUX stream */ EVAL0("sio_detach(A_sios_sillymux)", { rc = sio_detach(A_sio, A_sios_sillymux); }); EVAL0("sio_detach(A_sios_fd)", { rc = sio_detach(A_sio, A_sios_fd); }); EVAL0("sio_destroy_stage(A_sios_sillymux)", { rc = sio_destroy_stage(A_sio, A_sios_sillymux); }); EVAL0("sio_destroy_stage(A_sios_fd)", { rc = sio_destroy_stage(A_sio, A_sios_fd); }); EVAL0("sio_destroy(A_sio)", { rc = 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) { 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 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", { rc = sio_create(&sio); }); if (rc != SIO_OK) return -1; EVAL0("sio_create_stage(&sios_fd)", { rc = sio_create_stage(sio, &sio_module_fd, &sios_fd); }); if (rc != SIO_OK) return -1; EVAL0("sio_create_stage(&sios_hello)", { rc = sio_create_stage(sio, &sio_module_hello, &sios_hello); }); if (rc != SIO_OK) return -1; EVAL0("sio_configure_stage(sios_fd, buflen)", { rc = sio_configure_stage(sio, sios_fd, "buflen", &buflen); }); if (rc != SIO_OK) return -1; EVAL0("sio_configure_stage(sios_fd, fd)", { rc = sio_configure_stage(sio, sios_fd, "fd", &fd); }); if (rc != SIO_OK) goto badwrite; EVAL0("sio_attach(sios_fd)", { rc = sio_attach(sio, sios_fd, SIO_MODE_READWRITE); }); if (rc != SIO_OK) goto badwrite; EVAL0("sio_attach(sios_hello)", { rc = sio_attach(sio, sios_hello, SIO_MODE_READWRITE); }); if (rc != SIO_OK) goto badwrite2; EVAL0("sio_write", { rc = sio_write(sio, S, len, &actual); }); if (rc != SIO_OK) error = 1; EVAL0("sio_push", { rc = sio_push(sio); }); if (rc != SIO_OK) error = 1; EVAL0("sio_detach(sios_hello)", { rc = sio_detach(sio, sios_hello); }); if (rc != SIO_OK) error = 1; result = error; badwrite2: EVAL0("sio_detach(sios_fd)", { rc = sio_detach(sio, sios_fd); }); if (rc != SIO_OK) result = -1; badwrite: /* * common cleanup */ EVAL0("sio_destroy_stage", { rc = sio_destroy_stage(sio, sios_hello); }); if (rc != SIO_OK) result = -1; EVAL0("sio_destroy_stage(sios_fd)", { rc = sio_destroy_stage(sio, sios_fd); }); if (rc != SIO_OK) result = -1; EVAL0("sio_destroy", { rc = sio_destroy(sio); }); if (rc != SIO_OK) result = -1; return result; } TS_TEST(test_sio_hello) { pid_t child; int pd[2]; int status; fflush(stdout); fflush(stderr); if (socketpair(AF_UNIX, SOCK_STREAM, 0, 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)); } if (child == 0) { int result; close(pd[1]); result = test_sio_hello_client(NULL, pd[0]); close(pd[0]); exit(result ? 1 : 0); } else { close(pd[0]); test_sio_hello_server(_t, pd[1]); close(pd[1]); waitpid(child, &status, 0); if (status != 0) { ts_test_fail(TS_CTX, "child returned status %08lx\n", status); } } } #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", { rc = sio_create(&sio); }); if (rc != SIO_OK) return; EVAL0("sio_create_stage(&sios_fd)", { rc = sio_create_stage(sio, &sio_module_fd, &sios_fd); }); if (rc != SIO_OK) return; EVAL0("sio_create_stage(&sios_zlib)", { rc = sio_create_stage(sio, &sio_module_zlib, &sios_zlib); }); if (rc != SIO_OK) return; EVAL0("sio_configure_stage(sios_fd, buflen)", { rc = sio_configure_stage(sio, sios_fd, "buflen", &buflen); }); if (rc != SIO_OK) return; EVAL0("sio_configure_stage(sios_zlib)", { rc = sio_configure_stage(sio, sios_zlib, "outputsize", &bufsize); }); if (rc != SIO_OK) return; EVAL0("sio_configure_stage(sios_zlib, inputsize)", { rc = sio_configure_stage(sio, sios_zlib, "inputsize", &bufsize); }); if (rc != SIO_OK) return; EVAL0("sio_configure_stage(sios_zlib, outputlevel)", { rc = sio_configure_stage(sio, sios_zlib, "outputlevel", &zoutlevel); }); if (rc != SIO_OK) return; EVAL0("sio_configure_stage(sios_zlib, inputlevel)", { rc = 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(sios_fd, fd)", { rc = sio_configure_stage(sio, sios_fd, "fd", &fd); }); if (rc != SIO_OK) goto badwrite; EVAL0("sio_attach(sios_fd)", { rc = sio_attach(sio, sios_fd, SIO_MODE_READWRITE); }); if (rc != SIO_OK) goto badwrite; EVAL0("sio_attach(sios_zlib)", { rc = sio_attach(sio, sios_zlib, SIO_MODE_READWRITE); }); if (rc != SIO_OK) goto badwrite2; for (i=0; i