From patchwork Wed Nov 11 17:29:03 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 38154 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 2090FB6F1E for ; Thu, 12 Nov 2009 04:47:55 +1100 (EST) Received: from localhost ([127.0.0.1]:40591 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N8HIN-0001JD-SN for incoming@patchwork.ozlabs.org; Wed, 11 Nov 2009 12:47:51 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1N8H0a-0006sg-Rr for qemu-devel@nongnu.org; Wed, 11 Nov 2009 12:29:29 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1N8H0S-0006mG-Ms for qemu-devel@nongnu.org; Wed, 11 Nov 2009 12:29:24 -0500 Received: from [199.232.76.173] (port=48456 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N8H0R-0006lY-Og for qemu-devel@nongnu.org; Wed, 11 Nov 2009 12:29:19 -0500 Received: from e33.co.us.ibm.com ([32.97.110.151]:59488) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1N8H0Q-000211-Hg for qemu-devel@nongnu.org; Wed, 11 Nov 2009 12:29:19 -0500 Received: from d03relay02.boulder.ibm.com (d03relay02.boulder.ibm.com [9.17.195.227]) by e33.co.us.ibm.com (8.14.3/8.13.1) with ESMTP id nABHQbQN021311 for ; Wed, 11 Nov 2009 10:26:37 -0700 Received: from d03av03.boulder.ibm.com (d03av03.boulder.ibm.com [9.17.195.169]) by d03relay02.boulder.ibm.com (8.13.8/8.13.8/NCO v9.1) with ESMTP id nABHTADF252324 for ; Wed, 11 Nov 2009 10:29:12 -0700 Received: from d03av03.boulder.ibm.com (loopback [127.0.0.1]) by d03av03.boulder.ibm.com (8.14.3/8.13.1/NCO v10.0 AVout) with ESMTP id nABHT9D7007409 for ; Wed, 11 Nov 2009 10:29:09 -0700 Received: from localhost.localdomain (sig-9-65-32-87.mts.ibm.com [9.65.32.87]) by d03av03.boulder.ibm.com (8.14.3/8.13.1/NCO v10.0 AVin) with ESMTP id nABHT43M007179; Wed, 11 Nov 2009 10:29:09 -0700 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Wed, 11 Nov 2009 11:29:03 -0600 Message-Id: <1257960543-26373-11-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.6.2.5 In-Reply-To: <1257960543-26373-1-git-send-email-aliguori@us.ibm.com> References: <1257960543-26373-1-git-send-email-aliguori@us.ibm.com> X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) Cc: Anthony Liguori , Luiz Capitulino Subject: [Qemu-devel] [PATCH 11/11] Add a unit test for JSON support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Anthony Liguori --- Makefile | 1 + check-qjson.c | 608 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure | 2 +- 3 files changed, 610 insertions(+), 1 deletions(-) create mode 100644 check-qjson.c diff --git a/Makefile b/Makefile index 3818c51..4b678e6 100644 --- a/Makefile +++ b/Makefile @@ -229,6 +229,7 @@ check-qstring: check-qstring.o qstring.o qemu-malloc.o check-qdict: check-qdict.o qdict.o qint.o qstring.o qemu-malloc.o check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o check-qfloat: check-qfloat.o qfloat.o qemu-malloc.o +check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o qemu-malloc.o clean: # avoid old build problems by removing potentially incorrect old files diff --git a/check-qjson.c b/check-qjson.c new file mode 100644 index 0000000..f763de6 --- /dev/null +++ b/check-qjson.c @@ -0,0 +1,608 @@ +/* + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * 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 +#include + +#include "qstring.h" +#include "qint.h" +#include "qdict.h" +#include "qlist.h" +#include "qfloat.h" +#include "qbool.h" +#include "qjson.h" + +#include "qemu-common.h" + +START_TEST(escaped_string) +{ + int i; + struct { + const char *encoded; + const char *decoded; + } test_cases[] = { + { "\"\\\"\"", "\"" }, + { "\"hello world \\\"embedded string\\\"\"", + "hello world \"embedded string\"" }, + { "\"hello world\\nwith new line\"", "hello world\nwith new line" }, + { "\"single byte utf-8 \\u0020\"", "single byte utf-8 " }, + { "\"double byte utf-8 \\u00A2\"", "double byte utf-8 \xc2\xa2" }, + { "\"triple byte utf-8 \\u20AC\"", "triple byte utf-8 \xe2\x82\xac" }, + {} + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + QString *str; + + obj = qobject_from_json(test_cases[i].encoded); + + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QSTRING); + + str = qobject_to_qstring(obj); + fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); + + QDECREF(str); + } +} +END_TEST + +START_TEST(simple_string) +{ + int i; + struct { + const char *encoded; + const char *decoded; + } test_cases[] = { + { "\"hello world\"", "hello world" }, + { "\"the quick brown fox jumped over the fence\"", + "the quick brown fox jumped over the fence" }, + {} + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + QString *str; + + obj = qobject_from_json(test_cases[i].encoded); + + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QSTRING); + + str = qobject_to_qstring(obj); + fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); + + QDECREF(str); + } +} +END_TEST + +START_TEST(single_quote_string) +{ + int i; + struct { + const char *encoded; + const char *decoded; + } test_cases[] = { + { "'hello world'", "hello world" }, + { "'the quick brown fox \\' jumped over the fence'", + "the quick brown fox ' jumped over the fence" }, + {} + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + QString *str; + + obj = qobject_from_json(test_cases[i].encoded); + + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QSTRING); + + str = qobject_to_qstring(obj); + fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); + + QDECREF(str); + } +} +END_TEST + +START_TEST(vararg_string) +{ + int i; + struct { + const char *decoded; + } test_cases[] = { + { "hello world" }, + { "the quick brown fox jumped over the fence" }, + {} + }; + + for (i = 0; test_cases[i].decoded; i++) { + QObject *obj; + QString *str; + + obj = qobject_from_jsonf("%s", test_cases[i].decoded); + + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QSTRING); + + str = qobject_to_qstring(obj); + fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); + + QDECREF(str); + } +} +END_TEST + +START_TEST(simple_number) +{ + int i; + struct { + const char *encoded; + int64_t decoded; + } test_cases[] = { + { "0", 0 }, + { "1234", 1234 }, + { "1", 1 }, + { "-32", -32 }, + { "-0", 0 }, + { }, + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + QInt *qint; + + obj = qobject_from_json(test_cases[i].encoded); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QINT); + + qint = qobject_to_qint(obj); + fail_unless(qint_get_int(qint) == test_cases[i].decoded); + + QDECREF(qint); + } +} +END_TEST + +START_TEST(float_number) +{ + int i; + struct { + const char *encoded; + double decoded; + } test_cases[] = { + { "32.43", 32.43 }, + { "0.222", 0.222 }, + { "-32.12313", -32.12313 }, + { "-32.20e-10", -32.20e-10 }, + { }, + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + QFloat *qfloat; + + obj = qobject_from_json(test_cases[i].encoded); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QFLOAT); + + qfloat = qobject_to_qfloat(obj); + fail_unless(qfloat_get_double(qfloat) == test_cases[i].decoded); + + QDECREF(qfloat); + } +} +END_TEST + +START_TEST(vararg_number) +{ + QObject *obj; + QInt *qint; + QFloat *qfloat; + int value = 0x2342; + int64_t value64 = 0x2342342343LL; + double valuef = 2.323423423; + + obj = qobject_from_jsonf("%d", value); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QINT); + + qint = qobject_to_qint(obj); + fail_unless(qint_get_int(qint) == value); + + QDECREF(qint); + + obj = qobject_from_jsonf("%" PRId64, value64); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QINT); + + qint = qobject_to_qint(obj); + fail_unless(qint_get_int(qint) == value64); + + QDECREF(qint); + + obj = qobject_from_jsonf("%f", valuef); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QFLOAT); + + qfloat = qobject_to_qfloat(obj); + fail_unless(qfloat_get_double(qfloat) == valuef); + + QDECREF(qfloat); +} +END_TEST + +START_TEST(keyword_literal) +{ + QObject *obj; + QBool *qbool; + + obj = qobject_from_json("true"); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QBOOL); + + qbool = qobject_to_qbool(obj); + fail_unless(qbool_get_int(qbool) != 0); + + QDECREF(qbool); + + obj = qobject_from_json("false"); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QBOOL); + + qbool = qobject_to_qbool(obj); + fail_unless(qbool_get_int(qbool) == 0); + + QDECREF(qbool); + + obj = qobject_from_jsonf("%i", false); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QBOOL); + + qbool = qobject_to_qbool(obj); + fail_unless(qbool_get_int(qbool) == 0); + + QDECREF(qbool); + + obj = qobject_from_jsonf("%i", true); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QBOOL); + + qbool = qobject_to_qbool(obj); + fail_unless(qbool_get_int(qbool) != 0); + + QDECREF(qbool); +} +END_TEST + +typedef struct LiteralQDictEntry LiteralQDictEntry; +typedef struct LiteralQObject LiteralQObject; + +struct LiteralQObject +{ + int type; + union { + int64_t qint; + const char *qstr; + LiteralQDictEntry *qdict; + LiteralQObject *qlist; + } value; +}; + +struct LiteralQDictEntry +{ + const char *key; + LiteralQObject value; +}; + +#define QLIT_QINT(val) (LiteralQObject){.type = QTYPE_QINT, .value.qint = (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; + int result; +} QListCompareHelper; + +static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs); + +static void compare_helper(QObject *obj, void *opaque) +{ + QListCompareHelper *helper = opaque; + + if (helper->result == 0) { + return; + } + + if (helper->objs[helper->index].type == QTYPE_NONE) { + helper->result = 0; + return; + } + + helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj); +} + +static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs) +{ + if (lhs->type != qobject_type(rhs)) { + return 0; + } + + switch (lhs->type) { + case QTYPE_QINT: + return lhs->value.qint == qint_get_int(qobject_to_qint(rhs)); + case QTYPE_QSTRING: + return (strcmp(lhs->value.qstr, qstring_get_str(qobject_to_qstring(rhs))) == 0); + case QTYPE_QDICT: { + int i; + + for (i = 0; lhs->value.qdict[i].key; i++) { + QObject *obj = qdict_get(qobject_to_qdict(rhs), lhs->value.qdict[i].key); + + if (!compare_litqobj_to_qobj(&lhs->value.qdict[i].value, obj)) { + return 0; + } + } + + return 1; + } + case QTYPE_QLIST: { + QListCompareHelper helper; + + helper.index = 0; + helper.objs = lhs->value.qlist; + helper.result = 1; + + qlist_iter(qobject_to_qlist(rhs), compare_helper, &helper); + + return helper.result; + } + default: + break; + } + + return 0; +} + +START_TEST(simple_dict) +{ + int i; + struct { + const char *encoded; + LiteralQObject decoded; + } test_cases[] = { + { + .encoded = "{\"foo\":42,\"bar\":\"hello world\"}", + .decoded = QLIT_QDICT(((LiteralQDictEntry[]){ + { "foo", QLIT_QINT(42) }, + { "bar", QLIT_QSTR("hello world") }, + { } + })), + }, { + .encoded = "{}", + .decoded = QLIT_QDICT(((LiteralQDictEntry[]){ + { } + })), + }, { + .encoded = "{\"foo\":43}", + .decoded = QLIT_QDICT(((LiteralQDictEntry[]){ + { "foo", QLIT_QINT(43) }, + { } + })), + }, + { } + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + + obj = qobject_from_json(test_cases[i].encoded); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QDICT); + + fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1); + + qobject_decref(obj); + } +} +END_TEST + +START_TEST(simple_list) +{ + int i; + struct { + const char *encoded; + LiteralQObject decoded; + } test_cases[] = { + { + .encoded = "[43,42]", + .decoded = QLIT_QLIST(((LiteralQObject[]){ + QLIT_QINT(43), + QLIT_QINT(42), + { } + })), + }, + { + .encoded = "[43]", + .decoded = QLIT_QLIST(((LiteralQObject[]){ + QLIT_QINT(43), + { } + })), + }, + { + .encoded = "[]", + .decoded = QLIT_QLIST(((LiteralQObject[]){ + { } + })), + }, + { } + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + + obj = qobject_from_json(test_cases[i].encoded); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QLIST); + + fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1); + + qobject_decref(obj); + } +} +END_TEST + +START_TEST(simple_whitespace) +{ + int i; + struct { + const char *encoded; + LiteralQObject decoded; + } test_cases[] = { + { + .encoded = " [ 43 , 42 ]", + .decoded = QLIT_QLIST(((LiteralQObject[]){ + QLIT_QINT(43), + QLIT_QINT(42), + { } + })), + }, + { + .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]", + .decoded = QLIT_QLIST(((LiteralQObject[]){ + QLIT_QINT(43), + QLIT_QDICT(((LiteralQDictEntry[]){ + { "h", QLIT_QSTR("b") }, + { }})), + QLIT_QLIST(((LiteralQObject[]){ + { }})), + QLIT_QINT(42), + { } + })), + }, + { + .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]", + .decoded = QLIT_QLIST(((LiteralQObject[]){ + QLIT_QINT(43), + QLIT_QDICT(((LiteralQDictEntry[]){ + { "h", QLIT_QSTR("b") }, + { "a", QLIT_QINT(32) }, + { }})), + QLIT_QLIST(((LiteralQObject[]){ + { }})), + QLIT_QINT(42), + { } + })), + }, + { } + }; + + for (i = 0; test_cases[i].encoded; i++) { + QObject *obj; + + obj = qobject_from_json(test_cases[i].encoded); + fail_unless(obj != NULL); + fail_unless(qobject_type(obj) == QTYPE_QLIST); + + fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1); + + qobject_decref(obj); + } +} +END_TEST + +START_TEST(simple_varargs) +{ + QObject *embedded_obj; + QObject *obj; + LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){ + QLIT_QINT(1), + QLIT_QINT(2), + QLIT_QLIST(((LiteralQObject[]){ + QLIT_QINT(32), + QLIT_QINT(42), + {}})), + {}})); + + embedded_obj = qobject_from_json("[32, 42]"); + fail_unless(embedded_obj != NULL); + + obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj); + fail_unless(obj != NULL); + + fail_unless(compare_litqobj_to_qobj(&decoded, obj) == 1); + + qobject_decref(obj); +} +END_TEST + +static Suite *qjson_suite(void) +{ + Suite *suite; + TCase *string_literals, *number_literals, *keyword_literals; + TCase *dicts, *lists, *whitespace, *varargs; + + string_literals = tcase_create("String Literals"); + tcase_add_test(string_literals, simple_string); + tcase_add_test(string_literals, escaped_string); + tcase_add_test(string_literals, single_quote_string); + tcase_add_test(string_literals, vararg_string); + + number_literals = tcase_create("Number Literals"); + tcase_add_test(number_literals, simple_number); + tcase_add_test(number_literals, float_number); + tcase_add_test(number_literals, vararg_number); + + keyword_literals = tcase_create("Keywords"); + tcase_add_test(keyword_literals, keyword_literal); + dicts = tcase_create("Objects"); + tcase_add_test(dicts, simple_dict); + lists = tcase_create("Lists"); + tcase_add_test(lists, simple_list); + + whitespace = tcase_create("Whitespace"); + tcase_add_test(whitespace, simple_whitespace); + + varargs = tcase_create("Varargs"); + tcase_add_test(varargs, simple_varargs); + + suite = suite_create("QJSON test-suite"); + suite_add_tcase(suite, string_literals); + suite_add_tcase(suite, number_literals); + suite_add_tcase(suite, keyword_literals); + suite_add_tcase(suite, dicts); + suite_add_tcase(suite, lists); + suite_add_tcase(suite, whitespace); + suite_add_tcase(suite, varargs); + + return suite; +} + +int main(void) +{ + int nf; + Suite *s; + SRunner *sr; + + s = qjson_suite(); + sr = srunner_create(s); + + srunner_run_all(sr, CK_NORMAL); + nf = srunner_ntests_failed(sr); + srunner_free(sr); + + return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/configure b/configure index 55be0fb..20bd39c 100755 --- a/configure +++ b/configure @@ -2092,7 +2092,7 @@ if test `expr "$target_list" : ".*softmmu.*"` != 0 ; then tools="qemu-nbd\$(EXESUF) qemu-io\$(EXESUF) $tools" if [ "$check_utests" = "yes" ]; then tools="check-qint check-qstring check-qdict check-qlist $tools" - tools="check-qfloat $tools" + tools="check-qfloat check-qjson $tools" fi elif test "$mingw32" = "yes" ; then tools="qemu-io\$(EXESUF) $tools"