diff mbox

[03/26] qboject: add literal qobject type

Message ID 20170727154126.11339-4-marcandre.lureau@redhat.com
State New
Headers show

Commit Message

Marc-André Lureau July 27, 2017, 3:41 p.m. UTC
Promote LiteralQObject from tests/check-qjson.c to qobject/qlit.c,
allowing to statically declare complex qobjects.

Add a helper qobject_from_qlit() to instantiate a literal qobject to a
real QObject. Add some simple test.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qapi/qmp/qlit.h | 53 ++++++++++++++++++++++++++++++++++++
 qobject/qlit.c          | 53 ++++++++++++++++++++++++++++++++++++
 tests/check-qjson.c     | 72 +++++++++++++++++--------------------------------
 tests/check-qlit.c      | 52 +++++++++++++++++++++++++++++++++++
 qobject/Makefile.objs   |  2 +-
 tests/Makefile.include  |  5 +++-
 6 files changed, 187 insertions(+), 50 deletions(-)
 create mode 100644 include/qapi/qmp/qlit.h
 create mode 100644 qobject/qlit.c
 create mode 100644 tests/check-qlit.c

Comments

Markus Armbruster Aug. 16, 2017, 8:59 a.m. UTC | #1
In the subject: s/qboject: add/qobject: Add/

Marc-André Lureau <marcandre.lureau@redhat.com> writes:

> Promote LiteralQObject from tests/check-qjson.c to qobject/qlit.c,
> allowing to statically declare complex qobjects.
>
> Add a helper qobject_from_qlit() to instantiate a literal qobject to a
> real QObject. Add some simple test.

Suggest

* to also move the existing function making use of LiteralQObject
  compare_litqobj_to_qobj(), so other tests can use it to verify a
  QObject matches expectations, and

* to split the patch into the move and the addition of
  qobject_from_qlit().

>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/qapi/qmp/qlit.h | 53 ++++++++++++++++++++++++++++++++++++
>  qobject/qlit.c          | 53 ++++++++++++++++++++++++++++++++++++
>  tests/check-qjson.c     | 72 +++++++++++++++++--------------------------------
>  tests/check-qlit.c      | 52 +++++++++++++++++++++++++++++++++++
>  qobject/Makefile.objs   |  2 +-
>  tests/Makefile.include  |  5 +++-
>  6 files changed, 187 insertions(+), 50 deletions(-)
>  create mode 100644 include/qapi/qmp/qlit.h
>  create mode 100644 qobject/qlit.c
>  create mode 100644 tests/check-qlit.c
>
> diff --git a/include/qapi/qmp/qlit.h b/include/qapi/qmp/qlit.h
> new file mode 100644
> index 0000000000..fd6bfc3e69
> --- /dev/null
> +++ b/include/qapi/qmp/qlit.h
> @@ -0,0 +1,53 @@
> +/*
> + * Copyright IBM, Corp. 2009
> + * Copyright (c) 2013, 2015 Red Hat Inc.
> + *
> + * Authors:
> + *  Anthony Liguori   <aliguori@us.ibm.com>
> + *  Markus Armbruster <armbru@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +#ifndef QLIT_H_
> +#define QLIT_H_
> +
> +#include "qapi-types.h"
> +#include "qobject.h"
> +
> +typedef struct QLitDictEntry QLitDictEntry;
> +typedef struct QLitObject QLitObject;
> +
> +struct QLitObject {
> +    int type;
> +    union {
> +        bool qbool;
> +        int64_t qnum;

Not this patch's fault: QLitObject restricts numbers to signed integers.

> +        const char *qstr;
> +        QLitDictEntry *qdict;
> +        QLitObject *qlist;
> +    } value;
> +};
> +
> +struct QLitDictEntry {
> +    const char *key;
> +    QLitObject value;
> +};
> +
> +#define QLIT_QNULL \
> +    { .type = QTYPE_QNULL }
> +#define QLIT_QBOOL(val) \
> +    { .type = QTYPE_QBOOL, .value.qbool = (val) }
> +#define QLIT_QNUM(val) \
> +    { .type = QTYPE_QNUM, .value.qnum = (val) }
> +#define QLIT_QSTR(val) \
> +    { .type = QTYPE_QSTRING, .value.qstr = (val) }
> +#define QLIT_QDICT(val) \
> +    { .type = QTYPE_QDICT, .value.qdict = (val) }
> +#define QLIT_QLIST(val) \
> +    { .type = QTYPE_QLIST, .value.qlist = (val) }
> +
> +QObject *qobject_from_qlit(const QLitObject *qlit);
> +
> +#endif /* QLIT_H_ */
> diff --git a/qobject/qlit.c b/qobject/qlit.c
> new file mode 100644
> index 0000000000..d7407b4b34
> --- /dev/null
> +++ b/qobject/qlit.c
> @@ -0,0 +1,53 @@
> +/*
> + * QLit literal qobject
> + *
> + * Copyright (C) 2017 Red Hat Inc.
> + *
> + * Authors:
> + *  Marc-André Lureau <marcandre.lureau@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "qapi/qmp/qlit.h"
> +#include "qapi/qmp/types.h"
> +
> +QObject *qobject_from_qlit(const QLitObject *qlit)
> +{
> +    int i;
> +
> +    switch (qlit->type) {
> +    case QTYPE_QNULL:
> +        return QOBJECT(qnull());
> +    case QTYPE_QNUM:
> +        return QOBJECT(qnum_from_int(qlit->value.qnum));
> +    case QTYPE_QSTRING:
> +        return QOBJECT(qstring_from_str(qlit->value.qstr));
> +    case QTYPE_QDICT: {
> +        QDict *qdict = qdict_new();
> +        for (i = 0; qlit->value.qdict[i].value.type != QTYPE_NONE; i++) {
> +            QLitDictEntry *e = &qlit->value.qdict[i];
> +
> +            qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
> +        }
> +        return QOBJECT(qdict);
> +    }
> +    case QTYPE_QLIST: {
> +        QList *qlist = qlist_new();
> +
> +        for (i = 0; qlit->value.qlist[i].type != QTYPE_NONE; i++) {
> +            qlist_append_obj(qlist, qobject_from_qlit(&qlit->value.qlist[i]));
> +        }
> +        return QOBJECT(qlist);
> +    }
> +    case QTYPE_QBOOL:
> +        return QOBJECT(qbool_from_bool(qlit->value.qbool));
> +    case QTYPE_NONE:
> +        assert(0);
> +    }
> +
> +    return NULL;
> +}
> diff --git a/tests/check-qjson.c b/tests/check-qjson.c
> index 9c42a46b7d..1a95aff2ba 100644
> --- a/tests/check-qjson.c
> +++ b/tests/check-qjson.c
> @@ -16,6 +16,7 @@
>  #include "qapi/error.h"
>  #include "qapi/qmp/types.h"
>  #include "qapi/qmp/qjson.h"
> +#include "qapi/qmp/qlit.h"
>  #include "qemu-common.h"
>  
>  static void escaped_string(void)
> @@ -1059,39 +1060,14 @@ static void keyword_literal(void)
>      QDECREF(null);
>  }
>  
> -typedef struct LiteralQDictEntry LiteralQDictEntry;
> -typedef struct LiteralQObject LiteralQObject;
> -
> -struct LiteralQObject
> -{
> -    int type;
> -    union {
> -        int64_t qnum;
> -        const char *qstr;
> -        LiteralQDictEntry *qdict;
> -        LiteralQObject *qlist;
> -    } value;
> -};
> -
> -struct LiteralQDictEntry
> -{
> -    const char *key;
> -    LiteralQObject value;
> -};
> -
> -#define QLIT_QNUM(val) (LiteralQObject){.type = QTYPE_QNUM, .value.qnum = (val)}
> -#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
> -#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
> -#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
> -
>  typedef struct QListCompareHelper
>  {
>      int index;
> -    LiteralQObject *objs;
> +    QLitObject *objs;
>      int result;
>  } QListCompareHelper;
>  
> -static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
> +static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs);
>  
>  static void compare_helper(QObject *obj, void *opaque)
>  {
> @@ -1109,7 +1085,7 @@ static void compare_helper(QObject *obj, void *opaque)
>      helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
>  }
>  
> -static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
> +static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs)
>  {
>      int64_t val;
>  
> @@ -1159,23 +1135,23 @@ static void simple_dict(void)
>      int i;
>      struct {
>          const char *encoded;
> -        LiteralQObject decoded;
> +        QLitObject decoded;
>      } test_cases[] = {
>          {
>              .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>                          { "foo", QLIT_QNUM(42) },
>                          { "bar", QLIT_QSTR("hello world") },
>                          { }
>                      })),
>          }, {
>              .encoded = "{}",
> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>                          { }
>                      })),
>          }, {
>              .encoded = "{\"foo\": 43}",
> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>                          { "foo", QLIT_QNUM(43) },
>                          { }
>                      })),
> @@ -1257,11 +1233,11 @@ static void simple_list(void)
>      int i;
>      struct {
>          const char *encoded;
> -        LiteralQObject decoded;
> +        QLitObject decoded;
>      } test_cases[] = {
>          {
>              .encoded = "[43,42]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
>                          QLIT_QNUM(42),
>                          { }
> @@ -1269,21 +1245,21 @@ static void simple_list(void)
>          },
>          {
>              .encoded = "[43]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
>                          { }
>                      })),
>          },
>          {
>              .encoded = "[]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          { }
>                      })),
>          },
>          {
>              .encoded = "[{}]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> -                        QLIT_QDICT(((LiteralQDictEntry[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
> +                        QLIT_QDICT(((QLitDictEntry[]){
>                                      {},
>                                          })),
>                          {},
> @@ -1314,11 +1290,11 @@ static void simple_whitespace(void)
>      int i;
>      struct {
>          const char *encoded;
> -        LiteralQObject decoded;
> +        QLitObject decoded;
>      } test_cases[] = {
>          {
>              .encoded = " [ 43 , 42 ]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
>                          QLIT_QNUM(42),
>                          { }
> @@ -1326,12 +1302,12 @@ static void simple_whitespace(void)
>          },
>          {
>              .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
> -                        QLIT_QDICT(((LiteralQDictEntry[]){
> +                        QLIT_QDICT(((QLitDictEntry[]){
>                                      { "h", QLIT_QSTR("b") },
>                                      { }})),
> -                        QLIT_QLIST(((LiteralQObject[]){
> +                        QLIT_QLIST(((QLitObject[]){
>                                      { }})),
>                          QLIT_QNUM(42),
>                          { }
> @@ -1339,13 +1315,13 @@ static void simple_whitespace(void)
>          },
>          {
>              .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
> +            .decoded = QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(43),
> -                        QLIT_QDICT(((LiteralQDictEntry[]){
> +                        QLIT_QDICT(((QLitDictEntry[]){
>                                      { "h", QLIT_QSTR("b") },
>                                      { "a", QLIT_QNUM(32) },
>                                      { }})),
> -                        QLIT_QLIST(((LiteralQObject[]){
> +                        QLIT_QLIST(((QLitObject[]){
>                                      { }})),
>                          QLIT_QNUM(42),
>                          { }
> @@ -1393,10 +1369,10 @@ static void simple_varargs(void)
>  {
>      QObject *embedded_obj;
>      QObject *obj;
> -    LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
> +    QLitObject decoded = QLIT_QLIST(((QLitObject[]){
>              QLIT_QNUM(1),
>              QLIT_QNUM(2),
> -            QLIT_QLIST(((LiteralQObject[]){
> +            QLIT_QLIST(((QLitObject[]){
>                          QLIT_QNUM(32),
>                          QLIT_QNUM(42),
>                          {}})),
> diff --git a/tests/check-qlit.c b/tests/check-qlit.c
> new file mode 100644
> index 0000000000..cda4620f92
> --- /dev/null
> +++ b/tests/check-qlit.c
> @@ -0,0 +1,52 @@
> +/*
> + * QLit unit-tests.
> + *
> + * Copyright (C) 2017 Red Hat Inc.
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "qapi/qmp/qlit.h"
> +
> +static void qobject_from_qlit_test(void)
> +{
> +    char *str;
> +    QObject *qobj = NULL;
> +    QLitObject qlit = QLIT_QDICT((
> +        (QLitDictEntry[]) {
> +            { "foo", QLIT_QNUM(42) },
> +            { "bar", QLIT_QSTR("hello world") },
> +            { "baz", QLIT_QNULL },
> +            { "bee", QLIT_QLIST((
> +                (QLitObject[]) {
> +                    QLIT_QNUM(43),
> +                    QLIT_QNUM(44),
> +                    QLIT_QBOOL(true),
> +                    { },
> +                }))
> +            },
> +            { },
> +        }));
> +
> +    qobj = qobject_from_qlit(&qlit);
> +
> +    str = qobject_to_string(qobj);
> +    g_assert_cmpstr(str, ==,
> +                    "bee:\n    [0]: 43\n    [1]: 44\n    [2]: true\n"   \
> +                    "baz: null\nbar: hello world\nfoo: 42\n");

I don't like this.  The order of QDict members in @str depends on
qdict_first()/qdict_next() iteration order, which is unspecified.

Here's how we check elsewhere that a QObject matches expectations:

    static void qobject_from_qlit_test(void)
    {
        QLitObject qlit = QLIT_QDICT((
            (QLitDictEntry[]) {
                { "foo", QLIT_QNUM(42) },
                { "bar", QLIT_QSTR("hello world") },
                { "baz", QLIT_QNULL },
                { "bee", QLIT_QLIST((
                    (QLitObject[]) {
                        QLIT_QNUM(43),
                        QLIT_QNUM(44),
                        QLIT_QBOOL(true),
                        { },
                    }))
                },
                { },
            }));
        QObject *qobj = qobject_from_qlit(&qlit);
        QDict *qdict;
        QList *baz;

        qdict = qobject_to_qdict(qobj);
        g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42);
        g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world");
        g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL);
        baz = qdict_get_qlist(qdict, "bee");
        g_assert_cmpint(qnum_get_int(qobject_to_qnum(qlist_pop(baz))), ==, 43);
        g_assert_cmpint(qnum_get_int(qobject_to_qnum(qlist_pop(baz))), ==, 44);
        g_assert_cmpint(qbool_get_bool(qobject_to_qbool(qlist_pop(baz))), ==, 1);

        qobject_decref(qobj);
    }

Robust, just tedious.

check-qjson.c uses a differently tedious technique: compare expected
QLitObject to actual QObject with compare_litqobj_to_qobj().  I like
that better, because I find the QLIT... initializers easier to read, and
less error prone to write.  You might prefer not to use QLit to test
QLit, though.

> +
> +    g_free(str);
> +    qobject_decref(qobj);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    g_test_init(&argc, &argv, NULL);
> +
> +    g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test);
> +
> +    return g_test_run();
> +}
> diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
> index fc8885c9a4..002d25873a 100644
> --- a/qobject/Makefile.objs
> +++ b/qobject/Makefile.objs
> @@ -1,2 +1,2 @@
> -util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o
> +util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o
>  util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index 7af278db55..960ab8c6dd 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -12,6 +12,8 @@ check-unit-y += tests/test-char$(EXESUF)
>  gcov-files-check-qdict-y = chardev/char.c
>  check-unit-y += tests/check-qnum$(EXESUF)
>  gcov-files-check-qnum-y = qobject/qnum.c
> +check-unit-y += tests/check-qlit$(EXESUF)
> +gcov-files-check-qlit-y = qobject/qlit.c
>  check-unit-y += tests/check-qstring$(EXESUF)
>  gcov-files-check-qstring-y = qobject/qstring.c
>  check-unit-y += tests/check-qlist$(EXESUF)
> @@ -523,7 +525,7 @@ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
>  	tests/rcutorture.o tests/test-rcu-list.o \
>  	tests/test-qdist.o tests/test-shift128.o \
>  	tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
> -	tests/atomic_add-bench.o
> +	tests/atomic_add-bench.o tests/check-qlit.o

Please add it right next to the other qobject-related tests:

   test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
 	tests/check-qlist.o tests/check-qnull.o \
-	tests/check-qjson.o \
+	tests/check-qjson.o tests/check-qlit.o \

>  
>  $(test-obj-y): QEMU_INCLUDES += -Itests
>  QEMU_CFLAGS += -I$(SRC_PATH)/tests
> @@ -541,6 +543,7 @@ test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
>  test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
>  
>  tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
> +tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
>  tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
>  tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
>  tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)

Same order as in test-obj-y, please.
Marc-André Lureau Aug. 22, 2017, 11:16 a.m. UTC | #2
Hi

On Wed, Aug 16, 2017 at 10:59 AM, Markus Armbruster <armbru@redhat.com> wrote:
> In the subject: s/qboject: add/qobject: Add/
>
> Marc-André Lureau <marcandre.lureau@redhat.com> writes:
>
>> Promote LiteralQObject from tests/check-qjson.c to qobject/qlit.c,
>> allowing to statically declare complex qobjects.
>>
>> Add a helper qobject_from_qlit() to instantiate a literal qobject to a
>> real QObject. Add some simple test.
>
> Suggest
>
> * to also move the existing function making use of LiteralQObject
>   compare_litqobj_to_qobj(), so other tests can use it to verify a
>   QObject matches expectations, and
>
> * to split the patch into the move and the addition of
>   qobject_from_qlit().
>

ok

>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  include/qapi/qmp/qlit.h | 53 ++++++++++++++++++++++++++++++++++++
>>  qobject/qlit.c          | 53 ++++++++++++++++++++++++++++++++++++
>>  tests/check-qjson.c     | 72 +++++++++++++++++--------------------------------
>>  tests/check-qlit.c      | 52 +++++++++++++++++++++++++++++++++++
>>  qobject/Makefile.objs   |  2 +-
>>  tests/Makefile.include  |  5 +++-
>>  6 files changed, 187 insertions(+), 50 deletions(-)
>>  create mode 100644 include/qapi/qmp/qlit.h
>>  create mode 100644 qobject/qlit.c
>>  create mode 100644 tests/check-qlit.c
>>
>> diff --git a/include/qapi/qmp/qlit.h b/include/qapi/qmp/qlit.h
>> new file mode 100644
>> index 0000000000..fd6bfc3e69
>> --- /dev/null
>> +++ b/include/qapi/qmp/qlit.h
>> @@ -0,0 +1,53 @@
>> +/*
>> + * Copyright IBM, Corp. 2009
>> + * Copyright (c) 2013, 2015 Red Hat Inc.
>> + *
>> + * Authors:
>> + *  Anthony Liguori   <aliguori@us.ibm.com>
>> + *  Markus Armbruster <armbru@redhat.com>
>> + *
>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>> + * See the COPYING.LIB file in the top-level directory.
>> + *
>> + */
>> +#ifndef QLIT_H_
>> +#define QLIT_H_
>> +
>> +#include "qapi-types.h"
>> +#include "qobject.h"
>> +
>> +typedef struct QLitDictEntry QLitDictEntry;
>> +typedef struct QLitObject QLitObject;
>> +
>> +struct QLitObject {
>> +    int type;
>> +    union {
>> +        bool qbool;
>> +        int64_t qnum;
>
> Not this patch's fault: QLitObject restricts numbers to signed integers.
>
>> +        const char *qstr;
>> +        QLitDictEntry *qdict;
>> +        QLitObject *qlist;
>> +    } value;
>> +};
>> +
>> +struct QLitDictEntry {
>> +    const char *key;
>> +    QLitObject value;
>> +};
>> +
>> +#define QLIT_QNULL \
>> +    { .type = QTYPE_QNULL }
>> +#define QLIT_QBOOL(val) \
>> +    { .type = QTYPE_QBOOL, .value.qbool = (val) }
>> +#define QLIT_QNUM(val) \
>> +    { .type = QTYPE_QNUM, .value.qnum = (val) }
>> +#define QLIT_QSTR(val) \
>> +    { .type = QTYPE_QSTRING, .value.qstr = (val) }
>> +#define QLIT_QDICT(val) \
>> +    { .type = QTYPE_QDICT, .value.qdict = (val) }
>> +#define QLIT_QLIST(val) \
>> +    { .type = QTYPE_QLIST, .value.qlist = (val) }
>> +
>> +QObject *qobject_from_qlit(const QLitObject *qlit);
>> +
>> +#endif /* QLIT_H_ */
>> diff --git a/qobject/qlit.c b/qobject/qlit.c
>> new file mode 100644
>> index 0000000000..d7407b4b34
>> --- /dev/null
>> +++ b/qobject/qlit.c
>> @@ -0,0 +1,53 @@
>> +/*
>> + * QLit literal qobject
>> + *
>> + * Copyright (C) 2017 Red Hat Inc.
>> + *
>> + * Authors:
>> + *  Marc-André Lureau <marcandre.lureau@redhat.com>
>> + *
>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>> + * See the COPYING.LIB file in the top-level directory.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +
>> +#include "qapi/qmp/qlit.h"
>> +#include "qapi/qmp/types.h"
>> +
>> +QObject *qobject_from_qlit(const QLitObject *qlit)
>> +{
>> +    int i;
>> +
>> +    switch (qlit->type) {
>> +    case QTYPE_QNULL:
>> +        return QOBJECT(qnull());
>> +    case QTYPE_QNUM:
>> +        return QOBJECT(qnum_from_int(qlit->value.qnum));
>> +    case QTYPE_QSTRING:
>> +        return QOBJECT(qstring_from_str(qlit->value.qstr));
>> +    case QTYPE_QDICT: {
>> +        QDict *qdict = qdict_new();
>> +        for (i = 0; qlit->value.qdict[i].value.type != QTYPE_NONE; i++) {
>> +            QLitDictEntry *e = &qlit->value.qdict[i];
>> +
>> +            qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
>> +        }
>> +        return QOBJECT(qdict);
>> +    }
>> +    case QTYPE_QLIST: {
>> +        QList *qlist = qlist_new();
>> +
>> +        for (i = 0; qlit->value.qlist[i].type != QTYPE_NONE; i++) {
>> +            qlist_append_obj(qlist, qobject_from_qlit(&qlit->value.qlist[i]));
>> +        }
>> +        return QOBJECT(qlist);
>> +    }
>> +    case QTYPE_QBOOL:
>> +        return QOBJECT(qbool_from_bool(qlit->value.qbool));
>> +    case QTYPE_NONE:
>> +        assert(0);
>> +    }
>> +
>> +    return NULL;
>> +}
>> diff --git a/tests/check-qjson.c b/tests/check-qjson.c
>> index 9c42a46b7d..1a95aff2ba 100644
>> --- a/tests/check-qjson.c
>> +++ b/tests/check-qjson.c
>> @@ -16,6 +16,7 @@
>>  #include "qapi/error.h"
>>  #include "qapi/qmp/types.h"
>>  #include "qapi/qmp/qjson.h"
>> +#include "qapi/qmp/qlit.h"
>>  #include "qemu-common.h"
>>
>>  static void escaped_string(void)
>> @@ -1059,39 +1060,14 @@ static void keyword_literal(void)
>>      QDECREF(null);
>>  }
>>
>> -typedef struct LiteralQDictEntry LiteralQDictEntry;
>> -typedef struct LiteralQObject LiteralQObject;
>> -
>> -struct LiteralQObject
>> -{
>> -    int type;
>> -    union {
>> -        int64_t qnum;
>> -        const char *qstr;
>> -        LiteralQDictEntry *qdict;
>> -        LiteralQObject *qlist;
>> -    } value;
>> -};
>> -
>> -struct LiteralQDictEntry
>> -{
>> -    const char *key;
>> -    LiteralQObject value;
>> -};
>> -
>> -#define QLIT_QNUM(val) (LiteralQObject){.type = QTYPE_QNUM, .value.qnum = (val)}
>> -#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
>> -#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
>> -#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
>> -
>>  typedef struct QListCompareHelper
>>  {
>>      int index;
>> -    LiteralQObject *objs;
>> +    QLitObject *objs;
>>      int result;
>>  } QListCompareHelper;
>>
>> -static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
>> +static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs);
>>
>>  static void compare_helper(QObject *obj, void *opaque)
>>  {
>> @@ -1109,7 +1085,7 @@ static void compare_helper(QObject *obj, void *opaque)
>>      helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
>>  }
>>
>> -static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
>> +static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs)
>>  {
>>      int64_t val;
>>
>> @@ -1159,23 +1135,23 @@ static void simple_dict(void)
>>      int i;
>>      struct {
>>          const char *encoded;
>> -        LiteralQObject decoded;
>> +        QLitObject decoded;
>>      } test_cases[] = {
>>          {
>>              .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
>> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
>> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>>                          { "foo", QLIT_QNUM(42) },
>>                          { "bar", QLIT_QSTR("hello world") },
>>                          { }
>>                      })),
>>          }, {
>>              .encoded = "{}",
>> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
>> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>>                          { }
>>                      })),
>>          }, {
>>              .encoded = "{\"foo\": 43}",
>> -            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
>> +            .decoded = QLIT_QDICT(((QLitDictEntry[]){
>>                          { "foo", QLIT_QNUM(43) },
>>                          { }
>>                      })),
>> @@ -1257,11 +1233,11 @@ static void simple_list(void)
>>      int i;
>>      struct {
>>          const char *encoded;
>> -        LiteralQObject decoded;
>> +        QLitObject decoded;
>>      } test_cases[] = {
>>          {
>>              .encoded = "[43,42]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>>                          QLIT_QNUM(42),
>>                          { }
>> @@ -1269,21 +1245,21 @@ static void simple_list(void)
>>          },
>>          {
>>              .encoded = "[43]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>>                          { }
>>                      })),
>>          },
>>          {
>>              .encoded = "[]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          { }
>>                      })),
>>          },
>>          {
>>              .encoded = "[{}]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> -                        QLIT_QDICT(((LiteralQDictEntry[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>> +                        QLIT_QDICT(((QLitDictEntry[]){
>>                                      {},
>>                                          })),
>>                          {},
>> @@ -1314,11 +1290,11 @@ static void simple_whitespace(void)
>>      int i;
>>      struct {
>>          const char *encoded;
>> -        LiteralQObject decoded;
>> +        QLitObject decoded;
>>      } test_cases[] = {
>>          {
>>              .encoded = " [ 43 , 42 ]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>>                          QLIT_QNUM(42),
>>                          { }
>> @@ -1326,12 +1302,12 @@ static void simple_whitespace(void)
>>          },
>>          {
>>              .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>> -                        QLIT_QDICT(((LiteralQDictEntry[]){
>> +                        QLIT_QDICT(((QLitDictEntry[]){
>>                                      { "h", QLIT_QSTR("b") },
>>                                      { }})),
>> -                        QLIT_QLIST(((LiteralQObject[]){
>> +                        QLIT_QLIST(((QLitObject[]){
>>                                      { }})),
>>                          QLIT_QNUM(42),
>>                          { }
>> @@ -1339,13 +1315,13 @@ static void simple_whitespace(void)
>>          },
>>          {
>>              .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
>> -            .decoded = QLIT_QLIST(((LiteralQObject[]){
>> +            .decoded = QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(43),
>> -                        QLIT_QDICT(((LiteralQDictEntry[]){
>> +                        QLIT_QDICT(((QLitDictEntry[]){
>>                                      { "h", QLIT_QSTR("b") },
>>                                      { "a", QLIT_QNUM(32) },
>>                                      { }})),
>> -                        QLIT_QLIST(((LiteralQObject[]){
>> +                        QLIT_QLIST(((QLitObject[]){
>>                                      { }})),
>>                          QLIT_QNUM(42),
>>                          { }
>> @@ -1393,10 +1369,10 @@ static void simple_varargs(void)
>>  {
>>      QObject *embedded_obj;
>>      QObject *obj;
>> -    LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
>> +    QLitObject decoded = QLIT_QLIST(((QLitObject[]){
>>              QLIT_QNUM(1),
>>              QLIT_QNUM(2),
>> -            QLIT_QLIST(((LiteralQObject[]){
>> +            QLIT_QLIST(((QLitObject[]){
>>                          QLIT_QNUM(32),
>>                          QLIT_QNUM(42),
>>                          {}})),
>> diff --git a/tests/check-qlit.c b/tests/check-qlit.c
>> new file mode 100644
>> index 0000000000..cda4620f92
>> --- /dev/null
>> +++ b/tests/check-qlit.c
>> @@ -0,0 +1,52 @@
>> +/*
>> + * QLit unit-tests.
>> + *
>> + * Copyright (C) 2017 Red Hat Inc.
>> + *
>> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
>> + * See the COPYING.LIB file in the top-level directory.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +
>> +#include "qapi/qmp/qlit.h"
>> +
>> +static void qobject_from_qlit_test(void)
>> +{
>> +    char *str;
>> +    QObject *qobj = NULL;
>> +    QLitObject qlit = QLIT_QDICT((
>> +        (QLitDictEntry[]) {
>> +            { "foo", QLIT_QNUM(42) },
>> +            { "bar", QLIT_QSTR("hello world") },
>> +            { "baz", QLIT_QNULL },
>> +            { "bee", QLIT_QLIST((
>> +                (QLitObject[]) {
>> +                    QLIT_QNUM(43),
>> +                    QLIT_QNUM(44),
>> +                    QLIT_QBOOL(true),
>> +                    { },
>> +                }))
>> +            },
>> +            { },
>> +        }));
>> +
>> +    qobj = qobject_from_qlit(&qlit);
>> +
>> +    str = qobject_to_string(qobj);
>> +    g_assert_cmpstr(str, ==,
>> +                    "bee:\n    [0]: 43\n    [1]: 44\n    [2]: true\n"   \
>> +                    "baz: null\nbar: hello world\nfoo: 42\n");
>
> I don't like this.  The order of QDict members in @str depends on
> qdict_first()/qdict_next() iteration order, which is unspecified.
>
> Here's how we check elsewhere that a QObject matches expectations:
>
>     static void qobject_from_qlit_test(void)
>     {
>         QLitObject qlit = QLIT_QDICT((
>             (QLitDictEntry[]) {
>                 { "foo", QLIT_QNUM(42) },
>                 { "bar", QLIT_QSTR("hello world") },
>                 { "baz", QLIT_QNULL },
>                 { "bee", QLIT_QLIST((
>                     (QLitObject[]) {
>                         QLIT_QNUM(43),
>                         QLIT_QNUM(44),
>                         QLIT_QBOOL(true),
>                         { },
>                     }))
>                 },
>                 { },
>             }));
>         QObject *qobj = qobject_from_qlit(&qlit);
>         QDict *qdict;
>         QList *baz;
>
>         qdict = qobject_to_qdict(qobj);
>         g_assert_cmpint(qdict_get_int(qdict, "foo"), ==, 42);
>         g_assert_cmpstr(qdict_get_str(qdict, "bar"), ==, "hello world");
>         g_assert(qobject_type(qdict_get(qdict, "baz")) == QTYPE_QNULL);
>         baz = qdict_get_qlist(qdict, "bee");
>         g_assert_cmpint(qnum_get_int(qobject_to_qnum(qlist_pop(baz))), ==, 43);
>         g_assert_cmpint(qnum_get_int(qobject_to_qnum(qlist_pop(baz))), ==, 44);
>         g_assert_cmpint(qbool_get_bool(qobject_to_qbool(qlist_pop(baz))), ==, 1);
>
>         qobject_decref(qobj);
>     }
>
> Robust, just tedious.

done the tedious way

>
> check-qjson.c uses a differently tedious technique: compare expected
> QLitObject to actual QObject with compare_litqobj_to_qobj().  I like
> that better, because I find the QLIT... initializers easier to read, and
> less error prone to write.  You might prefer not to use QLit to test
> QLit, though.
>

ok

>> +
>> +    g_free(str);
>> +    qobject_decref(qobj);
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    g_test_init(&argc, &argv, NULL);
>> +
>> +    g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test);
>> +
>> +    return g_test_run();
>> +}
>> diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
>> index fc8885c9a4..002d25873a 100644
>> --- a/qobject/Makefile.objs
>> +++ b/qobject/Makefile.objs
>> @@ -1,2 +1,2 @@
>> -util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o
>> +util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o
>>  util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
>> diff --git a/tests/Makefile.include b/tests/Makefile.include
>> index 7af278db55..960ab8c6dd 100644
>> --- a/tests/Makefile.include
>> +++ b/tests/Makefile.include
>> @@ -12,6 +12,8 @@ check-unit-y += tests/test-char$(EXESUF)
>>  gcov-files-check-qdict-y = chardev/char.c
>>  check-unit-y += tests/check-qnum$(EXESUF)
>>  gcov-files-check-qnum-y = qobject/qnum.c
>> +check-unit-y += tests/check-qlit$(EXESUF)
>> +gcov-files-check-qlit-y = qobject/qlit.c
>>  check-unit-y += tests/check-qstring$(EXESUF)
>>  gcov-files-check-qstring-y = qobject/qstring.c
>>  check-unit-y += tests/check-qlist$(EXESUF)
>> @@ -523,7 +525,7 @@ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
>>       tests/rcutorture.o tests/test-rcu-list.o \
>>       tests/test-qdist.o tests/test-shift128.o \
>>       tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
>> -     tests/atomic_add-bench.o
>> +     tests/atomic_add-bench.o tests/check-qlit.o
>
> Please add it right next to the other qobject-related tests:
>

ok

>    test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
>         tests/check-qlist.o tests/check-qnull.o \
> -       tests/check-qjson.o \
> +       tests/check-qjson.o tests/check-qlit.o \
>
>>
>>  $(test-obj-y): QEMU_INCLUDES += -Itests
>>  QEMU_CFLAGS += -I$(SRC_PATH)/tests
>> @@ -541,6 +543,7 @@ test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
>>  test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
>>
>>  tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
>> +tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
>>  tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
>>  tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
>>  tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)
>
> Same order as in test-obj-y, please.
>

ok
diff mbox

Patch

diff --git a/include/qapi/qmp/qlit.h b/include/qapi/qmp/qlit.h
new file mode 100644
index 0000000000..fd6bfc3e69
--- /dev/null
+++ b/include/qapi/qmp/qlit.h
@@ -0,0 +1,53 @@ 
+/*
+ * Copyright IBM, Corp. 2009
+ * Copyright (c) 2013, 2015 Red Hat Inc.
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Markus Armbruster <armbru@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#ifndef QLIT_H_
+#define QLIT_H_
+
+#include "qapi-types.h"
+#include "qobject.h"
+
+typedef struct QLitDictEntry QLitDictEntry;
+typedef struct QLitObject QLitObject;
+
+struct QLitObject {
+    int type;
+    union {
+        bool qbool;
+        int64_t qnum;
+        const char *qstr;
+        QLitDictEntry *qdict;
+        QLitObject *qlist;
+    } value;
+};
+
+struct QLitDictEntry {
+    const char *key;
+    QLitObject value;
+};
+
+#define QLIT_QNULL \
+    { .type = QTYPE_QNULL }
+#define QLIT_QBOOL(val) \
+    { .type = QTYPE_QBOOL, .value.qbool = (val) }
+#define QLIT_QNUM(val) \
+    { .type = QTYPE_QNUM, .value.qnum = (val) }
+#define QLIT_QSTR(val) \
+    { .type = QTYPE_QSTRING, .value.qstr = (val) }
+#define QLIT_QDICT(val) \
+    { .type = QTYPE_QDICT, .value.qdict = (val) }
+#define QLIT_QLIST(val) \
+    { .type = QTYPE_QLIST, .value.qlist = (val) }
+
+QObject *qobject_from_qlit(const QLitObject *qlit);
+
+#endif /* QLIT_H_ */
diff --git a/qobject/qlit.c b/qobject/qlit.c
new file mode 100644
index 0000000000..d7407b4b34
--- /dev/null
+++ b/qobject/qlit.c
@@ -0,0 +1,53 @@ 
+/*
+ * QLit literal qobject
+ *
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * Authors:
+ *  Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qapi/qmp/qlit.h"
+#include "qapi/qmp/types.h"
+
+QObject *qobject_from_qlit(const QLitObject *qlit)
+{
+    int i;
+
+    switch (qlit->type) {
+    case QTYPE_QNULL:
+        return QOBJECT(qnull());
+    case QTYPE_QNUM:
+        return QOBJECT(qnum_from_int(qlit->value.qnum));
+    case QTYPE_QSTRING:
+        return QOBJECT(qstring_from_str(qlit->value.qstr));
+    case QTYPE_QDICT: {
+        QDict *qdict = qdict_new();
+        for (i = 0; qlit->value.qdict[i].value.type != QTYPE_NONE; i++) {
+            QLitDictEntry *e = &qlit->value.qdict[i];
+
+            qdict_put_obj(qdict, e->key, qobject_from_qlit(&e->value));
+        }
+        return QOBJECT(qdict);
+    }
+    case QTYPE_QLIST: {
+        QList *qlist = qlist_new();
+
+        for (i = 0; qlit->value.qlist[i].type != QTYPE_NONE; i++) {
+            qlist_append_obj(qlist, qobject_from_qlit(&qlit->value.qlist[i]));
+        }
+        return QOBJECT(qlist);
+    }
+    case QTYPE_QBOOL:
+        return QOBJECT(qbool_from_bool(qlit->value.qbool));
+    case QTYPE_NONE:
+        assert(0);
+    }
+
+    return NULL;
+}
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index 9c42a46b7d..1a95aff2ba 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -16,6 +16,7 @@ 
 #include "qapi/error.h"
 #include "qapi/qmp/types.h"
 #include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qlit.h"
 #include "qemu-common.h"
 
 static void escaped_string(void)
@@ -1059,39 +1060,14 @@  static void keyword_literal(void)
     QDECREF(null);
 }
 
-typedef struct LiteralQDictEntry LiteralQDictEntry;
-typedef struct LiteralQObject LiteralQObject;
-
-struct LiteralQObject
-{
-    int type;
-    union {
-        int64_t qnum;
-        const char *qstr;
-        LiteralQDictEntry *qdict;
-        LiteralQObject *qlist;
-    } value;
-};
-
-struct LiteralQDictEntry
-{
-    const char *key;
-    LiteralQObject value;
-};
-
-#define QLIT_QNUM(val) (LiteralQObject){.type = QTYPE_QNUM, .value.qnum = (val)}
-#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
-#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
-#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
-
 typedef struct QListCompareHelper
 {
     int index;
-    LiteralQObject *objs;
+    QLitObject *objs;
     int result;
 } QListCompareHelper;
 
-static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
+static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs);
 
 static void compare_helper(QObject *obj, void *opaque)
 {
@@ -1109,7 +1085,7 @@  static void compare_helper(QObject *obj, void *opaque)
     helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
 }
 
-static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
+static int compare_litqobj_to_qobj(QLitObject *lhs, QObject *rhs)
 {
     int64_t val;
 
@@ -1159,23 +1135,23 @@  static void simple_dict(void)
     int i;
     struct {
         const char *encoded;
-        LiteralQObject decoded;
+        QLitObject decoded;
     } test_cases[] = {
         {
             .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
-            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+            .decoded = QLIT_QDICT(((QLitDictEntry[]){
                         { "foo", QLIT_QNUM(42) },
                         { "bar", QLIT_QSTR("hello world") },
                         { }
                     })),
         }, {
             .encoded = "{}",
-            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+            .decoded = QLIT_QDICT(((QLitDictEntry[]){
                         { }
                     })),
         }, {
             .encoded = "{\"foo\": 43}",
-            .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+            .decoded = QLIT_QDICT(((QLitDictEntry[]){
                         { "foo", QLIT_QNUM(43) },
                         { }
                     })),
@@ -1257,11 +1233,11 @@  static void simple_list(void)
     int i;
     struct {
         const char *encoded;
-        LiteralQObject decoded;
+        QLitObject decoded;
     } test_cases[] = {
         {
             .encoded = "[43,42]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
                         QLIT_QNUM(42),
                         { }
@@ -1269,21 +1245,21 @@  static void simple_list(void)
         },
         {
             .encoded = "[43]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
                         { }
                     })),
         },
         {
             .encoded = "[]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         { }
                     })),
         },
         {
             .encoded = "[{}]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
-                        QLIT_QDICT(((LiteralQDictEntry[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
+                        QLIT_QDICT(((QLitDictEntry[]){
                                     {},
                                         })),
                         {},
@@ -1314,11 +1290,11 @@  static void simple_whitespace(void)
     int i;
     struct {
         const char *encoded;
-        LiteralQObject decoded;
+        QLitObject decoded;
     } test_cases[] = {
         {
             .encoded = " [ 43 , 42 ]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
                         QLIT_QNUM(42),
                         { }
@@ -1326,12 +1302,12 @@  static void simple_whitespace(void)
         },
         {
             .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
-                        QLIT_QDICT(((LiteralQDictEntry[]){
+                        QLIT_QDICT(((QLitDictEntry[]){
                                     { "h", QLIT_QSTR("b") },
                                     { }})),
-                        QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QLIST(((QLitObject[]){
                                     { }})),
                         QLIT_QNUM(42),
                         { }
@@ -1339,13 +1315,13 @@  static void simple_whitespace(void)
         },
         {
             .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
-            .decoded = QLIT_QLIST(((LiteralQObject[]){
+            .decoded = QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(43),
-                        QLIT_QDICT(((LiteralQDictEntry[]){
+                        QLIT_QDICT(((QLitDictEntry[]){
                                     { "h", QLIT_QSTR("b") },
                                     { "a", QLIT_QNUM(32) },
                                     { }})),
-                        QLIT_QLIST(((LiteralQObject[]){
+                        QLIT_QLIST(((QLitObject[]){
                                     { }})),
                         QLIT_QNUM(42),
                         { }
@@ -1393,10 +1369,10 @@  static void simple_varargs(void)
 {
     QObject *embedded_obj;
     QObject *obj;
-    LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
+    QLitObject decoded = QLIT_QLIST(((QLitObject[]){
             QLIT_QNUM(1),
             QLIT_QNUM(2),
-            QLIT_QLIST(((LiteralQObject[]){
+            QLIT_QLIST(((QLitObject[]){
                         QLIT_QNUM(32),
                         QLIT_QNUM(42),
                         {}})),
diff --git a/tests/check-qlit.c b/tests/check-qlit.c
new file mode 100644
index 0000000000..cda4620f92
--- /dev/null
+++ b/tests/check-qlit.c
@@ -0,0 +1,52 @@ 
+/*
+ * QLit unit-tests.
+ *
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qapi/qmp/qlit.h"
+
+static void qobject_from_qlit_test(void)
+{
+    char *str;
+    QObject *qobj = NULL;
+    QLitObject qlit = QLIT_QDICT((
+        (QLitDictEntry[]) {
+            { "foo", QLIT_QNUM(42) },
+            { "bar", QLIT_QSTR("hello world") },
+            { "baz", QLIT_QNULL },
+            { "bee", QLIT_QLIST((
+                (QLitObject[]) {
+                    QLIT_QNUM(43),
+                    QLIT_QNUM(44),
+                    QLIT_QBOOL(true),
+                    { },
+                }))
+            },
+            { },
+        }));
+
+    qobj = qobject_from_qlit(&qlit);
+
+    str = qobject_to_string(qobj);
+    g_assert_cmpstr(str, ==,
+                    "bee:\n    [0]: 43\n    [1]: 44\n    [2]: true\n"   \
+                    "baz: null\nbar: hello world\nfoo: 42\n");
+
+    g_free(str);
+    qobject_decref(qobj);
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/qlit/qobject_from_qlit", qobject_from_qlit_test);
+
+    return g_test_run();
+}
diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs
index fc8885c9a4..002d25873a 100644
--- a/qobject/Makefile.objs
+++ b/qobject/Makefile.objs
@@ -1,2 +1,2 @@ 
-util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o
+util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o
 util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 7af278db55..960ab8c6dd 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -12,6 +12,8 @@  check-unit-y += tests/test-char$(EXESUF)
 gcov-files-check-qdict-y = chardev/char.c
 check-unit-y += tests/check-qnum$(EXESUF)
 gcov-files-check-qnum-y = qobject/qnum.c
+check-unit-y += tests/check-qlit$(EXESUF)
+gcov-files-check-qlit-y = qobject/qlit.c
 check-unit-y += tests/check-qstring$(EXESUF)
 gcov-files-check-qstring-y = qobject/qstring.c
 check-unit-y += tests/check-qlist$(EXESUF)
@@ -523,7 +525,7 @@  test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
 	tests/rcutorture.o tests/test-rcu-list.o \
 	tests/test-qdist.o tests/test-shift128.o \
 	tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \
-	tests/atomic_add-bench.o
+	tests/atomic_add-bench.o tests/check-qlit.o
 
 $(test-obj-y): QEMU_INCLUDES += -Itests
 QEMU_CFLAGS += -I$(SRC_PATH)/tests
@@ -541,6 +543,7 @@  test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
 test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
 
 tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
+tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
 tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
 tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
 tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)