e1bc2f7b3f
Modify logic such that we never assign values to the list head argument to progress through the list on subsequent iterations, instead rely only on having our return value passed back in as an argument on the next call. Also update QMP I/O visitors and test cases accordingly, and add a missing test case for QmpOutputVisitor. Reviewed-by: Anthony Liguori <aliguori@us.ibm.com> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com> Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
339 lines
9.1 KiB
C
339 lines
9.1 KiB
C
#include <glib.h>
|
|
#include "qapi/qmp-output-visitor.h"
|
|
#include "qapi/qmp-input-visitor.h"
|
|
#include "test-qapi-types.h"
|
|
#include "test-qapi-visit.h"
|
|
#include "qemu-objects.h"
|
|
|
|
typedef struct TestStruct
|
|
{
|
|
int64_t x;
|
|
int64_t y;
|
|
} TestStruct;
|
|
|
|
typedef struct TestStructList
|
|
{
|
|
TestStruct *value;
|
|
struct TestStructList *next;
|
|
} TestStructList;
|
|
|
|
static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp)
|
|
{
|
|
visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), errp);
|
|
visit_type_int(v, &(*obj)->x, "x", errp);
|
|
visit_type_int(v, &(*obj)->y, "y", errp);
|
|
visit_end_struct(v, errp);
|
|
}
|
|
|
|
static void visit_type_TestStructList(Visitor *m, TestStructList ** obj, const char *name, Error **errp)
|
|
{
|
|
GenericList *i, **head = (GenericList **)obj;
|
|
|
|
visit_start_list(m, name, errp);
|
|
|
|
for (*head = i = visit_next_list(m, head, errp); i; i = visit_next_list(m, &i, errp)) {
|
|
TestStructList *native_i = (TestStructList *)i;
|
|
visit_type_TestStruct(m, &native_i->value, NULL, errp);
|
|
}
|
|
|
|
visit_end_list(m, errp);
|
|
}
|
|
|
|
/* test core visitor methods */
|
|
static void test_visitor_core(void)
|
|
{
|
|
QmpOutputVisitor *mo;
|
|
QmpInputVisitor *mi;
|
|
Visitor *v;
|
|
TestStruct ts = { 42, 82 };
|
|
TestStruct *pts = &ts;
|
|
TestStructList *lts = NULL;
|
|
Error *err = NULL;
|
|
QObject *obj;
|
|
QList *qlist;
|
|
QDict *qdict;
|
|
QString *str;
|
|
int64_t value = 0;
|
|
|
|
mo = qmp_output_visitor_new();
|
|
v = qmp_output_get_visitor(mo);
|
|
|
|
visit_type_TestStruct(v, &pts, NULL, &err);
|
|
|
|
obj = qmp_output_get_qobject(mo);
|
|
|
|
str = qobject_to_json(obj);
|
|
|
|
printf("%s\n", qstring_get_str(str));
|
|
|
|
QDECREF(str);
|
|
|
|
obj = QOBJECT(qint_from_int(0x42));
|
|
|
|
mi = qmp_input_visitor_new(obj);
|
|
v = qmp_input_get_visitor(mi);
|
|
|
|
visit_type_int(v, &value, NULL, &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
|
|
g_assert(value == 0x42);
|
|
|
|
qobject_decref(obj);
|
|
|
|
obj = qobject_from_json("{'x': 42, 'y': 84}");
|
|
mi = qmp_input_visitor_new(obj);
|
|
v = qmp_input_get_visitor(mi);
|
|
|
|
pts = NULL;
|
|
|
|
visit_type_TestStruct(v, &pts, NULL, &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
|
|
g_assert(pts != NULL);
|
|
g_assert(pts->x == 42);
|
|
g_assert(pts->y == 84);
|
|
|
|
qobject_decref(obj);
|
|
g_free(pts);
|
|
|
|
/* test list input visitor */
|
|
obj = qobject_from_json("[{'x': 42, 'y': 84}, {'x': 12, 'y': 24}]");
|
|
mi = qmp_input_visitor_new(obj);
|
|
v = qmp_input_get_visitor(mi);
|
|
|
|
visit_type_TestStructList(v, <s, NULL, &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
|
|
g_assert(lts != NULL);
|
|
g_assert(lts->value->x == 42);
|
|
g_assert(lts->value->y == 84);
|
|
|
|
g_assert(lts->next != NULL);
|
|
g_assert(lts->next->value->x == 12);
|
|
g_assert(lts->next->value->y == 24);
|
|
g_assert(lts->next->next == NULL);
|
|
|
|
qobject_decref(obj);
|
|
|
|
/* test list output visitor */
|
|
mo = qmp_output_visitor_new();
|
|
v = qmp_output_get_visitor(mo);
|
|
visit_type_TestStructList(v, <s, NULL, &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
obj = qmp_output_get_qobject(mo);
|
|
g_print("obj: %s\n", qstring_get_str(qobject_to_json(obj)));
|
|
|
|
qlist = qobject_to_qlist(obj);
|
|
assert(qlist);
|
|
obj = qlist_pop(qlist);
|
|
qdict = qobject_to_qdict(obj);
|
|
assert(qdict);
|
|
assert(qdict_get_int(qdict, "x") == 42);
|
|
assert(qdict_get_int(qdict, "y") == 84);
|
|
qobject_decref(obj);
|
|
|
|
obj = qlist_pop(qlist);
|
|
qdict = qobject_to_qdict(obj);
|
|
assert(qdict);
|
|
assert(qdict_get_int(qdict, "x") == 12);
|
|
assert(qdict_get_int(qdict, "y") == 24);
|
|
qobject_decref(obj);
|
|
|
|
qmp_output_visitor_cleanup(mo);
|
|
QDECREF(qlist);
|
|
}
|
|
|
|
/* test deep nesting with refs to other user-defined types */
|
|
static void test_nested_structs(void)
|
|
{
|
|
QmpOutputVisitor *mo;
|
|
QmpInputVisitor *mi;
|
|
Visitor *v;
|
|
UserDefOne ud1;
|
|
UserDefOne *ud1_p = &ud1, *ud1c_p = NULL;
|
|
UserDefTwo ud2;
|
|
UserDefTwo *ud2_p = &ud2, *ud2c_p = NULL;
|
|
Error *err = NULL;
|
|
QObject *obj;
|
|
QString *str;
|
|
|
|
ud1.integer = 42;
|
|
ud1.string = strdup("fourty two");
|
|
|
|
/* sanity check */
|
|
mo = qmp_output_visitor_new();
|
|
v = qmp_output_get_visitor(mo);
|
|
visit_type_UserDefOne(v, &ud1_p, "o_O", &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
obj = qmp_output_get_qobject(mo);
|
|
g_assert(obj);
|
|
qobject_decref(obj);
|
|
|
|
ud2.string = strdup("fourty three");
|
|
ud2.dict.string = strdup("fourty four");
|
|
ud2.dict.dict.userdef = ud1_p;
|
|
ud2.dict.dict.string = strdup("fourty five");
|
|
ud2.dict.has_dict2 = true;
|
|
ud2.dict.dict2.userdef = ud1_p;
|
|
ud2.dict.dict2.string = strdup("fourty six");
|
|
|
|
/* c type -> qobject */
|
|
mo = qmp_output_visitor_new();
|
|
v = qmp_output_get_visitor(mo);
|
|
visit_type_UserDefTwo(v, &ud2_p, "unused", &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
obj = qmp_output_get_qobject(mo);
|
|
g_assert(obj);
|
|
str = qobject_to_json_pretty(obj);
|
|
g_print("%s\n", qstring_get_str(str));
|
|
QDECREF(str);
|
|
|
|
/* qobject -> c type, should match original struct */
|
|
mi = qmp_input_visitor_new(obj);
|
|
v = qmp_input_get_visitor(mi);
|
|
visit_type_UserDefTwo(v, &ud2c_p, NULL, &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
|
|
g_assert(!g_strcmp0(ud2c_p->string, ud2.string));
|
|
g_assert(!g_strcmp0(ud2c_p->dict.string, ud2.dict.string));
|
|
|
|
ud1c_p = ud2c_p->dict.dict.userdef;
|
|
g_assert(ud1c_p->integer == ud1_p->integer);
|
|
g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string));
|
|
|
|
g_assert(!g_strcmp0(ud2c_p->dict.dict.string, ud2.dict.dict.string));
|
|
|
|
ud1c_p = ud2c_p->dict.dict2.userdef;
|
|
g_assert(ud1c_p->integer == ud1_p->integer);
|
|
g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string));
|
|
|
|
g_assert(!g_strcmp0(ud2c_p->dict.dict2.string, ud2.dict.dict2.string));
|
|
g_free(ud1.string);
|
|
g_free(ud2.string);
|
|
g_free(ud2.dict.string);
|
|
g_free(ud2.dict.dict.string);
|
|
g_free(ud2.dict.dict2.string);
|
|
|
|
qapi_free_UserDefTwo(ud2c_p);
|
|
|
|
qobject_decref(obj);
|
|
}
|
|
|
|
/* test enum values */
|
|
static void test_enums(void)
|
|
{
|
|
QmpOutputVisitor *mo;
|
|
QmpInputVisitor *mi;
|
|
Visitor *v;
|
|
EnumOne enum1 = ENUM_ONE_VALUE2, enum1_cpy = ENUM_ONE_VALUE1;
|
|
Error *err = NULL;
|
|
QObject *obj;
|
|
QString *str;
|
|
|
|
/* C type -> QObject */
|
|
mo = qmp_output_visitor_new();
|
|
v = qmp_output_get_visitor(mo);
|
|
visit_type_EnumOne(v, &enum1, "unused", &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
obj = qmp_output_get_qobject(mo);
|
|
g_assert(obj);
|
|
str = qobject_to_json_pretty(obj);
|
|
g_print("%s\n", qstring_get_str(str));
|
|
QDECREF(str);
|
|
g_assert(g_strcmp0(qstring_get_str(qobject_to_qstring(obj)), "value2") == 0);
|
|
|
|
/* QObject -> C type */
|
|
mi = qmp_input_visitor_new(obj);
|
|
v = qmp_input_get_visitor(mi);
|
|
visit_type_EnumOne(v, &enum1_cpy, "unused", &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
g_debug("enum1_cpy, enum1: %d, %d", enum1_cpy, enum1);
|
|
g_assert(enum1_cpy == enum1);
|
|
|
|
qobject_decref(obj);
|
|
}
|
|
|
|
/* test enum values nested in schema-defined structs */
|
|
static void test_nested_enums(void)
|
|
{
|
|
QmpOutputVisitor *mo;
|
|
QmpInputVisitor *mi;
|
|
Visitor *v;
|
|
NestedEnumsOne *nested_enums, *nested_enums_cpy = NULL;
|
|
Error *err = NULL;
|
|
QObject *obj;
|
|
QString *str;
|
|
|
|
nested_enums = g_malloc0(sizeof(NestedEnumsOne));
|
|
nested_enums->enum1 = ENUM_ONE_VALUE1;
|
|
nested_enums->enum2 = ENUM_ONE_VALUE2;
|
|
nested_enums->enum3 = ENUM_ONE_VALUE3;
|
|
nested_enums->enum4 = ENUM_ONE_VALUE3;
|
|
nested_enums->has_enum2 = false;
|
|
nested_enums->has_enum4 = true;
|
|
|
|
/* C type -> QObject */
|
|
mo = qmp_output_visitor_new();
|
|
v = qmp_output_get_visitor(mo);
|
|
visit_type_NestedEnumsOne(v, &nested_enums, NULL, &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
obj = qmp_output_get_qobject(mo);
|
|
g_assert(obj);
|
|
str = qobject_to_json_pretty(obj);
|
|
g_print("%s\n", qstring_get_str(str));
|
|
QDECREF(str);
|
|
|
|
/* QObject -> C type */
|
|
mi = qmp_input_visitor_new(obj);
|
|
v = qmp_input_get_visitor(mi);
|
|
visit_type_NestedEnumsOne(v, &nested_enums_cpy, NULL, &err);
|
|
if (err) {
|
|
g_error("%s", error_get_pretty(err));
|
|
}
|
|
g_assert(nested_enums_cpy);
|
|
g_assert(nested_enums_cpy->enum1 == nested_enums->enum1);
|
|
g_assert(nested_enums_cpy->enum3 == nested_enums->enum3);
|
|
g_assert(nested_enums_cpy->enum4 == nested_enums->enum4);
|
|
g_assert(nested_enums_cpy->has_enum2 == false);
|
|
g_assert(nested_enums_cpy->has_enum4 == true);
|
|
|
|
qmp_output_visitor_cleanup(mo);
|
|
qmp_input_visitor_cleanup(mi);
|
|
qapi_free_NestedEnumsOne(nested_enums);
|
|
qapi_free_NestedEnumsOne(nested_enums_cpy);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
g_test_add_func("/0.15/visitor_core", test_visitor_core);
|
|
g_test_add_func("/0.15/nested_structs", test_nested_structs);
|
|
g_test_add_func("/0.15/enums", test_enums);
|
|
g_test_add_func("/0.15/nested_enums", test_nested_enums);
|
|
|
|
g_test_run();
|
|
|
|
return 0;
|
|
}
|