All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michael Roth <mdroth@linux.vnet.ibm.com>
To: qemu-devel@nongnu.org
Cc: pbonzini@redhat.com, aliguori@us.ibm.com, qemulist@gmail.com,
	stefanha@redhat.com
Subject: [Qemu-devel] [PATCH 6/9] QContext: add unit tests
Date: Fri,  3 May 2013 11:03:49 -0500	[thread overview]
Message-ID: <1367597032-28934-7-git-send-email-mdroth@linux.vnet.ibm.com> (raw)
In-Reply-To: <1367597032-28934-1-git-send-email-mdroth@linux.vnet.ibm.com>


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

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();
+}
-- 
1.7.9.5

  parent reply	other threads:[~2013-05-03 16:05 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-05-03 16:03 [Qemu-devel] [RFC 0/9] QContext: QOM class to support multiple event loops Michael Roth
2013-05-03 16:03 ` [Qemu-devel] [PATCH 1/9] qom: add qom_init_completion Michael Roth
2013-05-06  7:45   ` Paolo Bonzini
2013-05-06 19:01     ` mdroth
2013-05-03 16:03 ` [Qemu-devel] [PATCH 2/9] qom: add object_property_add_unnamed_child Michael Roth
2013-05-06  7:44   ` Paolo Bonzini
2013-05-06 18:48     ` mdroth
2013-05-08 11:33       ` Stefan Hajnoczi
2013-05-03 16:03 ` [Qemu-devel] [PATCH 3/9] QSource: QEMU event source object Michael Roth
2013-05-03 16:03 ` [Qemu-devel] [PATCH 4/9] QContext: QEMU event loop context, abstract base class Michael Roth
2013-05-03 16:03 ` [Qemu-devel] [PATCH 5/9] GlibQContext: a QContext wrapper around GMainContexts Michael Roth
2013-05-03 16:03 ` Michael Roth [this message]
2013-05-03 16:03 ` [Qemu-devel] [PATCH 7/9] iohandler: associate with main event loop via a QSource Michael Roth
2013-05-06  7:53   ` Paolo Bonzini
2013-05-06 19:03     ` mdroth
2013-08-15  6:07     ` Wenchao Xia
2013-05-03 16:03 ` [Qemu-devel] [PATCH 8/9] main-loop: drive main event loop via QContext Michael Roth
2013-05-03 16:03 ` [Qemu-devel] [PATCH 9/9] dataplane: use a QContext event loop in place of custom thread Michael Roth
2013-05-06  7:54   ` Paolo Bonzini
2013-05-06 19:13     ` mdroth
2013-05-06  3:26 ` [Qemu-devel] [RFC 0/9] QContext: QOM class to support multiple event loops liu ping fan
2013-05-06 18:43   ` mdroth
2013-05-06  7:54 ` Paolo Bonzini
2013-05-06 12:25   ` Anthony Liguori
2013-05-06 18:35     ` mdroth
2013-05-06 20:04       ` Paolo Bonzini
2013-05-06 18:17   ` mdroth
2013-05-08 11:54     ` Stefan Hajnoczi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1367597032-28934-7-git-send-email-mdroth@linux.vnet.ibm.com \
    --to=mdroth@linux.vnet.ibm.com \
    --cc=aliguori@us.ibm.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemulist@gmail.com \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.