s3/lib: add a tevent_glib_glue subsystem test
authorRalph Boehme <slow@samba.org>
Thu, 28 Jan 2016 07:29:28 +0000 (08:29 +0100)
committerRalph Boehme <slow@samba.org>
Wed, 24 Apr 2019 18:32:14 +0000 (18:32 +0000)
Tests adapted from glib2 glib/tests/mainloop.c.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Noel Power <npower@samba.org>
source3/lib/tevent_glib_glue_tests.c [new file with mode: 0644]
source3/script/tests/test_tevent_glib_glue.sh [new file with mode: 0755]
source3/selftest/tests.py
source3/wscript_build

diff --git a/source3/lib/tevent_glib_glue_tests.c b/source3/lib/tevent_glib_glue_tests.c
new file mode 100644 (file)
index 0000000..d6cf664
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   testing of the tevent glib glue subsystem
+
+   Copyright (C) Ralph Boehme      2016
+
+   glib tests adapted from glib2 glib/tests/mainloop.c
+   Copyright (C) 2011 Red Hat Inc., Matthias Clasen
+
+     ** NOTE! The following LGPL license applies to the tevent
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 3 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+/*
+ * glib uses TRUE and FALSE which may have redefined by "includes.h" to be
+ * unusable. Unndefine so glib can establish its own working replacement.
+ */
+#undef TRUE
+#undef FALSE
+#include <glib.h>
+#include <glib-unix.h>
+#include "lib/tevent_glib_glue.h"
+
+/*
+ * Unfortunately the glib test suite runner doesn't pass args to tests
+ * so we must keep a few globals here.
+ */
+static struct tevent_context *ev;
+
+static gboolean count_calls(gpointer data)
+{
+       gint *i = (gint *)data;
+
+       (*i)++;
+
+       return TRUE;
+}
+
+static gboolean quit_loop(gpointer data)
+{
+       struct tevent_glib_glue *glue = talloc_get_type_abort(
+               data, struct tevent_glib_glue);
+
+       samba_tevent_glib_glue_quit(glue);
+
+       return G_SOURCE_REMOVE;
+}
+
+static void test_timeouts(void)
+{
+       GMainContext *ctx = NULL;
+       struct tevent_glib_glue *glue = NULL;
+       GSource *source = NULL;
+       gint a;
+       gint b;
+       gint c;
+
+       a = b = c = 0;
+
+       ctx = g_main_context_new();
+       glue = samba_tevent_glib_glue_create(ev, ev, ctx);
+       g_assert(glue != NULL);
+
+       source = g_timeout_source_new(100);
+       g_source_set_callback(source, count_calls, &a, NULL);
+       g_source_attach(source, ctx);
+       g_source_unref(source);
+
+       source = g_timeout_source_new(250);
+       g_source_set_callback(source, count_calls, &b, NULL);
+       g_source_attach(source, ctx);
+       g_source_unref(source);
+
+       source = g_timeout_source_new(330);
+       g_source_set_callback(source, count_calls, &c, NULL);
+       g_source_attach(source, ctx);
+       g_source_unref(source);
+
+       source = g_timeout_source_new(1050);
+       g_source_set_callback(source, quit_loop, glue, NULL);
+       g_source_attach(source, ctx);
+       g_source_unref(source);
+
+       g_assert(tevent_loop_wait(ev) == 0);
+
+       /* We may be delayed for an arbitrary amount of time - for example,
+        * it's possible for all timeouts to fire exactly once.
+        */
+       g_assert_cmpint(a, >, 0);
+       g_assert_cmpint(a, >=, b);
+       g_assert_cmpint(b, >=, c);
+
+       g_assert_cmpint(a, <=, 10);
+       g_assert_cmpint(b, <=, 4);
+       g_assert_cmpint(c, <=, 3);
+
+       samba_tevent_glib_glue_quit(glue);
+       TALLOC_FREE(glue);
+       g_main_context_unref(ctx);
+}
+
+struct test_glib_ev_source_data {
+       GMainContext *ctx;
+       struct tevent_glib_glue *glue;
+};
+
+static gboolean test_glib_ev_source_quit_loop(gpointer data);
+
+static gboolean test_glib_ev_source_timeout_cb(gpointer data)
+{
+       struct test_glib_ev_source_data *state = talloc_get_type_abort(
+               data, struct test_glib_ev_source_data);
+       GSource *source = NULL;
+
+       source = g_timeout_source_new(100);
+       g_source_set_callback(source,
+                             test_glib_ev_source_quit_loop,
+                             state,
+                             NULL);
+       g_source_attach(source, state->ctx);
+       g_source_unref(source);
+
+       return TRUE;
+}
+
+static gboolean test_glib_ev_source_quit_loop(gpointer data)
+{
+       struct test_glib_ev_source_data *state = talloc_get_type_abort(
+               data, struct test_glib_ev_source_data);
+
+       samba_tevent_glib_glue_quit(state->glue);
+
+       return G_SOURCE_REMOVE;
+}
+
+static void test_glib_ev_source(void)
+{
+       GMainContext *ctx = NULL;
+       struct tevent_glib_glue *glue = NULL;
+       struct test_glib_ev_source_data *state = NULL;
+       GSource *source = NULL;
+
+       ctx = g_main_context_new();
+       g_assert(ctx != NULL);
+
+       glue = samba_tevent_glib_glue_create(ev, ev, ctx);
+       g_assert(glue != NULL);
+
+       state = talloc_zero(glue, struct test_glib_ev_source_data);
+       g_assert(state != NULL);
+
+       state->ctx = ctx;
+       state->glue = glue;
+
+       source = g_timeout_source_new(100);
+       g_source_set_callback(source,
+                             test_glib_ev_source_timeout_cb,
+                             state,
+                             NULL);
+       g_source_attach(source, ctx);
+       g_source_unref(source);
+
+       g_assert(tevent_loop_wait(ev) == 0);
+
+       TALLOC_FREE(glue);
+       g_main_context_unref(ctx);
+}
+
+struct test_tevent_ev_source_data {
+       GMainContext *ctx;
+       struct tevent_glib_glue *glue;
+};
+
+static gboolean test_tevent_ev_source_quit_loop(gpointer data);
+
+static void test_tevent_ev_source_timeout_cb(struct tevent_context *_ev,
+                                            struct tevent_timer *te,
+                                            struct timeval current_time,
+                                            void *data)
+{
+       struct test_tevent_ev_source_data *state = talloc_get_type_abort(
+               data, struct test_tevent_ev_source_data);
+       GSource *source = NULL;
+
+       source = g_timeout_source_new(100);
+       g_source_set_callback(source,
+                             test_tevent_ev_source_quit_loop,
+                             state,
+                             NULL);
+       g_source_attach(source, state->ctx);
+       g_source_unref(source);
+
+       return;
+}
+
+static gboolean test_tevent_ev_source_quit_loop(gpointer data)
+{
+       struct test_tevent_ev_source_data *state = talloc_get_type_abort(
+               data, struct test_tevent_ev_source_data);
+
+       samba_tevent_glib_glue_quit(state->glue);
+
+       return G_SOURCE_REMOVE;
+}
+
+static void test_tevent_ev_source(void)
+{
+       GMainContext *ctx = NULL;
+       struct tevent_glib_glue *glue = NULL;
+       struct test_tevent_ev_source_data *state = NULL;
+       struct tevent_timer *timer = NULL;
+
+       ctx = g_main_context_new();
+       g_assert(ctx != NULL);
+
+       glue = samba_tevent_glib_glue_create(ev, ev, ctx);
+       g_assert(glue != NULL);
+
+       state = talloc_zero(glue, struct test_tevent_ev_source_data);
+       g_assert(state != NULL);
+
+       state->ctx = ctx;
+       state->glue = glue;
+
+       timer = tevent_add_timer(ev,
+                                state,
+                                tevent_timeval_current_ofs(0, 1000),
+                                test_tevent_ev_source_timeout_cb,
+                                state);
+       g_assert(timer != NULL);
+
+       g_assert(tevent_loop_wait(ev) == 0);
+
+       TALLOC_FREE(glue);
+       g_main_context_unref(ctx);
+}
+
+static gchar zeros[1024];
+
+static gsize fill_a_pipe(gint fd)
+{
+       gsize written = 0;
+       GPollFD pfd;
+
+       pfd.fd = fd;
+       pfd.events = G_IO_OUT;
+       while (g_poll(&pfd, 1, 0) == 1)
+               /* we should never see -1 here */
+               written += write(fd, zeros, sizeof zeros);
+
+       return written;
+}
+
+static gboolean write_bytes(gint         fd,
+                            GIOCondition condition,
+                            gpointer     user_data)
+{
+       gssize *to_write = user_data;
+       gint limit;
+
+       if (*to_write == 0)
+               return FALSE;
+
+       /* Detect if we run before we should */
+       g_assert(*to_write >= 0);
+
+       limit = MIN(*to_write, sizeof zeros);
+       *to_write -= write(fd, zeros, limit);
+
+       return TRUE;
+}
+
+static gboolean read_bytes(gint         fd,
+                           GIOCondition condition,
+                           gpointer     user_data)
+{
+       static gchar buffer[1024];
+       gssize *to_read = user_data;
+
+       *to_read -= read(fd, buffer, sizeof buffer);
+
+       /* The loop will exit when there is nothing else to read, then we will
+        * use g_source_remove() to destroy this source.
+        */
+       return TRUE;
+}
+
+static void test_unix_fd(void)
+{
+       gssize to_write = -1;
+       gssize to_read;
+       gint fds[2];
+       gint a, b;
+       gint s;
+       GSource *source_a = NULL;
+       GSource *source_b = NULL;
+       struct tevent_glib_glue *glue = NULL;
+
+       glue = samba_tevent_glib_glue_create(ev, ev, g_main_context_default());
+       g_assert(glue != NULL);
+
+       s = pipe(fds);
+       g_assert(s == 0);
+
+       to_read = fill_a_pipe(fds[1]);
+       /* write at higher priority to keep the pipe full... */
+       a = g_unix_fd_add_full(G_PRIORITY_HIGH,
+                              fds[1],
+                              G_IO_OUT,
+                              write_bytes,
+                              &to_write,
+                              NULL);
+       source_a = g_source_ref(g_main_context_find_source_by_id(NULL, a));
+       /* make sure no 'writes' get dispatched yet */
+       while (tevent_loop_once(ev));
+
+       to_read += 128 * 1024 * 1024;
+       to_write = 128 * 1024 * 1024;
+       b = g_unix_fd_add(fds[0], G_IO_IN, read_bytes, &to_read);
+       source_b = g_source_ref(g_main_context_find_source_by_id(NULL, b));
+
+       /* Assuming the kernel isn't internally 'laggy' then there will always
+        * be either data to read or room in which to write.  That will keep
+        * the loop running until all data has been read and written.
+        */
+       while (to_write > 0 || to_read > 0)
+       {
+               gssize to_write_was = to_write;
+               gssize to_read_was = to_read;
+
+               if (tevent_loop_once(ev) != 0)
+                       break;
+
+               /* Since the sources are at different priority, only one of them
+                * should possibly have run.
+                */
+               g_assert(to_write == to_write_was || to_read == to_read_was);
+       }
+
+       g_assert(to_write == 0);
+       g_assert(to_read == 0);
+
+       /* 'a' is already removed by itself */
+       g_assert(g_source_is_destroyed(source_a));
+       g_source_unref(source_a);
+       g_source_remove(b);
+       g_assert(g_source_is_destroyed(source_b));
+       g_source_unref(source_b);
+
+       samba_tevent_glib_glue_quit(glue);
+       TALLOC_FREE(glue);
+
+       close(fds[1]);
+       close(fds[0]);
+}
+
+int main(int argc, const char *argv[])
+{
+       int test_argc = 3;
+       char *test_argv[] = {
+               discard_const("test_glib_glue"),
+               discard_const("-m"),
+               discard_const("no-undefined")
+       };
+       char **argvp = test_argv;
+
+       g_test_init(&test_argc, &argvp, NULL);
+
+       ev = tevent_context_init(NULL);
+       if (ev == NULL) {
+               exit(1);
+       }
+
+       g_test_add_func("/mainloop/timeouts", test_timeouts);
+       g_test_add_func("/mainloop/glib_ev_source", test_glib_ev_source);
+       g_test_add_func("/mainloop/tevent_ev_source", test_tevent_ev_source);
+       g_test_add_func("/mainloop/unix-fd", test_unix_fd);
+
+       return g_test_run();
+}
diff --git a/source3/script/tests/test_tevent_glib_glue.sh b/source3/script/tests/test_tevent_glib_glue.sh
new file mode 100755 (executable)
index 0000000..ce8cb3d
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+failed=0
+TESTNAME="tevent_glib_glue_test"
+
+if [ ! -x $BINDIR/tevent_glib_glue_test ] ; then
+       subunit_start_test "$TESTNAME"
+       subunit_skip_test "$TESTNAME" <<EOF
+Test needs glib2-devel
+EOF
+       testok $0 $failed
+fi
+
+
+testit "$TESTNAME" $VALGRIND $BINDIR/tevent_glib_glue_test ||
+       failed=`expr $failed + 1`
+
+testok $0 $failed
index c856f28ddb12a51bdf3ffa8ea87eb7558c730856..b18b8f07ebaca076e489eeacb4611ce835243e05 100755 (executable)
@@ -419,6 +419,9 @@ plantestsuite(
     "samba3.resolvconf", "none",
     [os.path.join(samba3srcdir, "script/tests/test_resolvconf.sh")])
 
+plantestsuite("samba3.tevent_glib_glue", "none",
+    [os.path.join(samba3srcdir, "script/tests/test_tevent_glib_glue.sh")])
+
 plantestsuite("samba3.async_req", "nt4_dc",
               [os.path.join(samba3srcdir, "script/tests/test_async_req.sh")])
 
index 6a28a05f1f20b1c554d0e4751da1e8336cbf07d1..8cefb7684803e1856f6f70579c3c638229417025 100644 (file)
@@ -1323,6 +1323,16 @@ bld.SAMBA3_BINARY('spotlight2sparql',
                  enabled=bld.env.with_spotlight,
                  install=False)
 
+bld.SAMBA3_BINARY('tevent_glib_glue_test',
+                 source='lib/tevent_glib_glue_tests.c',
+                 deps='''
+                 talloc
+                 libsmb
+                 popt_samba3
+                 tevent-glib-glue''',
+                 enabled=bld.CONFIG_SET('WITH_TEVENT_GLIB_GLUE'),
+                 install=False)
+
 ########################## INCLUDES #################################
 
 bld.RECURSE('auth')