2014-06-18 08:43:29 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-02-08 19:08:51 +01:00
|
|
|
#include "qemu/osdep.h"
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
#include "qemu-common.h"
|
2018-02-01 12:18:31 +01:00
|
|
|
#include "qapi/error.h"
|
2018-02-01 12:18:35 +01:00
|
|
|
#include "qapi/qmp/qbool.h"
|
2018-02-01 12:18:39 +01:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2018-02-01 12:18:36 +01:00
|
|
|
#include "qapi/qmp/qnum.h"
|
2018-02-01 12:18:35 +01:00
|
|
|
#include "qapi/qmp/qstring.h"
|
2014-06-18 08:43:29 +02:00
|
|
|
#include "qapi/qmp-event.h"
|
2018-02-11 10:36:05 +01:00
|
|
|
#include "test-qapi-events.h"
|
2019-02-14 16:22:38 +01:00
|
|
|
#include "test-qapi-emit-events.h"
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
typedef struct TestEventData {
|
|
|
|
QDict *expect;
|
|
|
|
} TestEventData;
|
|
|
|
|
|
|
|
typedef struct QDictCmpData {
|
|
|
|
QDict *expect;
|
|
|
|
bool result;
|
|
|
|
} QDictCmpData;
|
|
|
|
|
|
|
|
TestEventData *test_event_data;
|
2018-05-04 16:34:46 +02:00
|
|
|
static GMutex test_event_lock;
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
/* 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;
|
2017-06-07 18:35:58 +02:00
|
|
|
int64_t val1, val2;
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
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:
|
2018-02-24 16:40:29 +01:00
|
|
|
d->result = (qbool_get_bool(qobject_to(QBool, obj1)) ==
|
|
|
|
qbool_get_bool(qobject_to(QBool, obj2)));
|
2014-06-18 08:43:29 +02:00
|
|
|
return;
|
2017-06-07 18:35:58 +02:00
|
|
|
case QTYPE_QNUM:
|
2018-02-24 16:40:29 +01:00
|
|
|
g_assert(qnum_get_try_int(qobject_to(QNum, obj1), &val1));
|
|
|
|
g_assert(qnum_get_try_int(qobject_to(QNum, obj2), &val2));
|
2017-06-07 18:35:58 +02:00
|
|
|
d->result = val1 == val2;
|
2014-06-18 08:43:29 +02:00
|
|
|
return;
|
|
|
|
case QTYPE_QSTRING:
|
2018-02-24 16:40:29 +01:00
|
|
|
d->result = g_strcmp0(qstring_get_str(qobject_to(QString, obj1)),
|
|
|
|
qstring_get_str(qobject_to(QString, obj2))) == 0;
|
2014-06-18 08:43:29 +02:00
|
|
|
return;
|
|
|
|
case QTYPE_QDICT:
|
2018-02-24 16:40:29 +01:00
|
|
|
d_new.expect = qobject_to(QDict, obj2);
|
2014-06-18 08:43:29 +02:00
|
|
|
d_new.result = true;
|
2018-02-24 16:40:29 +01:00
|
|
|
qdict_iter(qobject_to(QDict, obj1), qdict_cmp_do_simple, &d_new);
|
2014-06-18 08:43:29 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
qapi: Eliminate indirection through qmp_event_get_func_emit()
The qapi_event_send_FOO() functions emit events like this:
QMPEventFuncEmit emit;
emit = qmp_event_get_func_emit();
if (!emit) {
return;
}
qmp = qmp_event_build_dict("FOO");
[put event arguments into @qmp...]
emit(QAPI_EVENT_FOO, qmp);
The value of qmp_event_get_func_emit() depends only on the program:
* In qemu-system-FOO, it's always monitor_qapi_event_queue.
* In tests/test-qmp-event, it's always event_test_emit.
* In all other programs, it's always null.
This is exactly the kind of dependence the linker is supposed to
resolve; we don't actually need an indirection.
Note that things would fall apart if we linked more than one QAPI
schema into a single program: each set of qapi_event_send_FOO() uses
its own event enumeration, yet they share a single emit function.
Which takes the event enumeration as an argument. Which one if
there's more than one?
More seriously: how does this work even now? qemu-system-FOO wants
QAPIEvent, and passes a function taking that to
qmp_event_set_func_emit(). test-qmp-event wants test_QAPIEvent, and
passes a function taking that to qmp_event_set_func_emit().
It works by type trickery, of course:
typedef void (*QMPEventFuncEmit)(unsigned event, QDict *dict);
void qmp_event_set_func_emit(QMPEventFuncEmit emit);
QMPEventFuncEmit qmp_event_get_func_emit(void);
We use unsigned instead of the enumeration type. Relies on both
enumerations boiling down to unsigned, which happens to be true for
the compilers we use.
Clean this up as follows:
* Generate qapi_event_send_FOO() that call PREFIX_qapi_event_emit()
instead of the value of qmp_event_set_func_emit().
* Generate a prototype for PREFIX_qapi_event_emit() into
qapi-events.h.
* PREFIX_ is empty for qapi/qapi-schema.json, and test_ for
tests/qapi-schema/qapi-schema-test.json. It's qga_ for
qga/qapi-schema.json, and doc-good- for
tests/qapi-schema/doc-good.json, but those don't define any events.
* Rename monitor_qapi_event_queue() to qapi_event_emit() instead of
passing it to qmp_event_set_func_emit(). This takes care of
qemu-system-FOO.
* Rename event_test_emit() to test_qapi_event_emit() instead of
passing it to qmp_event_set_func_emit(). This takes care of
tests/test-qmp-event.
* Add a qapi_event_emit() that does nothing to stubs/monitor.c. This
takes care of all other programs that link code emitting QMP events.
* Drop qmp_event_set_func_emit(), qmp_event_get_func_emit().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20181218182234.28876-3-armbru@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
[Commit message typos fixed]
2018-12-18 19:22:21 +01:00
|
|
|
void test_qapi_event_emit(test_QAPIEvent event, QDict *d)
|
2014-06-18 08:43:29 +02:00
|
|
|
{
|
|
|
|
QDict *t;
|
|
|
|
int64_t s, ms;
|
|
|
|
|
|
|
|
/* Verify that we have timestamp, then remove it to compare other fields */
|
2017-02-17 21:38:17 +01:00
|
|
|
t = qdict_get_qdict(d, "timestamp");
|
2014-06-18 08:43:29 +02:00
|
|
|
g_assert(t);
|
2017-02-17 21:38:17 +01:00
|
|
|
s = qdict_get_try_int(t, "seconds", -2);
|
|
|
|
ms = qdict_get_try_int(t, "microseconds", -2);
|
2014-06-18 08:43:29 +02:00
|
|
|
if (s == -1) {
|
|
|
|
g_assert(ms == -1);
|
|
|
|
} else {
|
2017-02-17 21:38:17 +01:00
|
|
|
g_assert(s >= 0);
|
2014-06-18 08:43:29 +02:00
|
|
|
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)
|
|
|
|
{
|
2018-04-19 17:01:43 +02:00
|
|
|
qobject_unref(data->expect);
|
2014-06-18 08:43:29 +02:00
|
|
|
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;
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_str(d, "event", "EVENT_A");
|
2018-08-15 15:37:37 +02:00
|
|
|
qapi_event_send_event_a();
|
2014-06-18 08:43:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_event_b(TestEventData *data,
|
|
|
|
const void *unused)
|
|
|
|
{
|
|
|
|
QDict *d;
|
|
|
|
d = data->expect;
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_str(d, "event", "EVENT_B");
|
2018-08-15 15:37:37 +02:00
|
|
|
qapi_event_send_event_b();
|
2014-06-18 08:43:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_event_c(TestEventData *data,
|
|
|
|
const void *unused)
|
|
|
|
{
|
|
|
|
QDict *d, *d_data, *d_b;
|
|
|
|
|
|
|
|
UserDefOne b;
|
qapi: Unbox base members
Rather than storing a base class as a pointer to a box, just
store the fields of that base class in the same order, so that
a child struct can be directly cast to its parent. This gives
less malloc overhead, less pointer dereferencing, and even less
generated code. Compare to the earlier commit 1e6c1616a "qapi:
Generate a nicer struct for flat unions" (although that patch
had fewer places to change, as less of qemu was directly using
qapi structs for flat unions). It also allows us to turn on
automatic type-safe wrappers for upcasting to the base class
of a struct.
Changes to the generated code look like this in qapi-types.h:
| struct SpiceChannel {
|- SpiceBasicInfo *base;
|+ /* Members inherited from SpiceBasicInfo: */
|+ char *host;
|+ char *port;
|+ NetworkAddressFamily family;
|+ /* Own members: */
| int64_t connection_id;
as well as additional upcast functions like qapi_SpiceChannel_base().
Meanwhile, changes to qapi-visit.c look like:
| static void visit_type_SpiceChannel_fields(Visitor *v, SpiceChannel **obj, Error **errp)
| {
| Error *err = NULL;
|
|- visit_type_implicit_SpiceBasicInfo(v, &(*obj)->base, &err);
|+ visit_type_SpiceBasicInfo_fields(v, (SpiceBasicInfo **)obj, &err);
| if (err) {
(the cast is necessary, since our upcast wrappers only deal with a
single pointer, not pointer-to-pointer); plus the wholesale
elimination of some now-unused visit_type_implicit_FOO() functions.
Without boxing, the corner case of one empty struct having
another empty struct as its base type now requires inserting a
dummy member (previously, the 'Base *base' member sufficed).
And now that we no longer consume a 'base' member in the generated
C struct, we can delete the former negative struct-base-clash-base
test.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1445898903-12082-11-git-send-email-eblake@redhat.com>
[Commit message tweaked slightly]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-10-26 23:34:49 +01:00
|
|
|
b.integer = 2;
|
2014-06-18 08:43:29 +02:00
|
|
|
b.string = g_strdup("test1");
|
|
|
|
b.has_enum1 = false;
|
|
|
|
|
|
|
|
d_b = qdict_new();
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_int(d_b, "integer", 2);
|
|
|
|
qdict_put_str(d_b, "string", "test1");
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
d_data = qdict_new();
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_int(d_data, "a", 1);
|
2014-06-18 08:43:29 +02:00
|
|
|
qdict_put(d_data, "b", d_b);
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_str(d_data, "c", "test2");
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
d = data->expect;
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_str(d, "event", "EVENT_C");
|
2014-06-18 08:43:29 +02:00
|
|
|
qdict_put(d, "data", d_data);
|
|
|
|
|
2018-08-15 15:37:37 +02:00
|
|
|
qapi_event_send_event_c(true, 1, true, &b, "test2");
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
qapi: Unbox base members
Rather than storing a base class as a pointer to a box, just
store the fields of that base class in the same order, so that
a child struct can be directly cast to its parent. This gives
less malloc overhead, less pointer dereferencing, and even less
generated code. Compare to the earlier commit 1e6c1616a "qapi:
Generate a nicer struct for flat unions" (although that patch
had fewer places to change, as less of qemu was directly using
qapi structs for flat unions). It also allows us to turn on
automatic type-safe wrappers for upcasting to the base class
of a struct.
Changes to the generated code look like this in qapi-types.h:
| struct SpiceChannel {
|- SpiceBasicInfo *base;
|+ /* Members inherited from SpiceBasicInfo: */
|+ char *host;
|+ char *port;
|+ NetworkAddressFamily family;
|+ /* Own members: */
| int64_t connection_id;
as well as additional upcast functions like qapi_SpiceChannel_base().
Meanwhile, changes to qapi-visit.c look like:
| static void visit_type_SpiceChannel_fields(Visitor *v, SpiceChannel **obj, Error **errp)
| {
| Error *err = NULL;
|
|- visit_type_implicit_SpiceBasicInfo(v, &(*obj)->base, &err);
|+ visit_type_SpiceBasicInfo_fields(v, (SpiceBasicInfo **)obj, &err);
| if (err) {
(the cast is necessary, since our upcast wrappers only deal with a
single pointer, not pointer-to-pointer); plus the wholesale
elimination of some now-unused visit_type_implicit_FOO() functions.
Without boxing, the corner case of one empty struct having
another empty struct as its base type now requires inserting a
dummy member (previously, the 'Base *base' member sufficed).
And now that we no longer consume a 'base' member in the generated
C struct, we can delete the former negative struct-base-clash-base
test.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1445898903-12082-11-git-send-email-eblake@redhat.com>
[Commit message tweaked slightly]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-10-26 23:34:49 +01:00
|
|
|
struct1.integer = 2;
|
2014-06-18 08:43:29 +02:00
|
|
|
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();
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_int(d_struct1, "integer", 2);
|
|
|
|
qdict_put_str(d_struct1, "string", "test1");
|
|
|
|
qdict_put_str(d_struct1, "enum1", "value1");
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
d_a = qdict_new();
|
|
|
|
qdict_put(d_a, "struct1", d_struct1);
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_str(d_a, "string", "test2");
|
|
|
|
qdict_put_str(d_a, "enum2", "value2");
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
d_data = qdict_new();
|
|
|
|
qdict_put(d_data, "a", d_a);
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_str(d_data, "b", "test3");
|
|
|
|
qdict_put_str(d_data, "enum3", "value3");
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
d = data->expect;
|
2017-04-27 23:58:17 +02:00
|
|
|
qdict_put_str(d, "event", "EVENT_D");
|
2014-06-18 08:43:29 +02:00
|
|
|
qdict_put(d, "data", d_data);
|
|
|
|
|
2018-08-15 15:37:37 +02:00
|
|
|
qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3);
|
2014-06-18 08:43:29 +02:00
|
|
|
|
|
|
|
g_free(struct1.string);
|
|
|
|
g_free(a.string);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|