From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:44567) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UYIU3-0001bx-GB for qemu-devel@nongnu.org; Fri, 03 May 2013 12:05:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UYIU1-0007f5-Ik for qemu-devel@nongnu.org; Fri, 03 May 2013 12:05:19 -0400 Received: from mail-ve0-x22c.google.com ([2607:f8b0:400c:c01::22c]:59035) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UYIU1-0007ew-Ds for qemu-devel@nongnu.org; Fri, 03 May 2013 12:05:17 -0400 Received: by mail-ve0-f172.google.com with SMTP id da11so1645930veb.31 for ; Fri, 03 May 2013 09:05:17 -0700 (PDT) Sender: fluxion From: Michael Roth Date: Fri, 3 May 2013 11:03:49 -0500 Message-Id: <1367597032-28934-7-git-send-email-mdroth@linux.vnet.ibm.com> In-Reply-To: <1367597032-28934-1-git-send-email-mdroth@linux.vnet.ibm.com> References: <1367597032-28934-1-git-send-email-mdroth@linux.vnet.ibm.com> Subject: [Qemu-devel] [PATCH 6/9] QContext: add unit tests List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: pbonzini@redhat.com, aliguori@us.ibm.com, qemulist@gmail.com, stefanha@redhat.com Signed-off-by: Michael Roth --- 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 +#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