GVariant test: add a new vector serialisation test
[platform/upstream/glib.git] / glib / tests / gvariant.c
index af648a7..177a603 100644 (file)
  * Author: Ryan Lortie <desrt@desrt.ca>
  */
 
+#include "config.h"
+
 #include <glib/gvariant-internal.h>
+#include <glib/glib-private.h>
 #include <string.h>
 #include <stdlib.h>
 #include <glib.h>
 #define INVALIDS "cefjklpwz&@^$"
 #define N_INVALIDS (G_N_ELEMENTS (INVALIDS) - 1)
 
+/* see comment in gvariant-serialiser.c about this madness.
+ *
+ * we use this to get testing of non-strictly-aligned GVariant instances
+ * on machines that can tolerate it.  it is necessary to support this
+ * because some systems have malloc() that returns non-8-aligned
+ * pointers.  it is necessary to have special support in the tests
+ * because on most machines malloc() is 8-aligned.
+ */
+#define ALIGN_BITS (sizeof (struct { char a; union {                       \
+                      guint64 x; void *y; gdouble z; } b; }) - 9)
+
 static gboolean
 randomly (gdouble prob)
 {
@@ -599,6 +613,7 @@ test_gvarianttype (void)
        */
       ctype = G_VARIANT_TYPE (type_string->str);
       g_assert (g_variant_type_equal (ctype, type));
+      g_assert (g_variant_type_hash (ctype) == g_variant_type_hash (type));
       g_assert (g_variant_type_is_subtype_of (ctype, type));
       g_assert (g_variant_type_is_subtype_of (type, ctype));
 
@@ -1138,7 +1153,7 @@ random_instance_write (RandomInstance *instance,
   GRand *rand;
   gint i;
 
-  g_assert_cmpint ((gsize) buffer & instance->alignment, ==, 0);
+  g_assert_cmpint ((gsize) buffer & ALIGN_BITS & instance->alignment, ==, 0);
 
   rand = g_rand_new_with_seed (instance->seed);
   for (i = 0; i < instance->size; i++)
@@ -1165,7 +1180,7 @@ random_instance_assert (RandomInstance *instance,
   GRand *rand;
   gint i;
 
-  g_assert_cmpint ((gsize) buffer & instance->alignment, ==, 0);
+  g_assert_cmpint ((gsize) buffer & ALIGN_BITS & instance->alignment, ==, 0);
   g_assert_cmpint (size, ==, instance->size);
 
   rand = g_rand_new_with_seed (instance->seed);
@@ -1188,7 +1203,7 @@ random_instance_check (RandomInstance *instance,
   GRand *rand;
   gint i;
 
-  g_assert_cmpint ((gsize) buffer & instance->alignment, ==, 0);
+  g_assert_cmpint ((gsize) buffer & ALIGN_BITS & instance->alignment, ==, 0);
 
   if (size != instance->size)
     return FALSE;
@@ -1251,13 +1266,41 @@ flavoured_malloc (gsize size, gsize flavour)
   if (size == 0)
     return NULL;
 
-  return g_malloc (size + flavour) + flavour;
+  return ((gchar *) g_malloc (size + flavour)) + flavour;
+}
+
+static void
+flavoured_free (gpointer data,
+                gsize flavour)
+{
+  if (!data)
+    return;
+  g_free (((gchar *) data) - flavour);
+}
+
+static gpointer
+align_malloc (gsize size)
+{
+  gpointer mem;
+
+#ifdef HAVE_POSIX_MEMALIGN
+  if (posix_memalign (&mem, 8, size))
+    g_error ("posix_memalign failed");
+#else
+  /* NOTE: there may be platforms that lack posix_memalign() and also
+   * have malloc() that returns non-8-aligned.  if so, we need to try
+   * harder here.
+   */
+  mem = malloc (size);
+#endif
+
+  return mem;
 }
 
 static void
-flavoured_free (gpointer data)
+align_free (gpointer mem)
 {
-  g_free ((gpointer) (((gsize) data) & ~7));
+  free (mem);
 }
 
 static void
@@ -1329,7 +1372,7 @@ test_maybe (void)
   {
     guchar *ptr;
 
-    ptr = data = g_malloc (needed_size);
+    ptr = data = align_malloc (needed_size);
     append_instance_data (instance, &ptr);
 
     if (!instance->is_fixed_sized)
@@ -1342,7 +1385,7 @@ test_maybe (void)
     guint alignment;
     guint flavour;
 
-    alignment = instance->alignment + 1;
+    alignment = (instance->alignment & ALIGN_BITS) + 1;
 
     for (flavour = 0; flavour < 8; flavour += alignment)
       {
@@ -1360,13 +1403,13 @@ test_maybe (void)
         g_assert (child.type_info == instance->type_info);
         random_instance_assert (instance, child.data, child.size);
         g_variant_type_info_unref (child.type_info);
-        flavoured_free (serialised.data);
+        flavoured_free (serialised.data, flavour);
       }
   }
 
   g_variant_type_info_unref (type_info);
   random_instance_free (instance);
-  g_free (data);
+  align_free (data);
 }
 
 static void
@@ -1446,7 +1489,7 @@ test_array (void)
     guchar *offset_ptr, *body_ptr;
     guint i;
 
-    body_ptr = data = g_malloc (needed_size);
+    body_ptr = data = align_malloc (needed_size);
     offset_ptr = body_ptr + needed_size - offset_size * n_children;
 
     for (i = 0; i < n_children; i++)
@@ -1465,7 +1508,7 @@ test_array (void)
     guint i;
 
     g_variant_type_info_query (array_info, &alignment, NULL);
-    alignment++;
+    alignment = (alignment & ALIGN_BITS) + 1;
 
     for (flavour = 0; flavour < 8; flavour += alignment)
       {
@@ -1478,7 +1521,9 @@ test_array (void)
         g_variant_serialiser_serialise (serialised, random_instance_filler,
                                         (gpointer *) instances, n_children);
 
-        g_assert (memcmp (serialised.data, data, serialised.size) == 0);
+        if (serialised.size)
+          g_assert (memcmp (serialised.data, data, serialised.size) == 0);
+
         g_assert (g_variant_serialised_n_children (serialised) == n_children);
 
         for (i = 0; i < n_children; i++)
@@ -1491,7 +1536,7 @@ test_array (void)
             g_variant_type_info_unref (child.type_info);
           }
 
-        flavoured_free (serialised.data);
+        flavoured_free (serialised.data, flavour);
       }
   }
 
@@ -1505,7 +1550,7 @@ test_array (void)
 
   g_variant_type_info_unref (element_info);
   g_variant_type_info_unref (array_info);
-  g_free (data);
+  align_free (data);
 }
 
 static void
@@ -1596,7 +1641,7 @@ test_tuple (void)
     guchar *ofs_ptr;
     guint i;
 
-    body_ptr = data = g_malloc (needed_size);
+    body_ptr = data = align_malloc (needed_size);
     ofs_ptr = body_ptr + needed_size;
 
     for (i = 0; i < n_children; i++)
@@ -1626,7 +1671,7 @@ test_tuple (void)
     gsize flavour;
     guint i;
 
-    alignment++;
+    alignment = (alignment & ALIGN_BITS) + 1;
 
     for (flavour = 0; flavour < 8; flavour += alignment)
       {
@@ -1639,7 +1684,9 @@ test_tuple (void)
         g_variant_serialiser_serialise (serialised, random_instance_filler,
                                         (gpointer *) instances, n_children);
 
-        g_assert (memcmp (serialised.data, data, serialised.size) == 0);
+        if (serialised.size)
+          g_assert (memcmp (serialised.data, data, serialised.size) == 0);
+
         g_assert (g_variant_serialised_n_children (serialised) == n_children);
 
         for (i = 0; i < n_children; i++)
@@ -1652,7 +1699,7 @@ test_tuple (void)
             g_variant_type_info_unref (child.type_info);
           }
 
-        flavoured_free (serialised.data);
+        flavoured_free (serialised.data, flavour);
       }
   }
 
@@ -1665,7 +1712,7 @@ test_tuple (void)
   }
 
   g_variant_type_info_unref (type_info);
-  g_free (data);
+  align_free (data);
 }
 
 static void
@@ -1704,7 +1751,7 @@ test_variant (void)
   {
     guchar *ptr;
 
-    ptr = data = g_malloc (needed_size);
+    ptr = data = align_malloc (needed_size);
     append_instance_data (instance, &ptr);
     *ptr++ = '\0';
     memcpy (ptr, type_string, len);
@@ -1714,31 +1761,41 @@ test_variant (void)
   }
 
   {
-    /* variants are 8-aligned, so no extra flavouring */
-    GVariantSerialised serialised;
-    GVariantSerialised child;
+    gsize alignment;
+    gsize flavour;
 
-    serialised.type_info = type_info;
-    serialised.data = flavoured_malloc (needed_size, 0);
-    serialised.size = needed_size;
+    /* variants are always 8-aligned */
+    alignment = ALIGN_BITS + 1;
 
-    g_variant_serialiser_serialise (serialised, random_instance_filler,
-                                    (gpointer *) &instance, 1);
+    for (flavour = 0; flavour < 8; flavour += alignment)
+      {
+        GVariantSerialised serialised;
+        GVariantSerialised child;
 
-    g_assert (memcmp (serialised.data, data, serialised.size) == 0);
-    g_assert (g_variant_serialised_n_children (serialised) == 1);
+        serialised.type_info = type_info;
+        serialised.data = flavoured_malloc (needed_size, flavour);
+        serialised.size = needed_size;
+
+        g_variant_serialiser_serialise (serialised, random_instance_filler,
+                                        (gpointer *) &instance, 1);
 
-    child = g_variant_serialised_get_child (serialised, 0);
-    g_assert (child.type_info == instance->type_info);
-    random_instance_check (instance, child.data, child.size);
+        if (serialised.size)
+          g_assert (memcmp (serialised.data, data, serialised.size) == 0);
 
-    g_variant_type_info_unref (child.type_info);
-    flavoured_free (serialised.data);
+        g_assert (g_variant_serialised_n_children (serialised) == 1);
+
+        child = g_variant_serialised_get_child (serialised, 0);
+        g_assert (child.type_info == instance->type_info);
+        random_instance_check (instance, child.data, child.size);
+
+        g_variant_type_info_unref (child.type_info);
+        flavoured_free (serialised.data, flavour);
+      }
   }
 
   g_variant_type_info_unref (type_info);
   random_instance_free (instance);
-  g_free (data);
+  align_free (data);
 }
 
 static void
@@ -1766,10 +1823,12 @@ test_strings (void)
 #define is_sig            is_string | 4
     { is_sig,       1, "" },
     { is_nval,      0, NULL },
+    { is_nval,     13, "hello\xffworld!" },
     { is_string,   13, "hello world!" },
     { is_nval,     13, "hello world\0" },
     { is_nval,     13, "hello\0world!" },
     { is_nval,     12, "hello world!" },
+    { is_nval,     13, "hello world!\xff" },
 
     { is_objpath,   2, "/" },
     { is_objpath,   3, "/a" },
@@ -1827,7 +1886,7 @@ struct _TreeInstance
   union {
     guint64 integer;
     gdouble floating;
-    gchar string[32];
+    gchar string[200];
   } data;
   gsize data_size;
 };
@@ -1946,7 +2005,7 @@ tree_instance_new (const GVariantType *type,
       break;
 
     case 's': case 'o': case 'g':
-      instance->data_size = g_test_rand_int_range (10, 20);
+      instance->data_size = g_test_rand_int_range (10, 200);
       make_random_string (instance->data.string, instance->data_size, type);
       break;
     }
@@ -2143,7 +2202,7 @@ static void
 serialise_tree (TreeInstance       *tree,
                 GVariantSerialised *serialised)
 {
-  GVariantSerialised empty = {  };
+  GVariantSerialised empty = {0, };
 
   *serialised = empty;
   tree_filler (serialised, tree);
@@ -2401,6 +2460,19 @@ tree_instance_get_gvariant (TreeInstance *tree)
   return result;
 }
 
+static GVariant *
+create_random_gvariant (guint depth)
+{
+  TreeInstance *tree;
+  GVariant *value;
+
+  tree = tree_instance_new (NULL, depth);
+  value = g_variant_take_ref (tree_instance_get_gvariant (tree));
+  tree_instance_free (tree);
+
+  return value;
+}
+
 static gboolean
 tree_instance_check_gvariant (TreeInstance *tree,
                               GVariant     *value)
@@ -2665,6 +2737,57 @@ test_container (void)
 }
 
 static void
+test_string (void)
+{
+  /* Test some different methods of creating strings */
+  GVariant *v;
+
+  v = g_variant_new_string ("foo");
+  g_assert_cmpstr (g_variant_get_string (v, NULL), ==, "foo");
+  g_variant_unref (v);
+
+
+  v = g_variant_new_take_string (g_strdup ("foo"));
+  g_assert_cmpstr (g_variant_get_string (v, NULL), ==, "foo");
+  g_variant_unref (v);
+
+  v = g_variant_new_printf ("%s %d", "foo", 123);
+  g_assert_cmpstr (g_variant_get_string (v, NULL), ==, "foo 123");
+  g_variant_unref (v);
+}
+
+static void
+test_utf8 (void)
+{
+  const gchar invalid[] = "hello\xffworld";
+  GVariant *value;
+
+  /* ensure that the test data is not valid utf8... */
+  g_assert (!g_utf8_validate (invalid, -1, NULL));
+
+  /* load the data untrusted */
+  value = g_variant_new_from_data (G_VARIANT_TYPE_STRING,
+                                   invalid, sizeof invalid,
+                                   FALSE, NULL, NULL);
+
+  /* ensure that the problem is caught and we get valid UTF-8 */
+  g_assert (g_utf8_validate (g_variant_get_string (value, NULL), -1, NULL));
+  g_variant_unref (value);
+
+
+  /* now load it trusted */
+  value = g_variant_new_from_data (G_VARIANT_TYPE_STRING,
+                                   invalid, sizeof invalid,
+                                   TRUE, NULL, NULL);
+
+  /* ensure we get the invalid data (ie: make sure that time wasn't
+   * wasted on validating data that was marked as trusted)
+   */
+  g_assert (g_variant_get_string (value, NULL) == invalid);
+  g_variant_unref (value);
+}
+
+static void
 test_containers (void)
 {
   gint i;
@@ -2699,6 +2822,13 @@ test_format_strings (void)
             *end == '\0');
   g_assert (g_variant_format_string_scan ("{yv}", NULL, &end) &&
             *end == '\0');
+  g_assert (!g_variant_format_string_scan ("{&?v}", NULL, &end));
+  g_assert (g_variant_format_string_scan ("{@?v}", NULL, &end) &&
+            *end == '\0');
+  g_assert (!g_variant_format_string_scan ("{&@sv}", NULL, &end));
+  g_assert (!g_variant_format_string_scan ("{@&sv}", NULL, &end));
+  g_assert (g_variant_format_string_scan ("{&sv}", NULL, &end) &&
+            *end == '\0');
   g_assert (!g_variant_format_string_scan ("{vv}", NULL, &end));
   g_assert (!g_variant_format_string_scan ("{y}", NULL, &end));
   g_assert (!g_variant_format_string_scan ("{yyy}", NULL, &end));
@@ -2719,48 +2849,47 @@ test_format_strings (void)
 }
 
 static void
-exit_on_abort (int signal)
+do_failed_test (const char *test,
+                const gchar *pattern)
 {
-  exit (signal);
-}
-
-static gboolean
-do_failed_test (const gchar *pattern)
-{
-  if (g_test_trap_fork (1000000, G_TEST_TRAP_SILENCE_STDERR))
-    {
-      signal (SIGABRT, exit_on_abort);
-      return TRUE;
-    }
-
+  g_test_trap_subprocess (test, 1000000, 0);
   g_test_trap_assert_failed ();
   g_test_trap_assert_stderr (pattern);
-
-  return FALSE;
 }
 
 static void
 test_invalid_varargs (void)
 {
-  if (do_failed_test ("*not a valid GVariant format string*"))
-    {
-      g_variant_new ("z");
-      abort ();
-    }
-
-  if (do_failed_test ("*valid GVariant format string as a prefix*"))
-    {
-      const gchar *end;
-
-      g_variant_new_va ("z", &end, NULL);
-      abort ();
-    }
+  GVariant *value;
+  const gchar *end;
 
-  if (do_failed_test ("*type of `q' but * has a type of `y'*"))
-    {
-      g_variant_get (g_variant_new ("y", 'a'), "q");
-      abort ();
-    }
+  if (!g_test_undefined ())
+    return;
+
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                         "*GVariant format string*");
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                         "*valid_format_string*");
+  value = g_variant_new ("z");
+  g_test_assert_expected_messages ();
+  g_assert (value == NULL);
+
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                         "*valid GVariant format string as a prefix*");
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                         "*valid_format_string*");
+  value = g_variant_new_va ("z", &end, NULL);
+  g_test_assert_expected_messages ();
+  g_assert (value == NULL);
+
+  value = g_variant_new ("y", 'a');
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                         "*type of 'q' but * has a type of 'y'*");
+  g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                         "*valid_format_string*");
+  g_variant_get (value, "q");
+  g_test_assert_expected_messages ();
+  g_variant_unref (value);
 }
 
 static void
@@ -2774,14 +2903,21 @@ check_and_free (GVariant    *value,
 }
 
 static void
+test_varargs_empty_array (void)
+{
+  g_variant_new ("(a{s*})", NULL);
+
+  g_assert_not_reached ();
+}
+
+static void
 test_varargs (void)
 {
   {
     GVariantBuilder array;
 
     g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
-    g_variant_builder_add (&array, "{sv}", "size",
-                           g_variant_new ("(ii)", 800, 600));
+    g_variant_builder_add_parsed (&array, "{'size', <(%i, %i)> }", 800, 600);
     g_variant_builder_add (&array, "{sv}", "title",
                            g_variant_new_string ("Test case"));
     g_variant_builder_add_value (&array,
@@ -2790,7 +2926,7 @@ test_varargs (void)
                                   g_variant_new_double (37.5))));
     check_and_free (g_variant_new ("(ma{sv}m(a{sv})ma{sv}ii)",
                                    NULL, FALSE, NULL, &array, 7777, 8888),
-                    "(Nothing, Nothing, {'size': <(800, 600)>, "
+                    "(nothing, nothing, {'size': <(800, 600)>, "
                                         "'title': <'Test case'>, "
                                         "'temperature': <37.5>}, "
                      "7777, 8888)");
@@ -2802,7 +2938,7 @@ test_varargs (void)
                                    FALSE, TRUE, 321,
                                    TRUE, FALSE, 321,
                                    TRUE, TRUE, 123),
-                    "(123, Nothing, 123, Nothing, Just Nothing, 123)");
+                    "(123, nothing, 123, nothing, just nothing, 123)");
 
     check_and_free (g_variant_new ("(ybnixd)",
                                    'a', 1, 22, 33, (guint64) 44, 5.5),
@@ -2893,7 +3029,60 @@ test_varargs (void)
     gchar *str;
     gint i;
 
-    g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+    g_variant_builder_add (&builder, "s", "/foo");
+    g_variant_builder_add (&builder, "s", "/bar");
+    g_variant_builder_add (&builder, "s", "/baz");
+    value = g_variant_new("(as^as^a&s)", &builder, strvector, strvector);
+    g_variant_iter_init (&tuple, value);
+    g_variant_iter_next (&tuple, "as", &array);
+
+    i = 0;
+    while (g_variant_iter_loop (array, "s", &str))
+      g_assert_cmpstr (str, ==, test_strs[i++]);
+    g_assert (i == 3);
+
+    g_variant_iter_free (array);
+
+    /* start over */
+    g_variant_iter_init (&tuple, value);
+    g_variant_iter_next (&tuple, "as", &array);
+
+    i = 0;
+    while (g_variant_iter_loop (array, "&s", &str))
+      g_assert_cmpstr (str, ==, test_strs[i++]);
+    g_assert (i == 3);
+
+    g_variant_iter_free (array);
+
+    g_variant_iter_next (&tuple, "^a&s", &strv);
+    g_variant_iter_next (&tuple, "^as", &my_strv);
+
+    g_assert_cmpstr (strv[0], ==, "/hello");
+    g_assert_cmpstr (strv[1], ==, "/world");
+    g_assert (strv[2] == NULL);
+    g_assert_cmpstr (my_strv[0], ==, "/hello");
+    g_assert_cmpstr (my_strv[1], ==, "/world");
+    g_assert (my_strv[2] == NULL);
+
+    g_variant_unref (value);
+    g_strfreev (my_strv);
+    g_free (strv);
+  }
+
+  {
+    const gchar *strvector[] = {"/hello", "/world", NULL};
+    const gchar *test_strs[] = {"/foo", "/bar", "/baz" };
+    GVariantBuilder builder;
+    GVariantIter *array;
+    GVariantIter tuple;
+    const gchar **strv;
+    gchar **my_strv;
+    GVariant *value;
+    gchar *str;
+    gint i;
+
+    g_variant_builder_init (&builder, G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
     g_variant_builder_add (&builder, "o", "/foo");
     g_variant_builder_add (&builder, "o", "/bar");
     g_variant_builder_add (&builder, "o", "/baz");
@@ -2945,61 +3134,59 @@ test_varargs (void)
     gchar **strv;
     gint i;
 
-    g_variant_builder_init (&builder, G_VARIANT_TYPE ("aag"));
-    g_variant_builder_open (&builder, G_VARIANT_TYPE ("ag"));
+    g_variant_builder_init (&builder, G_VARIANT_TYPE ("aas"));
+    g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
     for (i = 0; i < 6; i++)
       if (i & 1)
-        g_variant_builder_add (&builder, "g", strvector[i]);
+        g_variant_builder_add (&builder, "s", strvector[i]);
       else
-        g_variant_builder_add (&builder, "&g", strvector[i]);
+        g_variant_builder_add (&builder, "&s", strvector[i]);
     g_variant_builder_close (&builder);
-    g_variant_builder_add (&builder, "^ag", strvector);
-    g_variant_builder_add (&builder, "^ag", strvector);
-    value = g_variant_new ("aag", &builder);
+    g_variant_builder_add (&builder, "^as", strvector);
+    g_variant_builder_add (&builder, "^as", strvector);
+    value = g_variant_new ("aas", &builder);
 
     g_variant_iter_init (&iter, value);
-    while (g_variant_iter_loop (&iter, "^ag", &strv))
+    while (g_variant_iter_loop (&iter, "^as", &strv))
       for (i = 0; i < 6; i++)
         g_assert_cmpstr (strv[i], ==, strvector[i]);
 
     g_variant_iter_init (&iter, value);
-    while (g_variant_iter_loop (&iter, "^a&g", &strv))
+    while (g_variant_iter_loop (&iter, "^a&s", &strv))
       for (i = 0; i < 6; i++)
         g_assert_cmpstr (strv[i], ==, strvector[i]);
 
     g_variant_iter_init (&iter, value);
-    while (g_variant_iter_loop (&iter, "ag", &i2))
+    while (g_variant_iter_loop (&iter, "as", &i2))
       {
         gchar *str;
 
         i = 0;
-        while (g_variant_iter_loop (i2, "g", &str))
+        while (g_variant_iter_loop (i2, "s", &str))
           g_assert_cmpstr (str, ==, strvector[i++]);
         g_assert (i == 6);
       }
 
     g_variant_iter_init (&iter, value);
     i3 = g_variant_iter_copy (&iter);
-    while (g_variant_iter_loop (&iter, "@ag", &sub))
+    while (g_variant_iter_loop (&iter, "@as", &sub))
       {
         gchar *str = g_variant_print (sub, TRUE);
         g_assert_cmpstr (str, ==,
-                         "[signature 'i', 'ii', 'iii', 'iv', 'v', 'vi']");
+                         "['i', 'ii', 'iii', 'iv', 'v', 'vi']");
         g_free (str);
       }
 
-  if (do_failed_test ("*NULL has already been returned*"))
-    {
-      g_variant_iter_next_value (&iter);
-      abort ();
-    }
-
+    g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL,
+                           "*NULL has already been returned*");
+    g_variant_iter_next_value (&iter);
+    g_test_assert_expected_messages ();
 
     while (g_variant_iter_loop (i3, "*", &sub))
       {
         gchar *str = g_variant_print (sub, TRUE);
         g_assert_cmpstr (str, ==,
-                         "[signature 'i', 'ii', 'iii', 'iv', 'v', 'vi']");
+                         "['i', 'ii', 'iii', 'iv', 'v', 'vi']");
         g_free (str);
       }
 
@@ -3016,11 +3203,11 @@ test_varargs (void)
             const gchar *str = NULL;
             GVariant *cval;
 
-            g_variant_get_child (sub, j, "&g", &str);
+            g_variant_get_child (sub, j, "&s", &str);
             g_assert_cmpstr (str, ==, strvector[j]);
 
             cval = g_variant_get_child_value (sub, j);
-            g_variant_get (cval, "&g", &str);
+            g_variant_get (cval, "&s", &str);
             g_assert_cmpstr (str, ==, strvector[j]);
             g_variant_unref (cval);
           }
@@ -3047,7 +3234,7 @@ test_varargs (void)
     gdouble dval;
     gint32 hval;
 
-    /* test all 'Nothing' */
+    /* test all 'nothing' */
     value = g_variant_new ("(mymbmnmqmimumxmtmhmdmv)",
                            FALSE, 'a',
                            FALSE, TRUE,
@@ -3144,7 +3331,7 @@ test_varargs (void)
     g_variant_unref (value);
 
 
-    /* test all 'Just' */
+    /* test all 'just' */
     value = g_variant_new ("(mymbmnmqmimumxmtmhmdmv)",
                            TRUE, 'a',
                            TRUE, TRUE,
@@ -3243,6 +3430,22 @@ test_varargs (void)
     g_variant_unref (value);
   }
 
+  {
+    GVariant *value;
+    gchar *str;
+
+    value = g_variant_new ("(masas)", NULL, NULL);
+    g_variant_ref_sink (value);
+
+    str = g_variant_print (value, TRUE);
+    g_assert_cmpstr (str, ==, "(@mas nothing, @as [])");
+    g_variant_unref (value);
+    g_free (str);
+
+    do_failed_test ("/gvariant/varargs/subprocess/empty-array",
+                    "*which type of empty array*");
+  }
+
   g_variant_type_info_assert_no_infos ();
 }
 
@@ -3341,8 +3544,7 @@ test_builder_memory (void)
 static void
 test_hashing (void)
 {
-  const gint n_items = 4096;
-  GVariant *items[n_items];
+  GVariant *items[4096];
   GHashTable *table;
   gint i;
 
@@ -3350,7 +3552,7 @@ test_hashing (void)
                                  (GDestroyNotify ) g_variant_unref,
                                  NULL);
 
-  for (i = 0; i < n_items; i++)
+  for (i = 0; i < G_N_ELEMENTS (items); i++)
     {
       TreeInstance *tree;
       gint j;
@@ -3372,7 +3574,7 @@ test_hashing (void)
                            GINT_TO_POINTER (i));
     }
 
-  for (i = 0; i < n_items; i++)
+  for (i = 0; i < G_N_ELEMENTS (items); i++)
     {
       gpointer result;
 
@@ -3385,6 +3587,1025 @@ test_hashing (void)
   g_variant_type_info_assert_no_infos ();
 }
 
+static void
+test_gv_byteswap (void)
+{
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+# define native16(x)  x, 0
+# define swapped16(x) 0, x
+#else
+# define native16(x)  0, x
+# define swapped16(x) x, 0
+#endif
+  /* all kinds of of crazy randomised testing already performed on the
+   * byteswapper in the /gvariant/serialiser/byteswap test and all kinds
+   * of crazy randomised testing performed against the serialiser
+   * normalisation functions in the /gvariant/serialiser/fuzz/ tests.
+   *
+   * just test a few simple cases here to make sure they each work
+   */
+  guchar validbytes[] = { 'a', '\0', swapped16(66), 2,
+                          0,
+                          'b', '\0', swapped16(77), 2,
+                          5, 11 };
+  guchar corruptbytes[] = { 'a', '\0', swapped16(66), 2,
+                            0,
+                            'b', '\0', swapped16(77), 2,
+                            6, 11 };
+  guint valid_data[4], corrupt_data[4];
+  GVariant *value, *swapped;
+  gchar *string, *string2;
+
+  memcpy (valid_data, validbytes, sizeof validbytes);
+  memcpy (corrupt_data, corruptbytes, sizeof corruptbytes);
+
+  /* trusted */
+  value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+                                   valid_data, sizeof validbytes, TRUE,
+                                   NULL, NULL);
+  swapped = g_variant_byteswap (value);
+  g_variant_unref (value);
+  g_assert (g_variant_get_size (swapped) == 13);
+  string = g_variant_print (swapped, FALSE);
+  g_variant_unref (swapped);
+  g_assert_cmpstr (string, ==, "[('a', 66), ('b', 77)]");
+  g_free (string);
+
+  /* untrusted but valid */
+  value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+                                   valid_data, sizeof validbytes, FALSE,
+                                   NULL, NULL);
+  swapped = g_variant_byteswap (value);
+  g_variant_unref (value);
+  g_assert (g_variant_get_size (swapped) == 13);
+  string = g_variant_print (swapped, FALSE);
+  g_variant_unref (swapped);
+  g_assert_cmpstr (string, ==, "[('a', 66), ('b', 77)]");
+  g_free (string);
+
+  /* untrusted, invalid */
+  value = g_variant_new_from_data (G_VARIANT_TYPE ("a(sn)"),
+                                   corrupt_data, sizeof corruptbytes, FALSE,
+                                   NULL, NULL);
+  string = g_variant_print (value, FALSE);
+  swapped = g_variant_byteswap (value);
+  g_variant_unref (value);
+  g_assert (g_variant_get_size (swapped) == 13);
+  value = g_variant_byteswap (swapped);
+  g_variant_unref (swapped);
+  string2 = g_variant_print (value, FALSE);
+  g_assert (g_variant_get_size (value) == 13);
+  g_variant_unref (value);
+  g_assert_cmpstr (string, ==, string2);
+  g_free (string2);
+  g_free (string);
+}
+
+static void
+test_parser (void)
+{
+  TreeInstance *tree;
+  GVariant *parsed;
+  GVariant *value;
+  gchar *pt, *p;
+  gchar *res;
+
+  tree = tree_instance_new (NULL, 3);
+  value = tree_instance_get_gvariant (tree);
+  tree_instance_free (tree);
+
+  pt = g_variant_print (value, TRUE);
+  p = g_variant_print (value, FALSE);
+
+  parsed = g_variant_parse (NULL, pt, NULL, NULL, NULL);
+  res = g_variant_print (parsed, FALSE);
+  g_assert_cmpstr (p, ==, res);
+  g_variant_unref (parsed);
+  g_free (res);
+
+  parsed = g_variant_parse (g_variant_get_type (value), p,
+                            NULL, NULL, NULL);
+  res = g_variant_print (parsed, TRUE);
+  g_assert_cmpstr (pt, ==, res);
+  g_variant_unref (parsed);
+  g_free (res);
+
+  g_variant_unref (value);
+  g_free (pt);
+  g_free (p);
+}
+
+static void
+test_parses (void)
+{
+  gint i;
+
+  for (i = 0; i < 100; i++)
+    {
+      test_parser ();
+    }
+
+  /* mini test */
+  {
+    GError *error = NULL;
+    gchar str[128];
+    GVariant *val;
+    gchar *p, *p2;
+
+    for (i = 0; i < 127; i++)
+      str[i] = i + 1;
+    str[i] = 0;
+
+    val = g_variant_new_string (str);
+    p = g_variant_print (val, FALSE);
+    g_variant_unref (val);
+
+    val = g_variant_parse (NULL, p, NULL, NULL, &error);
+    p2 = g_variant_print (val, FALSE);
+
+    g_assert_cmpstr (str, ==, g_variant_get_string (val, NULL));
+    g_assert_cmpstr (p, ==, p2);
+
+    g_variant_unref (val);
+    g_free (p2);
+    g_free (p);
+  }
+
+  /* another mini test */
+  {
+    const gchar *end;
+    GVariant *value;
+
+    value = g_variant_parse (G_VARIANT_TYPE_INT32, "1 2 3", NULL, &end, NULL);
+    g_assert_cmpint (g_variant_get_int32 (value), ==, 1);
+    /* make sure endptr returning works */
+    g_assert_cmpstr (end, ==, " 2 3");
+    g_variant_unref (value);
+  }
+
+  /* unicode mini test */
+  {
+    /* ał𝄞 */
+    const gchar orig[] = "a\xc5\x82\xf0\x9d\x84\x9e \t\n";
+    GVariant *value;
+    gchar *printed;
+
+    value = g_variant_new_string (orig);
+    printed = g_variant_print (value, FALSE);
+    g_variant_unref (value);
+
+    g_assert_cmpstr (printed, ==, "'a\xc5\x82\xf0\x9d\x84\x9e \\t\\n'");
+    value = g_variant_parse (NULL, printed, NULL, NULL, NULL);
+    g_assert_cmpstr (g_variant_get_string (value, NULL), ==, orig);
+    g_variant_unref (value);
+    g_free (printed);
+  }
+
+  /* escapes */
+  {
+    const gchar orig[] = " \342\200\254 \360\220\210\240 \a \b \f \n \r \t \v ";
+    GVariant *value;
+    gchar *printed;
+
+    value = g_variant_new_string (orig);
+    printed = g_variant_print (value, FALSE);
+    g_variant_unref (value);
+
+    g_assert_cmpstr (printed, ==, "' \\u202c \\U00010220 \\a \\b \\f \\n \\r \\t \\v '");
+    value = g_variant_parse (NULL, printed, NULL, NULL, NULL);
+    g_assert_cmpstr (g_variant_get_string (value, NULL), ==, orig);
+    g_variant_unref (value);
+    g_free (printed);
+  }
+
+#ifndef _MSC_VER
+  /* inf/nan strings are C99 features which Visual C++ does not support */
+  /* inf/nan mini test */
+  {
+    const gchar *tests[] = { "inf", "-inf", "nan" };
+    GVariant *value;
+    gchar *printed;
+    gchar *printed_down;
+    gint i;
+
+    for (i = 0; i < G_N_ELEMENTS (tests); i++)
+      {
+        GError *error = NULL;
+        value = g_variant_parse (NULL, tests[i], NULL, NULL, &error);
+        printed = g_variant_print (value, FALSE);
+        /* Canonicalize to lowercase; https://bugzilla.gnome.org/show_bug.cgi?id=704585 */
+        printed_down = g_ascii_strdown (printed, -1);
+        g_assert (g_str_has_prefix (printed_down, tests[i]));
+        g_free (printed);
+        g_free (printed_down);
+        g_variant_unref (value);
+      }
+  }
+#endif
+
+  g_variant_type_info_assert_no_infos ();
+}
+
+static void
+test_parse_failures (void)
+{
+  const gchar *test[] = {
+    "[1, 2,",                   "6:",              "expected value",
+    "",                         "0:",              "expected value",
+    "(1, 2,",                   "6:",              "expected value",
+    "<1",                       "2:",              "expected '>'",
+    "[]",                       "0-2:",            "unable to infer",
+    "(,",                       "1:",              "expected value",
+    "[4,'']",                   "1-2,3-5:",        "common type",
+    "[4, '', 5]",               "1-2,4-6:",        "common type",
+    "['', 4, 5]",               "1-3,5-6:",        "common type",
+    "[4, 5, '']",               "1-2,7-9:",        "common type",
+    "[[4], [], ['']]",          "1-4,10-14:",      "common type",
+    "[[], [4], ['']]",          "5-8,10-14:",      "common type",
+    "just",                     "4:",              "expected value",
+    "nothing",                  "0-7:",            "unable to infer",
+    "just [4, '']",             "6-7,9-11:",       "common type",
+    "[[4,'']]",                 "2-3,4-6:",        "common type",
+    "([4,''],)",                "2-3,4-6:",        "common type",
+    "(4)",                      "2:",              "','",
+    "{}",                       "0-2:",            "unable to infer",
+    "{[1,2],[3,4]}",            "0-13:",           "basic types",
+    "{[1,2]:[3,4]}",            "0-13:",           "basic types",
+    "justt",                    "0-5:",            "unknown keyword",
+    "nothng",                   "0-6:",            "unknown keyword",
+    "uint33",                   "0-6:",            "unknown keyword",
+    "@mi just ''",              "9-11:",           "can not parse as",
+    "@ai ['']",                 "5-7:",            "can not parse as",
+    "@(i) ('',)",               "6-8:",            "can not parse as",
+    "[[], 5]",                  "1-3,5-6:",        "common type",
+    "[[5], 5]",                 "1-4,6-7:",        "common type",
+    "5 5",                      "2:",              "expected end of input",
+    "[5, [5, '']]",             "5-6,8-10:",       "common type",
+    "@i just 5",                "3-9:",            "can not parse as",
+    "@i nothing",               "3-10:",           "can not parse as",
+    "@i []",                    "3-5:",            "can not parse as",
+    "@i ()",                    "3-5:",            "can not parse as",
+    "@ai (4,)",                 "4-8:",            "can not parse as",
+    "@(i) []",                  "5-7:",            "can not parse as",
+    "(5 5)",                    "3:",              "expected ','",
+    "[5 5]",                    "3:",              "expected ',' or ']'",
+    "(5, 5 5)",                 "6:",              "expected ',' or ')'",
+    "[5, 5 5]",                 "6:",              "expected ',' or ']'",
+    "<@i []>",                  "4-6:",            "can not parse as",
+    "<[5 5]>",                  "4:",              "expected ',' or ']'",
+    "{[4,''],5}",               "2-3,4-6:",        "common type",
+    "{5,[4,'']}",               "4-5,6-8:",        "common type",
+    "@i {1,2}",                 "3-8:",            "can not parse as",
+    "{@i '', 5}",               "4-6:",            "can not parse as",
+    "{5, @i ''}",               "7-9:",            "can not parse as",
+    "@ai {}",                   "4-6:",            "can not parse as",
+    "{@i '': 5}",               "4-6:",            "can not parse as",
+    "{5: @i ''}",               "7-9:",            "can not parse as",
+    "{<4,5}",                   "3:",              "expected '>'",
+    "{4,<5}",                   "5:",              "expected '>'",
+    "{4,5,6}",                  "4:",              "expected '}'",
+    "{5 5}",                    "3:",              "expected ':' or ','",
+    "{4: 5: 6}",                "5:",              "expected ',' or '}'",
+    "{4:5,<6:7}",               "7:",              "expected '>'",
+    "{4:5,6:<7}",               "9:",              "expected '>'",
+    "{4:5,6 7}",                "7:",              "expected ':'",
+    "@o 'foo'",                 "3-8:",            "object path",
+    "@g 'zzz'",                 "3-8:",            "signature",
+    "@i true",                  "3-7:",            "can not parse as",
+    "@z 4",                     "0-2:",            "invalid type",
+    "@a* []",                   "0-3:",            "definite",
+    "@ai [3 3]",                "7:",              "expected ',' or ']'",
+    "18446744073709551616",     "0-20:",           "too big for any type",
+    "-18446744073709551616",    "0-21:",           "too big for any type",
+    "byte 256",                 "5-8:",            "out of range for type",
+    "byte -1",                  "5-7:",            "out of range for type",
+    "int16 32768",              "6-11:",           "out of range for type",
+    "int16 -32769",             "6-12:",           "out of range for type",
+    "uint16 -1",                "7-9:",            "out of range for type",
+    "uint16 65536",             "7-12:",           "out of range for type",
+    "2147483648",               "0-10:",           "out of range for type",
+    "-2147483649",              "0-11:",           "out of range for type",
+    "uint32 -1",                "7-9:",            "out of range for type",
+    "uint32 4294967296",        "7-17:",           "out of range for type",
+    "@x 9223372036854775808",   "3-22:",           "out of range for type",
+    "@x -9223372036854775809",  "3-23:",           "out of range for type",
+    "@t -1",                    "3-5:",            "out of range for type",
+    "@t 18446744073709551616",  "3-23:",           "too big for any type",
+    "handle 2147483648",        "7-17:",           "out of range for type",
+    "handle -2147483649",       "7-18:",           "out of range for type",
+    "1.798e308",                "0-9:",            "too big for any type",
+    "37.5a488",                 "4-5:",            "invalid character",
+    "0x7ffgf",                  "5-6:",            "invalid character",
+    "07758",                    "4-5:",            "invalid character",
+    "123a5",                    "3-4:",            "invalid character",
+    "@ai 123",                  "4-7:",            "can not parse as",
+    "'\"\\'",                   "0-4:",            "unterminated string",
+    "'\"\\'\\",                 "0-5:",            "unterminated string",
+    "boolean 4",                "8-9:",            "can not parse as",
+    "int32 true",               "6-10:",           "can not parse as",
+    "[double 5, int32 5]",      "1-9,11-18:",      "common type",
+    "string 4",                 "7-8:",            "can not parse as"
+  };
+  gint i;
+
+  for (i = 0; i < G_N_ELEMENTS (test); i += 3)
+    {
+      GError *error = NULL;
+      GVariant *value;
+
+      value = g_variant_parse (NULL, test[i], NULL, NULL, &error);
+      g_assert (value == NULL);
+
+      if (!strstr (error->message, test[i+2]))
+        g_error ("test %d: Can't find '%s' in '%s'", i / 3,
+                 test[i+2], error->message);
+
+      if (!g_str_has_prefix (error->message, test[i+1]))
+        g_error ("test %d: Expected location '%s' in '%s'", i / 3,
+                 test[i+1], error->message);
+
+      g_error_free (error);
+    }
+}
+
+static void
+test_parse_bad_format_char (void)
+{
+  g_variant_new_parsed ("%z");
+
+  g_assert_not_reached ();
+}
+
+static void
+test_parse_bad_format_string (void)
+{
+  g_variant_new_parsed ("uint32 %i", 2);
+
+  g_assert_not_reached ();
+}
+
+static void
+test_parse_bad_args (void)
+{
+  g_variant_new_parsed ("%@i", g_variant_new_uint32 (2));
+
+  g_assert_not_reached ();
+}
+
+static void
+test_parse_positional (void)
+{
+  GVariant *value;
+  check_and_free (g_variant_new_parsed ("[('one', 1), (%s, 2),"
+                                        " ('three', %i)]", "two", 3),
+                  "[('one', 1), ('two', 2), ('three', 3)]");
+  value = g_variant_new_parsed ("[('one', 1), (%s, 2),"
+                                " ('three', %u)]", "two", 3);
+  g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("a(su)")));
+  check_and_free (value, "[('one', 1), ('two', 2), ('three', 3)]");
+  check_and_free (g_variant_new_parsed ("{%s:%i}", "one", 1), "{'one': 1}");
+
+  if (g_test_undefined ())
+    {
+      do_failed_test ("/gvariant/parse/subprocess/bad-format-char",
+                      "*GVariant format string*");
+
+      do_failed_test ("/gvariant/parse/subprocess/bad-format-string",
+                      "*can not parse as*");
+
+      do_failed_test ("/gvariant/parse/subprocess/bad-args",
+                      "*expected GVariant of type 'i'*");
+    }
+}
+
+static void
+test_floating (void)
+{
+  GVariant *value;
+
+  value = g_variant_new_int32 (42);
+  g_assert (g_variant_is_floating (value));
+  g_variant_ref_sink (value);
+  g_assert (!g_variant_is_floating (value));
+  g_variant_unref (value);
+}
+
+static void
+test_bytestring (void)
+{
+  const gchar *test_string = "foo,bar,baz,quux,\xffoooo";
+  GVariant *value;
+  gchar **strv;
+  gchar *str;
+  const gchar *const_str;
+  GVariant *untrusted_empty;
+
+  strv = g_strsplit (test_string, ",", 0);
+
+  value = g_variant_new_bytestring_array ((const gchar **) strv, -1);
+  g_assert (g_variant_is_floating (value));
+  g_strfreev (strv);
+
+  str = g_variant_print (value, FALSE);
+  g_variant_unref (value);
+
+  value = g_variant_parse (NULL, str, NULL, NULL, NULL);
+  g_free (str);
+
+  strv = g_variant_dup_bytestring_array (value, NULL);
+  g_variant_unref (value);
+
+  str = g_strjoinv (",", strv);
+  g_strfreev (strv);
+
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  strv = g_strsplit (test_string, ",", 0);
+  value = g_variant_new ("(^aay^a&ay^ay^&ay)",
+                         strv, strv, strv[0], strv[0]);
+  g_strfreev (strv);
+
+  g_variant_get_child (value, 0, "^a&ay", &strv);
+  str = g_strjoinv (",", strv);
+  g_free (strv);
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  g_variant_get_child (value, 0, "^aay", &strv);
+  str = g_strjoinv (",", strv);
+  g_strfreev (strv);
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  g_variant_get_child (value, 1, "^a&ay", &strv);
+  str = g_strjoinv (",", strv);
+  g_free (strv);
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  g_variant_get_child (value, 1, "^aay", &strv);
+  str = g_strjoinv (",", strv);
+  g_strfreev (strv);
+  g_assert_cmpstr (str, ==, test_string);
+  g_free (str);
+
+  g_variant_get_child (value, 2, "^ay", &str);
+  g_assert_cmpstr (str, ==, "foo");
+  g_free (str);
+
+  g_variant_get_child (value, 2, "^&ay", &str);
+  g_assert_cmpstr (str, ==, "foo");
+
+  g_variant_get_child (value, 3, "^ay", &str);
+  g_assert_cmpstr (str, ==, "foo");
+  g_free (str);
+
+  g_variant_get_child (value, 3, "^&ay", &str);
+  g_assert_cmpstr (str, ==, "foo");
+  g_variant_unref (value);
+
+  untrusted_empty = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), NULL, 0, FALSE, NULL, NULL);
+  value = g_variant_get_normal_form (untrusted_empty);
+  const_str = g_variant_get_bytestring (value);
+  (void) const_str;
+  g_variant_unref (value);
+  g_variant_unref (untrusted_empty);
+}
+
+static void
+test_lookup_value (void)
+{
+  struct {
+    const gchar *dict, *key, *value;
+  } cases[] = {
+    { "@a{ss} {'x':  'y'}",   "x",  "'y'" },
+    { "@a{ss} {'x':  'y'}",   "y"         },
+    { "@a{os} {'/x': 'y'}",   "/x", "'y'" },
+    { "@a{os} {'/x': 'y'}",   "/y"        },
+    { "@a{sv} {'x':  <'y'>}", "x",  "'y'" },
+    { "@a{sv} {'x':  <5>}",   "x",  "5"   },
+    { "@a{sv} {'x':  <'y'>}", "y"         }
+  };
+  gint i;
+
+  for (i = 0; i < G_N_ELEMENTS (cases); i++)
+    {
+      GVariant *dictionary;
+      GVariant *value;
+      gchar *p;
+      
+      dictionary = g_variant_parse (NULL, cases[i].dict, NULL, NULL, NULL);
+      value = g_variant_lookup_value (dictionary, cases[i].key, NULL);
+      g_variant_unref (dictionary);
+
+      if (value == NULL && cases[i].value == NULL)
+        continue;
+
+      g_assert (value && cases[i].value);
+      p = g_variant_print (value, FALSE);
+      g_assert_cmpstr (cases[i].value, ==, p);
+      g_variant_unref (value);
+      g_free (p);
+    }
+}
+
+static void
+test_lookup (void)
+{
+  const gchar *str;
+  GVariant *dict;
+  gboolean ok;
+  gint num;
+
+  dict = g_variant_parse (NULL,
+                          "{'a': <5>, 'b': <'c'>}",
+                          NULL, NULL, NULL);
+
+  ok = g_variant_lookup (dict, "a", "i", &num);
+  g_assert (ok);
+  g_assert_cmpint (num, ==, 5);
+
+  ok = g_variant_lookup (dict, "a", "&s", &str);
+  g_assert (!ok);
+
+  ok = g_variant_lookup (dict, "q", "&s", &str);
+  g_assert (!ok);
+
+  ok = g_variant_lookup (dict, "b", "i", &num);
+  g_assert (!ok);
+
+  ok = g_variant_lookup (dict, "b", "&s", &str);
+  g_assert (ok);
+  g_assert_cmpstr (str, ==, "c");
+
+  ok = g_variant_lookup (dict, "q", "&s", &str);
+  g_assert (!ok);
+
+  g_variant_unref (dict);
+}
+
+static GVariant *
+untrusted (GVariant *a)
+{
+  GVariant *b;
+  const GVariantType *type;
+  GBytes *bytes;
+
+  type = g_variant_get_type (a);
+  bytes = g_variant_get_data_as_bytes (a);
+  b = g_variant_new_from_bytes (type, bytes, FALSE);
+  g_bytes_unref (bytes);
+  g_variant_unref (a);
+
+  return b;
+}
+
+static void
+test_compare (void)
+{
+  GVariant *a;
+  GVariant *b;
+
+  a = untrusted (g_variant_new_byte (5));
+  b = g_variant_new_byte (6);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_int16 (G_MININT16));
+  b = g_variant_new_int16 (G_MAXINT16);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_uint16 (0));
+  b = g_variant_new_uint16 (G_MAXUINT16);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_int32 (G_MININT32));
+  b = g_variant_new_int32 (G_MAXINT32);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_uint32 (0));
+  b = g_variant_new_uint32 (G_MAXUINT32);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_int64 (G_MININT64));
+  b = g_variant_new_int64 (G_MAXINT64);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_uint64 (0));
+  b = g_variant_new_uint64 (G_MAXUINT64);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_double (G_MINDOUBLE));
+  b = g_variant_new_double (G_MAXDOUBLE);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_string ("abc"));
+  b = g_variant_new_string ("abd");
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_object_path ("/abc"));
+  b = g_variant_new_object_path ("/abd");
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_signature ("g"));
+  b = g_variant_new_signature ("o");
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_boolean (FALSE));
+  b = g_variant_new_boolean (TRUE);
+  g_assert (g_variant_compare (a, b) < 0);
+  g_variant_unref (a);
+  g_variant_unref (b);
+}
+
+static void
+test_equal (void)
+{
+  GVariant *a;
+  GVariant *b;
+
+  a = untrusted (g_variant_new_byte (5));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_int16 (G_MININT16));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_uint16 (0));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_int32 (G_MININT32));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_uint32 (0));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_int64 (G_MININT64));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_uint64 (0));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_double (G_MINDOUBLE));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_string ("abc"));
+  g_assert (g_variant_equal (a, a));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_object_path ("/abc"));
+  g_assert (g_variant_equal (a, a));
+  b = g_variant_get_normal_form (a);
+  a = untrusted (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_signature ("g"));
+  g_assert (g_variant_equal (a, a));
+  b = g_variant_get_normal_form (a);
+  a = untrusted (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+  a = untrusted (g_variant_new_boolean (FALSE));
+  b = g_variant_get_normal_form (a);
+  g_assert (g_variant_equal (a, b));
+  g_variant_unref (a);
+  g_variant_unref (b);
+}
+
+static void
+test_fixed_array (void)
+{
+  GVariant *a;
+  gint32 values[5];
+  const gint32 *elts;
+  gsize n_elts;
+  gint i;
+
+  n_elts = 0;
+  a = g_variant_new_parsed ("[1,2,3,4,5]");
+  elts = g_variant_get_fixed_array (a, &n_elts, sizeof (gint32));
+  g_assert (n_elts == 5);
+  for (i = 0; i < 5; i++)
+    g_assert_cmpint (elts[i], ==, i + 1);
+  g_variant_unref (a);
+
+  n_elts = 0;
+  for (i = 0; i < 5; i++)
+    values[i] = i + 1;
+  a = g_variant_new_fixed_array (G_VARIANT_TYPE_INT32, values,
+                                 G_N_ELEMENTS (values), sizeof (values[0]));
+  g_assert_cmpstr (g_variant_get_type_string (a), ==, "ai");
+  elts = g_variant_get_fixed_array (a, &n_elts, sizeof (gint32));
+  g_assert (n_elts == 5);
+  for (i = 0; i < 5; i++)
+    g_assert_cmpint (elts[i], ==, i + 1);
+  g_variant_unref (a);
+}
+
+static void
+test_check_format_string (void)
+{
+  GVariant *value;
+
+  value = g_variant_new ("(sas)", "foo", NULL);
+  g_variant_ref_sink (value);
+
+  g_assert (g_variant_check_format_string (value, "(s*)", TRUE));
+  g_assert (g_variant_check_format_string (value, "(s*)", FALSE));
+  g_assert (!g_variant_check_format_string (value, "(u*)", TRUE));
+  g_assert (!g_variant_check_format_string (value, "(u*)", FALSE));
+
+  g_assert (g_variant_check_format_string (value, "(&s*)", FALSE));
+  g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "*contains a '&' character*");
+  g_assert (!g_variant_check_format_string (value, "(&s*)", TRUE));
+  g_test_assert_expected_messages ();
+
+  g_assert (g_variant_check_format_string (value, "(s^as)", TRUE));
+  g_assert (g_variant_check_format_string (value, "(s^as)", FALSE));
+
+  g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "*contains a '&' character*");
+  g_assert (!g_variant_check_format_string (value, "(s^a&s)", TRUE));
+  g_test_assert_expected_messages ();
+  g_assert (g_variant_check_format_string (value, "(s^a&s)", FALSE));
+
+  g_variant_unref (value);
+
+  /* Do it again with a type that will let us put a '&' after a '^' */
+  value = g_variant_new ("(say)", "foo", NULL);
+  g_variant_ref_sink (value);
+
+  g_assert (g_variant_check_format_string (value, "(s*)", TRUE));
+  g_assert (g_variant_check_format_string (value, "(s*)", FALSE));
+  g_assert (!g_variant_check_format_string (value, "(u*)", TRUE));
+  g_assert (!g_variant_check_format_string (value, "(u*)", FALSE));
+
+  g_assert (g_variant_check_format_string (value, "(&s*)", FALSE));
+  g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "*contains a '&' character*");
+  g_assert (!g_variant_check_format_string (value, "(&s*)", TRUE));
+  g_test_assert_expected_messages ();
+
+  g_assert (g_variant_check_format_string (value, "(s^ay)", TRUE));
+  g_assert (g_variant_check_format_string (value, "(s^ay)", FALSE));
+
+  g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "*contains a '&' character*");
+  g_assert (!g_variant_check_format_string (value, "(s^&ay)", TRUE));
+  g_test_assert_expected_messages ();
+  g_assert (g_variant_check_format_string (value, "(s^&ay)", FALSE));
+
+  g_assert (g_variant_check_format_string (value, "r", FALSE));
+  g_assert (g_variant_check_format_string (value, "(?a?)", FALSE));
+
+  g_variant_unref (value);
+}
+
+static void
+verify_gvariant_checksum (const gchar  *sha256,
+                         GVariant     *v)
+            
+{
+  gchar *checksum;
+  checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256,
+                                         g_variant_get_data (v),
+                                         g_variant_get_size (v));
+  g_assert_cmpstr (sha256, ==, checksum);
+  g_free (checksum);
+}
+
+static void
+verify_gvariant_checksum_va (const gchar *sha256,
+                            const gchar *fmt,
+                            ...)
+{
+  va_list args;
+  GVariant *v;
+
+  va_start (args, fmt);
+
+  v = g_variant_new_va (fmt, NULL, &args);
+  g_variant_ref_sink (v);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+  {
+    GVariant *byteswapped = g_variant_byteswap (v);
+    g_variant_unref (v);
+    v = byteswapped;
+  }
+#endif
+
+  va_end (args);
+
+  verify_gvariant_checksum (sha256, v);
+
+  g_variant_unref (v);
+}
+
+static void
+test_checksum_basic (void)
+{
+  verify_gvariant_checksum_va ("e8a4b2ee7ede79a3afb332b5b6cc3d952a65fd8cffb897f5d18016577c33d7cc",
+                              "u", 42);
+  verify_gvariant_checksum_va ("c53e363c33b00cfce298229ee83856b8a98c2e6126cab13f65899f62473b0df5",
+                              "s", "moocow");
+  verify_gvariant_checksum_va ("2b4c342f5433ebe591a1da77e013d1b72475562d48578dca8b84bac6651c3cb9",
+                              "y", 9);
+  verify_gvariant_checksum_va ("12a3ae445661ce5dee78d0650d33362dec29c4f82af05e7e57fb595bbbacf0ca",
+                              "t", G_MAXUINT64);
+  verify_gvariant_checksum_va ("e25a59b24440eb6c833aa79c93b9840e6eab6966add0dacf31df7e9e7000f5b3",
+                              "d", 3.14159);
+  verify_gvariant_checksum_va ("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",
+                              "b", TRUE);
+  verify_gvariant_checksum_va ("ca2fd00fa001190744c15c317643ab092e7048ce086a243e2be9437c898de1bb",
+                              "q", G_MAXUINT16);
+}
+
+static void
+test_checksum_nested (void)
+{
+  static const char* const strv[] = {"foo", "bar", "baz", NULL};
+
+  verify_gvariant_checksum_va ("31fbc92f08fddaca716188fe4b5d44ae122fc6306fd3c6925af53cfa47ea596d",
+                              "(uu)", 41, 43);
+  verify_gvariant_checksum_va ("01759d683cead856d1d386d59af0578841698a424a265345ad5413122f220de8",
+                              "(su)", "moocow", 79);
+  verify_gvariant_checksum_va ("52b3ae95f19b3e642ea1d01185aea14a09004c1d1712672644427403a8a0afe6",
+                              "(qyst)", G_MAXUINT16, 9, "moocow", G_MAXUINT64);
+  verify_gvariant_checksum_va ("6fc6f4524161c3ae0d316812d7088e3fcd372023edaea2d7821093be40ae1060",
+                              "(@ay)", g_variant_new_bytestring ("\xFF\xFF\xFF"));
+  verify_gvariant_checksum_va ("572aca386e1a983dd23bb6eb6e3dfa72eef9ca7c7744581aa800e18d7d9d0b0b",
+                              "(^as)", strv);
+  verify_gvariant_checksum_va ("4bddf6174c791bb44fc6a4106573031690064df34b741033a0122ed8dc05bcf3",
+                              "(yvu)", 254, g_variant_new ("(^as)", strv), 42);
+}
+
+static void
+test_gbytes (void)
+{
+  GVariant *a;
+  GVariant *tuple;
+  GBytes *bytes;
+  GBytes *bytes2;
+  const guint8 values[5] = { 1, 2, 3, 4, 5 };
+  const guint8 *elts;
+  gsize n_elts;
+  gint i;
+
+  bytes = g_bytes_new (&values, 5);
+  a = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes, TRUE);
+  g_bytes_unref (bytes);
+  n_elts = 0;
+  elts = g_variant_get_fixed_array (a, &n_elts, sizeof (guint8));
+  g_assert (n_elts == 5);
+  for (i = 0; i < 5; i++)
+    g_assert_cmpint (elts[i], ==, i + 1);
+
+  bytes2 = g_variant_get_data_as_bytes (a);
+  g_variant_unref (a);
+
+  bytes = g_bytes_new (&values, 5);
+  g_assert (g_bytes_equal (bytes, bytes2));
+  g_bytes_unref (bytes);
+  g_bytes_unref (bytes2);
+
+  tuple = g_variant_new_parsed ("['foo', 'bar']");
+  bytes = g_variant_get_data_as_bytes (tuple); /* force serialisation */
+  a = g_variant_get_child_value (tuple, 1);
+  bytes2 = g_variant_get_data_as_bytes (a);
+  g_assert (!g_bytes_equal (bytes, bytes2));
+
+  g_bytes_unref (bytes);
+  g_bytes_unref (bytes2);
+  g_variant_unref (a);
+  g_variant_unref (tuple);
+}
+
+typedef struct {
+  const GVariantType *type;
+  const gchar *in;
+  const gchar *out;
+} ContextTest;
+
+static void
+test_print_context (void)
+{
+  ContextTest tests[] = {
+    { NULL, "(1, 2, 3, 'abc", "          ^^^^" },
+    { NULL, "[1, 2, 3, 'str']", " ^        ^^^^^" },
+    { G_VARIANT_TYPE_UINT16, "{ 'abc':'def' }", "  ^^^^^^^^^^^^^^^" },
+    { NULL, "<5", "    ^" },
+    { NULL, "'ab\\ux'", "  ^^^^^^^" },
+    { NULL, "'ab\\U00efx'", "  ^^^^^^^^^^^" }
+  };
+  GVariant *v;
+  gchar *s;
+  gint i;
+  GError *error = NULL;
+
+  for (i = 0; i < G_N_ELEMENTS (tests); i++)
+    {
+      v = g_variant_parse (tests[i].type, tests[i].in, NULL, NULL, &error);
+      g_assert_null (v);
+      s = g_variant_parse_error_print_context (error, tests[i].in);
+      g_assert (strstr (s, tests[i].out) != NULL);
+      g_free (s);
+      g_clear_error (&error);
+    }
+}
+
+static void
+test_error_quark (void)
+{
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+  g_assert (g_variant_parser_get_error_quark () == g_variant_parse_error_quark ());
+G_GNUC_END_IGNORE_DEPRECATIONS
+}
+
+static GByteArray *
+flatten_vectors (GVariantVectors *v)
+{
+  GByteArray *result;
+  guint i;
+
+  result = g_byte_array_new ();
+
+  for (i = 0; i < v->vectors->len; i++)
+    {
+      GVariantVector vec = g_array_index (v->vectors, GVariantVector, i);
+
+      if (vec.gbytes)
+        g_byte_array_append (result, vec.data.pointer, vec.size);
+      else
+        g_byte_array_append (result, v->extra_bytes->data + vec.data.offset, vec.size);
+    }
+
+  return result;
+}
+
+static void
+test_vector_serialiser (void)
+{
+  GVariantVectors vectors;
+  GByteArray *flattened;
+  GVariant *value;
+  guint i;
+
+  for (i = 0; i < 100; i++)
+    {
+      value = create_random_gvariant (4);
+
+      GLIB_PRIVATE_CALL(g_variant_to_vectors) (value, &vectors);
+      flattened = flatten_vectors (&vectors);
+      g_byte_array_free (vectors.extra_bytes, TRUE);
+      g_byte_array_free (vectors.offsets, TRUE);
+      g_array_free (vectors.vectors, TRUE);
+
+#if 0
+      if (flattened->len != g_variant_get_size (value) ||
+          memcmp (flattened->data, g_variant_get_data (value), flattened->len) != 0)
+        {
+          g_file_set_contents ("flattened", flattened->data, flattened->len, NULL);
+          g_file_set_contents ("serialised", g_variant_get_data (value), g_variant_get_size (value), NULL);
+          g_print ("type is %s\n", g_variant_get_type_string (value));
+          g_assert_not_reached ();
+        }
+#endif
+
+      g_assert_cmpint (flattened->len, ==, g_variant_get_size (value));
+      g_assert (memcmp (flattened->data, g_variant_get_data (value), flattened->len) == 0);
+
+      g_byte_array_free (flattened, TRUE);
+      g_variant_unref (value);
+    }
+}
+
 int
 main (int argc, char **argv)
 {
@@ -3411,13 +4632,39 @@ main (int argc, char **argv)
       g_free (testname);
     }
 
+  g_test_add_func ("/gvariant/string", test_string);
+  g_test_add_func ("/gvariant/utf8", test_utf8);
   g_test_add_func ("/gvariant/containers", test_containers);
   g_test_add_func ("/gvariant/format-strings", test_format_strings);
   g_test_add_func ("/gvariant/invalid-varargs", test_invalid_varargs);
   g_test_add_func ("/gvariant/varargs", test_varargs);
+  g_test_add_func ("/gvariant/varargs/subprocess/empty-array", test_varargs_empty_array);
   g_test_add_func ("/gvariant/valist", test_valist);
   g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
   g_test_add_func ("/gvariant/hashing", test_hashing);
+  g_test_add_func ("/gvariant/byteswap", test_gv_byteswap);
+  g_test_add_func ("/gvariant/parser", test_parses);
+  g_test_add_func ("/gvariant/parse-failures", test_parse_failures);
+  g_test_add_func ("/gvariant/parse-positional", test_parse_positional);
+  g_test_add_func ("/gvariant/parse/subprocess/bad-format-char", test_parse_bad_format_char);
+  g_test_add_func ("/gvariant/parse/subprocess/bad-format-string", test_parse_bad_format_string);
+  g_test_add_func ("/gvariant/parse/subprocess/bad-args", test_parse_bad_args);
+  g_test_add_func ("/gvariant/floating", test_floating);
+  g_test_add_func ("/gvariant/bytestring", test_bytestring);
+  g_test_add_func ("/gvariant/lookup-value", test_lookup_value);
+  g_test_add_func ("/gvariant/lookup", test_lookup);
+  g_test_add_func ("/gvariant/compare", test_compare);
+  g_test_add_func ("/gvariant/equal", test_equal);
+  g_test_add_func ("/gvariant/fixed-array", test_fixed_array);
+  g_test_add_func ("/gvariant/check-format-string", test_check_format_string);
+
+  g_test_add_func ("/gvariant/checksum-basic", test_checksum_basic);
+  g_test_add_func ("/gvariant/checksum-nested", test_checksum_nested);
+
+  g_test_add_func ("/gvariant/gbytes", test_gbytes);
+  g_test_add_func ("/gvariant/print-context", test_print_context);
+  g_test_add_func ("/gvariant/error-quark", test_error_quark);
+  g_test_add_func ("/gvariant/vector-serialiser", test_vector_serialiser);
 
   return g_test_run ();
 }