(root)/
glib-2.79.0/
gio/
tests/
mock-resolver.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   *
       3   * Copyright (C) 2018 Igalia S.L.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   *
       7   * This library is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU Lesser General Public
       9   * License as published by the Free Software Foundation; either
      10   * version 2.1 of the License, or (at your option) any later version.
      11   *
      12   * This library is distributed in the hope that it will be useful,
      13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15   * Lesser General Public License for more details.
      16   *
      17   * You should have received a copy of the GNU Lesser General
      18   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19   */
      20  
      21  #include "mock-resolver.h"
      22  
      23  struct _MockResolver
      24  {
      25    GResolver parent_instance;
      26    guint ipv4_delay_ms;
      27    guint ipv6_delay_ms;
      28    GList *ipv4_results;
      29    GList *ipv6_results;
      30    GError *ipv4_error;
      31    GError *ipv6_error;
      32  };
      33  
      34  G_DEFINE_TYPE (MockResolver, mock_resolver, G_TYPE_RESOLVER)
      35  
      36  MockResolver *
      37  mock_resolver_new (void)
      38  {
      39    return g_object_new (MOCK_TYPE_RESOLVER, NULL);
      40  }
      41  
      42  void
      43  mock_resolver_set_ipv4_delay_ms (MockResolver *self, guint delay_ms)
      44  {
      45    self->ipv4_delay_ms = delay_ms;
      46  }
      47  
      48  static gpointer
      49  copy_object (gconstpointer obj, gpointer user_data)
      50  {
      51    return g_object_ref (G_OBJECT (obj));
      52  }
      53  
      54  void
      55  mock_resolver_set_ipv4_results (MockResolver *self, GList *results)
      56  {
      57    if (self->ipv4_results)
      58      g_list_free_full (self->ipv4_results, g_object_unref);
      59    self->ipv4_results = g_list_copy_deep (results, copy_object, NULL);
      60  }
      61  
      62  void
      63  mock_resolver_set_ipv4_error (MockResolver *self, GError *error)
      64  {
      65    g_clear_error (&self->ipv4_error);
      66    if (error)
      67      self->ipv4_error = g_error_copy (error);
      68  }
      69  
      70  void
      71  mock_resolver_set_ipv6_delay_ms (MockResolver *self, guint delay_ms)
      72  {
      73    self->ipv6_delay_ms = delay_ms;
      74  }
      75  
      76  void
      77  mock_resolver_set_ipv6_results (MockResolver *self, GList *results)
      78  {
      79    if (self->ipv6_results)
      80      g_list_free_full (self->ipv6_results, g_object_unref);
      81    self->ipv6_results = g_list_copy_deep (results, copy_object, NULL);
      82  }
      83  
      84  void
      85  mock_resolver_set_ipv6_error (MockResolver *self, GError *error)
      86  {
      87    g_clear_error (&self->ipv6_error);
      88    if (error)
      89      self->ipv6_error = g_error_copy (error);
      90  }
      91  
      92  static gboolean lookup_by_name_cb (gpointer user_data);
      93  
      94  /* Core of the implementation of `lookup_by_name()` in the mock resolver.
      95   *
      96   * It creates a #GSource which will become ready with the resolver results. It
      97   * will become ready either after a timeout, or as an idle callback. This
      98   * simulates doing some actual network-based resolution work.
      99   *
     100   * A previous implementation of this did the work in a thread, but that made it
     101   * hard to synchronise the timeouts with the #GResolver failure timeouts in the
     102   * calling thread, as spawning a worker thread could be subject to non-trivial
     103   * delays. */
     104  static void
     105  do_lookup_by_name (MockResolver             *self,
     106                     GTask                    *task,
     107                     GResolverNameLookupFlags  flags)
     108  {
     109    GSource *source = NULL;
     110  
     111    g_task_set_task_data (task, GINT_TO_POINTER (flags), NULL);
     112  
     113    if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
     114      source = g_timeout_source_new (self->ipv4_delay_ms);
     115    else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
     116      source = g_timeout_source_new (self->ipv6_delay_ms);
     117    else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
     118      source = g_idle_source_new ();
     119    else
     120      g_assert_not_reached ();
     121  
     122    g_source_set_callback (source, lookup_by_name_cb, g_object_ref (task), g_object_unref);
     123    g_source_attach (source, g_main_context_get_thread_default ());
     124    g_source_unref (source);
     125  }
     126  
     127  static gboolean
     128  lookup_by_name_cb (gpointer user_data)
     129  {
     130    GTask *task = G_TASK (user_data);
     131    MockResolver *self = g_task_get_source_object (task);
     132    GResolverNameLookupFlags flags = GPOINTER_TO_INT (g_task_get_task_data (task));
     133  
     134    if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
     135      {
     136        if (self->ipv4_error)
     137          g_task_return_error (task, g_error_copy (self->ipv4_error));
     138        else
     139          g_task_return_pointer (task, g_list_copy_deep (self->ipv4_results, copy_object, NULL), NULL);
     140      }
     141    else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
     142      {
     143        if (self->ipv6_error)
     144          g_task_return_error (task, g_error_copy (self->ipv6_error));
     145        else
     146          g_task_return_pointer (task, g_list_copy_deep (self->ipv6_results, copy_object, NULL), NULL);
     147      }
     148    else if (flags == G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT)
     149      {
     150        /* This is only the minimal implementation needed for some tests */
     151        g_assert (self->ipv4_error == NULL && self->ipv6_error == NULL && self->ipv6_results == NULL);
     152        g_task_return_pointer (task, g_list_copy_deep (self->ipv4_results, copy_object, NULL), NULL);
     153      }
     154    else
     155      g_assert_not_reached ();
     156  
     157    return G_SOURCE_REMOVE;
     158  }
     159  
     160  static void
     161  lookup_by_name_with_flags_async (GResolver                *resolver,
     162                                   const gchar              *hostname,
     163                                   GResolverNameLookupFlags  flags,
     164                                   GCancellable             *cancellable,
     165                                   GAsyncReadyCallback       callback,
     166                                   gpointer                  user_data)
     167  {
     168    MockResolver *self = MOCK_RESOLVER (resolver);
     169    GTask *task = NULL;
     170  
     171    task = g_task_new (resolver, cancellable, callback, user_data);
     172    g_task_set_source_tag (task, lookup_by_name_with_flags_async);
     173  
     174    do_lookup_by_name (self, task, flags);
     175  
     176    g_object_unref (task);
     177  }
     178  
     179  static void
     180  async_result_cb (GObject      *source_object,
     181                   GAsyncResult *result,
     182                   gpointer      user_data)
     183  {
     184    GAsyncResult **result_out = user_data;
     185  
     186    g_assert (*result_out == NULL);
     187    *result_out = g_object_ref (result);
     188  
     189    g_main_context_wakeup (g_main_context_get_thread_default ());
     190  }
     191  
     192  static GList *
     193  lookup_by_name (GResolver     *resolver,
     194                  const gchar   *hostname,
     195                  GCancellable  *cancellable,
     196                  GError       **error)
     197  {
     198    MockResolver *self = MOCK_RESOLVER (resolver);
     199    GMainContext *context = NULL;
     200    GList *result = NULL;
     201    GAsyncResult *async_result = NULL;
     202    GTask *task = NULL;
     203  
     204    context = g_main_context_new ();
     205    g_main_context_push_thread_default (context);
     206  
     207    task = g_task_new (resolver, cancellable, async_result_cb, &async_result);
     208    g_task_set_source_tag (task, lookup_by_name);
     209  
     210    /* Set up the resolution job. */
     211    do_lookup_by_name (self, task, G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT);
     212  
     213    /* Wait for it to complete synchronously. */
     214    while (async_result == NULL)
     215      g_main_context_iteration (context, TRUE);
     216  
     217    result = g_task_propagate_pointer (G_TASK (async_result), error);
     218    g_object_unref (async_result);
     219  
     220    g_assert_finalize_object (task);
     221  
     222    g_main_context_pop_thread_default (context);
     223    g_main_context_unref (context);
     224  
     225    return g_steal_pointer (&result);
     226  }
     227  
     228  static GList *
     229  lookup_by_name_with_flags_finish (GResolver     *resolver,
     230                                    GAsyncResult  *result,
     231                                    GError       **error)
     232  {
     233    return g_task_propagate_pointer (G_TASK (result), error);
     234  }
     235  
     236  static void
     237  mock_resolver_finalize (GObject *object)
     238  {
     239    MockResolver *self = (MockResolver*)object;
     240  
     241    g_clear_error (&self->ipv4_error);
     242    g_clear_error (&self->ipv6_error);
     243    if (self->ipv6_results)
     244      g_list_free_full (self->ipv6_results, g_object_unref);
     245    if (self->ipv4_results)
     246      g_list_free_full (self->ipv4_results, g_object_unref);
     247  
     248    G_OBJECT_CLASS (mock_resolver_parent_class)->finalize (object);
     249  }
     250  
     251  static void
     252  mock_resolver_class_init (MockResolverClass *klass)
     253  {
     254    GResolverClass *resolver_class = G_RESOLVER_CLASS (klass);
     255    GObjectClass *object_class = G_OBJECT_CLASS (klass);
     256    resolver_class->lookup_by_name_with_flags_async  = lookup_by_name_with_flags_async;
     257    resolver_class->lookup_by_name_with_flags_finish = lookup_by_name_with_flags_finish;
     258    resolver_class->lookup_by_name = lookup_by_name;
     259    object_class->finalize = mock_resolver_finalize;
     260  }
     261  
     262  static void
     263  mock_resolver_init (MockResolver *self)
     264  {
     265  }