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..7048a15b6e 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..21f5d119e8
--- /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
+ * .
+ */
+#include
+#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
+ * .
+ */
+
+#include "virhash.h"
+#include "virjson.h"
+#include "virbuffer.h"
+
+int
+testQEMUSchemaValidate(virJSONValuePtr obj,
+ virJSONValuePtr root,
+ virHashTablePtr schema,
+ virBufferPtr debug);
+
+virHashTablePtr
+testQEMUSchemaLoad(void);