[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 6/9] QContext: add unit tests
From: |
Michael Roth |
Subject: |
[Qemu-devel] [PATCH 6/9] QContext: add unit tests |
Date: |
Fri, 3 May 2013 11:03:49 -0500 |
Signed-off-by: Michael Roth <address@hidden>
---
Makefile.objs | 6 +-
qcontext/Makefile.objs | 1 +
qom/Makefile.objs | 6 +-
tests/Makefile | 7 ++
tests/test-qcontext.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 276 insertions(+), 3 deletions(-)
create mode 100644 qcontext/Makefile.objs
create mode 100644 tests/test-qcontext.c
diff --git a/Makefile.objs b/Makefile.objs
index fcb303a..3c571ee 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -1,7 +1,7 @@
#######################################################################
# Common libraries for tools and emulators
stub-obj-y = stubs/
-util-obj-y = util/ qobject/ qapi/ trace/
+util-obj-y = util/ qobject/ qapi/ trace/ qcontext/ qom/
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
@@ -98,6 +98,10 @@ common-obj-y += disas/
# by libqemuutil.a. These should be moved to a separate .json schema.
qga-obj-y = qga/ qapi-types.o qapi-visit.o
+######################################################################
+# qom
+qom-obj-y = qom/ qobject
+
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
diff --git a/qcontext/Makefile.objs b/qcontext/Makefile.objs
new file mode 100644
index 0000000..778af68
--- /dev/null
+++ b/qcontext/Makefile.objs
@@ -0,0 +1 @@
+util-obj-y = qcontext.o glib-qcontext.o qsource.o
diff --git a/qom/Makefile.objs b/qom/Makefile.objs
index 6a93ac7..b29b5f1 100644
--- a/qom/Makefile.objs
+++ b/qom/Makefile.objs
@@ -1,2 +1,4 @@
-common-obj-y = object.o container.o qom-qobject.o
-common-obj-y += cpu.o
+qom-obj-y = object.o container.o qom-qobject.o
+qom-obj-y += cpu.o
+common-obj-y = $(qom-obj-y)
+util-obj-y = $(qom-obj-y)
diff --git a/tests/Makefile b/tests/Makefile
index 72bf2cd..818196e 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -31,6 +31,12 @@ gcov-files-test-iov-y = util/iov.c
check-unit-y += tests/test-aio$(EXESUF)
gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c
gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c
+check-unit-y += tests/test-qcontext$(EXESUF)
+gcov-files-test-qcontext-y = util/qcontext.c
+gcov-files-test-qcontext-y = util/glib-qcontext.c
+gcov-files-test-qcontext-y = util/qsource.c
+gcov-files-test-qcontext-y = iohandler.c
+gcov-files-test-qcontext-y = main-loop.c
check-unit-y += tests/test-thread-pool$(EXESUF)
gcov-files-test-thread-pool-y = thread-pool.c
gcov-files-test-hbitmap-y = util/hbitmap.c
@@ -91,6 +97,7 @@ tests/check-qfloat$(EXESUF): tests/check-qfloat.o
libqemuutil.a
tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y)
libqemuutil.a libqemustub.a
tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a
libqemustub.a
+tests/test-qcontext$(EXESUF): tests/test-qcontext.o libqemuutil.a
libqemustub.a qom/object.o qom/container.o qom/qom-qobject.o
qcontext/qcontext.o qcontext/glib-qcontext.o qcontext/qsource.o $(block-obj-y)
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y)
libqemuutil.a libqemustub.a
tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a
tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a
diff --git a/tests/test-qcontext.c b/tests/test-qcontext.c
new file mode 100644
index 0000000..52c5f54
--- /dev/null
+++ b/tests/test-qcontext.c
@@ -0,0 +1,259 @@
+#include <glib.h>
+#include "qom/object.h"
+#include "qemu/module.h"
+#include "qcontext/qcontext.h"
+#include "qcontext/glib-qcontext.h"
+#include "qcontext/qsource.h"
+#include "qemu/event_notifier.h"
+
+typedef struct TestEventState {
+ QSource *qsource;
+ GPollFD poll_fds[8];
+ int n_poll_fds;
+ bool dispatched;
+ bool skip_poll;
+#define CB_VALUE_PASS 42
+#define CB_VALUE_FAIL 0
+ int cb_value;
+} TestEventState;
+
+static bool test_event_prepare(QSource *evt, int *timeout)
+{
+ QSourceClass *evtk = QSOURCE_GET_CLASS(evt);
+ TestEventState *s = evtk->get_user_data(evt);
+
+ return s->skip_poll;
+}
+
+static bool test_event_check(QSource *qevt)
+{
+ QSourceClass *qevtk = QSOURCE_GET_CLASS(qevt);
+ TestEventState *s = qevtk->get_user_data(qevt);
+ int i;
+ bool needs_dispatch = false;
+
+ if (!s->skip_poll) {
+ for (i = 0; i < s->n_poll_fds; i++) {
+ if (s->poll_fds[i].revents & s->poll_fds[i].events) {
+ needs_dispatch = true;
+ }
+ }
+ }
+
+ return needs_dispatch;
+}
+
+static bool test_event_dispatch(QSource *evt)
+{
+ QSourceClass *evtk = QSOURCE_GET_CLASS(evt);
+ QSourceCB cb = evtk->get_callback_func(evt);
+
+ if (cb) {
+ return cb(evt);
+ }
+
+ return true;
+}
+
+static void test_event_finalize(QSource *qsource)
+{
+}
+
+static bool test_cb(QSource *evt)
+{
+ QSourceClass *evtk = QSOURCE_GET_CLASS(evt);
+ TestEventState *s = evtk->get_user_data(evt);
+
+ s->cb_value = CB_VALUE_PASS;
+ s->dispatched = true;
+
+ if (!s->skip_poll) {
+ int i;
+ for (i = 0; i < s->n_poll_fds; i++) {
+ /* unless we short-circuited execution, we should've
+ * only dispatched if the corresponding events we're
+ * listening for were set in the poll() call
+ */
+ if (!(s->poll_fds[i].revents & s->poll_fds[i].events)) {
+ s->cb_value = CB_VALUE_FAIL;
+ }
+ }
+ }
+
+ return true;
+}
+
+QSourceFuncs test_funcs = {
+ test_event_prepare,
+ test_event_check,
+ test_event_dispatch,
+ test_event_finalize,
+};
+
+static void test_qcontext_init(void)
+{
+ Error *err = NULL;
+ GlibQContext *ctx;
+
+ ctx = glib_qcontext_new("test", false, &err);
+ g_assert(!err);
+
+ object_unref(OBJECT(ctx));
+}
+
+static void test_qsource_init(void)
+{
+ QSource *event = qsource_new(test_funcs, test_cb, NULL, NULL);
+
+ object_unref(OBJECT(event));
+}
+
+static void test_qcontext_attach(void)
+{
+ GlibQContext *ctx;
+ QContextClass *ctxk;
+ QSource *evt;
+ Error *err = NULL;
+
+ ctx = glib_qcontext_new("test2", false, &err);
+ if (err) {
+ g_warning("error: %s", error_get_pretty(err));
+ g_assert(0);
+ }
+ ctxk = QCONTEXT_GET_CLASS(QCONTEXT(ctx));
+ evt = qsource_new(test_funcs, test_cb, NULL, NULL);
+
+ ctxk->attach(QCONTEXT(ctx), evt, NULL);
+ ctxk->detach(QCONTEXT(ctx), evt, NULL);
+
+ object_unref(OBJECT(evt));
+ object_unref(OBJECT(ctx));
+}
+
+static void test_qcontext_iterate(void)
+{
+ GlibQContext *ctx;
+ QContextClass *ctxk;
+ QSource *evt;
+ QSourceClass *evtk;
+ Error *err = NULL;
+ EventNotifier notifier1, notifier2;
+ TestEventState s = { 0 };
+
+ /* TODO: generalize this test case to act on any QContext
+ * sub-class so we can re-use it for non-glib implementations
+ */
+ ctx = glib_qcontext_new("test3", false, &err);
+ if (err) {
+ g_warning("error: %s", error_get_pretty(err));
+ g_assert(0);
+ }
+ ctxk = QCONTEXT_GET_CLASS(QCONTEXT(ctx));
+
+ /* test first iteration. glib uses an internal GPollFD to
+ * trigger wake-up when GSources/GPollFDs are added to a
+ * context, so poll may return true even before we add
+ * QSources to the associated GlibQContext. Since this is
+ * an implementation detail of glib we don't explicitly
+ * test for poll() return value here, just the other
+ * interfaces
+ */
+ g_assert(ctxk->prepare(QCONTEXT(ctx), NULL) == false);
+ ctxk->poll(QCONTEXT(ctx), 0);
+ g_assert(ctxk->check(QCONTEXT(ctx)) == false);
+ ctxk->dispatch(QCONTEXT(ctx));
+
+ /* attach some events to the context and initialize
+ * test state to probe callback behavior
+ */
+ event_notifier_init(¬ifier1, false);
+ event_notifier_init(¬ifier2, false);
+ s.poll_fds[0].fd = event_notifier_get_fd(¬ifier1);
+ s.poll_fds[1].fd = event_notifier_get_fd(¬ifier2);
+ s.poll_fds[0].events = s.poll_fds[1].events = G_IO_IN;
+ s.n_poll_fds = 2;
+ s.skip_poll = false;
+ s.dispatched = false;
+
+ evt = qsource_new(test_funcs, test_cb, NULL, &s);
+ evtk = QSOURCE_GET_CLASS(evt);
+ evtk->add_poll(evt, &s.poll_fds[0]);
+ evtk->add_poll(evt, &s.poll_fds[1]);
+ ctxk->attach(QCONTEXT(ctx), evt, NULL);
+
+ /* looping with events attached, but no GPollFD events set */
+ g_assert(ctxk->prepare(QCONTEXT(ctx), NULL) == false);
+ /* poll() should return true when we add events to a QContext */
+ g_assert(ctxk->poll(QCONTEXT(ctx), 0) == true);
+ g_assert(ctxk->check(QCONTEXT(ctx)) == false);
+ ctxk->dispatch(QCONTEXT(ctx));
+ /* no events, so no callbacks should have been dispatched for
+ * our GPollFDs
+ */
+ g_assert(!s.dispatched);
+
+ /* try again with some G_IO_IN events set */
+ event_notifier_set(¬ifier1);
+ event_notifier_set(¬ifier2);
+ s.dispatched = false;
+
+ g_assert(ctxk->prepare(QCONTEXT(ctx), NULL) == false);
+ g_assert(ctxk->poll(QCONTEXT(ctx), 0) == true);
+ g_assert(ctxk->check(QCONTEXT(ctx)) == true);
+ ctxk->dispatch(QCONTEXT(ctx));
+ g_assert(s.dispatched);
+
+ s.dispatched = false;
+
+ /* try again with events cleared */
+ event_notifier_test_and_clear(¬ifier1);
+ event_notifier_test_and_clear(¬ifier2);
+
+ g_assert(ctxk->prepare(QCONTEXT(ctx), NULL) == false);
+ g_assert(ctxk->poll(QCONTEXT(ctx), 0) == true);
+ g_assert(ctxk->check(QCONTEXT(ctx)) == false);
+ ctxk->dispatch(QCONTEXT(ctx));
+ g_assert(!s.dispatched);
+
+ /* try again with short-circuited dispatch */
+ s.skip_poll = true;
+
+ g_assert(ctxk->prepare(QCONTEXT(ctx), NULL) == true);
+ g_assert(ctxk->poll(QCONTEXT(ctx), 0) == false);
+ g_assert(ctxk->check(QCONTEXT(ctx)) == true);
+ ctxk->dispatch(QCONTEXT(ctx));
+ g_assert(s.dispatched);
+ g_assert(s.cb_value == CB_VALUE_PASS);
+ s.skip_poll = false;
+
+ s.dispatched = false;
+
+ /* again with all QSources removed */
+ ctxk->detach(QCONTEXT(ctx), evt, NULL);
+
+ g_assert(ctxk->prepare(QCONTEXT(ctx), NULL) == false);
+ g_assert(ctxk->poll(QCONTEXT(ctx), 0) == true);
+ g_assert(ctxk->check(QCONTEXT(ctx)) == false);
+ ctxk->dispatch(QCONTEXT(ctx));
+ g_assert(!s.dispatched);
+
+ /* cleanup */
+ evtk->remove_poll(evt, &s.poll_fds[0]);
+ evtk->remove_poll(evt, &s.poll_fds[1]);
+
+ object_unref(OBJECT(evt));
+ object_unref(OBJECT(ctx));
+}
+
+int main(int argc, char **argv)
+{
+ module_call_init(MODULE_INIT_QOM);
+
+ g_test_init(&argc, &argv, NULL);
+ g_test_add_func("/qcontext/init", test_qcontext_init);
+ g_test_add_func("/qsource/init", test_qsource_init);
+ g_test_add_func("/qcontext/attach", test_qcontext_attach);
+ g_test_add_func("/qcontext/iterate", test_qcontext_iterate);
+
+ return g_test_run();
+}
--
1.7.9.5
- [Qemu-devel] [PATCH 2/9] qom: add object_property_add_unnamed_child, (continued)
- [Qemu-devel] [PATCH 1/9] qom: add qom_init_completion, Michael Roth, 2013/05/03
- [Qemu-devel] [PATCH 3/9] QSource: QEMU event source object, Michael Roth, 2013/05/03
- [Qemu-devel] [PATCH 5/9] GlibQContext: a QContext wrapper around GMainContexts, Michael Roth, 2013/05/03
- [Qemu-devel] [PATCH 4/9] QContext: QEMU event loop context, abstract base class, Michael Roth, 2013/05/03
- [Qemu-devel] [PATCH 6/9] QContext: add unit tests,
Michael Roth <=
- [Qemu-devel] [PATCH 8/9] main-loop: drive main event loop via QContext, Michael Roth, 2013/05/03
- [Qemu-devel] [PATCH 7/9] iohandler: associate with main event loop via a QSource, Michael Roth, 2013/05/03
- [Qemu-devel] [PATCH 9/9] dataplane: use a QContext event loop in place of custom thread, Michael Roth, 2013/05/03
- Re: [Qemu-devel] [RFC 0/9] QContext: QOM class to support multiple event loops, liu ping fan, 2013/05/05
- Re: [Qemu-devel] [RFC 0/9] QContext: QOM class to support multiple event loops, Paolo Bonzini, 2013/05/06