[libvirt] [PATCH 08/11] tests: qemu: Add infrastructure for QAPI schema testing

Peter Krempa pkrempa at redhat.com
Thu Mar 22 18:31:45 UTC 2018


Add a function which will allow to test whether a JSON object conforms
to the QAPI schema. This greatly helps when developing formatters for
new JSON objects and will help make sure that the code will not break in
cases which have unit tests but were actually not function-tested
(mostly various disk access protocols).

Signed-off-by: Peter Krempa <pkrempa at redhat.com>
---
 tests/Makefile.am           |   2 +
 tests/qemumonitorjsontest.c | 108 ++++++++-
 tests/testutilsqemuschema.c | 536 ++++++++++++++++++++++++++++++++++++++++++++
 tests/testutilsqemuschema.h |  30 +++
 4 files changed, 675 insertions(+), 1 deletion(-)
 create mode 100644 tests/testutilsqemuschema.c
 create mode 100644 tests/testutilsqemuschema.h

diff --git a/tests/Makefile.am b/tests/Makefile.am
index fe8847386e..cf254f65c3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -616,6 +616,7 @@ qemumonitorjsontest_SOURCES = \
 	qemumonitorjsontest.c \
 	testutils.c testutils.h \
 	testutilsqemu.c testutilsqemu.h \
+	testutilsqemuschema.c testutilsqemuschema.h \
 	$(NULL)
 qemumonitorjsontest_LDADD = libqemumonitortestutils.la \
 	$(qemu_LDADDS) $(LDADDS)
@@ -694,6 +695,7 @@ else ! WITH_QEMU
 EXTRA_DIST += qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c \
 	qemuhelptest.c domainsnapshotxml2xmltest.c \
 	qemumonitortest.c testutilsqemu.c testutilsqemu.h \
+	testutilsqemuschema.c testutilsqemuschema.h \
 	qemumonitorjsontest.c qemuhotplugtest.c \
 	qemuagenttest.c qemucapabilitiestest.c \
 	qemucaps2xmltest.c qemucommandutiltest.c \
diff --git a/tests/qemumonitorjsontest.c b/tests/qemumonitorjsontest.c
index 908ec3a3c8..394e5dc446 100644
--- a/tests/qemumonitorjsontest.c
+++ b/tests/qemumonitorjsontest.c
@@ -21,10 +21,12 @@

 #include "testutils.h"
 #include "testutilsqemu.h"
+#include "testutilsqemuschema.h"
 #include "qemumonitortestutils.h"
 #include "qemu/qemu_domain.h"
 #include "qemu/qemu_block.h"
 #include "qemu/qemu_monitor_json.h"
+#include "qemu/qemu_qapi.h"
 #include "virthread.h"
 #include "virerror.h"
 #include "virstring.h"
@@ -2828,12 +2830,60 @@ testBlockNodeNameDetect(const void *opaque)
 }


+struct testQAPISchemaData {
+    virHashTablePtr schema;
+    const char *name;
+    const char *query;
+    const char *json;
+    bool success;
+};
+
+
+static int
+testQAPISchema(const void *opaque)
+{
+    const struct testQAPISchemaData *data = opaque;
+    virBuffer debug = VIR_BUFFER_INITIALIZER;
+    virJSONValuePtr schemaroot;
+    virJSONValuePtr json = NULL;
+    int ret = -1;
+
+    if (virQEMUQapiSchemaPathGet(data->query, data->schema, &schemaroot) < 0)
+        goto cleanup;
+
+    if (!(json = virJSONValueFromString(data->json)))
+        goto cleanup;
+
+    if ((testQEMUSchemaValidate(json, schemaroot, data->schema, &debug) == 0) != data->success) {
+        if (!data->success)
+            VIR_TEST_VERBOSE("\nschema validation should have failed\n");
+    } else {
+        ret = 0;
+    }
+
+    if (virTestGetDebug() ||
+        (ret < 0 && virTestGetVerbose())) {
+        char *debugstr = virBufferContentAndReset(&debug);
+        fprintf(stderr, "\n%s\n", debugstr);
+        VIR_FREE(debugstr);
+    }
+
+
+ cleanup:
+    virBufferFreeAndReset(&debug);
+    virJSONValueFree(json);
+    return ret;
+}
+
+
 static int
 mymain(void)
 {
     int ret = 0;
     virQEMUDriver driver;
     testQemuMonitorJSONSimpleFuncData simpleFunc;
+    struct testQAPISchemaData qapiData;
+    char *metaschema = NULL;

 #if !WITH_YAJL
     fputs("libvirt not compiled with yajl, skipping this test\n", stderr);
@@ -2982,8 +3032,64 @@ mymain(void)

 #undef DO_TEST_BLOCK_NODE_DETECT

-    qemuTestDriverFree(&driver);
+#define DO_TEST_QAPI_SCHEMA(nme, rootquery, scc, jsonstr) \
+    do { \
+        qapiData.name = nme; \
+        qapiData.query = rootquery; \
+        qapiData.success = scc; \
+        qapiData.json = jsonstr; \
+        if (virTestRun("qapi schema " nme, testQAPISchema, &qapiData) < 0)\
+            ret = -1; \
+    } while (0)
+
+    if (!(qapiData.schema = testQEMUSchemaLoad())) {
+        VIR_TEST_VERBOSE("failed to load qapi schema\n");
+        ret = -1;
+        goto cleanup;
+    }

+    DO_TEST_QAPI_SCHEMA("string", "trace-event-get-state/arg-type", true,
+                        "{\"name\":\"test\"}");
+    DO_TEST_QAPI_SCHEMA("all attrs", "trace-event-get-state/arg-type", true,
+                        "{\"name\":\"test\", \"vcpu\":123}");
+    DO_TEST_QAPI_SCHEMA("attr type mismatch", "trace-event-get-state/arg-type", false,
+                        "{\"name\":123}");
+    DO_TEST_QAPI_SCHEMA("missing mandatory attr", "trace-event-get-state/arg-type", false,
+                        "{\"vcpu\":123}");
+    DO_TEST_QAPI_SCHEMA("attr name not present", "trace-event-get-state/arg-type", false,
+                        "{\"name\":\"test\", \"blah\":123}");
+    DO_TEST_QAPI_SCHEMA("variant", "blockdev-add/arg-type", true,
+                        "{\"driver\":\"file\", \"filename\":\"ble\"}");
+    DO_TEST_QAPI_SCHEMA("variant wrong", "blockdev-add/arg-type", false,
+                        "{\"driver\":\"filefilefilefile\", \"filename\":\"ble\"}");
+    DO_TEST_QAPI_SCHEMA("variant missing mandatory", "blockdev-add/arg-type", false,
+                        "{\"driver\":\"file\", \"pr-manager\":\"ble\"}");
+    DO_TEST_QAPI_SCHEMA("variant missing discriminator", "blockdev-add/arg-type", false,
+                        "{\"node-name\":\"dfgfdg\"}");
+    DO_TEST_QAPI_SCHEMA("alternate 1", "blockdev-add/arg-type", true,
+                        "{\"driver\":\"qcow2\","
+                         "\"file\": { \"driver\":\"file\", \"filename\":\"ble\"}}");
+    DO_TEST_QAPI_SCHEMA("alternate 2", "blockdev-add/arg-type", true,
+                        "{\"driver\":\"qcow2\",\"file\": \"somepath\"}");
+    DO_TEST_QAPI_SCHEMA("alternate 2", "blockdev-add/arg-type", false,
+                        "{\"driver\":\"qcow2\",\"file\": 1234}");
+
+    if (!(metaschema = virTestLoadFilePath("qemuqapischema.json", NULL))) {
+        VIR_TEST_VERBOSE("failed to load qapi schema\n");
+        ret = -1;
+        goto cleanup;
+    }
+
+    DO_TEST_QAPI_SCHEMA("schema-meta", "query-qmp-schema/ret-type", true,
+                        metaschema);
+
+
+#undef DO_TEST_QAPI_SCHEMA
+
+ cleanup:
+    VIR_FREE(metaschema);
+    virHashFree(qapiData.schema);
+    qemuTestDriverFree(&driver);
     return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }

diff --git a/tests/testutilsqemuschema.c b/tests/testutilsqemuschema.c
new file mode 100644
index 0000000000..b4c94ed3f1
--- /dev/null
+++ b/tests/testutilsqemuschema.c
@@ -0,0 +1,536 @@
+/*
+ * testutilsqemuschema.c: helper functions for QEMU QAPI schema testing
+ *
+ * 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/>.
+ */
+#include <config.h>
+#include "testutils.h"
+#include "testutilsqemuschema.h"
+#include "qemu/qemu_qapi.h"
+
+static int
+testQEMUSchemaValidateRecurse(virJSONValuePtr obj,
+                              virJSONValuePtr root,
+                              virHashTablePtr schema,
+                              virBufferPtr debug);
+
+static int
+testQEMUSchemaValidateArrayBuiltin(virJSONValuePtr obj,
+                                   virJSONValuePtr root,
+                                   virBufferPtr debug)
+{
+    const char *t = virJSONValueObjectGetString(root, "json-type");
+    const char *s = NULL;
+    bool b = false;
+    int ret = -1;
+
+    if (STREQ_NULLABLE(t, "value")) {
+        s = "{any}";
+        ret = 0;
+        goto cleanup;
+    }
+
+    switch (virJSONValueGetType(obj)) {
+    case VIR_JSON_TYPE_STRING:
+        if (STRNEQ_NULLABLE(t, "string"))
+            goto cleanup;
+        s = virJSONValueGetString(obj);
+        break;
+
+    case VIR_JSON_TYPE_NUMBER:
+        if (STRNEQ_NULLABLE(t, "int") &&
+            STRNEQ_NULLABLE(t, "number"))
+            goto cleanup;
+        s = "{number}";
+        break;
+
+    case VIR_JSON_TYPE_BOOLEAN:
+        if (STRNEQ_NULLABLE(t, "boolean"))
+            goto cleanup;
+        virJSONValueGetBoolean(obj, &b);
+        if (b)
+            s = "true";
+        else
+            s = "false";
+        break;
+
+    case VIR_JSON_TYPE_NULL:
+        if (STRNEQ_NULLABLE(t, "null"))
+            goto cleanup;
+        break;
+
+    case VIR_JSON_TYPE_OBJECT:
+    case VIR_JSON_TYPE_ARRAY:
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    if (ret == 0)
+        virBufferAsprintf(debug, "'%s': OK", s);
+    else
+        virBufferAsprintf(debug, "ERROR: expected type '%s', actual type %d",
+                          t, virJSONValueGetType(obj));
+    return ret;
+}
+
+struct testQEMUSchemaValidateObjectMemberData {
+    virJSONValuePtr rootmembers;
+    virHashTablePtr schema;
+    virBufferPtr debug;
+    bool missingMandatory;
+};
+
+
+static virJSONValuePtr
+testQEMUSchemaStealObjectMemberByName(const char *name,
+                                      virJSONValuePtr members)
+{
+    virJSONValuePtr member;
+    virJSONValuePtr ret = NULL;
+    size_t n;
+    size_t i;
+
+    n = virJSONValueArraySize(members);
+    for (i = 0; i < n; i++) {
+        member = virJSONValueArrayGet(members, i);
+
+        if (STREQ_NULLABLE(name, virJSONValueObjectGetString(member, "name"))) {
+            ret = virJSONValueArraySteal(members, i);
+            break;
+        }
+    }
+
+    return ret;
+}
+
+
+static int
+testQEMUSchemaValidateObjectMember(const char *key,
+                                   virJSONValuePtr value,
+                                   void *opaque)
+{
+    struct testQEMUSchemaValidateObjectMemberData *data = opaque;
+    virJSONValuePtr keymember = NULL;
+    const char *keytype;
+    virJSONValuePtr keyschema = NULL;
+    int ret = -1;
+
+    virBufferStrcat(data->debug, key, ": ", NULL);
+
+    /* lookup 'member' entry for key */
+    if (!(keymember = testQEMUSchemaStealObjectMemberByName(key, data->rootmembers))) {
+        virBufferAddLit(data->debug, "ERROR: attribute not in schema");
+        goto cleanup;
+    }
+
+    /* lookup schema entry for keytype */
+    if (!(keytype = virJSONValueObjectGetString(keymember, "type")) ||
+        !(keyschema = virHashLookup(data->schema, keytype))) {
+        virBufferAsprintf(data->debug, "ERROR: can't find schema for type '%s'",
+                          NULLSTR(keytype));
+        ret = -2;
+        goto cleanup;
+    }
+
+    /* recurse */
+    ret = testQEMUSchemaValidateRecurse(value, keyschema, data->schema,
+                                        data->debug);
+
+ cleanup:
+    virBufferAddLit(data->debug, "\n");
+    virJSONValueFree(keymember);
+    return ret;
+}
+
+
+static int
+testQEMUSchemaValidateObjectMergeVariantMember(size_t pos ATTRIBUTE_UNUSED,
+                                               virJSONValuePtr item,
+                                               void *opaque)
+{
+    virJSONValuePtr array = opaque;
+    virJSONValuePtr copy;
+
+    if (!(copy = virJSONValueCopy(item)))
+        return -1;
+
+    if (virJSONValueArrayAppend(array, copy) < 0)
+        return -1;
+
+    return 1;
+}
+
+
+/**
+ * testQEMUSchemaValidateObjectMergeVariant:
+ *
+ * Merges schema of variant @variantname in @root into @root and removes the
+ * 'variants' array from @root.
+ */
+static int
+testQEMUSchemaValidateObjectMergeVariant(virJSONValuePtr root,
+                                         const char *variantfield,
+                                         const char *variantname,
+                                         virHashTablePtr schema,
+                                         virBufferPtr debug)
+{
+    size_t n;
+    size_t i;
+    virJSONValuePtr variants = NULL;
+    virJSONValuePtr variant;
+    virJSONValuePtr variantschema;
+    virJSONValuePtr variantschemamembers;
+    virJSONValuePtr rootmembers;
+    const char *varianttype = NULL;
+    int ret = -1;
+
+    if (!(variants = virJSONValueObjectStealArray(root, "variants"))) {
+        virBufferAddLit(debug, "ERROR: missing 'variants' in schema\n");
+        return -2;
+    }
+
+    n = virJSONValueArraySize(variants);
+    for (i = 0; i < n; i++) {
+        variant = virJSONValueArrayGet(variants, i);
+
+        if (STREQ_NULLABLE(variantname,
+                           virJSONValueObjectGetString(variant, "case"))) {
+            varianttype = virJSONValueObjectGetString(variant, "type");
+            break;
+        }
+    }
+
+    if (!varianttype) {
+        virBufferAsprintf(debug, "ERROR: variant '%s' for discriminator '%s' not found\n",
+                          variantname, variantfield);
+        goto cleanup;
+
+    }
+
+    if (!(variantschema = virHashLookup(schema, varianttype)) ||
+        !(variantschemamembers = virJSONValueObjectGetArray(variantschema, "members"))) {
+        virBufferAsprintf(debug,
+                          "ERROR: missing schema or schema members for variant '%s'(%s)\n",
+                          variantname, varianttype);
+        ret = -2;
+        goto cleanup;
+    }
+
+    rootmembers = virJSONValueObjectGetArray(root, "members");
+
+    if (virJSONValueArrayForeachSteal(variantschemamembers,
+                                      testQEMUSchemaValidateObjectMergeVariantMember,
+                                      rootmembers) < 0) {
+        ret = -2;
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(variants);
+    return ret;
+}
+
+
+static int
+testQEMUSchemaValidateObjectMandatoryMember(size_t pos ATTRIBUTE_UNUSED,
+                                            virJSONValuePtr item,
+                                            void *opaque ATTRIBUTE_UNUSED)
+{
+    struct testQEMUSchemaValidateObjectMemberData *data = opaque;
+
+    if (virJSONValueObjectHasKey(item, "default") != 1) {
+        virBufferAsprintf(data->debug, "ERROR: missing mandatory attribute '%s'\n",
+                          NULLSTR(virJSONValueObjectGetString(item, "name")));
+        data->missingMandatory = true;
+    }
+
+    return 1;
+}
+
+
+static int
+testQEMUSchemaValidateObject(virJSONValuePtr obj,
+                             virJSONValuePtr root,
+                             virHashTablePtr schema,
+                             virBufferPtr debug)
+{
+    struct testQEMUSchemaValidateObjectMemberData data = { NULL, schema,
+                                                           debug, false };
+    virJSONValuePtr localroot = NULL;
+    const char *variantfield;
+    const char *variantname;
+    int ret = -1;
+
+    if (virJSONValueGetType(obj) != VIR_JSON_TYPE_OBJECT) {
+        virBufferAddLit(debug, "ERROR: not an object");
+        return -1;
+    }
+
+    virBufferAddLit(debug, "{\n");
+    virBufferAdjustIndent(debug, 3);
+
+    /* copy schema */
+    if (!(localroot = virJSONValueCopy(root))) {
+        ret = -2;
+        goto cleanup;
+    }
+
+    /* remove variant */
+    if ((variantfield = virJSONValueObjectGetString(localroot, "tag"))) {
+        if (!(variantname = virJSONValueObjectGetString(obj, variantfield))) {
+            virBufferAsprintf(debug, "ERROR: missing variant discriminator attribute '%s'\n",
+                              variantfield);
+            goto cleanup;
+        }
+
+        if (testQEMUSchemaValidateObjectMergeVariant(localroot, variantfield,
+                                                     variantname,
+                                                     schema, debug) < 0)
+            goto cleanup;
+    }
+
+
+    /* validate members */
+    data.rootmembers = virJSONValueObjectGetArray(localroot, "members");
+    if (virJSONValueObjectForeachKeyValue(obj,
+                                          testQEMUSchemaValidateObjectMember,
+                                          &data) < 0)
+        goto cleanup;
+
+    /* check missing mandatory values */
+    if (virJSONValueArrayForeachSteal(data.rootmembers,
+                                      testQEMUSchemaValidateObjectMandatoryMember,
+                                      &data) < 0) {
+        ret = -2;
+        goto cleanup;
+    }
+
+    if (data.missingMandatory)
+        goto cleanup;
+
+    virBufferAdjustIndent(debug, -3);
+    virBufferAddLit(debug, "} OK");
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(localroot);
+    return ret;
+}
+
+
+static int
+testQEMUSchemaValidateEnum(virJSONValuePtr obj,
+                           virJSONValuePtr root,
+                           virBufferPtr debug)
+{
+    const char *objstr;
+    virJSONValuePtr values = NULL;
+    virJSONValuePtr value;
+    size_t n;
+    size_t i;
+
+    if (virJSONValueGetType(obj) != VIR_JSON_TYPE_STRING) {
+        virBufferAddLit(debug, "ERROR: not a string");
+        return -1;
+    }
+
+    objstr = virJSONValueGetString(obj);
+
+    if (!(values = virJSONValueObjectGetArray(root, "values"))) {
+        virBufferAsprintf(debug, "ERROR: missing enum values in schema '%s'",
+                          NULLSTR(virJSONValueObjectGetString(root, "name")));
+        return -2;
+    }
+
+    n = virJSONValueArraySize(values);
+    for (i = 0; i < n; i++) {
+        value = virJSONValueArrayGet(values, i);
+
+        if (STREQ_NULLABLE(objstr, virJSONValueGetString(value))) {
+            virBufferAsprintf(debug, "'%s' OK", NULLSTR(objstr));
+            return 0;
+        }
+    }
+
+    virBufferAsprintf(debug, "ERROR: enum value '%s' is not in schema",
+                      NULLSTR(objstr));
+    return -1;
+}
+
+
+static int
+testQEMUSchemaValidateArray(virJSONValuePtr objs,
+                            virJSONValuePtr root,
+                            virHashTablePtr schema,
+                            virBufferPtr debug)
+{
+    const char *elemtypename = virJSONValueObjectGetString(root, "element-type");
+    virJSONValuePtr elementschema;
+    virJSONValuePtr obj;
+    size_t n;
+    size_t i;
+
+    if (virJSONValueGetType(objs) != VIR_JSON_TYPE_ARRAY) {
+        virBufferAddLit(debug, "ERROR: not an array\n");
+        return -1;
+    }
+
+    if (!elemtypename ||
+        !(elementschema = virHashLookup(schema, elemtypename))) {
+        virBufferAsprintf(debug, "ERROR: missing schema for array element type '%s'",
+                         NULLSTR(elemtypename));
+        return -2;
+    }
+
+    virBufferAddLit(debug, "[\n");
+    virBufferAdjustIndent(debug, 3);
+
+    n = virJSONValueArraySize(objs);
+    for (i = 0; i < n; i++) {
+        obj = virJSONValueArrayGet(objs, i);
+
+        if (testQEMUSchemaValidateRecurse(obj, elementschema, schema, debug) < 0)
+            return -1;
+        virBufferAddLit(debug, ",\n");
+    }
+    virBufferAddLit(debug, "] OK");
+    virBufferAdjustIndent(debug, -3);
+
+    return 0;
+}
+
+static int
+testQEMUSchemaValidateAlternate(virJSONValuePtr obj,
+                                virJSONValuePtr root,
+                                virHashTablePtr schema,
+                                virBufferPtr debug)
+{
+    virJSONValuePtr members;
+    virJSONValuePtr member;
+    size_t n;
+    size_t i;
+    const char *membertype;
+    virJSONValuePtr memberschema;
+    int indent;
+    int rc;
+
+    if (!(members = virJSONValueObjectGetArray(root, "members"))) {
+        virBufferAddLit(debug, "ERROR: missing 'members' for alternate schema");
+        return -2;
+    }
+
+    virBufferAddLit(debug, "(\n");
+    virBufferAdjustIndent(debug, 3);
+    indent = virBufferGetIndent(debug, false);
+
+    n = virJSONValueArraySize(members);
+    for (i = 0; i < n; i++) {
+        membertype = NULL;
+
+        /* P != NP */
+        virBufferAsprintf(debug, "(alternate %zu/%zu)\n", i + 1, n);
+        virBufferAdjustIndent(debug, 3);
+
+        if (!(member = virJSONValueArrayGet(members, i)) ||
+            !(membertype = virJSONValueObjectGetString(member, "type")) ||
+            !(memberschema = virHashLookup(schema, membertype))) {
+            virBufferAsprintf(debug, "ERROR: missing schema for alternate type '%s'",
+                              NULLSTR(membertype));
+            return -2;
+        }
+
+        rc = testQEMUSchemaValidateRecurse(obj, memberschema, schema, debug);
+
+        virBufferAddLit(debug, "\n");
+        virBufferSetIndent(debug, indent);
+        virBufferAsprintf(debug, "(/alternate %zu/%zu)\n", i + 1, n);
+
+        if (rc == 0) {
+            virBufferAdjustIndent(debug, -3);
+            virBufferAddLit(debug, ") OK");
+            return 0;
+        }
+    }
+
+    virBufferAddLit(debug, "ERROR: no alternate type was matched");
+    return -1;
+}
+
+
+static int
+testQEMUSchemaValidateRecurse(virJSONValuePtr obj,
+                              virJSONValuePtr root,
+                              virHashTablePtr schema,
+                              virBufferPtr debug)
+{
+    const char *n = virJSONValueObjectGetString(root, "name");
+    const char *t = virJSONValueObjectGetString(root, "meta-type");
+
+    if (STREQ_NULLABLE(t, "builtin")) {
+        return testQEMUSchemaValidateArrayBuiltin(obj, root, debug);
+    } else if (STREQ_NULLABLE(t, "object")) {
+        return testQEMUSchemaValidateObject(obj, root, schema, debug);
+    } else if (STREQ_NULLABLE(t, "enum")) {
+        return testQEMUSchemaValidateEnum(obj, root, debug);
+    } else if (STREQ_NULLABLE(t, "array")) {
+        return testQEMUSchemaValidateArray(obj, root, schema, debug);
+    } else if (STREQ_NULLABLE(t, "alternate")) {
+        return testQEMUSchemaValidateAlternate(obj, root, schema, debug);
+    }
+
+    virBufferAsprintf(debug,
+                      "qapi schema meta-type '%s' of type '%s' not handled\n",
+                      NULLSTR(t), NULLSTR(n));
+    return -2;
+}
+
+
+/**
+ * testQEMUSchemaValidate:
+ * @obj: object to validate
+ * @root: schema entry to start from
+ * @schema: hash table containing schema entries
+ * @debug: a virBuffer which will be filled with debug information if provided
+ *
+ * Validates whether @obj conforms to the QAPI schema passed in via @schema,
+ * starting from the node @root. Returns 0, if @obj matches @schema, -1 if it
+ * does not and -2 if there is a problem with the schema or with internals.
+ *
+ * @debug is filled with information regarding the validation process
+ */
+int
+testQEMUSchemaValidate(virJSONValuePtr obj,
+                       virJSONValuePtr root,
+                       virHashTablePtr schema,
+                       virBufferPtr debug)
+{
+    return testQEMUSchemaValidateRecurse(obj, root, schema, debug);
+}
+
+
+virHashTablePtr
+testQEMUSchemaLoad(void)
+{
+    virJSONValuePtr schemajson;
+
+    if (!(schemajson = virTestLoadFileJSON("qemuqapischema.json", NULL)))
+        return NULL;
+
+    return virQEMUQapiSchemaConvert(schemajson);
+}
diff --git a/tests/testutilsqemuschema.h b/tests/testutilsqemuschema.h
new file mode 100644
index 0000000000..cb383db174
--- /dev/null
+++ b/tests/testutilsqemuschema.h
@@ -0,0 +1,30 @@
+/*
+ * testutilsqemuschema.h: helper functions for QEMU QAPI schema testing
+ *
+ * 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/>.
+ */
+
+#include "virhash.h"
+#include "virjson.h"
+#include "virbuffer.h"
+
+int
+testQEMUSchemaValidate(virJSONValuePtr obj,
+                       virJSONValuePtr root,
+                       virHashTablePtr schema,
+                       virBufferPtr debug);
+
+virHashTablePtr
+testQEMUSchemaLoad(void);
-- 
2.16.2




More information about the libvir-list mailing list