Signed-off-by: Dmitry Guryanov <dguryanov(a)parallels.com>
---
tests/jsontest.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 201 insertions(+), 4 deletions(-)
diff --git a/tests/jsontest.c b/tests/jsontest.c
index 98a6069..107d772 100644
--- a/tests/jsontest.c
+++ b/tests/jsontest.c
@@ -4,14 +4,23 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <unistd.h>
+#include <poll.h>
+#include <signal.h>
+#include <sched.h>
#include "internal.h"
#include "virjson.h"
#include "testutils.h"
+#include "vircommand.h"
+#include "virprocess.h"
+#include "virtime.h"
+#include "virfile.h"
struct testInfo {
const char *doc;
bool pass;
+ size_t chunk;
};
@@ -53,21 +62,185 @@ cleanup:
return ret;
}
+ATTRIBUTE_NORETURN static int
+testJSONReadProcess(int fd, int finishFd)
+{
+ int n = 0;
+ int exitcode = EXIT_FAILURE;
+ virJSONValuePtr v;
+ virJSONStreamParserState state;
+ int x;
+
+ if (safewrite(finishFd, " ", 1) != 1) {
+ if (virTestGetVerbose())
+ perror("write");
+ _exit(exitcode);
+ }
+ /* There must be exactly two objects, each must have "valid"
+ * field with integer value */
+
+ memset(&state, 0, sizeof(state));
+ while (1) {
+ v = virJSONValueFromStream(fd, &state);
+
+ if (v == (void *)-1) {
+ if (virTestGetVerbose())
+ fprintf(stderr, "virJSONValueFromStream returned error\n");
+ goto cleanup;
+ }
+
+ if (v == 0)
+ break;
+
+ n++;
+
+ if (virJSONValueObjectGetNumberInt(v, "valid", &x) < 0) {
+ if (virTestGetVerbose())
+ fprintf(stderr, "Parsed value in object %d doesn't have "
+ "'valid' integer field\n", n);
+ goto cleanup;
+ }
+ }
+
+ if (n != 2) {
+ if (virTestGetVerbose())
+ fprintf(stderr, "Invalid number of objects: %d, must be 2\n", n);
+ } else {
+ exitcode = EXIT_SUCCESS;
+ }
+
+cleanup:
+ if (safewrite(finishFd, " ", 1) != 1) {
+ if (virTestGetVerbose())
+ perror("write");
+ _exit(exitcode);
+ }
+
+ VIR_FORCE_CLOSE(fd);
+ VIR_FORCE_CLOSE(finishFd);
+ _exit(exitcode);
+}
+
+/*
+ * This test creates a separate process, which reads JSON data
+ * from a pipe with help of virJSONValueFromStream function. It expects
+ * 2 objects, each must have 'valid' integer key. Parent process writes
+ * data to the pipe and handles child exit code.
+ */
+static int
+testJSONFromStream(const void *data)
+{
+ struct testInfo *info = (struct testInfo *)data;
+ int ret = -1;
+ int pret;
+ int pipefd[2];
+ int wpipefd[2];
+ ssize_t w;
+ pid_t pid;
+ struct pollfd pollfd;
+ int status;
+ size_t docLen, i;
+ char c;
+
+ if (pipe(pipefd) < 0) {
+ if (virTestGetVerbose())
+ perror("pipe");
+ return -1;
+ }
+
+ if (pipe(wpipefd) < 0) {
+ if (virTestGetVerbose())
+ perror("pipe");
+ goto cleanup;
+ }
+
+ if (virFork(&pid) < 0) {
+ if (virTestGetVerbose())
+ perror("fork");
+ goto cleanup2;
+ }
+
+ if (pid == 0) {
+ VIR_FORCE_CLOSE(pipefd[1]);
+ VIR_FORCE_CLOSE(wpipefd[0]);
+ testJSONReadProcess(pipefd[0], wpipefd[1]);
+ /* couldn't be reached */
+ }
+
+ /* write test data */
+ docLen = strlen(info->doc);
+
+ if (read(wpipefd[0], &c, 1) < 0) {
+ if (virTestGetVerbose())
+ perror("read");
+ goto cleanup2;
+ }
+
+ for (i = 0; i < docLen; i += info->chunk) {
+ size_t len = i + info->chunk <= docLen ? info->chunk : docLen %
info->chunk;
+
+ w = safewrite(pipefd[1], info->doc + i * info->chunk, len);
+ if (w < 0) {
+ if (virTestGetVerbose())
+ perror("write");
+ goto cleanup2;
+ }
+
+ if (w < len) {
+ if (virTestGetVerbose())
+ fprintf(stderr, "Couldn't write entire json string to the
pipe\n");
+ goto cleanup2;
+ }
+
+ sched_yield();
+ }
+
+ VIR_FORCE_CLOSE(pipefd[1]);
+
+ /* wait for read process */
+ pollfd.fd = wpipefd[0];
+ pollfd.events = POLLIN;
+
+ pret = poll(&pollfd, 1, 1000);
+ if (pret < 0) {
+ if (virTestGetVerbose())
+ perror("poll");
+ goto cleanup2;
+ }
+
+ if (pret == 0) {
+ if (virTestGetVerbose())
+ fprintf(stderr, "timeout reached\n");
+ virProcessKill(pid, SIGTERM);
+ }
+
+ if (virProcessWait(pid, &status) == 0 && !WIFSIGNALED(status)
+ && WEXITSTATUS(status) == 0)
+ ret = 0;
+
+cleanup2:
+ VIR_FORCE_CLOSE(wpipefd[0]);
+ VIR_FORCE_CLOSE(wpipefd[1]);
+cleanup:
+ VIR_FORCE_CLOSE(pipefd[0]);
+ VIR_FORCE_CLOSE(pipefd[1]);
+ return ret;
+}
static int
mymain(void)
{
int ret = 0;
-#define DO_TEST_FULL(name, cmd, doc, pass) \
+#define DO_TEST_FULL(name, cmd, doc, pass, chunk) \
do { \
- struct testInfo info = { doc, pass }; \
+ struct testInfo info = { doc, pass, chunk }; \
if (virtTestRun(name, 1, testJSON ## cmd, &info) < 0) \
ret = -1; \
} while (0)
#define DO_TEST_PARSE(name, doc) \
- DO_TEST_FULL(name, FromString, doc, true)
+ DO_TEST_FULL(name, FromString, doc, true, 0)
DO_TEST_PARSE("Simple", "{\"return\": {}, \"id\":
\"libvirt-1\"}");
DO_TEST_PARSE("NotSoSimple", "{\"QMP\":
{\"version\": {\"qemu\":"
@@ -105,6 +278,30 @@ mymain(void)
"\"query-uuid\"}, {\"name\":
\"query-migrate\"}, {\"name\": "
"\"query-balloon\"}], \"id\":
\"libvirt-2\"}");
+#define DO_TEST_PARSE_STREAM(name, doc, chunk) \
+ DO_TEST_FULL(name, FromStream, doc, true, chunk)
+
+ DO_TEST_PARSE_STREAM("StreamSimple", "{\"valid\":
10}{\"valid\": 10}", 1);
+
+ char *largeText;
+ size_t largeTextSize = 8192;
+ size_t pos = 0;
+
+ if (VIR_ALLOC_N(largeText, largeTextSize) < 0)
+ return EXIT_FAILURE;
+
+ memset(largeText, 0, largeTextSize);
+ pos += snprintf(largeText + pos, 64, "{");
+ while (pos < largeTextSize / 2 - 100)
+ pos += snprintf(largeText + pos, 64, "\"x%ld\": %ld, ", pos,
pos);
+ pos += snprintf(largeText + pos, 64, "\"valid\": 1}");
+ pos += snprintf(largeText + pos, strlen(largeText) + 1, "%s", largeText);
+
+ DO_TEST_PARSE_STREAM("StreamLargeChunks", largeText, largeTextSize);
+ DO_TEST_PARSE_STREAM("StreamSmallChunks", largeText, 1);
+
+ VIR_FREE(largeText);
+
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
--
1.7.1