[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH V4 1/3] util: Introduce virJSONStringCompare for JSON doc comparisons
From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> Comparing JSON docs using strcmp is simple, but is not flexible as it is sensitive to whitespace used in the doc generation. When comparing objects it may also be desirable to treat the existance of keys in the actual object but not expected object as non-fatal. Introduce a virJSONStringCompare function which takes two strings representing expected and actual JSON docs and then does a DOM comparison. Comparison is controled with the ignore_contexts and flags parameters. No comparison is done on context paths specified in ignore_contexts. The VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL flag can be used to ignore actual values that have changed from an expected value of null. Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx> Signed-off-by: Jim Fehlig <jfehlig@xxxxxxxx> --- Beyond rebasing, unchanged from V3. src/libvirt_private.syms | 1 + src/util/virjson.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++ src/util/virjson.h | 16 ++++ 3 files changed, 259 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fdf4548..b0c0625 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1446,6 +1446,7 @@ virISCSIScanTargets; # util/virjson.h +virJSONStringCompare; virJSONValueArrayAppend; virJSONValueArrayGet; virJSONValueArraySize; diff --git a/src/util/virjson.c b/src/util/virjson.c index ec83b2f..73b71f4 100644 --- a/src/util/virjson.c +++ b/src/util/virjson.c @@ -47,6 +47,11 @@ VIR_LOG_INIT("util.json"); +VIR_ENUM_DECL(virJSONType) +VIR_ENUM_IMPL(virJSONType, VIR_JSON_TYPE_LAST, + "object", "array", "string", + "number", "boolean", "null") + typedef struct _virJSONParserState virJSONParserState; typedef virJSONParserState *virJSONParserStatePtr; struct _virJSONParserState { @@ -91,6 +96,7 @@ virJSONValueFree(virJSONValuePtr value) break; case VIR_JSON_TYPE_BOOLEAN: case VIR_JSON_TYPE_NULL: + case VIR_JSON_TYPE_LAST: break; } @@ -1107,6 +1113,204 @@ virJSONParserHandleEndArray(void *ctx) } +static bool +virJSONValueCompare(virJSONValuePtr expect, + virJSONValuePtr actual, + const char *context, + const char **ignore_contexts, + unsigned int flags) +{ + size_t i, j; + + if (expect->type != actual->type) { + if (expect->type == VIR_JSON_TYPE_NULL && + (flags & VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL)) + return true; + + const char *expectType = virJSONTypeTypeToString(expect->type); + const char *actualType = virJSONTypeTypeToString(actual->type); + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected value type '%s' but got value type '%s' at '%s'"), + expectType, actualType, context); + return false; + } + + switch (expect->type) { + case VIR_JSON_TYPE_OBJECT: + /* Ensure actual data contains all expected data */ + for (i = 0; i < expect->data.object.npairs; i++) { + bool found = false; + char *childcontext; + + if (virAsprintf(&childcontext, "%s%s%s", + context, + STREQ(context, "/") ? "" : "/", + expect->data.object.pairs[i].key) < 0) + return false; + + /* Bypass paths we've been asked to ignore */ + if (ignore_contexts) { + bool ignored = false; + + j = 0; + while (ignore_contexts[j]) { + if (STREQ(childcontext, ignore_contexts[j])) { + ignored = true; + break; + } + j++; + } + + if (ignored) + continue; + } + + for (j = 0; j < actual->data.object.npairs; j++) { + if (STREQ(expect->data.object.pairs[i].key, + actual->data.object.pairs[j].key)) { + found = true; + break; + } + } + + if (!found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected object key '%s' not found in actual object at '%s'"), + expect->data.object.pairs[i].key, context); + VIR_FREE(childcontext); + return false; + } + + if (!virJSONValueCompare(expect->data.object.pairs[i].value, + actual->data.object.pairs[j].value, + childcontext, + ignore_contexts, + flags)) { + VIR_FREE(childcontext); + return false; + } + VIR_FREE(childcontext); + } + + /* Ensure expected data contains all actual data */ + for (i = 0; i < actual->data.object.npairs; i++) { + bool found = false; + char *childcontext; + + if (virAsprintf(&childcontext, "%s%s%s", + context, + STREQ(context, "/") ? "" : "/", + actual->data.object.pairs[i].key) < 0) + return false; + + /* Bypass paths we've been asked to ignore */ + if (ignore_contexts) { + bool ignored = false; + + j = 0; + while (ignore_contexts[j]) { + if (STREQ(childcontext, ignore_contexts[j])) { + ignored = true; + break; + } + j++; + } + + if (ignored) + continue; + } + + for (j = 0; j < expect->data.object.npairs; j++) { + if (STREQ(actual->data.object.pairs[i].key, + expect->data.object.pairs[j].key)) { + found = true; + break; + } + } + + if (!found) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Actual object key '%s' not found in expected object at '%s'"), + actual->data.object.pairs[i].key, context); + VIR_FREE(childcontext); + return false; + } + + if (!virJSONValueCompare(actual->data.object.pairs[i].value, + expect->data.object.pairs[j].value, + childcontext, + ignore_contexts, + flags)) { + VIR_FREE(childcontext); + } + } + break; + + case VIR_JSON_TYPE_ARRAY: + if (expect->data.array.nvalues != + actual->data.array.nvalues) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected array size '%zu' but got size '%zu' at '%s'"), + expect->data.array.nvalues, + actual->data.array.nvalues, + context); + return false; + } + + for (i = 0; i < expect->data.array.nvalues; i++) { + if (!virJSONValueCompare(expect->data.array.values[i], + actual->data.array.values[i], + context, + ignore_contexts, + flags)) + return false; + } + break; + + case VIR_JSON_TYPE_STRING: + if (STRNEQ(expect->data.string, + actual->data.string)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected string value '%s' but got '%s' at '%s'"), + expect->data.string, actual->data.string, context); + return false; + } + break; + + case VIR_JSON_TYPE_NUMBER: + if (STRNEQ(expect->data.number, + actual->data.number)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected number value '%s' but got '%s' at '%s'"), + expect->data.number, actual->data.number, context); + return false; + } + break; + + case VIR_JSON_TYPE_BOOLEAN: + if (expect->data.boolean != + actual->data.boolean) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Expected bool value '%d' but got '%d' at '%s'"), + expect->data.boolean, actual->data.boolean, context); + return false; + } + break; + + case VIR_JSON_TYPE_NULL: + /* trivially equal */ + break; + + case VIR_JSON_TYPE_LAST: + /* nothing */ + break; + } + + return true; +} + + static const yajl_callbacks parserCallbacks = { virJSONParserHandleNull, virJSONParserHandleBoolean, @@ -1306,6 +1510,30 @@ virJSONValueToString(virJSONValuePtr object, } +bool +virJSONStringCompare(const char *expect, + const char *actual, + const char **ignore_contexts, + unsigned int flags) +{ + virJSONValuePtr expectVal = NULL; + virJSONValuePtr actualVal = NULL; + int ret = false; + + if (!(expectVal = virJSONValueFromString(expect))) + goto cleanup; + if (!(actualVal = virJSONValueFromString(actual))) + goto cleanup; + + ret = virJSONValueCompare(expectVal, actualVal, "/", ignore_contexts, flags); + + cleanup: + virJSONValueFree(expectVal); + virJSONValueFree(actualVal); + return ret; +} + + #else virJSONValuePtr virJSONValueFromString(const char *jsonstring ATTRIBUTE_UNUSED) @@ -1324,4 +1552,18 @@ virJSONValueToString(virJSONValuePtr object ATTRIBUTE_UNUSED, _("No JSON parser implementation is available")); return NULL; } + + +bool +virJSONStringCompare(const char *expect ATTRIBUTE_UNUSED, + const char *actual ATTRIBUTE_UNUSED, + const char **ignore_contexts ATTRIBUTE_UNUSED, + unsigned int flags) +{ + virCheckFlags(VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL, false); + + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("No JSON parser implementation is available")); + return false; +} #endif diff --git a/src/util/virjson.h b/src/util/virjson.h index 9487729..5dc948f 100644 --- a/src/util/virjson.h +++ b/src/util/virjson.h @@ -34,6 +34,8 @@ typedef enum { VIR_JSON_TYPE_NUMBER, VIR_JSON_TYPE_BOOLEAN, VIR_JSON_TYPE_NULL, + + VIR_JSON_TYPE_LAST, } virJSONType; typedef struct _virJSONValue virJSONValue; @@ -141,4 +143,18 @@ virJSONValuePtr virJSONValueFromString(const char *jsonstring); char *virJSONValueToString(virJSONValuePtr object, bool pretty); +typedef enum { + /* + * when comparing two values, if their types are different, + * and the 'expected' value type is 'null', then this will + * be considered non-fatal. + */ + VIR_JSON_COMPARE_IGNORE_EXPECTED_NULL = (1 << 0), +} virJSONCompareFlags; + +bool virJSONStringCompare(const char *expect, + const char *actual, + const char **ignore_contexts, + unsigned int flags); + #endif /* __VIR_JSON_H_ */ -- 1.8.4.5 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |