[libvirt] [PATCH 3/3] Add a test case for the fdstream file read/write code

Daniel P. Berrange berrange at redhat.com
Fri May 10 17:17:27 UTC 2013


From: "Daniel P. Berrange" <berrange at redhat.com>

Add a test case which exercises the virFDStreamOpenFile
and virFDStreamCreateFile methods. Ensure that both the
synchronous and non-blocking iohelper code paths work.
This validates the regression recently fixed which
broke reading in non-blocking mode

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 .gitignore           |   1 +
 tests/Makefile.am    |   5 +
 tests/fdstreamtest.c | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 359 insertions(+)
 create mode 100644 tests/fdstreamtest.c

diff --git a/.gitignore b/.gitignore
index 5e50b52..e327b37 100644
--- a/.gitignore
+++ b/.gitignore
@@ -145,6 +145,7 @@
 /tests/esxutilstest
 /tests/eventtest
 /tests/hashtest
+/tests/fdstreamtest
 /tests/jsontest
 /tests/libvirtdconftest
 /tests/networkxml2argvtest
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 68dbb27..68cdd53 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 0000000..aa68fb7
--- /dev/null
+++ b/tests/fdstreamtest.c
@@ -0,0 +1,353 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <fcntl.h>
+
+#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, 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;
+    int ret = 0;
+    const char *iohelper = abs_builddir "/../src/libvirt_iohelper";
+
+    virFDStreamSetIOHelper(iohelper);
+
+    if (!(scratchdir = strdup(SCRATCHDIRTEMPLATE))) {
+        fprintf(stderr, "Out of memory\n");
+        abort();
+    }
+
+    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);
+
+    VIR_FREE(scratchdir);
+
+    return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN(mymain)
-- 
1.8.2.1




More information about the libvir-list mailing list