diff --git a/.gitignore b/.gitignore index 5e50b521b7..f181f895a1 100644 --- a/.gitignore +++ b/.gitignore @@ -144,6 +144,7 @@ /tests/domainsnapshotxml2xmltest /tests/esxutilstest /tests/eventtest +/tests/fdstreamtest /tests/hashtest /tests/jsontest /tests/libvirtdconftest diff --git a/tests/Makefile.am b/tests/Makefile.am index 68dbb273d7..68cdd53f76 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -107,6 +107,7 @@ test_programs = virshtest sockettest \ virportallocatortest \ sysinfotest \ virstoragetest \ + fdstreamtest \ $(NULL) if WITH_GNUTLS @@ -710,6 +711,10 @@ sysinfotest_SOURCES = \ sysinfotest.c testutils.h testutils.c sysinfotest_LDADD = $(LDADDS) +fdstreamtest_SOURCES = \ + fdstreamtest.c testutils.h testutils.c +fdstreamtest_LDADD = $(LDADDS) + if WITH_CIL CILOPTFLAGS = CILOPTINCS = diff --git a/tests/fdstreamtest.c b/tests/fdstreamtest.c new file mode 100644 index 0000000000..cda2689cc2 --- /dev/null +++ b/tests/fdstreamtest.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2013 Red Hat, Inc. + * + * 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, see + * . + * + * Author: Daniel P. Berrange + */ + +#include + +#include +#include + +#include "testutils.h" + +#include "fdstream.h" +#include "datatypes.h" +#include "virerror.h" +#include "viralloc.h" +#include "virlog.h" +#include "virstring.h" +#include "virfile.h" +#include "virutil.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define PATTERN_LEN 256 + +static int testFDStreamReadCommon(const char *scratchdir, bool blocking) +{ + int fd = -1; + char *tmpfile = NULL; + int ret = -1; + char *pattern = NULL; + char *buf = NULL; + virStreamPtr st = NULL; + size_t i; + virConnectPtr conn = NULL; + int flags = 0; + + if (!blocking) + flags |= VIR_STREAM_NONBLOCK; + + if (!(conn = virConnectOpen("test:///default"))) + goto cleanup; + + if (VIR_ALLOC_N(pattern, PATTERN_LEN) < 0 || + VIR_ALLOC_N(buf, PATTERN_LEN) < 0) + goto cleanup; + + for (i = 0 ; i < PATTERN_LEN ; i++) + pattern[i] = i; + + if (virAsprintf(&tmpfile, "%s/input.data", scratchdir) < 0) + goto cleanup; + + if ((fd = open(tmpfile, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) + goto cleanup; + + for (i = 0 ; i < 10 ; i++) { + if (safewrite(fd, pattern, PATTERN_LEN) != PATTERN_LEN) + goto cleanup; + } + + if (VIR_CLOSE(fd) < 0) + goto cleanup; + + if (!(st = virStreamNew(conn, flags))) + goto cleanup; + + /* Start reading 1/2 way through first pattern + * and end 1/2 way through last pattern + */ + if (virFDStreamOpenFile(st, tmpfile, + PATTERN_LEN / 2, PATTERN_LEN * 9, + O_RDONLY) < 0) + goto cleanup; + + for (i = 0 ; i < 10 ; i++) { + size_t offset = 0; + size_t want; + if (i == 0) + want = PATTERN_LEN / 2; + else + want = PATTERN_LEN; + + while (want > 0) { + int got; + reread: + got = st->driver->streamRecv(st, buf + offset, want); + if (got < 0) { + if (got == -2 && !blocking) { + usleep(20 * 1000); + goto reread; + } + fprintf(stderr, "Failed to read stream: %s\n", + virGetLastErrorMessage()); + goto cleanup; + } + if (got == 0) { + /* Expect EOF 1/2 through last pattern */ + if (i == 9 && want == (PATTERN_LEN / 2)) + break; + fprintf(stderr, "Unexpected EOF block %zu want %zu\n", + i, want); + goto cleanup; + } + offset += got; + want -= got; + } + if (i == 0) { + if (memcmp(buf, pattern + (PATTERN_LEN / 2), PATTERN_LEN / 2) != 0) { + fprintf(stderr, "Mismatched pattern data iteration %zu\n", i); + goto cleanup; + } + } else if (i == 9) { + if (memcmp(buf, pattern, PATTERN_LEN / 2) != 0) { + fprintf(stderr, "Mismatched pattern data iteration %zu\n", i); + goto cleanup; + } + } else { + if (memcmp(buf, pattern, PATTERN_LEN) != 0) { + fprintf(stderr, "Mismatched pattern data iteration %zu\n", i); + goto cleanup; + } + } + } + + if (st->driver->streamFinish(st) != 0) { + fprintf(stderr, "Failed to finish stream: %s\n", + virGetLastErrorMessage()); + goto cleanup; + } + + ret = 0; +cleanup: + if (st) + virStreamFree(st); + VIR_FORCE_CLOSE(fd); + if (tmpfile != NULL) + unlink(tmpfile); + if (conn) + virConnectClose(conn); + VIR_FREE(tmpfile); + VIR_FREE(pattern); + VIR_FREE(buf); + return ret; +} + + +static int testFDStreamReadBlock(const void *data) +{ + return testFDStreamReadCommon(data, true); +} +static int testFDStreamReadNonblock(const void *data) +{ + return testFDStreamReadCommon(data, false); +} + + +static int testFDStreamWriteCommon(const char *scratchdir, bool blocking) +{ + int fd = -1; + char *tmpfile = NULL; + int ret = -1; + char *pattern = NULL; + char *buf = NULL; + virStreamPtr st = NULL; + size_t i; + virConnectPtr conn = NULL; + int flags = 0; + + if (!blocking) + flags |= VIR_STREAM_NONBLOCK; + + if (!(conn = virConnectOpen("test:///default"))) + goto cleanup; + + if (VIR_ALLOC_N(pattern, PATTERN_LEN) < 0 || + VIR_ALLOC_N(buf, PATTERN_LEN) < 0) + goto cleanup; + + for (i = 0 ; i < PATTERN_LEN ; i++) + pattern[i] = i; + + if (virAsprintf(&tmpfile, "%s/input.data", scratchdir) < 0) + goto cleanup; + + if (!(st = virStreamNew(conn, flags))) + goto cleanup; + + /* Start writing 1/2 way through first pattern + * and end 1/2 way through last pattern + */ + if (virFDStreamCreateFile(st, tmpfile, + PATTERN_LEN / 2, PATTERN_LEN * 9, + O_WRONLY, 0600) < 0) + goto cleanup; + + for (i = 0 ; i < 10 ; i++) { + size_t offset = 0; + size_t want; + if (i == 0) + want = PATTERN_LEN / 2; + else + want = PATTERN_LEN; + + while (want > 0) { + int got; + rewrite: + got = st->driver->streamSend(st, pattern + offset, want); + if (got < 0) { + if (got == -2 && !blocking) { + usleep(20 * 1000); + goto rewrite; + } + if (i == 9 && + want == (PATTERN_LEN / 2)) + break; + fprintf(stderr, "Failed to write stream: %s\n", + virGetLastErrorMessage()); + goto cleanup; + } + offset += got; + want -= got; + } + } + + if (st->driver->streamFinish(st) != 0) { + fprintf(stderr, "Failed to finish stream: %s\n", + virGetLastErrorMessage()); + goto cleanup; + } + + if ((fd = open(tmpfile, O_RDONLY)) < 0) + goto cleanup; + + for (i = 0 ; i < 10 ; i++) { + size_t want; + if (i == 9) + want = PATTERN_LEN / 2; + else + want = PATTERN_LEN; + + if (saferead(fd, buf, want) != want) { + fprintf(stderr, "Short read from data\n"); + goto cleanup; + } + + if (i == 0) { + size_t j; + for (j = 0 ; j < (PATTERN_LEN / 2) ; j++) { + if (buf[j] != 0) { + fprintf(stderr, "Mismatched pattern data iteration %zu\n", i); + goto cleanup; + } + } + if (memcmp(buf + (PATTERN_LEN / 2), pattern, PATTERN_LEN / 2) != 0) { + fprintf(stderr, "Mismatched pattern data iteration %zu\n", i); + goto cleanup; + } + } else if (i == 9) { + if (memcmp(buf, pattern, PATTERN_LEN / 2) != 0) { + fprintf(stderr, "Mismatched pattern data iteration %zu\n", i); + goto cleanup; + } + } else { + if (memcmp(buf, pattern, PATTERN_LEN) != 0) { + fprintf(stderr, "Mismatched pattern data iteration %zu\n", i); + goto cleanup; + } + } + } + + if (VIR_CLOSE(fd) < 0) + goto cleanup; + + ret = 0; +cleanup: + if (st) + virStreamFree(st); + VIR_FORCE_CLOSE(fd); + if (tmpfile != NULL) + unlink(tmpfile); + if (conn) + virConnectClose(conn); + VIR_FREE(tmpfile); + VIR_FREE(pattern); + VIR_FREE(buf); + return ret; +} + + +static int testFDStreamWriteBlock(const void *data) +{ + return testFDStreamWriteCommon(data, true); +} +static int testFDStreamWriteNonblock(const void *data) +{ + return testFDStreamWriteCommon(data, false); +} + +#define SCRATCHDIRTEMPLATE abs_builddir "/fakesysfsdir-XXXXXX" + +static int +mymain(void) +{ + char scratchdir[] = SCRATCHDIRTEMPLATE; + int ret = 0; + const char *iohelper = abs_builddir "/../src/libvirt_iohelper"; + + virFDStreamSetIOHelper(iohelper); + + if (!mkdtemp(scratchdir)) { + fprintf(stderr, "Cannot create fakesysfsdir"); + abort(); + } + + if (virtTestRun("Stream read blocking ", 1, testFDStreamReadBlock, scratchdir) < 0) + ret = -1; + if (virtTestRun("Stream read non-blocking ", 1, testFDStreamReadNonblock, scratchdir) < 0) + ret = -1; + if (virtTestRun("Stream write blocking ", 1, testFDStreamWriteBlock, scratchdir) < 0) + ret = -1; + if (virtTestRun("Stream write non-blocking ", 1, testFDStreamWriteNonblock, scratchdir) < 0) + ret = -1; + + if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL) + virFileDeleteTree(scratchdir); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN(mymain)