The macro expansions of qdict_put_TYPE() and qlist_append_TYPE() need qbool.h, qnull.h, qnum.h and qstring.h to compile. We include qnull.h and qnum.h in the headers, but not qbool.h and qstring.h. Works, because we include those wherever the macros get used. Open-coding these helpers is of dubious value. Turn them into functions and drop the includes from the headers. This cleanup makes the number of objects depending on qapi/qmp/qnum.h from 4551 (out of 4743) to 46 in my "build everything" tree. For qapi/qmp/qnull.h, the number drops from 4552 to 21. Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20180201111846.21846-10-armbru@redhat.com>
264 lines
6.4 KiB
C
264 lines
6.4 KiB
C
/*
|
|
* qapi event unit-tests.
|
|
*
|
|
* Copyright (c) 2014 Wenchao Xia
|
|
*
|
|
* Authors:
|
|
* Wenchao Xia <wenchaoqemu@gmail.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 "qemu-common.h"
|
|
#include "test-qapi-visit.h"
|
|
#include "test-qapi-event.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qmp/qbool.h"
|
|
#include "qapi/qmp/qnum.h"
|
|
#include "qapi/qmp/qobject.h"
|
|
#include "qapi/qmp/qstring.h"
|
|
#include "qapi/qmp-event.h"
|
|
|
|
typedef struct TestEventData {
|
|
QDict *expect;
|
|
} TestEventData;
|
|
|
|
typedef struct QDictCmpData {
|
|
QDict *expect;
|
|
bool result;
|
|
} QDictCmpData;
|
|
|
|
TestEventData *test_event_data;
|
|
static CompatGMutex test_event_lock;
|
|
|
|
/* Only compares bool, int, string */
|
|
static
|
|
void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque)
|
|
|
|
{
|
|
QObject *obj2;
|
|
QDictCmpData d_new, *d = opaque;
|
|
int64_t val1, val2;
|
|
|
|
if (!d->result) {
|
|
return;
|
|
}
|
|
|
|
obj2 = qdict_get(d->expect, key);
|
|
if (!obj2) {
|
|
d->result = false;
|
|
return;
|
|
}
|
|
|
|
if (qobject_type(obj1) != qobject_type(obj2)) {
|
|
d->result = false;
|
|
return;
|
|
}
|
|
|
|
switch (qobject_type(obj1)) {
|
|
case QTYPE_QBOOL:
|
|
d->result = (qbool_get_bool(qobject_to_qbool(obj1)) ==
|
|
qbool_get_bool(qobject_to_qbool(obj2)));
|
|
return;
|
|
case QTYPE_QNUM:
|
|
g_assert(qnum_get_try_int(qobject_to_qnum(obj1), &val1));
|
|
g_assert(qnum_get_try_int(qobject_to_qnum(obj2), &val2));
|
|
d->result = val1 == val2;
|
|
return;
|
|
case QTYPE_QSTRING:
|
|
d->result = g_strcmp0(qstring_get_str(qobject_to_qstring(obj1)),
|
|
qstring_get_str(qobject_to_qstring(obj2))) == 0;
|
|
return;
|
|
case QTYPE_QDICT:
|
|
d_new.expect = qobject_to_qdict(obj2);
|
|
d_new.result = true;
|
|
qdict_iter(qobject_to_qdict(obj1), qdict_cmp_do_simple, &d_new);
|
|
d->result = d_new.result;
|
|
return;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static bool qdict_cmp_simple(QDict *a, QDict *b)
|
|
{
|
|
QDictCmpData d;
|
|
|
|
d.expect = b;
|
|
d.result = true;
|
|
qdict_iter(a, qdict_cmp_do_simple, &d);
|
|
return d.result;
|
|
}
|
|
|
|
/* This function is hooked as final emit function, which can verify the
|
|
correctness. */
|
|
static void event_test_emit(test_QAPIEvent event, QDict *d, Error **errp)
|
|
{
|
|
QDict *t;
|
|
int64_t s, ms;
|
|
|
|
/* Verify that we have timestamp, then remove it to compare other fields */
|
|
t = qdict_get_qdict(d, "timestamp");
|
|
g_assert(t);
|
|
s = qdict_get_try_int(t, "seconds", -2);
|
|
ms = qdict_get_try_int(t, "microseconds", -2);
|
|
if (s == -1) {
|
|
g_assert(ms == -1);
|
|
} else {
|
|
g_assert(s >= 0);
|
|
g_assert(ms >= 0 && ms <= 999999);
|
|
}
|
|
g_assert(qdict_size(t) == 2);
|
|
|
|
qdict_del(d, "timestamp");
|
|
|
|
g_assert(qdict_cmp_simple(d, test_event_data->expect));
|
|
|
|
}
|
|
|
|
static void event_prepare(TestEventData *data,
|
|
const void *unused)
|
|
{
|
|
/* Global variable test_event_data was used to pass the expectation, so
|
|
test cases can't be executed at same time. */
|
|
g_mutex_lock(&test_event_lock);
|
|
|
|
data->expect = qdict_new();
|
|
test_event_data = data;
|
|
}
|
|
|
|
static void event_teardown(TestEventData *data,
|
|
const void *unused)
|
|
{
|
|
QDECREF(data->expect);
|
|
test_event_data = NULL;
|
|
|
|
g_mutex_unlock(&test_event_lock);
|
|
}
|
|
|
|
static void event_test_add(const char *testpath,
|
|
void (*test_func)(TestEventData *data,
|
|
const void *user_data))
|
|
{
|
|
g_test_add(testpath, TestEventData, NULL, event_prepare, test_func,
|
|
event_teardown);
|
|
}
|
|
|
|
|
|
/* Test cases */
|
|
|
|
static void test_event_a(TestEventData *data,
|
|
const void *unused)
|
|
{
|
|
QDict *d;
|
|
d = data->expect;
|
|
qdict_put_str(d, "event", "EVENT_A");
|
|
qapi_event_send_event_a(&error_abort);
|
|
}
|
|
|
|
static void test_event_b(TestEventData *data,
|
|
const void *unused)
|
|
{
|
|
QDict *d;
|
|
d = data->expect;
|
|
qdict_put_str(d, "event", "EVENT_B");
|
|
qapi_event_send_event_b(&error_abort);
|
|
}
|
|
|
|
static void test_event_c(TestEventData *data,
|
|
const void *unused)
|
|
{
|
|
QDict *d, *d_data, *d_b;
|
|
|
|
UserDefOne b;
|
|
b.integer = 2;
|
|
b.string = g_strdup("test1");
|
|
b.has_enum1 = false;
|
|
|
|
d_b = qdict_new();
|
|
qdict_put_int(d_b, "integer", 2);
|
|
qdict_put_str(d_b, "string", "test1");
|
|
|
|
d_data = qdict_new();
|
|
qdict_put_int(d_data, "a", 1);
|
|
qdict_put(d_data, "b", d_b);
|
|
qdict_put_str(d_data, "c", "test2");
|
|
|
|
d = data->expect;
|
|
qdict_put_str(d, "event", "EVENT_C");
|
|
qdict_put(d, "data", d_data);
|
|
|
|
qapi_event_send_event_c(true, 1, true, &b, "test2", &error_abort);
|
|
|
|
g_free(b.string);
|
|
}
|
|
|
|
/* Complex type */
|
|
static void test_event_d(TestEventData *data,
|
|
const void *unused)
|
|
{
|
|
UserDefOne struct1;
|
|
EventStructOne a;
|
|
QDict *d, *d_data, *d_a, *d_struct1;
|
|
|
|
struct1.integer = 2;
|
|
struct1.string = g_strdup("test1");
|
|
struct1.has_enum1 = true;
|
|
struct1.enum1 = ENUM_ONE_VALUE1;
|
|
|
|
a.struct1 = &struct1;
|
|
a.string = g_strdup("test2");
|
|
a.has_enum2 = true;
|
|
a.enum2 = ENUM_ONE_VALUE2;
|
|
|
|
d_struct1 = qdict_new();
|
|
qdict_put_int(d_struct1, "integer", 2);
|
|
qdict_put_str(d_struct1, "string", "test1");
|
|
qdict_put_str(d_struct1, "enum1", "value1");
|
|
|
|
d_a = qdict_new();
|
|
qdict_put(d_a, "struct1", d_struct1);
|
|
qdict_put_str(d_a, "string", "test2");
|
|
qdict_put_str(d_a, "enum2", "value2");
|
|
|
|
d_data = qdict_new();
|
|
qdict_put(d_data, "a", d_a);
|
|
qdict_put_str(d_data, "b", "test3");
|
|
qdict_put_str(d_data, "enum3", "value3");
|
|
|
|
d = data->expect;
|
|
qdict_put_str(d, "event", "EVENT_D");
|
|
qdict_put(d, "data", d_data);
|
|
|
|
qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3,
|
|
&error_abort);
|
|
|
|
g_free(struct1.string);
|
|
g_free(a.string);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
#if !GLIB_CHECK_VERSION(2, 31, 0)
|
|
if (!g_thread_supported()) {
|
|
g_thread_init(NULL);
|
|
}
|
|
#endif
|
|
|
|
qmp_event_set_func_emit(event_test_emit);
|
|
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
event_test_add("/event/event_a", test_event_a);
|
|
event_test_add("/event/event_b", test_event_b);
|
|
event_test_add("/event/event_c", test_event_c);
|
|
event_test_add("/event/event_d", test_event_d);
|
|
g_test_run();
|
|
|
|
return 0;
|
|
}
|