From nobody Wed May 14 16:58:44 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1521743609437466.61841597590944; Thu, 22 Mar 2018 11:33:29 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 1D69461D22; Thu, 22 Mar 2018 18:33:28 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E6C747B52F; Thu, 22 Mar 2018 18:33:27 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id A88C44CAA3; Thu, 22 Mar 2018 18:33:27 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id w2MIVxxp015658 for ; Thu, 22 Mar 2018 14:31:59 -0400 Received: by smtp.corp.redhat.com (Postfix) id 66B688442D; Thu, 22 Mar 2018 18:31:59 +0000 (UTC) Received: from angien.brq.redhat.com (unknown [10.43.2.136]) by smtp.corp.redhat.com (Postfix) with ESMTP id E444584433; Thu, 22 Mar 2018 18:31:58 +0000 (UTC) From: Peter Krempa To: libvir-list@redhat.com Date: Thu, 22 Mar 2018 19:31:45 +0100 Message-Id: <7dd1c18774b0bb49ce93e24ead7b2e4d57973aa4.1521743167.git.pkrempa@redhat.com> In-Reply-To: References: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-loop: libvir-list@redhat.com Cc: Peter Krempa Subject: [libvirt] [PATCH 08/11] tests: qemu: Add infrastructure for QAPI schema testing X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Thu, 22 Mar 2018 18:33:28 +0000 (UTC) X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" 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 --- 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 =3D \ qemumonitorjsontest.c \ testutils.c testutils.h \ testutilsqemu.c testutilsqemu.h \ + testutilsqemuschema.c testutilsqemuschema.h \ $(NULL) qemumonitorjsontest_LDADD =3D libqemumonitortestutils.la \ $(qemu_LDADDS) $(LDADDS) @@ -694,6 +695,7 @@ else ! WITH_QEMU EXTRA_DIST +=3D 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 =3D opaque; + virBuffer debug =3D VIR_BUFFER_INITIALIZER; + virJSONValuePtr schemaroot; + virJSONValuePtr json =3D NULL; + int ret =3D -1; + + if (virQEMUQapiSchemaPathGet(data->query, data->schema, &schemaroot) <= 0) + goto cleanup; + + if (!(json =3D virJSONValueFromString(data->json))) + goto cleanup; + + if ((testQEMUSchemaValidate(json, schemaroot, data->schema, &debug) = =3D=3D 0) !=3D data->success) { + if (!data->success) + VIR_TEST_VERBOSE("\nschema validation should have failed\n"); + } else { + ret =3D 0; + } + + if (virTestGetDebug() || + (ret < 0 && virTestGetVerbose())) { + char *debugstr =3D virBufferContentAndReset(&debug); + fprintf(stderr, "\n%s\n", debugstr); + VIR_FREE(debugstr); + } + + + cleanup: + virBufferFreeAndReset(&debug); + virJSONValueFree(json); + return ret; +} + + static int mymain(void) { int ret =3D 0; virQEMUDriver driver; testQemuMonitorJSONSimpleFuncData simpleFunc; + struct testQAPISchemaData qapiData; + char *metaschema =3D 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 =3D nme; \ + qapiData.query =3D rootquery; \ + qapiData.success =3D scc; \ + qapiData.json =3D jsonstr; \ + if (virTestRun("qapi schema " nme, testQAPISchema, &qapiData) < 0)\ + ret =3D -1; \ + } while (0) + + if (!(qapiData.schema =3D testQEMUSchemaLoad())) { + VIR_TEST_VERBOSE("failed to load qapi schema\n"); + ret =3D -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", tru= e, + "{\"name\":\"test\", \"vcpu\":123}"); + DO_TEST_QAPI_SCHEMA("attr type mismatch", "trace-event-get-state/arg-t= ype", false, + "{\"name\":123}"); + DO_TEST_QAPI_SCHEMA("missing mandatory attr", "trace-event-get-state/a= rg-type", false, + "{\"vcpu\":123}"); + DO_TEST_QAPI_SCHEMA("attr name not present", "trace-event-get-state/ar= g-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-typ= e", 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 =3D virTestLoadFilePath("qemuqapischema.json", NULL))= ) { + VIR_TEST_VERBOSE("failed to load qapi schema\n"); + ret =3D -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 =3D=3D 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 + * . + */ +#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 =3D virJSONValueObjectGetString(root, "json-type"); + const char *s =3D NULL; + bool b =3D false; + int ret =3D -1; + + if (STREQ_NULLABLE(t, "value")) { + s =3D "{any}"; + ret =3D 0; + goto cleanup; + } + + switch (virJSONValueGetType(obj)) { + case VIR_JSON_TYPE_STRING: + if (STRNEQ_NULLABLE(t, "string")) + goto cleanup; + s =3D virJSONValueGetString(obj); + break; + + case VIR_JSON_TYPE_NUMBER: + if (STRNEQ_NULLABLE(t, "int") && + STRNEQ_NULLABLE(t, "number")) + goto cleanup; + s =3D "{number}"; + break; + + case VIR_JSON_TYPE_BOOLEAN: + if (STRNEQ_NULLABLE(t, "boolean")) + goto cleanup; + virJSONValueGetBoolean(obj, &b); + if (b) + s =3D "true"; + else + s =3D "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 =3D 0; + + cleanup: + if (ret =3D=3D 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 =3D NULL; + size_t n; + size_t i; + + n =3D virJSONValueArraySize(members); + for (i =3D 0; i < n; i++) { + member =3D virJSONValueArrayGet(members, i); + + if (STREQ_NULLABLE(name, virJSONValueObjectGetString(member, "name= "))) { + ret =3D virJSONValueArraySteal(members, i); + break; + } + } + + return ret; +} + + +static int +testQEMUSchemaValidateObjectMember(const char *key, + virJSONValuePtr value, + void *opaque) +{ + struct testQEMUSchemaValidateObjectMemberData *data =3D opaque; + virJSONValuePtr keymember =3D NULL; + const char *keytype; + virJSONValuePtr keyschema =3D NULL; + int ret =3D -1; + + virBufferStrcat(data->debug, key, ": ", NULL); + + /* lookup 'member' entry for key */ + if (!(keymember =3D testQEMUSchemaStealObjectMemberByName(key, data->r= ootmembers))) { + virBufferAddLit(data->debug, "ERROR: attribute not in schema"); + goto cleanup; + } + + /* lookup schema entry for keytype */ + if (!(keytype =3D virJSONValueObjectGetString(keymember, "type")) || + !(keyschema =3D virHashLookup(data->schema, keytype))) { + virBufferAsprintf(data->debug, "ERROR: can't find schema for type = '%s'", + NULLSTR(keytype)); + ret =3D -2; + goto cleanup; + } + + /* recurse */ + ret =3D 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 =3D opaque; + virJSONValuePtr copy; + + if (!(copy =3D 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 t= he + * '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 =3D NULL; + virJSONValuePtr variant; + virJSONValuePtr variantschema; + virJSONValuePtr variantschemamembers; + virJSONValuePtr rootmembers; + const char *varianttype =3D NULL; + int ret =3D -1; + + if (!(variants =3D virJSONValueObjectStealArray(root, "variants"))) { + virBufferAddLit(debug, "ERROR: missing 'variants' in schema\n"); + return -2; + } + + n =3D virJSONValueArraySize(variants); + for (i =3D 0; i < n; i++) { + variant =3D virJSONValueArrayGet(variants, i); + + if (STREQ_NULLABLE(variantname, + virJSONValueObjectGetString(variant, "case"))) { + varianttype =3D virJSONValueObjectGetString(variant, "type"); + break; + } + } + + if (!varianttype) { + virBufferAsprintf(debug, "ERROR: variant '%s' for discriminator '%= s' not found\n", + variantname, variantfield); + goto cleanup; + + } + + if (!(variantschema =3D virHashLookup(schema, varianttype)) || + !(variantschemamembers =3D virJSONValueObjectGetArray(variantschem= a, "members"))) { + virBufferAsprintf(debug, + "ERROR: missing schema or schema members for var= iant '%s'(%s)\n", + variantname, varianttype); + ret =3D -2; + goto cleanup; + } + + rootmembers =3D virJSONValueObjectGetArray(root, "members"); + + if (virJSONValueArrayForeachSteal(variantschemamembers, + testQEMUSchemaValidateObjectMergeVar= iantMember, + rootmembers) < 0) { + ret =3D -2; + goto cleanup; + } + + ret =3D 0; + + cleanup: + virJSONValueFree(variants); + return ret; +} + + +static int +testQEMUSchemaValidateObjectMandatoryMember(size_t pos ATTRIBUTE_UNUSED, + virJSONValuePtr item, + void *opaque ATTRIBUTE_UNUSED) +{ + struct testQEMUSchemaValidateObjectMemberData *data =3D opaque; + + if (virJSONValueObjectHasKey(item, "default") !=3D 1) { + virBufferAsprintf(data->debug, "ERROR: missing mandatory attribute= '%s'\n", + NULLSTR(virJSONValueObjectGetString(item, "name"= ))); + data->missingMandatory =3D true; + } + + return 1; +} + + +static int +testQEMUSchemaValidateObject(virJSONValuePtr obj, + virJSONValuePtr root, + virHashTablePtr schema, + virBufferPtr debug) +{ + struct testQEMUSchemaValidateObjectMemberData data =3D { NULL, schema, + debug, false }; + virJSONValuePtr localroot =3D NULL; + const char *variantfield; + const char *variantname; + int ret =3D -1; + + if (virJSONValueGetType(obj) !=3D VIR_JSON_TYPE_OBJECT) { + virBufferAddLit(debug, "ERROR: not an object"); + return -1; + } + + virBufferAddLit(debug, "{\n"); + virBufferAdjustIndent(debug, 3); + + /* copy schema */ + if (!(localroot =3D virJSONValueCopy(root))) { + ret =3D -2; + goto cleanup; + } + + /* remove variant */ + if ((variantfield =3D virJSONValueObjectGetString(localroot, "tag"))) { + if (!(variantname =3D virJSONValueObjectGetString(obj, variantfiel= d))) { + virBufferAsprintf(debug, "ERROR: missing variant discriminator= attribute '%s'\n", + variantfield); + goto cleanup; + } + + if (testQEMUSchemaValidateObjectMergeVariant(localroot, variantfie= ld, + variantname, + schema, debug) < 0) + goto cleanup; + } + + + /* validate members */ + data.rootmembers =3D virJSONValueObjectGetArray(localroot, "members"); + if (virJSONValueObjectForeachKeyValue(obj, + testQEMUSchemaValidateObjectMemb= er, + &data) < 0) + goto cleanup; + + /* check missing mandatory values */ + if (virJSONValueArrayForeachSteal(data.rootmembers, + testQEMUSchemaValidateObjectMandator= yMember, + &data) < 0) { + ret =3D -2; + goto cleanup; + } + + if (data.missingMandatory) + goto cleanup; + + virBufferAdjustIndent(debug, -3); + virBufferAddLit(debug, "} OK"); + ret =3D 0; + + cleanup: + virJSONValueFree(localroot); + return ret; +} + + +static int +testQEMUSchemaValidateEnum(virJSONValuePtr obj, + virJSONValuePtr root, + virBufferPtr debug) +{ + const char *objstr; + virJSONValuePtr values =3D NULL; + virJSONValuePtr value; + size_t n; + size_t i; + + if (virJSONValueGetType(obj) !=3D VIR_JSON_TYPE_STRING) { + virBufferAddLit(debug, "ERROR: not a string"); + return -1; + } + + objstr =3D virJSONValueGetString(obj); + + if (!(values =3D virJSONValueObjectGetArray(root, "values"))) { + virBufferAsprintf(debug, "ERROR: missing enum values in schema '%s= '", + NULLSTR(virJSONValueObjectGetString(root, "name"= ))); + return -2; + } + + n =3D virJSONValueArraySize(values); + for (i =3D 0; i < n; i++) { + value =3D 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 =3D virJSONValueObjectGetString(root, "elemen= t-type"); + virJSONValuePtr elementschema; + virJSONValuePtr obj; + size_t n; + size_t i; + + if (virJSONValueGetType(objs) !=3D VIR_JSON_TYPE_ARRAY) { + virBufferAddLit(debug, "ERROR: not an array\n"); + return -1; + } + + if (!elemtypename || + !(elementschema =3D virHashLookup(schema, elemtypename))) { + virBufferAsprintf(debug, "ERROR: missing schema for array element = type '%s'", + NULLSTR(elemtypename)); + return -2; + } + + virBufferAddLit(debug, "[\n"); + virBufferAdjustIndent(debug, 3); + + n =3D virJSONValueArraySize(objs); + for (i =3D 0; i < n; i++) { + obj =3D virJSONValueArrayGet(objs, i); + + if (testQEMUSchemaValidateRecurse(obj, elementschema, schema, debu= g) < 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 =3D virJSONValueObjectGetArray(root, "members"))) { + virBufferAddLit(debug, "ERROR: missing 'members' for alternate sch= ema"); + return -2; + } + + virBufferAddLit(debug, "(\n"); + virBufferAdjustIndent(debug, 3); + indent =3D virBufferGetIndent(debug, false); + + n =3D virJSONValueArraySize(members); + for (i =3D 0; i < n; i++) { + membertype =3D NULL; + + /* P !=3D NP */ + virBufferAsprintf(debug, "(alternate %zu/%zu)\n", i + 1, n); + virBufferAdjustIndent(debug, 3); + + if (!(member =3D virJSONValueArrayGet(members, i)) || + !(membertype =3D virJSONValueObjectGetString(member, "type")) = || + !(memberschema =3D virHashLookup(schema, membertype))) { + virBufferAsprintf(debug, "ERROR: missing schema for alternate = type '%s'", + NULLSTR(membertype)); + return -2; + } + + rc =3D testQEMUSchemaValidateRecurse(obj, memberschema, schema, de= bug); + + virBufferAddLit(debug, "\n"); + virBufferSetIndent(debug, indent); + virBufferAsprintf(debug, "(/alternate %zu/%zu)\n", i + 1, n); + + if (rc =3D=3D 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 =3D virJSONValueObjectGetString(root, "name"); + const char *t =3D 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 prov= ided + * + * Validates whether @obj conforms to the QAPI schema passed in via @schem= a, + * 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 =3D 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); --=20 2.16.2 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list