summaryrefslogtreecommitdiff
path: root/source3/lib
diff options
context:
space:
mode:
authorRalph Boehme <slow@samba.org>2016-01-28 08:29:28 +0100
committerRalph Boehme <slow@samba.org>2019-04-24 18:32:14 +0000
commit1f836d4c7fa5376eb4b45116cb59755e1c363b7f (patch)
tree616ac52e453e9169f4bead4029ebc3e9c2f2f493 /source3/lib
parentfa39a7b2e6e3fd22d234b2c6c53772b08243db9d (diff)
downloadsamba-1f836d4c7fa5376eb4b45116cb59755e1c363b7f.tar.gz
s3/lib: add a tevent_glib_glue subsystem test
Tests adapted from glib2 glib/tests/mainloop.c. Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Noel Power <npower@samba.org>
Diffstat (limited to 'source3/lib')
-rw-r--r--source3/lib/tevent_glib_glue_tests.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/source3/lib/tevent_glib_glue_tests.c b/source3/lib/tevent_glib_glue_tests.c
new file mode 100644
index 00000000000..d6cf66462f6
--- /dev/null
+++ b/source3/lib/tevent_glib_glue_tests.c
@@ -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();
+}