LCOV - code coverage report
Current view: top level - source3/lib - tevent_glib_glue_tests.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 181 182 99.5 %
Date: 2024-04-21 15:09:00 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    testing of the tevent glib glue subsystem
       5             : 
       6             :    Copyright (C) Ralph Boehme      2016
       7             : 
       8             :    glib tests adapted from glib2 glib/tests/mainloop.c
       9             :    Copyright (C) 2011 Red Hat Inc., Matthias Clasen
      10             : 
      11             :      ** NOTE! The following LGPL license applies to the tevent
      12             :      ** library. This does NOT imply that all of Samba is released
      13             :      ** under the LGPL
      14             : 
      15             :    This library is free software; you can redistribute it and/or
      16             :    modify it under the terms of the GNU Lesser General Public
      17             :    License as published by the Free Software Foundation; either
      18             :    version 3 of the License, or (at your option) any later version.
      19             : 
      20             :    This library is distributed in the hope that it will be useful,
      21             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      22             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      23             :    Lesser General Public License for more details.
      24             : 
      25             :    You should have received a copy of the GNU Lesser General Public
      26             :    License along with this library; if not, see <http://www.gnu.org/licenses/>.
      27             : */
      28             : 
      29             : #include "replace.h"
      30             : 
      31             : /*
      32             :  * glib uses TRUE and FALSE which may have redefined by "includes.h" to be
      33             :  * unusable. Unndefine so glib can establish its own working replacement.
      34             :  */
      35             : #undef TRUE
      36             : #undef FALSE
      37             : #include <glib.h>
      38             : #include <glib-unix.h>
      39             : #include "lib/tevent_glib_glue.h"
      40             : 
      41             : /*
      42             :  * Unfortunately the glib test suite runner doesn't pass args to tests
      43             :  * so we must keep a few globals here.
      44             :  */
      45             : static struct tevent_context *ev;
      46             : 
      47          17 : static gboolean count_calls(gpointer data)
      48             : {
      49          17 :         gint *i = (gint *)data;
      50             : 
      51          17 :         (*i)++;
      52             : 
      53          17 :         return TRUE;
      54             : }
      55             : 
      56           1 : static gboolean quit_loop(gpointer data)
      57             : {
      58           1 :         struct tevent_glib_glue *glue = talloc_get_type_abort(
      59             :                 data, struct tevent_glib_glue);
      60             : 
      61           1 :         samba_tevent_glib_glue_quit(glue);
      62             : 
      63           1 :         return G_SOURCE_REMOVE;
      64             : }
      65             : 
      66           1 : static void test_timeouts(void)
      67             : {
      68           1 :         GMainContext *ctx = NULL;
      69           1 :         struct tevent_glib_glue *glue = NULL;
      70           1 :         GSource *source = NULL;
      71           1 :         gint a;
      72           1 :         gint b;
      73           1 :         gint c;
      74             : 
      75           1 :         a = b = c = 0;
      76             : 
      77           1 :         ctx = g_main_context_new();
      78           1 :         glue = samba_tevent_glib_glue_create(ev, ev, ctx);
      79           1 :         g_assert(glue != NULL);
      80             : 
      81           1 :         source = g_timeout_source_new(100);
      82           1 :         g_source_set_callback(source, count_calls, &a, NULL);
      83           1 :         g_source_attach(source, ctx);
      84           1 :         g_source_unref(source);
      85             : 
      86           1 :         source = g_timeout_source_new(250);
      87           1 :         g_source_set_callback(source, count_calls, &b, NULL);
      88           1 :         g_source_attach(source, ctx);
      89           1 :         g_source_unref(source);
      90             : 
      91           1 :         source = g_timeout_source_new(330);
      92           1 :         g_source_set_callback(source, count_calls, &c, NULL);
      93           1 :         g_source_attach(source, ctx);
      94           1 :         g_source_unref(source);
      95             : 
      96           1 :         source = g_timeout_source_new(1050);
      97           1 :         g_source_set_callback(source, quit_loop, glue, NULL);
      98           1 :         g_source_attach(source, ctx);
      99           1 :         g_source_unref(source);
     100             : 
     101           1 :         g_assert(tevent_loop_wait(ev) == 0);
     102             : 
     103             :         /* We may be delayed for an arbitrary amount of time - for example,
     104             :          * it's possible for all timeouts to fire exactly once.
     105             :          */
     106           1 :         g_assert_cmpint(a, >, 0);
     107           1 :         g_assert_cmpint(a, >=, b);
     108           1 :         g_assert_cmpint(b, >=, c);
     109             : 
     110           1 :         g_assert_cmpint(a, <=, 10);
     111           1 :         g_assert_cmpint(b, <=, 4);
     112           1 :         g_assert_cmpint(c, <=, 3);
     113             : 
     114           1 :         samba_tevent_glib_glue_quit(glue);
     115           1 :         TALLOC_FREE(glue);
     116           1 :         g_main_context_unref(ctx);
     117           1 : }
     118             : 
     119             : struct test_glib_ev_source_data {
     120             :         GMainContext *ctx;
     121             :         struct tevent_glib_glue *glue;
     122             : };
     123             : 
     124             : static gboolean test_glib_ev_source_quit_loop(gpointer data);
     125             : 
     126           2 : static gboolean test_glib_ev_source_timeout_cb(gpointer data)
     127             : {
     128           2 :         struct test_glib_ev_source_data *state = talloc_get_type_abort(
     129             :                 data, struct test_glib_ev_source_data);
     130           2 :         GSource *source = NULL;
     131             : 
     132           2 :         source = g_timeout_source_new(100);
     133           2 :         g_source_set_callback(source,
     134             :                               test_glib_ev_source_quit_loop,
     135             :                               state,
     136             :                               NULL);
     137           2 :         g_source_attach(source, state->ctx);
     138           2 :         g_source_unref(source);
     139             : 
     140           2 :         return TRUE;
     141             : }
     142             : 
     143           1 : static gboolean test_glib_ev_source_quit_loop(gpointer data)
     144             : {
     145           1 :         struct test_glib_ev_source_data *state = talloc_get_type_abort(
     146             :                 data, struct test_glib_ev_source_data);
     147             : 
     148           1 :         samba_tevent_glib_glue_quit(state->glue);
     149             : 
     150           1 :         return G_SOURCE_REMOVE;
     151             : }
     152             : 
     153           1 : static void test_glib_ev_source(void)
     154             : {
     155           1 :         GMainContext *ctx = NULL;
     156           1 :         struct tevent_glib_glue *glue = NULL;
     157           1 :         struct test_glib_ev_source_data *state = NULL;
     158           1 :         GSource *source = NULL;
     159             : 
     160           1 :         ctx = g_main_context_new();
     161           1 :         g_assert(ctx != NULL);
     162             : 
     163           1 :         glue = samba_tevent_glib_glue_create(ev, ev, ctx);
     164           1 :         g_assert(glue != NULL);
     165             : 
     166           1 :         state = talloc_zero(glue, struct test_glib_ev_source_data);
     167           1 :         g_assert(state != NULL);
     168             : 
     169           1 :         state->ctx = ctx;
     170           1 :         state->glue = glue;
     171             : 
     172           1 :         source = g_timeout_source_new(100);
     173           1 :         g_source_set_callback(source,
     174             :                               test_glib_ev_source_timeout_cb,
     175             :                               state,
     176             :                               NULL);
     177           1 :         g_source_attach(source, ctx);
     178           1 :         g_source_unref(source);
     179             : 
     180           1 :         g_assert(tevent_loop_wait(ev) == 0);
     181             : 
     182           1 :         TALLOC_FREE(glue);
     183           1 :         g_main_context_unref(ctx);
     184           1 : }
     185             : 
     186             : struct test_tevent_ev_source_data {
     187             :         GMainContext *ctx;
     188             :         struct tevent_glib_glue *glue;
     189             : };
     190             : 
     191             : static gboolean test_tevent_ev_source_quit_loop(gpointer data);
     192             : 
     193           1 : static void test_tevent_ev_source_timeout_cb(struct tevent_context *_ev,
     194             :                                              struct tevent_timer *te,
     195             :                                              struct timeval current_time,
     196             :                                              void *data)
     197             : {
     198           1 :         struct test_tevent_ev_source_data *state = talloc_get_type_abort(
     199             :                 data, struct test_tevent_ev_source_data);
     200           1 :         GSource *source = NULL;
     201             : 
     202           1 :         source = g_timeout_source_new(100);
     203           1 :         g_source_set_callback(source,
     204             :                               test_tevent_ev_source_quit_loop,
     205             :                               state,
     206             :                               NULL);
     207           1 :         g_source_attach(source, state->ctx);
     208           1 :         g_source_unref(source);
     209             : 
     210           1 :         return;
     211             : }
     212             : 
     213           1 : static gboolean test_tevent_ev_source_quit_loop(gpointer data)
     214             : {
     215           1 :         struct test_tevent_ev_source_data *state = talloc_get_type_abort(
     216             :                 data, struct test_tevent_ev_source_data);
     217             : 
     218           1 :         samba_tevent_glib_glue_quit(state->glue);
     219             : 
     220           1 :         return G_SOURCE_REMOVE;
     221             : }
     222             : 
     223           1 : static void test_tevent_ev_source(void)
     224             : {
     225           1 :         GMainContext *ctx = NULL;
     226           1 :         struct tevent_glib_glue *glue = NULL;
     227           1 :         struct test_tevent_ev_source_data *state = NULL;
     228           1 :         struct tevent_timer *timer = NULL;
     229             : 
     230           1 :         ctx = g_main_context_new();
     231           1 :         g_assert(ctx != NULL);
     232             : 
     233           1 :         glue = samba_tevent_glib_glue_create(ev, ev, ctx);
     234           1 :         g_assert(glue != NULL);
     235             : 
     236           1 :         state = talloc_zero(glue, struct test_tevent_ev_source_data);
     237           1 :         g_assert(state != NULL);
     238             : 
     239           1 :         state->ctx = ctx;
     240           1 :         state->glue = glue;
     241             : 
     242           1 :         timer = tevent_add_timer(ev,
     243             :                                  state,
     244             :                                  tevent_timeval_current_ofs(0, 1000),
     245             :                                  test_tevent_ev_source_timeout_cb,
     246             :                                  state);
     247           1 :         g_assert(timer != NULL);
     248             : 
     249           1 :         g_assert(tevent_loop_wait(ev) == 0);
     250             : 
     251           1 :         TALLOC_FREE(glue);
     252           1 :         g_main_context_unref(ctx);
     253           1 : }
     254             : 
     255             : static gchar zeros[1024];
     256             : 
     257           1 : static gsize fill_a_pipe(gint fd)
     258             : {
     259           1 :         gsize written = 0;
     260           1 :         GPollFD pfd;
     261             : 
     262           1 :         pfd.fd = fd;
     263           1 :         pfd.events = G_IO_OUT;
     264          62 :         while (g_poll(&pfd, 1, 0) == 1)
     265             :                 /* we should never see -1 here */
     266          61 :                 written += write(fd, zeros, sizeof zeros);
     267             : 
     268           1 :         return written;
     269             : }
     270             : 
     271      131073 : static gboolean write_bytes(gint          fd,
     272             :                              GIOCondition condition,
     273             :                              gpointer     user_data)
     274             : {
     275      131073 :         gssize *to_write = user_data;
     276      131073 :         gint limit;
     277             : 
     278      131073 :         if (*to_write == 0)
     279             :                 return FALSE;
     280             : 
     281             :         /* Detect if we run before we should */
     282      131072 :         g_assert(*to_write >= 0);
     283             : 
     284      131072 :         limit = MIN(*to_write, sizeof zeros);
     285      131072 :         *to_write -= write(fd, zeros, limit);
     286             : 
     287      131072 :         return TRUE;
     288             : }
     289             : 
     290      131133 : static gboolean read_bytes(gint  fd,
     291             :                             GIOCondition condition,
     292             :                             gpointer     user_data)
     293             : {
     294      131133 :         static gchar buffer[1024];
     295      131133 :         gssize *to_read = user_data;
     296             : 
     297      131133 :         *to_read -= read(fd, buffer, sizeof buffer);
     298             : 
     299             :         /* The loop will exit when there is nothing else to read, then we will
     300             :          * use g_source_remove() to destroy this source.
     301             :          */
     302      131133 :         return TRUE;
     303             : }
     304             : 
     305           1 : static void test_unix_fd(void)
     306             : {
     307           1 :         gssize to_write = -1;
     308           1 :         gssize to_read;
     309           1 :         gint fds[2];
     310           1 :         gint a, b;
     311           1 :         gint s;
     312           1 :         GSource *source_a = NULL;
     313           1 :         GSource *source_b = NULL;
     314           1 :         struct tevent_glib_glue *glue = NULL;
     315             : 
     316           1 :         glue = samba_tevent_glib_glue_create(ev, ev, g_main_context_default());
     317           1 :         g_assert(glue != NULL);
     318             : 
     319           1 :         s = pipe(fds);
     320           1 :         g_assert(s == 0);
     321             : 
     322           1 :         to_read = fill_a_pipe(fds[1]);
     323             :         /* write at higher priority to keep the pipe full... */
     324           1 :         a = g_unix_fd_add_full(G_PRIORITY_HIGH,
     325             :                                fds[1],
     326             :                                G_IO_OUT,
     327             :                                write_bytes,
     328             :                                &to_write,
     329             :                                NULL);
     330           1 :         source_a = g_source_ref(g_main_context_find_source_by_id(NULL, a));
     331             :         /* make sure no 'writes' get dispatched yet */
     332           1 :         while (tevent_loop_once(ev));
     333             : 
     334           1 :         to_read += 128 * 1024 * 1024;
     335           1 :         to_write = 128 * 1024 * 1024;
     336           1 :         b = g_unix_fd_add(fds[0], G_IO_IN, read_bytes, &to_read);
     337           1 :         source_b = g_source_ref(g_main_context_find_source_by_id(NULL, b));
     338             : 
     339             :         /* Assuming the kernel isn't internally 'laggy' then there will always
     340             :          * be either data to read or room in which to write.  That will keep
     341             :          * the loop running until all data has been read and written.
     342             :          */
     343         117 :         while (to_write > 0 || to_read > 0)
     344             :         {
     345      393338 :                 gssize to_write_was = to_write;
     346      393338 :                 gssize to_read_was = to_read;
     347             : 
     348      393338 :                 if (tevent_loop_once(ev) != 0)
     349             :                         break;
     350             : 
     351             :                 /* Since the sources are at different priority, only one of them
     352             :                  * should possibly have run.
     353             :                  */
     354      393339 :                 g_assert(to_write == to_write_was || to_read == to_read_was);
     355             :         }
     356             : 
     357           1 :         g_assert(to_write == 0);
     358           1 :         g_assert(to_read == 0);
     359             : 
     360             :         /* 'a' is already removed by itself */
     361           1 :         g_assert(g_source_is_destroyed(source_a));
     362           1 :         g_source_unref(source_a);
     363           1 :         g_source_remove(b);
     364           1 :         g_assert(g_source_is_destroyed(source_b));
     365           1 :         g_source_unref(source_b);
     366             : 
     367           1 :         samba_tevent_glib_glue_quit(glue);
     368           1 :         TALLOC_FREE(glue);
     369             : 
     370           1 :         close(fds[1]);
     371           1 :         close(fds[0]);
     372           1 : }
     373             : 
     374           1 : int main(int argc, const char *argv[])
     375             : {
     376           1 :         int test_argc = 3;
     377           1 :         char *test_argv[] = {
     378             :                 discard_const("test_glib_glue"),
     379             :                 discard_const("-m"),
     380             :                 discard_const("no-undefined")
     381             :         };
     382           1 :         char **argvp = test_argv;
     383             : 
     384           1 :         g_test_init(&test_argc, &argvp, NULL);
     385             : 
     386           1 :         ev = tevent_context_init(NULL);
     387           1 :         if (ev == NULL) {
     388           0 :                 exit(1);
     389             :         }
     390             : 
     391           1 :         g_test_add_func("/mainloop/timeouts", test_timeouts);
     392           1 :         g_test_add_func("/mainloop/glib_ev_source", test_glib_ev_source);
     393           1 :         g_test_add_func("/mainloop/tevent_ev_source", test_tevent_ev_source);
     394           1 :         g_test_add_func("/mainloop/unix-fd", test_unix_fd);
     395             : 
     396           1 :         return g_test_run();
     397             : }

Generated by: LCOV version 1.14