/*
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 .
*/
#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
#include
#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();
}