Re: [PATCH v2 1/4] util: Introduce virJSONStringCompare for JSON doc comparisons

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
> ---
>  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_ */
>   

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]