From: Jim Fehlig <jfehlig@suse.com>
To: "Daniel P. Berrange" <berrange@redhat.com>
Cc: libvir-list@redhat.com, xen-devel@lists.xensource.com,
Ian Campbell <Ian.Campbell@citrix.com>
Subject: Re: [libvirt] [PATCH v2 1/4] util: Introduce virJSONStringCompare for JSON doc comparisons
Date: Tue, 03 Jun 2014 15:18:26 -0600 [thread overview]
Message-ID: <538E3BA2.3070108@suse.com> (raw)
In-Reply-To: <1401793373-26680-2-git-send-email-berrange@redhat.com>
Daniel P. Berrange wrote:
> 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.
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
> src/libvirt_private.syms | 1 +
> src/util/virjson.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++
>
I'm not too familiar with this file, but didn't notice any problems with
this patch beyond a little nit below.
> src/util/virjson.h | 22 ++++++
> 3 files changed, 208 insertions(+)
>
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 4c1f61c..5f2b9d9 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1406,6 +1406,7 @@ virISCSIScanTargets;
>
>
> # util/virjson.h
> +virJSONStringCompare;
> virJSONValueArrayAppend;
> virJSONValueArrayGet;
> virJSONValueArraySize;
> diff --git a/src/util/virjson.c b/src/util/virjson.c
> index 35a8252..0a6d1be 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 {
> @@ -90,6 +95,7 @@ void virJSONValueFree(virJSONValuePtr value)
> break;
> case VIR_JSON_TYPE_BOOLEAN:
> case VIR_JSON_TYPE_NULL:
> + case VIR_JSON_TYPE_LAST:
> break;
> }
>
> @@ -1152,3 +1158,182 @@ char *virJSONValueToString(virJSONValuePtr object ATTRIBUTE_UNUSED,
> return NULL;
> }
> #endif
> +
> +
> +static bool
> +virJSONValueCompare(virJSONValuePtr expect,
> + virJSONValuePtr actual,
> + const char *context,
> + unsigned int flags)
> +{
> + size_t i, j;
> + if (expect->type != actual->type) {
>
Super nit: seems most of libvirt code would have a line between local
variable declarations and start of code, but this file is not consistent
in that regard anyhow.
ACK.
Regards,
Jim
> + 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:
> + for (i = 0; i < expect->data.object.npairs; i++) {
> + bool found = false;
> + char *childcontext;
> + 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);
> + return false;
> + }
> +
> + if (virAsprintf(&childcontext, "%s%s%s",
> + context,
> + STREQ(context, "/") ? "" : "/",
> + expect->data.object.pairs[i].key) < 0)
> + return false;
> +
> + if (!virJSONValueCompare(expect->data.object.pairs[i].value,
> + actual->data.object.pairs[j].value,
> + childcontext,
> + flags)) {
> + VIR_FREE(childcontext);
> + return false;
> + }
> + VIR_FREE(childcontext);
> + }
> + if (!(flags & VIR_JSON_COMPARE_IGNORE_EXTRA_OBJECT_KEYS)) {
> + for (i = 0; i < actual->data.object.npairs; i++) {
> + bool found = false;
> + char *childcontext;
> + 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);
> + return false;
> + }
> +
> + if (virAsprintf(&childcontext, "%s%s%s",
> + STREQ(context, "/") ? "" : "/",
> + context, actual->data.object.pairs[i].key) < 0)
> + return false;
> +
> + if (!virJSONValueCompare(actual->data.object.pairs[i].value,
> + expect->data.object.pairs[j].value,
> + childcontext,
> + flags)) {
> + VIR_FREE(childcontext);
> + return false;
> + }
> + 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++) {
> + char *childcontext;
> + if (virAsprintf(&childcontext, "%s%s[%zu]",
> + STREQ(context, "/") ? "" : "/",
> + context, i) < 0)
> + return false;
> +
> + if (!virJSONValueCompare(expect->data.array.values[i],
> + actual->data.array.values[i],
> + childcontext,
> + flags)) {
> + VIR_FREE(childcontext);
> + return false;
> + }
> + VIR_FREE(childcontext);
> + }
> + 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;
> +}
> +
> +
> +bool virJSONStringCompare(const char *expect,
> + const char *actual,
> + 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, "/", flags);
> +
> + cleanup:
> + virJSONValueFree(expectVal);
> + virJSONValueFree(actualVal);
> + return ret;
> +}
> diff --git a/src/util/virjson.h b/src/util/virjson.h
> index db11396..b1f96d3 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;
> @@ -139,4 +141,24 @@ virJSONValuePtr virJSONValueFromString(const char *jsonstring);
> char *virJSONValueToString(virJSONValuePtr object,
> bool pretty);
>
> +typedef enum {
> + /*
> + * When comparing a pair of Objects, if the 'actual' object
> + * has keys that are not present in the 'expected' object,
> + * the existance of these extra keys will be non-fatal.
> + */
> + VIR_JSON_COMPARE_IGNORE_EXTRA_OBJECT_KEYS = (1 << 0),
> +
> + /*
> + * 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 << 1),
> +} virJSONCompareFlags;
> +
> +bool virJSONStringCompare(const char *expect,
> + const char *actual,
> + unsigned int flags);
> +
> #endif /* __VIR_JSON_H_ */
>
next prev parent reply other threads:[~2014-06-03 21:18 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-06-03 11:02 [PATCH v2 0/4] Testing libvirt XML -> libxl_domain_config conversion Daniel P. Berrange
2014-06-03 11:02 ` [libvirt] [PATCH v2 1/4] util: Introduce virJSONStringCompare for JSON doc comparisons Daniel P. Berrange
2014-06-03 21:18 ` Jim Fehlig [this message]
2014-06-03 11:02 ` [PATCH v2 2/4] util: Allow port allocator to skip bind() check Daniel P. Berrange
2014-06-03 21:25 ` [libvirt] " Jim Fehlig
2014-06-03 11:02 ` [PATCH v2 3/4] tests: Add more test suite mock helpers Daniel P. Berrange
2014-06-03 21:30 ` [libvirt] " Jim Fehlig
2014-06-03 11:02 ` [PATCH v2 4/4] libxl: Add a test suite for libxl option generator Daniel P. Berrange
2014-06-03 21:45 ` [libvirt] " Jim Fehlig
2014-06-16 23:11 ` Jim Fehlig
2014-06-17 8:52 ` Ian Campbell
2014-06-17 18:40 ` Jim Fehlig
2014-06-18 8:33 ` Ian Campbell
2014-06-18 9:07 ` Daniel P. Berrange
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=538E3BA2.3070108@suse.com \
--to=jfehlig@suse.com \
--cc=Ian.Campbell@citrix.com \
--cc=berrange@redhat.com \
--cc=libvir-list@redhat.com \
--cc=xen-devel@lists.xensource.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).