Patchwork [6/9] QContext: add unit tests

login
register
mail settings
Submitter Michael Roth
Date May 3, 2013, 4:03 p.m.
Message ID <1367597032-28934-7-git-send-email-mdroth@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/241342/
State New
Headers show

Comments

Michael Roth - May 3, 2013, 4:03 p.m.
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
---
 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

Patch

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(&notifier1, false);
+    event_notifier_init(&notifier2, false);
+    s.poll_fds[0].fd = event_notifier_get_fd(&notifier1);
+    s.poll_fds[1].fd = event_notifier_get_fd(&notifier2);
+    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(&notifier1);
+    event_notifier_set(&notifier2);
+    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(&notifier1);
+    event_notifier_test_and_clear(&notifier2);
+
+    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();
+}