(root)/
glib-2.79.0/
gio/
tests/
sleepy-stream.c
       1  /*
       2   * Copyright © 2009 Codethink Limited
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * See the included COPYING file for more information.
      12   *
      13   * Author: Ryan Lortie <desrt@desrt.ca>
      14   */
      15  
      16  #include <gio/gio.h>
      17  #include <string.h>
      18  
      19  #define MAX_PIECE_SIZE  100
      20  #define MAX_PIECES       60
      21  
      22  static gchar *
      23  cook_piece (void)
      24  {
      25    char buffer[MAX_PIECE_SIZE * 2];
      26    gint symbols, i = 0;
      27  
      28    symbols = g_test_rand_int_range (1, MAX_PIECE_SIZE + 1);
      29  
      30    while (symbols--)
      31      {
      32        gint c = g_test_rand_int_range (0, 30);
      33  
      34        switch (c)
      35          {
      36           case 26:
      37            buffer[i++] = '\n';
      38            G_GNUC_FALLTHROUGH;
      39           case 27:
      40            buffer[i++] = '\r';
      41            break;
      42  
      43           case 28:
      44            buffer[i++] = '\r';
      45            G_GNUC_FALLTHROUGH;
      46           case 29:
      47            buffer[i++] = '\n';
      48            break;
      49  
      50           default:
      51            buffer[i++] = c + 'a';
      52            break;
      53          }
      54  
      55        g_assert_cmpint (i, <=, sizeof buffer);
      56      }
      57  
      58    return g_strndup (buffer, i);
      59  }
      60  
      61  static gchar **
      62  cook_pieces (void)
      63  {
      64    gchar **array;
      65    gint pieces;
      66  
      67    pieces = g_test_rand_int_range (0, MAX_PIECES + 1);
      68    array = g_new (char *, pieces + 1);
      69    array[pieces] = NULL;
      70  
      71    while (pieces--)
      72      array[pieces] = cook_piece ();
      73  
      74    return array;
      75  }
      76  
      77  typedef struct
      78  {
      79    GInputStream parent_instance;
      80  
      81    gboolean built_to_fail;
      82    gchar **pieces;
      83    gint index;
      84  
      85    const gchar *current;
      86  } SleepyStream;
      87  
      88  typedef GInputStreamClass SleepyStreamClass;
      89  
      90  GType sleepy_stream_get_type (void);
      91  
      92  G_DEFINE_TYPE (SleepyStream, sleepy_stream, G_TYPE_INPUT_STREAM)
      93  
      94  static gssize
      95  sleepy_stream_read (GInputStream  *stream,
      96                      void          *buffer,
      97                      gsize          length,
      98                      GCancellable  *cancellable,
      99                      GError       **error)
     100  {
     101    SleepyStream *sleepy = (SleepyStream *) stream;
     102  
     103    if (sleepy->pieces[sleepy->index] == NULL)
     104      {
     105        if (sleepy->built_to_fail)
     106          {
     107            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "fail");
     108            return -1;
     109          }
     110        else
     111          return 0;
     112      }
     113    else
     114      {
     115        if (!sleepy->current)
     116          sleepy->current = sleepy->pieces[sleepy->index++];
     117  
     118        length = MIN (strlen (sleepy->current), length);
     119        memcpy (buffer, sleepy->current, length);
     120  
     121        sleepy->current += length;
     122        if (*sleepy->current == '\0')
     123          sleepy->current = NULL;
     124  
     125        return length;
     126      }
     127  }
     128  
     129  static void
     130  sleepy_stream_init (SleepyStream *sleepy)
     131  {
     132    sleepy->pieces = cook_pieces ();
     133    sleepy->built_to_fail = FALSE;
     134    sleepy->index = 0;
     135  }
     136  
     137  static void
     138  sleepy_stream_finalize (GObject *object)
     139  {
     140    SleepyStream *sleepy = (SleepyStream *) object;
     141  
     142    g_strfreev (sleepy->pieces);
     143    G_OBJECT_CLASS (sleepy_stream_parent_class)
     144      ->finalize (object);
     145  }
     146  
     147  static void
     148  sleepy_stream_class_init (SleepyStreamClass *class)
     149  {
     150    G_OBJECT_CLASS (class)->finalize = sleepy_stream_finalize;
     151    class->read_fn = sleepy_stream_read;
     152  
     153    /* no read_async implementation.
     154     * main thread will sleep while read runs in a worker.
     155     */
     156  }
     157  
     158  static SleepyStream *
     159  sleepy_stream_new (void)
     160  {
     161    return g_object_new (sleepy_stream_get_type (), NULL);
     162  }
     163  
     164  static gboolean
     165  read_line (GDataInputStream  *stream,
     166             GString           *string,
     167             const gchar       *eol,
     168             GError           **error)
     169  {
     170    gsize length;
     171    char *str;
     172  
     173    str = g_data_input_stream_read_line (stream, &length, NULL, error);
     174  
     175    if (str == NULL)
     176      return FALSE;
     177  
     178    g_assert (strstr (str, eol) == NULL);
     179    g_assert (strlen (str) == length);
     180  
     181    g_string_append (string, str);
     182    g_string_append (string, eol);
     183    g_free (str);
     184  
     185    return TRUE;
     186  }
     187  
     188  static void
     189  build_comparison (GString      *str,
     190                    SleepyStream *stream)
     191  {
     192    /* build this for comparison */
     193    gint i;
     194  
     195    for (i = 0; stream->pieces[i]; i++)
     196      g_string_append (str, stream->pieces[i]);
     197  
     198    if (str->len && str->str[str->len - 1] != '\n')
     199      g_string_append_c (str, '\n');
     200  }
     201  
     202  
     203  static void
     204  test (void)
     205  {
     206    SleepyStream *stream = sleepy_stream_new ();
     207    GDataInputStream *data;
     208    GError *error = NULL;
     209    GString *one;
     210    GString *two;
     211  
     212    one = g_string_new (NULL);
     213    two = g_string_new (NULL);
     214  
     215    data = g_data_input_stream_new (G_INPUT_STREAM (stream));
     216    g_data_input_stream_set_newline_type (data, G_DATA_STREAM_NEWLINE_TYPE_LF);
     217    build_comparison (one, stream);
     218  
     219    while (read_line (data, two, "\n", &error));
     220  
     221    g_assert_cmpstr (one->str, ==, two->str);
     222    g_string_free (one, TRUE);
     223    g_string_free (two, TRUE);
     224    g_object_unref (stream);
     225    g_object_unref (data);
     226  }
     227  
     228  static GDataInputStream *data;
     229  static GString *one, *two;
     230  static GMainLoop *loop;
     231  static const gchar *eol;
     232  
     233  static void
     234  asynch_ready (GObject      *object,
     235                GAsyncResult *result,
     236                gpointer      user_data)
     237  {
     238    GError *error = NULL;
     239    gsize length;
     240    gchar *str;
     241  
     242    g_assert (data == G_DATA_INPUT_STREAM (object));
     243  
     244    str = g_data_input_stream_read_line_finish (data, result, &length, &error);
     245  
     246    if (str == NULL)
     247      {
     248        g_main_loop_quit (loop);
     249        if (error)
     250          g_error_free (error);
     251      }
     252    else
     253      {
     254        g_assert (length == strlen (str));
     255        g_string_append (two, str);
     256        g_string_append (two, eol);
     257        g_free (str);
     258  
     259        /* MOAR!! */
     260        g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL);
     261      }
     262  }
     263  
     264  
     265  static void
     266  asynch (void)
     267  {
     268    SleepyStream *sleepy = sleepy_stream_new ();
     269  
     270    data = g_data_input_stream_new (G_INPUT_STREAM (sleepy));
     271    one = g_string_new (NULL);
     272    two = g_string_new (NULL);
     273    eol = "\n";
     274  
     275    build_comparison (one, sleepy);
     276    g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL);
     277    g_main_loop_run (loop = g_main_loop_new (NULL, FALSE));
     278  
     279    g_assert_cmpstr (one->str, ==, two->str);
     280    g_string_free (one, TRUE);
     281    g_string_free (two, TRUE);
     282    g_object_unref (sleepy);
     283    g_object_unref (data);
     284  }
     285  
     286  int
     287  main (int argc, char **argv)
     288  {
     289    g_test_init (&argc, &argv, NULL);
     290  
     291    g_test_add_func ("/filter-stream/input", test);
     292    g_test_add_func ("/filter-stream/async", asynch);
     293  
     294    return g_test_run();
     295  }