Improve test coverage for gsettings
[platform/upstream/glib.git] / gio / tests / resolver.c
index 055e152..5f252f5 100644 (file)
@@ -28,7 +28,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#ifdef G_OS_UNIX
 #include <unistd.h>
+#endif
 
 #include <gio/gio.h>
 
@@ -36,16 +38,20 @@ static GResolver *resolver;
 static GCancellable *cancellable;
 static GMainLoop *loop;
 static int nlookups = 0;
+static gboolean synchronous = FALSE;
+static guint connectable_count = 0;
+static GResolverRecordType record_type = 0;
 
 static void G_GNUC_NORETURN
 usage (void)
 {
-       fprintf (stderr, "Usage: resolver [-t] [-s] [hostname | IP | service/protocol/domain ] ...\n");
-       fprintf (stderr, "       resolver [-t] [-s] -c [hostname | IP | service/protocol/domain ]\n");
-       fprintf (stderr, "       Use -t to enable threading.\n");
+       fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
+       fprintf (stderr, "Usage: resolver [-s] [-t MX|TXT|NS|SOA] rrname ...\n");
+       fprintf (stderr, "       resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
        fprintf (stderr, "       Use -s to do synchronous lookups.\n");
-       fprintf (stderr, "       Both together will result in simultaneous lookups in multiple threads\n");
-       fprintf (stderr, "       Use -c (and only a single resolvable argument) to test GSocketConnectable.\n");
+       fprintf (stderr, "       Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
+       fprintf (stderr, "       The given NUMBER determines how many times the connectable will be enumerated.\n");
+       fprintf (stderr, "       Use -t with MX, TXT, NS or SOA to lookup DNS records of those types.\n");
        exit (1);
 }
 
@@ -139,9 +145,9 @@ print_resolved_service (const char *service,
        {
          printf ("%s:%u (pri %u, weight %u)\n",
                  g_srv_target_get_hostname (t->data),
-                 g_srv_target_get_port (t->data),
-                 g_srv_target_get_priority (t->data),
-                 g_srv_target_get_weight (t->data));
+                 (guint)g_srv_target_get_port (t->data),
+                 (guint)g_srv_target_get_priority (t->data),
+                 (guint)g_srv_target_get_weight (t->data));
           g_srv_target_free (t->data);
        }
       g_list_free (targets);
@@ -153,11 +159,185 @@ print_resolved_service (const char *service,
 }
 
 static void
+print_resolved_mx (const char *rrname,
+                   GList      *records,
+                   GError     *error)
+{
+  const gchar *hostname;
+  guint16 priority;
+  GList *t;
+
+  G_LOCK (response);
+  printf ("Domain: %s\n", rrname);
+  if (error)
+    {
+      printf ("Error: %s\n", error->message);
+      g_error_free (error);
+    }
+  else if (!records)
+    {
+      printf ("no MX records\n");
+    }
+  else
+    {
+      for (t = records; t; t = t->next)
+        {
+          g_variant_get (t->data, "(q&s)", &priority, &hostname);
+          printf ("%s (pri %u)\n", hostname, (guint)priority);
+          g_variant_unref (t->data);
+        }
+      g_list_free (records);
+    }
+  printf ("\n");
+
+  done_lookup ();
+  G_UNLOCK (response);
+}
+
+static void
+print_resolved_txt (const char *rrname,
+                    GList      *records,
+                    GError     *error)
+{
+  const gchar **contents;
+  GList *t;
+  gint i;
+
+  G_LOCK (response);
+  printf ("Domain: %s\n", rrname);
+  if (error)
+    {
+      printf ("Error: %s\n", error->message);
+      g_error_free (error);
+    }
+  else if (!records)
+    {
+      printf ("no TXT records\n");
+    }
+  else
+    {
+      for (t = records; t; t = t->next)
+        {
+          if (t != records)
+            printf ("\n");
+          g_variant_get (t->data, "(^a&s)", &contents);
+          for (i = 0; contents[i] != NULL; i++)
+            printf ("%s\n", contents[i]);
+          g_variant_unref (t->data);
+        }
+      g_list_free (records);
+    }
+  printf ("\n");
+
+  done_lookup ();
+  G_UNLOCK (response);
+}
+
+static void
+print_resolved_soa (const char *rrname,
+                    GList      *records,
+                    GError     *error)
+{
+  GList *t;
+  const gchar *primary_ns;
+  const gchar *administrator;
+  guint32 serial, refresh, retry, expire, ttl;
+
+  G_LOCK (response);
+  printf ("Zone: %s\n", rrname);
+  if (error)
+    {
+      printf ("Error: %s\n", error->message);
+      g_error_free (error);
+    }
+  else if (!records)
+    {
+      printf ("no SOA records\n");
+    }
+  else
+    {
+      for (t = records; t; t = t->next)
+        {
+          g_variant_get (t->data, "(&s&suuuuu)", &primary_ns, &administrator,
+                         &serial, &refresh, &retry, &expire, &ttl);
+          printf ("%s %s (serial %u, refresh %u, retry %u, expire %u, ttl %u)\n",
+                  primary_ns, administrator, (guint)serial, (guint)refresh,
+                  (guint)retry, (guint)expire, (guint)ttl);
+          g_variant_unref (t->data);
+        }
+      g_list_free (records);
+    }
+  printf ("\n");
+
+  done_lookup ();
+  G_UNLOCK (response);
+}
+
+static void
+print_resolved_ns (const char *rrname,
+                    GList      *records,
+                    GError     *error)
+{
+  GList *t;
+  const gchar *hostname;
+
+  G_LOCK (response);
+  printf ("Zone: %s\n", rrname);
+  if (error)
+    {
+      printf ("Error: %s\n", error->message);
+      g_error_free (error);
+    }
+  else if (!records)
+    {
+      printf ("no NS records\n");
+    }
+  else
+    {
+      for (t = records; t; t = t->next)
+        {
+          g_variant_get (t->data, "(&s)", &hostname);
+          printf ("%s\n", hostname);
+          g_variant_unref (t->data);
+        }
+      g_list_free (records);
+    }
+  printf ("\n");
+
+  done_lookup ();
+  G_UNLOCK (response);
+}
+
+static void
 lookup_one_sync (const char *arg)
 {
   GError *error = NULL;
 
-  if (strchr (arg, '/'))
+  if (record_type != 0)
+    {
+      GList *records;
+
+      records = g_resolver_lookup_records (resolver, arg, record_type, cancellable, &error);
+      switch (record_type)
+      {
+        case G_RESOLVER_RECORD_MX:
+          print_resolved_mx (arg, records, error);
+          break;
+        case G_RESOLVER_RECORD_SOA:
+          print_resolved_soa (arg, records, error);
+          break;
+        case G_RESOLVER_RECORD_NS:
+          print_resolved_ns (arg, records, error);
+          break;
+        case G_RESOLVER_RECORD_TXT:
+          print_resolved_txt (arg, records, error);
+          break;
+        default:
+          g_warn_if_reached ();
+          break;
+      }
+    }
+  else if (strchr (arg, '/'))
     {
       GList *targets;
       /* service/protocol/domain */
@@ -197,21 +377,16 @@ lookup_thread (gpointer arg)
 }
 
 static void
-start_threaded_lookups (char **argv, int argc)
-{
-  int i;
-
-  for (i = 0; i < argc; i++)
-    g_thread_create (lookup_thread, argv[i], FALSE, NULL);
-}
-
-static void
 start_sync_lookups (char **argv, int argc)
 {
   int i;
 
   for (i = 0; i < argc; i++)
-    lookup_one_sync (argv[i]);
+    {
+      GThread *thread;
+      thread = g_thread_new ("lookup", lookup_thread, argv[i]);
+      g_thread_unref (thread);
+    }
 }
 
 static void
@@ -251,13 +426,49 @@ lookup_service_callback (GObject *source, GAsyncResult *result,
 }
 
 static void
+lookup_records_callback (GObject      *source,
+                         GAsyncResult *result,
+                         gpointer      user_data)
+{
+  const char *arg = user_data;
+  GError *error = NULL;
+  GList *records;
+
+  records = g_resolver_lookup_records_finish (resolver, result, &error);
+
+  switch (record_type)
+  {
+    case G_RESOLVER_RECORD_MX:
+      print_resolved_mx (arg, records, error);
+      break;
+    case G_RESOLVER_RECORD_SOA:
+      print_resolved_soa (arg, records, error);
+      break;
+    case G_RESOLVER_RECORD_NS:
+      print_resolved_ns (arg, records, error);
+      break;
+    case G_RESOLVER_RECORD_TXT:
+      print_resolved_txt (arg, records, error);
+      break;
+    default:
+      g_warn_if_reached ();
+      break;
+  }
+}
+
+static void
 start_async_lookups (char **argv, int argc)
 {
   int i;
 
   for (i = 0; i < argc; i++)
     {
-      if (strchr (argv[i], '/'))
+      if (record_type != 0)
+       {
+         g_resolver_lookup_records_async (resolver, argv[i], record_type,
+                                          cancellable, lookup_records_callback, argv[i]);
+       }
+      else if (strchr (argv[i], '/'))
        {
          /* service/protocol/domain */
          char **parts = g_strsplit (argv[i], "/", 3);
@@ -284,6 +495,9 @@ start_async_lookups (char **argv, int argc)
                                            lookup_by_name_callback,
                                            argv[i]);
        }
+
+      /* Stress-test the reloading code */
+      g_signal_emit_by_name (resolver, "reload");
     }
 }
 
@@ -357,7 +571,7 @@ do_async_connectable (GSocketAddressEnumerator *enumerator)
 }
 
 static void
-do_connectable (const char *arg, gboolean synchronous)
+do_connectable (const char *arg, gboolean synchronous, guint count)
 {
   char **parts;
   GSocketConnectable *connectable;
@@ -397,13 +611,17 @@ do_connectable (const char *arg, gboolean synchronous)
         connectable = g_network_address_new (arg, port);
     }
 
-  enumerator = g_socket_connectable_enumerate (connectable);
-  g_object_unref (connectable);
+  while (count--)
+    {
+      enumerator = g_socket_connectable_enumerate (connectable);
 
-  if (synchronous)
-    do_sync_connectable (enumerator);
-  else
-    do_async_connectable (enumerator);
+      if (synchronous)
+        do_sync_connectable (enumerator);
+      else
+        do_async_connectable (enumerator);
+    }
+  
+  g_object_unref (connectable);
 }
 
 #ifdef G_OS_UNIX
@@ -412,8 +630,11 @@ static int cancel_fds[2];
 static void
 interrupted (int sig)
 {
+  gssize c;
+
   signal (SIGINT, SIG_DFL);
-  write (cancel_fds[1], "x", 1);
+  c = write (cancel_fds[1], "x", 1);
+  g_assert_cmpint(c, ==, 1);
 }
 
 static gboolean
@@ -424,39 +645,57 @@ async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
 }
 #endif
 
+
+static gboolean
+record_type_arg (const gchar *option_name,
+                 const gchar *value,
+                 gpointer data,
+                 GError **error)
+{
+  if (g_ascii_strcasecmp (value, "MX") == 0) {
+    record_type = G_RESOLVER_RECORD_MX;
+  } else if (g_ascii_strcasecmp (value, "TXT") == 0) {
+    record_type = G_RESOLVER_RECORD_TXT;
+  } else if (g_ascii_strcasecmp (value, "SOA") == 0) {
+    record_type = G_RESOLVER_RECORD_SOA;
+  } else if (g_ascii_strcasecmp (value, "NS") == 0) {
+    record_type = G_RESOLVER_RECORD_NS;
+  } else {
+      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+                   "Specify MX, TXT, NS or SOA for the special record lookup types");
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+static const GOptionEntry option_entries[] = {
+  { "synchronous", 's', 0, G_OPTION_ARG_NONE, &synchronous, "Synchronous connections", NULL },
+  { "connectable", 'c', 0, G_OPTION_ARG_INT, &connectable_count, "Connectable count", "C" },
+  { "special-type", 't', 0, G_OPTION_ARG_CALLBACK, record_type_arg, "Record type like MX, TXT, NS or SOA", "RR" },
+  { NULL },
+};
+
 int
 main (int argc, char **argv)
 {
-  gboolean threaded = FALSE, synchronous = FALSE;
-  gboolean use_connectable = FALSE;
+  GOptionContext *context;
+  GError *error = NULL;
 #ifdef G_OS_UNIX
   GIOChannel *chan;
   guint watch;
 #endif
 
-  /* We can't use GOptionContext because we use the arguments to
-   * decide whether or not to call g_thread_init().
-   */
-  while (argc >= 2 && argv[1][0] == '-')
+  context = g_option_context_new ("lookups ...");
+  g_option_context_add_main_entries (context, option_entries, NULL);
+  if (!g_option_context_parse (context, &argc, &argv, &error))
     {
-      if (!strcmp (argv[1], "-t"))
-        {
-          g_thread_init (NULL);
-          threaded = TRUE;
-        }
-      else if (!strcmp (argv[1], "-s"))
-        synchronous = TRUE;
-      else if (!strcmp (argv[1], "-c"))
-        use_connectable = TRUE;
-      else
-        usage ();
-
-      argv++;
-      argc--;
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+      usage();
     }
-  g_type_init ();
 
-  if (argc < 2 || (argc > 2 && use_connectable))
+  if (argc < 2 || (argc > 2 && connectable_count))
     usage ();
 
   resolver = g_resolver_get_default ();
@@ -482,13 +721,14 @@ main (int argc, char **argv)
   nlookups = argc - 1;
   loop = g_main_loop_new (NULL, TRUE);
 
-  if (use_connectable)
-    do_connectable (argv[1], synchronous);
+  if (connectable_count)
+    {
+      nlookups = connectable_count;
+      do_connectable (argv[1], synchronous, connectable_count);
+    }
   else
     {
-      if (threaded && synchronous)
-        start_threaded_lookups (argv + 1, argc - 1);
-      else if (synchronous)
+      if (synchronous)
         start_sync_lookups (argv + 1, argc - 1);
       else
         start_async_lookups (argv + 1, argc - 1);