mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-27 06:26:15 +01:00
efe5b70192
When g_variant_get_child_value() is called for a child whose serialisation is an empty byte string (which is possible), `bytes_data` will be non-`NULL`, but `data` may be `NULL`. This results in a negative offset being passed to `g_bytes_new_from_bytes()`, and a critical warning. So if `data` is `NULL`, set it to point to `bytes_data` so the offset is calculated as zero. The actual value of the offset doesn’t matter, since in this situation the size is always zero. An offset of zero is never going to cause problems. Signed-off-by: Philip Withnall <withnall@endlessm.com> Fixes: #1865
5200 lines
153 KiB
C
5200 lines
153 KiB
C
/*
|
||
* Copyright © 2010 Codethink Limited
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2.1 of the License, or (at your option) any later version.
|
||
*
|
||
* See the included COPYING file for more information.
|
||
*
|
||
* Author: Ryan Lortie <desrt@desrt.ca>
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <glib/gvariant-internal.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <glib.h>
|
||
|
||
#define BASIC "bynqiuxthdsog?"
|
||
#define N_BASIC (G_N_ELEMENTS (BASIC) - 1)
|
||
|
||
#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)
|
||
{
|
||
return g_test_rand_double_range (0, 1) < prob;
|
||
}
|
||
|
||
/* corecursion */
|
||
static GVariantType *
|
||
append_tuple_type_string (GString *, GString *, gboolean, gint);
|
||
|
||
/* append a random GVariantType to a GString
|
||
* append a description of the type to another GString
|
||
* return what the type is
|
||
*/
|
||
static GVariantType *
|
||
append_type_string (GString *string,
|
||
GString *description,
|
||
gboolean definite,
|
||
gint depth)
|
||
{
|
||
if (!depth-- || randomly (0.3))
|
||
{
|
||
gchar b = BASIC[g_test_rand_int_range (0, N_BASIC - definite)];
|
||
g_string_append_c (string, b);
|
||
g_string_append_c (description, b);
|
||
|
||
switch (b)
|
||
{
|
||
case 'b':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_BOOLEAN);
|
||
case 'y':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_BYTE);
|
||
case 'n':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_INT16);
|
||
case 'q':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_UINT16);
|
||
case 'i':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_INT32);
|
||
case 'u':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_UINT32);
|
||
case 'x':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_INT64);
|
||
case 't':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_UINT64);
|
||
case 'h':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
|
||
case 'd':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
|
||
case 's':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_STRING);
|
||
case 'o':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_OBJECT_PATH);
|
||
case 'g':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_SIGNATURE);
|
||
case '?':
|
||
return g_variant_type_copy (G_VARIANT_TYPE_BASIC);
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
GVariantType *result;
|
||
|
||
switch (g_test_rand_int_range (0, definite ? 5 : 7))
|
||
{
|
||
case 0:
|
||
{
|
||
GVariantType *element;
|
||
|
||
g_string_append_c (string, 'a');
|
||
g_string_append (description, "a of ");
|
||
element = append_type_string (string, description,
|
||
definite, depth);
|
||
result = g_variant_type_new_array (element);
|
||
g_variant_type_free (element);
|
||
}
|
||
|
||
g_assert_true (g_variant_type_is_array (result));
|
||
break;
|
||
|
||
case 1:
|
||
{
|
||
GVariantType *element;
|
||
|
||
g_string_append_c (string, 'm');
|
||
g_string_append (description, "m of ");
|
||
element = append_type_string (string, description,
|
||
definite, depth);
|
||
result = g_variant_type_new_maybe (element);
|
||
g_variant_type_free (element);
|
||
}
|
||
|
||
g_assert_true (g_variant_type_is_maybe (result));
|
||
break;
|
||
|
||
case 2:
|
||
result = append_tuple_type_string (string, description,
|
||
definite, depth);
|
||
|
||
g_assert_true (g_variant_type_is_tuple (result));
|
||
break;
|
||
|
||
case 3:
|
||
{
|
||
GVariantType *key, *value;
|
||
|
||
g_string_append_c (string, '{');
|
||
g_string_append (description, "e of [");
|
||
key = append_type_string (string, description, definite, 0);
|
||
g_string_append (description, ", ");
|
||
value = append_type_string (string, description, definite, depth);
|
||
g_string_append_c (description, ']');
|
||
g_string_append_c (string, '}');
|
||
result = g_variant_type_new_dict_entry (key, value);
|
||
g_variant_type_free (key);
|
||
g_variant_type_free (value);
|
||
}
|
||
|
||
g_assert_true (g_variant_type_is_dict_entry (result));
|
||
break;
|
||
|
||
case 4:
|
||
g_string_append_c (string, 'v');
|
||
g_string_append_c (description, 'V');
|
||
result = g_variant_type_copy (G_VARIANT_TYPE_VARIANT);
|
||
g_assert_true (g_variant_type_equal (result, G_VARIANT_TYPE_VARIANT));
|
||
break;
|
||
|
||
case 5:
|
||
g_string_append_c (string, '*');
|
||
g_string_append_c (description, 'S');
|
||
result = g_variant_type_copy (G_VARIANT_TYPE_ANY);
|
||
g_assert_true (g_variant_type_equal (result, G_VARIANT_TYPE_ANY));
|
||
break;
|
||
|
||
case 6:
|
||
g_string_append_c (string, 'r');
|
||
g_string_append_c (description, 'R');
|
||
result = g_variant_type_copy (G_VARIANT_TYPE_TUPLE);
|
||
g_assert_true (g_variant_type_is_tuple (result));
|
||
break;
|
||
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
|
||
return result;
|
||
}
|
||
}
|
||
|
||
static GVariantType *
|
||
append_tuple_type_string (GString *string,
|
||
GString *description,
|
||
gboolean definite,
|
||
gint depth)
|
||
{
|
||
GVariantType *result, *other_result;
|
||
GVariantType **types;
|
||
gint size;
|
||
gsize i;
|
||
|
||
g_string_append_c (string, '(');
|
||
g_string_append (description, "t of [");
|
||
|
||
size = g_test_rand_int_range (0, 20);
|
||
types = g_new (GVariantType *, size + 1);
|
||
|
||
for (i = 0; i < size; i++)
|
||
{
|
||
types[i] = append_type_string (string, description, definite, depth);
|
||
|
||
if (i < size - 1)
|
||
g_string_append (description, ", ");
|
||
}
|
||
|
||
types[i] = NULL;
|
||
|
||
g_string_append_c (description, ']');
|
||
g_string_append_c (string, ')');
|
||
|
||
result = g_variant_type_new_tuple ((gpointer) types, size);
|
||
other_result = g_variant_type_new_tuple ((gpointer) types, -1);
|
||
g_assert_true (g_variant_type_equal (result, other_result));
|
||
g_variant_type_free (other_result);
|
||
for (i = 0; i < size; i++)
|
||
g_variant_type_free (types[i]);
|
||
g_free (types);
|
||
|
||
return result;
|
||
}
|
||
|
||
/* given a valid type string, make it invalid */
|
||
static gchar *
|
||
invalid_mutation (const gchar *type_string)
|
||
{
|
||
gboolean have_parens, have_braces;
|
||
|
||
/* it's valid, so '(' implies ')' and same for '{' and '}' */
|
||
have_parens = strchr (type_string, '(') != NULL;
|
||
have_braces = strchr (type_string, '{') != NULL;
|
||
|
||
if (have_parens && have_braces && randomly (0.3))
|
||
{
|
||
/* swap a paren and a brace */
|
||
gchar *pp, *bp;
|
||
gint np, nb;
|
||
gchar p, b;
|
||
gchar *new;
|
||
|
||
new = g_strdup (type_string);
|
||
|
||
if (randomly (0.5))
|
||
p = '(', b = '{';
|
||
else
|
||
p = ')', b = '}';
|
||
|
||
np = nb = 0;
|
||
pp = bp = new - 1;
|
||
|
||
/* count number of parens/braces */
|
||
while ((pp = strchr (pp + 1, p))) np++;
|
||
while ((bp = strchr (bp + 1, b))) nb++;
|
||
|
||
/* randomly pick one of each */
|
||
np = g_test_rand_int_range (0, np) + 1;
|
||
nb = g_test_rand_int_range (0, nb) + 1;
|
||
|
||
/* find it */
|
||
pp = bp = new - 1;
|
||
while (np--) pp = strchr (pp + 1, p);
|
||
while (nb--) bp = strchr (bp + 1, b);
|
||
|
||
/* swap */
|
||
g_assert_true (*bp == b && *pp == p);
|
||
*bp = p;
|
||
*pp = b;
|
||
|
||
return new;
|
||
}
|
||
|
||
if ((have_parens || have_braces) && randomly (0.3))
|
||
{
|
||
/* drop a paren/brace */
|
||
gchar *new;
|
||
gchar *pp;
|
||
gint np;
|
||
gchar p;
|
||
|
||
if (have_parens)
|
||
if (randomly (0.5)) p = '('; else p = ')';
|
||
else
|
||
if (randomly (0.5)) p = '{'; else p = '}';
|
||
|
||
new = g_strdup (type_string);
|
||
|
||
np = 0;
|
||
pp = new - 1;
|
||
while ((pp = strchr (pp + 1, p))) np++;
|
||
np = g_test_rand_int_range (0, np) + 1;
|
||
pp = new - 1;
|
||
while (np--) pp = strchr (pp + 1, p);
|
||
g_assert_cmpint (*pp, ==, p);
|
||
|
||
while (*pp)
|
||
{
|
||
*pp = *(pp + 1);
|
||
pp++;
|
||
}
|
||
|
||
return new;
|
||
}
|
||
|
||
/* else, perform a random mutation at a random point */
|
||
{
|
||
gint length, n;
|
||
gchar *new;
|
||
gchar p;
|
||
|
||
if (randomly (0.3))
|
||
{
|
||
/* insert a paren/brace */
|
||
if (randomly (0.5))
|
||
if (randomly (0.5)) p = '('; else p = ')';
|
||
else
|
||
if (randomly (0.5)) p = '{'; else p = '}';
|
||
}
|
||
else if (randomly (0.5))
|
||
{
|
||
/* insert junk */
|
||
p = INVALIDS[g_test_rand_int_range (0, N_INVALIDS)];
|
||
}
|
||
else
|
||
{
|
||
/* truncate */
|
||
p = '\0';
|
||
}
|
||
|
||
|
||
length = strlen (type_string);
|
||
new = g_malloc (length + 2);
|
||
n = g_test_rand_int_range (0, length);
|
||
memcpy (new, type_string, n);
|
||
new[n] = p;
|
||
memcpy (new + n + 1, type_string + n, length - n);
|
||
new[length + 1] = '\0';
|
||
|
||
return new;
|
||
}
|
||
}
|
||
|
||
/* describe a type using the same language as is generated
|
||
* while generating the type with append_type_string
|
||
*/
|
||
static gchar *
|
||
describe_type (const GVariantType *type)
|
||
{
|
||
gchar *result;
|
||
|
||
if (g_variant_type_is_container (type))
|
||
{
|
||
g_assert_false (g_variant_type_is_basic (type));
|
||
|
||
if (g_variant_type_is_array (type))
|
||
{
|
||
gchar *subtype = describe_type (g_variant_type_element (type));
|
||
result = g_strdup_printf ("a of %s", subtype);
|
||
g_free (subtype);
|
||
}
|
||
else if (g_variant_type_is_maybe (type))
|
||
{
|
||
gchar *subtype = describe_type (g_variant_type_element (type));
|
||
result = g_strdup_printf ("m of %s", subtype);
|
||
g_free (subtype);
|
||
}
|
||
else if (g_variant_type_is_tuple (type))
|
||
{
|
||
if (!g_variant_type_equal (type, G_VARIANT_TYPE_TUPLE))
|
||
{
|
||
const GVariantType *sub;
|
||
GString *string;
|
||
gint length;
|
||
gsize i;
|
||
|
||
string = g_string_new ("t of [");
|
||
|
||
length = g_variant_type_n_items (type);
|
||
sub = g_variant_type_first (type);
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
gchar *subtype = describe_type (sub);
|
||
g_string_append (string, subtype);
|
||
g_free (subtype);
|
||
|
||
if ((sub = g_variant_type_next (sub)))
|
||
g_string_append (string, ", ");
|
||
}
|
||
g_assert_null (sub);
|
||
g_string_append_c (string, ']');
|
||
|
||
result = g_string_free (string, FALSE);
|
||
}
|
||
else
|
||
result = g_strdup ("R");
|
||
}
|
||
else if (g_variant_type_is_dict_entry (type))
|
||
{
|
||
gchar *key, *value, *key2, *value2;
|
||
|
||
key = describe_type (g_variant_type_key (type));
|
||
value = describe_type (g_variant_type_value (type));
|
||
key2 = describe_type (g_variant_type_first (type));
|
||
value2 = describe_type (
|
||
g_variant_type_next (g_variant_type_first (type)));
|
||
g_assert_null (g_variant_type_next (g_variant_type_next (
|
||
g_variant_type_first (type))));
|
||
g_assert_cmpstr (key, ==, key2);
|
||
g_assert_cmpstr (value, ==, value2);
|
||
result = g_strjoin ("", "e of [", key, ", ", value, "]", NULL);
|
||
g_free (key2);
|
||
g_free (value2);
|
||
g_free (key);
|
||
g_free (value);
|
||
}
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT))
|
||
{
|
||
result = g_strdup ("V");
|
||
}
|
||
else
|
||
g_assert_not_reached ();
|
||
}
|
||
else
|
||
{
|
||
if (g_variant_type_is_definite (type))
|
||
{
|
||
g_assert_true (g_variant_type_is_basic (type));
|
||
|
||
if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
|
||
result = g_strdup ("b");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
|
||
result = g_strdup ("y");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
|
||
result = g_strdup ("n");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
|
||
result = g_strdup ("q");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
|
||
result = g_strdup ("i");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
|
||
result = g_strdup ("u");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
|
||
result = g_strdup ("x");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
|
||
result = g_strdup ("t");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
|
||
result = g_strdup ("h");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
|
||
result = g_strdup ("d");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
|
||
result = g_strdup ("s");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
|
||
result = g_strdup ("o");
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
|
||
result = g_strdup ("g");
|
||
else
|
||
g_assert_not_reached ();
|
||
}
|
||
else
|
||
{
|
||
if (g_variant_type_equal (type, G_VARIANT_TYPE_ANY))
|
||
{
|
||
result = g_strdup ("S");
|
||
}
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_BASIC))
|
||
{
|
||
result = g_strdup ("?");
|
||
}
|
||
else
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* given a type string, replace one of the indefinite type characters in
|
||
* it with a matching type (possibly the same type).
|
||
*/
|
||
static gchar *
|
||
generate_subtype (const gchar *type_string)
|
||
{
|
||
GVariantType *replacement;
|
||
GString *result, *junk;
|
||
gint l;
|
||
gsize length, n = 0;
|
||
|
||
result = g_string_new (NULL);
|
||
junk = g_string_new (NULL);
|
||
|
||
/* count the number of indefinite type characters */
|
||
for (length = 0; type_string[length]; length++)
|
||
n += type_string[length] == 'r' ||
|
||
type_string[length] == '?' ||
|
||
type_string[length] == '*';
|
||
/* length now is strlen (type_string) */
|
||
|
||
/* pick one at random to replace */
|
||
n = g_test_rand_int_range (0, n) + 1;
|
||
|
||
/* find it */
|
||
l = -1;
|
||
while (n--) l += strcspn (type_string + l + 1, "r?*") + 1;
|
||
g_assert_true (type_string[l] == 'r' ||
|
||
type_string[l] == '?' ||
|
||
type_string[l] == '*');
|
||
|
||
/* store up to that point in a GString */
|
||
g_string_append_len (result, type_string, l);
|
||
|
||
/* then store the replacement in the GString */
|
||
if (type_string[l] == 'r')
|
||
replacement = append_tuple_type_string (result, junk, FALSE, 3);
|
||
|
||
else if (type_string[l] == '?')
|
||
replacement = append_type_string (result, junk, FALSE, 0);
|
||
|
||
else if (type_string[l] == '*')
|
||
replacement = append_type_string (result, junk, FALSE, 3);
|
||
|
||
else
|
||
g_assert_not_reached ();
|
||
|
||
/* ensure the replacement has the proper type */
|
||
g_assert_true (g_variant_type_is_subtype_of (replacement,
|
||
(gpointer) &type_string[l]));
|
||
|
||
/* store the rest from the original type string */
|
||
g_string_append (result, type_string + l + 1);
|
||
|
||
g_variant_type_free (replacement);
|
||
g_string_free (junk, TRUE);
|
||
|
||
return g_string_free (result, FALSE);
|
||
}
|
||
|
||
struct typestack
|
||
{
|
||
const GVariantType *type;
|
||
struct typestack *parent;
|
||
};
|
||
|
||
/* given an indefinite type string, replace one of the indefinite
|
||
* characters in it with a matching type and ensure that the result is a
|
||
* subtype of the original. repeat.
|
||
*/
|
||
static void
|
||
subtype_check (const gchar *type_string,
|
||
struct typestack *parent_ts)
|
||
{
|
||
struct typestack ts, *node;
|
||
gchar *subtype;
|
||
gint depth = 0;
|
||
|
||
subtype = generate_subtype (type_string);
|
||
|
||
ts.type = G_VARIANT_TYPE (subtype);
|
||
ts.parent = parent_ts;
|
||
|
||
for (node = &ts; node; node = node->parent)
|
||
{
|
||
/* this type should be a subtype of each parent type */
|
||
g_assert_true (g_variant_type_is_subtype_of (ts.type, node->type));
|
||
|
||
/* it should only be a supertype when it is exactly equal */
|
||
g_assert_true (g_variant_type_is_subtype_of (node->type, ts.type) ==
|
||
g_variant_type_equal (ts.type, node->type));
|
||
|
||
depth++;
|
||
}
|
||
|
||
if (!g_variant_type_is_definite (ts.type) && depth < 5)
|
||
{
|
||
/* the type is still indefinite and we haven't repeated too many
|
||
* times. go once more.
|
||
*/
|
||
|
||
subtype_check (subtype, &ts);
|
||
}
|
||
|
||
g_free (subtype);
|
||
}
|
||
|
||
static void
|
||
test_gvarianttype (void)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < 2000; i++)
|
||
{
|
||
GString *type_string, *description;
|
||
GVariantType *type, *other_type;
|
||
const GVariantType *ctype;
|
||
gchar *invalid;
|
||
gchar *desc;
|
||
|
||
type_string = g_string_new (NULL);
|
||
description = g_string_new (NULL);
|
||
|
||
/* generate a random type, its type string and a description
|
||
*
|
||
* exercises type constructor functions and g_variant_type_copy()
|
||
*/
|
||
type = append_type_string (type_string, description, FALSE, 6);
|
||
|
||
/* convert the type string to a type and ensure that it is equal
|
||
* to the one produced with the type constructor routines
|
||
*/
|
||
ctype = G_VARIANT_TYPE (type_string->str);
|
||
g_assert_true (g_variant_type_equal (ctype, type));
|
||
g_assert_cmpuint (g_variant_type_hash (ctype), ==, g_variant_type_hash (type));
|
||
g_assert_true (g_variant_type_is_subtype_of (ctype, type));
|
||
g_assert_true (g_variant_type_is_subtype_of (type, ctype));
|
||
|
||
/* check if the type is indefinite */
|
||
if (!g_variant_type_is_definite (type))
|
||
{
|
||
struct typestack ts = { type, NULL };
|
||
|
||
/* if it is indefinite, then replace one of the indefinite
|
||
* characters with a matching type and ensure that the result
|
||
* is a subtype of the original type. repeat.
|
||
*/
|
||
subtype_check (type_string->str, &ts);
|
||
}
|
||
else
|
||
/* ensure that no indefinite characters appear */
|
||
g_assert_cmpint (strcspn (type_string->str, "r?*"), ==, type_string->len);
|
||
|
||
|
||
/* describe the type.
|
||
*
|
||
* exercises the type iterator interface
|
||
*/
|
||
desc = describe_type (type);
|
||
|
||
/* make sure the description matches */
|
||
g_assert_cmpstr (desc, ==, description->str);
|
||
g_free (desc);
|
||
|
||
/* make an invalid mutation to the type and make sure the type
|
||
* validation routines catch it */
|
||
invalid = invalid_mutation (type_string->str);
|
||
g_assert_true (g_variant_type_string_is_valid (type_string->str));
|
||
g_assert_false (g_variant_type_string_is_valid (invalid));
|
||
g_free (invalid);
|
||
|
||
/* concatenate another type to the type string and ensure that
|
||
* the result is recognised as being invalid
|
||
*/
|
||
other_type = append_type_string (type_string, description, FALSE, 2);
|
||
|
||
g_string_free (description, TRUE);
|
||
g_string_free (type_string, TRUE);
|
||
g_variant_type_free (other_type);
|
||
g_variant_type_free (type);
|
||
}
|
||
}
|
||
|
||
/* Test that scanning a deeply recursive type string doesn’t exhaust our
|
||
* stack space (which it would if the type string scanner was recursive). */
|
||
static void
|
||
test_gvarianttype_string_scan_recursion_tuple (void)
|
||
{
|
||
gchar *type_string = NULL;
|
||
gsize type_string_len = 1000001; /* not including nul terminator */
|
||
gsize i;
|
||
|
||
/* Build a long type string of ‘((…u…))’. */
|
||
type_string = g_new0 (gchar, type_string_len + 1);
|
||
for (i = 0; i < type_string_len; i++)
|
||
{
|
||
if (i < type_string_len / 2)
|
||
type_string[i] = '(';
|
||
else if (i == type_string_len / 2)
|
||
type_string[i] = 'u';
|
||
else
|
||
type_string[i] = ')';
|
||
}
|
||
|
||
/* Goes (way) over allowed recursion limit. */
|
||
g_assert_false (g_variant_type_string_is_valid (type_string));
|
||
|
||
g_free (type_string);
|
||
}
|
||
|
||
/* Same as above, except with an array rather than a tuple. */
|
||
static void
|
||
test_gvarianttype_string_scan_recursion_array (void)
|
||
{
|
||
gchar *type_string = NULL;
|
||
gsize type_string_len = 1000001; /* not including nul terminator */
|
||
gsize i;
|
||
|
||
/* Build a long type string of ‘aaa…aau’. */
|
||
type_string = g_new0 (gchar, type_string_len + 1);
|
||
for (i = 0; i < type_string_len; i++)
|
||
{
|
||
if (i < type_string_len - 1)
|
||
type_string[i] = 'a';
|
||
else
|
||
type_string[i] = 'u';
|
||
}
|
||
|
||
/* Goes (way) over allowed recursion limit. */
|
||
g_assert_false (g_variant_type_string_is_valid (type_string));
|
||
|
||
g_free (type_string);
|
||
}
|
||
|
||
#define ALIGNED(x, y) (((x + (y - 1)) / y) * y)
|
||
|
||
/* do our own calculation of the fixed_size and alignment of a type
|
||
* using a simple algorithm to make sure the "fancy" one in the
|
||
* implementation is correct.
|
||
*/
|
||
static void
|
||
calculate_type_info (const GVariantType *type,
|
||
gsize *fixed_size,
|
||
guint *alignment)
|
||
{
|
||
if (g_variant_type_is_array (type) ||
|
||
g_variant_type_is_maybe (type))
|
||
{
|
||
calculate_type_info (g_variant_type_element (type), NULL, alignment);
|
||
|
||
if (fixed_size)
|
||
*fixed_size = 0;
|
||
}
|
||
else if (g_variant_type_is_tuple (type) ||
|
||
g_variant_type_is_dict_entry (type))
|
||
{
|
||
if (g_variant_type_n_items (type))
|
||
{
|
||
const GVariantType *sub;
|
||
gboolean variable;
|
||
gsize size;
|
||
guint al;
|
||
|
||
variable = FALSE;
|
||
size = 0;
|
||
al = 0;
|
||
|
||
sub = g_variant_type_first (type);
|
||
do
|
||
{
|
||
gsize this_fs;
|
||
guint this_al;
|
||
|
||
calculate_type_info (sub, &this_fs, &this_al);
|
||
|
||
al = MAX (al, this_al);
|
||
|
||
if (!this_fs)
|
||
{
|
||
variable = TRUE;
|
||
size = 0;
|
||
}
|
||
|
||
if (!variable)
|
||
{
|
||
size = ALIGNED (size, this_al);
|
||
size += this_fs;
|
||
}
|
||
}
|
||
while ((sub = g_variant_type_next (sub)));
|
||
|
||
size = ALIGNED (size, al);
|
||
|
||
if (alignment)
|
||
*alignment = al;
|
||
|
||
if (fixed_size)
|
||
*fixed_size = size;
|
||
}
|
||
else
|
||
{
|
||
if (fixed_size)
|
||
*fixed_size = 1;
|
||
|
||
if (alignment)
|
||
*alignment = 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gint fs, al;
|
||
|
||
if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN) ||
|
||
g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
|
||
{
|
||
al = fs = 1;
|
||
}
|
||
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16) ||
|
||
g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
|
||
{
|
||
al = fs = 2;
|
||
}
|
||
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32) ||
|
||
g_variant_type_equal (type, G_VARIANT_TYPE_UINT32) ||
|
||
g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
|
||
{
|
||
al = fs = 4;
|
||
}
|
||
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64) ||
|
||
g_variant_type_equal (type, G_VARIANT_TYPE_UINT64) ||
|
||
g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
|
||
{
|
||
al = fs = 8;
|
||
}
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING) ||
|
||
g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH) ||
|
||
g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
|
||
{
|
||
al = 1;
|
||
fs = 0;
|
||
}
|
||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT))
|
||
{
|
||
al = 8;
|
||
fs = 0;
|
||
}
|
||
else
|
||
g_assert_not_reached ();
|
||
|
||
if (fixed_size)
|
||
*fixed_size = fs;
|
||
|
||
if (alignment)
|
||
*alignment = al;
|
||
}
|
||
}
|
||
|
||
/* same as the describe_type() function above, but iterates over
|
||
* typeinfo instead of types.
|
||
*/
|
||
static gchar *
|
||
describe_info (GVariantTypeInfo *info)
|
||
{
|
||
gchar *result;
|
||
|
||
switch (g_variant_type_info_get_type_char (info))
|
||
{
|
||
case G_VARIANT_TYPE_INFO_CHAR_MAYBE:
|
||
{
|
||
gchar *element;
|
||
|
||
element = describe_info (g_variant_type_info_element (info));
|
||
result = g_strdup_printf ("m of %s", element);
|
||
g_free (element);
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_ARRAY:
|
||
{
|
||
gchar *element;
|
||
|
||
element = describe_info (g_variant_type_info_element (info));
|
||
result = g_strdup_printf ("a of %s", element);
|
||
g_free (element);
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_TUPLE:
|
||
{
|
||
const gchar *sep = "";
|
||
GString *string;
|
||
gint length;
|
||
gsize i;
|
||
|
||
string = g_string_new ("t of [");
|
||
length = g_variant_type_info_n_members (info);
|
||
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
const GVariantMemberInfo *minfo;
|
||
gchar *subtype;
|
||
|
||
g_string_append (string, sep);
|
||
sep = ", ";
|
||
|
||
minfo = g_variant_type_info_member_info (info, i);
|
||
subtype = describe_info (minfo->type_info);
|
||
g_string_append (string, subtype);
|
||
g_free (subtype);
|
||
}
|
||
|
||
g_string_append_c (string, ']');
|
||
|
||
result = g_string_free (string, FALSE);
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY:
|
||
{
|
||
const GVariantMemberInfo *keyinfo, *valueinfo;
|
||
gchar *key, *value;
|
||
|
||
g_assert_cmpint (g_variant_type_info_n_members (info), ==, 2);
|
||
keyinfo = g_variant_type_info_member_info (info, 0);
|
||
valueinfo = g_variant_type_info_member_info (info, 1);
|
||
key = describe_info (keyinfo->type_info);
|
||
value = describe_info (valueinfo->type_info);
|
||
result = g_strjoin ("", "e of [", key, ", ", value, "]", NULL);
|
||
g_free (key);
|
||
g_free (value);
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_VARIANT:
|
||
result = g_strdup ("V");
|
||
break;
|
||
|
||
default:
|
||
result = g_strdup (g_variant_type_info_get_type_string (info));
|
||
g_assert_cmpint (strlen (result), ==, 1);
|
||
break;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/* check that the O(1) method of calculating offsets meshes with the
|
||
* results of simple iteration.
|
||
*/
|
||
static void
|
||
check_offsets (GVariantTypeInfo *info,
|
||
const GVariantType *type)
|
||
{
|
||
gsize flavour;
|
||
gint length;
|
||
|
||
length = g_variant_type_info_n_members (info);
|
||
g_assert_cmpint (length, ==, g_variant_type_n_items (type));
|
||
|
||
/* the 'flavour' is the low order bits of the ending point of
|
||
* variable-size items in the tuple. this lets us test that the type
|
||
* info is correct for various starting alignments.
|
||
*/
|
||
for (flavour = 0; flavour < 8; flavour++)
|
||
{
|
||
const GVariantType *subtype;
|
||
gsize last_offset_index;
|
||
gsize last_offset;
|
||
gsize position;
|
||
gsize i;
|
||
|
||
subtype = g_variant_type_first (type);
|
||
last_offset_index = -1;
|
||
last_offset = 0;
|
||
position = 0;
|
||
|
||
/* go through the tuple, keeping track of our position */
|
||
for (i = 0; i < length; i++)
|
||
{
|
||
gsize fixed_size;
|
||
guint alignment;
|
||
|
||
calculate_type_info (subtype, &fixed_size, &alignment);
|
||
|
||
position = ALIGNED (position, alignment);
|
||
|
||
/* compare our current aligned position (ie: the start of this
|
||
* item) to the start offset that would be calculated if we
|
||
* used the type info
|
||
*/
|
||
{
|
||
const GVariantMemberInfo *member;
|
||
gsize start;
|
||
|
||
member = g_variant_type_info_member_info (info, i);
|
||
g_assert_cmpint (member->i, ==, last_offset_index);
|
||
|
||
/* do the calculation using the typeinfo */
|
||
start = last_offset;
|
||
start += member->a;
|
||
start &= member->b;
|
||
start |= member->c;
|
||
|
||
/* did we reach the same spot? */
|
||
g_assert_cmpint (start, ==, position);
|
||
}
|
||
|
||
if (fixed_size)
|
||
{
|
||
/* fixed size. add that size. */
|
||
position += fixed_size;
|
||
}
|
||
else
|
||
{
|
||
/* variable size. do the flavouring. */
|
||
while ((position & 0x7) != flavour)
|
||
position++;
|
||
|
||
/* and store the offset, just like it would be in the
|
||
* serialised data.
|
||
*/
|
||
last_offset = position;
|
||
last_offset_index++;
|
||
}
|
||
|
||
/* next type */
|
||
subtype = g_variant_type_next (subtype);
|
||
}
|
||
|
||
/* make sure we used up exactly all the types */
|
||
g_assert_null (subtype);
|
||
}
|
||
}
|
||
|
||
static void
|
||
test_gvarianttypeinfo (void)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < 2000; i++)
|
||
{
|
||
GString *type_string, *description;
|
||
gsize fixed_size1, fixed_size2;
|
||
guint alignment1, alignment2;
|
||
GVariantTypeInfo *info;
|
||
GVariantType *type;
|
||
gchar *desc;
|
||
|
||
type_string = g_string_new (NULL);
|
||
description = g_string_new (NULL);
|
||
|
||
/* random type */
|
||
type = append_type_string (type_string, description, TRUE, 6);
|
||
|
||
/* create a typeinfo for it */
|
||
info = g_variant_type_info_get (type);
|
||
|
||
/* make sure the typeinfo has the right type string */
|
||
g_assert_cmpstr (g_variant_type_info_get_type_string (info), ==,
|
||
type_string->str);
|
||
|
||
/* calculate the alignment and fixed size, compare to the
|
||
* typeinfo's calculations
|
||
*/
|
||
calculate_type_info (type, &fixed_size1, &alignment1);
|
||
g_variant_type_info_query (info, &alignment2, &fixed_size2);
|
||
g_assert_cmpint (fixed_size1, ==, fixed_size2);
|
||
g_assert_cmpint (alignment1, ==, alignment2 + 1);
|
||
|
||
/* test the iteration functions over typeinfo structures by
|
||
* "describing" the typeinfo and verifying equality.
|
||
*/
|
||
desc = describe_info (info);
|
||
g_assert_cmpstr (desc, ==, description->str);
|
||
|
||
/* do extra checks for containers */
|
||
if (g_variant_type_is_array (type) ||
|
||
g_variant_type_is_maybe (type))
|
||
{
|
||
const GVariantType *element;
|
||
gsize efs1, efs2;
|
||
guint ea1, ea2;
|
||
|
||
element = g_variant_type_element (type);
|
||
calculate_type_info (element, &efs1, &ea1);
|
||
g_variant_type_info_query_element (info, &ea2, &efs2);
|
||
g_assert_cmpint (efs1, ==, efs2);
|
||
g_assert_cmpint (ea1, ==, ea2 + 1);
|
||
|
||
g_assert_cmpint (ea1, ==, alignment1);
|
||
g_assert_cmpint (0, ==, fixed_size1);
|
||
}
|
||
else if (g_variant_type_is_tuple (type) ||
|
||
g_variant_type_is_dict_entry (type))
|
||
{
|
||
/* make sure the "magic constants" are working */
|
||
check_offsets (info, type);
|
||
}
|
||
|
||
g_string_free (type_string, TRUE);
|
||
g_string_free (description, TRUE);
|
||
g_variant_type_info_unref (info);
|
||
g_variant_type_free (type);
|
||
g_free (desc);
|
||
}
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
#define MAX_FIXED_MULTIPLIER 256
|
||
#define MAX_INSTANCE_SIZE 1024
|
||
#define MAX_ARRAY_CHILDREN 128
|
||
#define MAX_TUPLE_CHILDREN 128
|
||
|
||
/* this function generates a random type such that all characteristics
|
||
* that are "interesting" to the serialiser are tested.
|
||
*
|
||
* this basically means:
|
||
* - test different alignments
|
||
* - test variable sized items and fixed sized items
|
||
* - test different fixed sizes
|
||
*/
|
||
static gchar *
|
||
random_type_string (void)
|
||
{
|
||
const guchar base_types[] = "ynix";
|
||
guchar base_type;
|
||
|
||
base_type = base_types[g_test_rand_int_range (0, 4)];
|
||
|
||
if (g_test_rand_bit ())
|
||
/* construct a fixed-sized type */
|
||
{
|
||
char type_string[MAX_FIXED_MULTIPLIER];
|
||
guint multiplier;
|
||
gsize i = 0;
|
||
|
||
multiplier = g_test_rand_int_range (1, sizeof type_string - 1);
|
||
|
||
type_string[i++] = '(';
|
||
while (multiplier--)
|
||
type_string[i++] = base_type;
|
||
type_string[i++] = ')';
|
||
|
||
return g_strndup (type_string, i);
|
||
}
|
||
else
|
||
/* construct a variable-sized type */
|
||
{
|
||
char type_string[2] = { 'a', base_type };
|
||
|
||
return g_strndup (type_string, 2);
|
||
}
|
||
}
|
||
|
||
typedef struct
|
||
{
|
||
GVariantTypeInfo *type_info;
|
||
guint alignment;
|
||
gsize size;
|
||
gboolean is_fixed_sized;
|
||
|
||
guint32 seed;
|
||
|
||
#define INSTANCE_MAGIC 1287582829
|
||
guint magic;
|
||
} RandomInstance;
|
||
|
||
static RandomInstance *
|
||
random_instance (GVariantTypeInfo *type_info)
|
||
{
|
||
RandomInstance *instance;
|
||
|
||
instance = g_slice_new (RandomInstance);
|
||
|
||
if (type_info == NULL)
|
||
{
|
||
gchar *str = random_type_string ();
|
||
instance->type_info = g_variant_type_info_get (G_VARIANT_TYPE (str));
|
||
g_free (str);
|
||
}
|
||
else
|
||
instance->type_info = g_variant_type_info_ref (type_info);
|
||
|
||
instance->seed = g_test_rand_int ();
|
||
|
||
g_variant_type_info_query (instance->type_info,
|
||
&instance->alignment,
|
||
&instance->size);
|
||
|
||
instance->is_fixed_sized = instance->size != 0;
|
||
|
||
if (!instance->is_fixed_sized)
|
||
instance->size = g_test_rand_int_range (0, MAX_INSTANCE_SIZE);
|
||
|
||
instance->magic = INSTANCE_MAGIC;
|
||
|
||
return instance;
|
||
}
|
||
|
||
static void
|
||
random_instance_free (RandomInstance *instance)
|
||
{
|
||
g_variant_type_info_unref (instance->type_info);
|
||
g_slice_free (RandomInstance, instance);
|
||
}
|
||
|
||
static void
|
||
append_instance_size (RandomInstance *instance,
|
||
gsize *offset)
|
||
{
|
||
*offset += (-*offset) & instance->alignment;
|
||
*offset += instance->size;
|
||
}
|
||
|
||
static void
|
||
random_instance_write (RandomInstance *instance,
|
||
guchar *buffer)
|
||
{
|
||
GRand *rand;
|
||
gsize i;
|
||
|
||
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++)
|
||
buffer[i] = g_rand_int (rand);
|
||
g_rand_free (rand);
|
||
}
|
||
|
||
static void
|
||
append_instance_data (RandomInstance *instance,
|
||
guchar **buffer)
|
||
{
|
||
while (((gsize) *buffer) & instance->alignment)
|
||
*(*buffer)++ = '\0';
|
||
|
||
random_instance_write (instance, *buffer);
|
||
*buffer += instance->size;
|
||
}
|
||
|
||
static gboolean
|
||
random_instance_assert (RandomInstance *instance,
|
||
guchar *buffer,
|
||
gsize size)
|
||
{
|
||
GRand *rand;
|
||
gsize i;
|
||
|
||
g_assert_cmpint ((gsize) buffer & ALIGN_BITS & instance->alignment, ==, 0);
|
||
g_assert_cmpint (size, ==, instance->size);
|
||
|
||
rand = g_rand_new_with_seed (instance->seed);
|
||
for (i = 0; i < instance->size; i++)
|
||
{
|
||
guchar byte = g_rand_int (rand);
|
||
|
||
g_assert_cmpuint (buffer[i], ==, byte);
|
||
}
|
||
g_rand_free (rand);
|
||
|
||
return i == instance->size;
|
||
}
|
||
|
||
static gboolean
|
||
random_instance_check (RandomInstance *instance,
|
||
guchar *buffer,
|
||
gsize size)
|
||
{
|
||
GRand *rand;
|
||
gsize i;
|
||
|
||
g_assert_cmpint ((gsize) buffer & ALIGN_BITS & instance->alignment, ==, 0);
|
||
|
||
if (size != instance->size)
|
||
return FALSE;
|
||
|
||
rand = g_rand_new_with_seed (instance->seed);
|
||
for (i = 0; i < instance->size; i++)
|
||
if (buffer[i] != (guchar) g_rand_int (rand))
|
||
break;
|
||
g_rand_free (rand);
|
||
|
||
return i == instance->size;
|
||
}
|
||
|
||
static void
|
||
random_instance_filler (GVariantSerialised *serialised,
|
||
gpointer data)
|
||
{
|
||
RandomInstance *instance = data;
|
||
|
||
g_assert_cmpuint (instance->magic, ==, INSTANCE_MAGIC);
|
||
|
||
if (serialised->type_info == NULL)
|
||
serialised->type_info = instance->type_info;
|
||
|
||
if (serialised->size == 0)
|
||
serialised->size = instance->size;
|
||
|
||
serialised->depth = 0;
|
||
|
||
g_assert_true (serialised->type_info == instance->type_info);
|
||
g_assert_cmpuint (serialised->size, ==, instance->size);
|
||
|
||
if (serialised->data)
|
||
random_instance_write (instance, serialised->data);
|
||
}
|
||
|
||
static gsize
|
||
calculate_offset_size (gsize body_size,
|
||
gsize n_offsets)
|
||
{
|
||
if (body_size == 0)
|
||
return 0;
|
||
|
||
if (body_size + n_offsets <= G_MAXUINT8)
|
||
return 1;
|
||
|
||
if (body_size + 2 * n_offsets <= G_MAXUINT16)
|
||
return 2;
|
||
|
||
if (body_size + 4 * n_offsets <= G_MAXUINT32)
|
||
return 4;
|
||
|
||
/* the test case won't generate anything bigger */
|
||
g_assert_not_reached ();
|
||
}
|
||
|
||
static gpointer
|
||
flavoured_malloc (gsize size, gsize flavour)
|
||
{
|
||
g_assert_cmpuint (flavour, <, 8);
|
||
|
||
if (size == 0)
|
||
return NULL;
|
||
|
||
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
|
||
align_free (gpointer mem)
|
||
{
|
||
free (mem);
|
||
}
|
||
|
||
static void
|
||
append_offset (guchar **offset_ptr,
|
||
gsize offset,
|
||
guint offset_size)
|
||
{
|
||
union
|
||
{
|
||
guchar bytes[sizeof (gsize)];
|
||
gsize integer;
|
||
} tmpvalue;
|
||
|
||
tmpvalue.integer = GSIZE_TO_LE (offset);
|
||
memcpy (*offset_ptr, tmpvalue.bytes, offset_size);
|
||
*offset_ptr += offset_size;
|
||
}
|
||
|
||
static void
|
||
prepend_offset (guchar **offset_ptr,
|
||
gsize offset,
|
||
guint offset_size)
|
||
{
|
||
union
|
||
{
|
||
guchar bytes[sizeof (gsize)];
|
||
gsize integer;
|
||
} tmpvalue;
|
||
|
||
*offset_ptr -= offset_size;
|
||
tmpvalue.integer = GSIZE_TO_LE (offset);
|
||
memcpy (*offset_ptr, tmpvalue.bytes, offset_size);
|
||
}
|
||
|
||
static void
|
||
test_maybe (void)
|
||
{
|
||
GVariantTypeInfo *type_info;
|
||
RandomInstance *instance;
|
||
gsize needed_size;
|
||
guchar *data;
|
||
|
||
instance = random_instance (NULL);
|
||
|
||
{
|
||
const gchar *element;
|
||
gchar *tmp;
|
||
|
||
element = g_variant_type_info_get_type_string (instance->type_info);
|
||
tmp = g_strdup_printf ("m%s", element);
|
||
type_info = g_variant_type_info_get (G_VARIANT_TYPE (tmp));
|
||
g_free (tmp);
|
||
}
|
||
|
||
needed_size = g_variant_serialiser_needed_size (type_info,
|
||
random_instance_filler,
|
||
NULL, 0);
|
||
g_assert_cmpint (needed_size, ==, 0);
|
||
|
||
needed_size = g_variant_serialiser_needed_size (type_info,
|
||
random_instance_filler,
|
||
(gpointer *) &instance, 1);
|
||
|
||
if (instance->is_fixed_sized)
|
||
g_assert_cmpint (needed_size, ==, instance->size);
|
||
else
|
||
g_assert_cmpint (needed_size, ==, instance->size + 1);
|
||
|
||
{
|
||
guchar *ptr;
|
||
|
||
ptr = data = align_malloc (needed_size);
|
||
append_instance_data (instance, &ptr);
|
||
|
||
if (!instance->is_fixed_sized)
|
||
*ptr++ = '\0';
|
||
|
||
g_assert_cmpint (ptr - data, ==, needed_size);
|
||
}
|
||
|
||
{
|
||
guint alignment;
|
||
gsize flavour;
|
||
|
||
alignment = (instance->alignment & ALIGN_BITS) + 1;
|
||
|
||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||
{
|
||
GVariantSerialised serialised;
|
||
GVariantSerialised child;
|
||
|
||
serialised.type_info = type_info;
|
||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||
serialised.size = needed_size;
|
||
serialised.depth = 0;
|
||
|
||
g_variant_serialiser_serialise (serialised,
|
||
random_instance_filler,
|
||
(gpointer *) &instance, 1);
|
||
child = g_variant_serialised_get_child (serialised, 0);
|
||
g_assert_true (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, flavour);
|
||
}
|
||
}
|
||
|
||
g_variant_type_info_unref (type_info);
|
||
random_instance_free (instance);
|
||
align_free (data);
|
||
}
|
||
|
||
static void
|
||
test_maybes (void)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < 1000; i++)
|
||
test_maybe ();
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static void
|
||
test_array (void)
|
||
{
|
||
GVariantTypeInfo *element_info;
|
||
GVariantTypeInfo *array_info;
|
||
RandomInstance **instances;
|
||
gsize needed_size;
|
||
gsize offset_size;
|
||
guint n_children;
|
||
guchar *data;
|
||
|
||
{
|
||
gchar *element_type, *array_type;
|
||
|
||
element_type = random_type_string ();
|
||
array_type = g_strdup_printf ("a%s", element_type);
|
||
|
||
element_info = g_variant_type_info_get (G_VARIANT_TYPE (element_type));
|
||
array_info = g_variant_type_info_get (G_VARIANT_TYPE (array_type));
|
||
g_assert_true (g_variant_type_info_element (array_info) == element_info);
|
||
|
||
g_free (element_type);
|
||
g_free (array_type);
|
||
}
|
||
|
||
{
|
||
gsize i;
|
||
|
||
n_children = g_test_rand_int_range (0, MAX_ARRAY_CHILDREN);
|
||
instances = g_new (RandomInstance *, n_children);
|
||
for (i = 0; i < n_children; i++)
|
||
instances[i] = random_instance (element_info);
|
||
}
|
||
|
||
needed_size = g_variant_serialiser_needed_size (array_info,
|
||
random_instance_filler,
|
||
(gpointer *) instances,
|
||
n_children);
|
||
|
||
{
|
||
gsize element_fixed_size;
|
||
gsize body_size = 0;
|
||
gsize i;
|
||
|
||
for (i = 0; i < n_children; i++)
|
||
append_instance_size (instances[i], &body_size);
|
||
|
||
g_variant_type_info_query (element_info, NULL, &element_fixed_size);
|
||
|
||
if (!element_fixed_size)
|
||
{
|
||
offset_size = calculate_offset_size (body_size, n_children);
|
||
|
||
if (offset_size == 0)
|
||
offset_size = 1;
|
||
}
|
||
else
|
||
offset_size = 0;
|
||
|
||
g_assert_cmpint (needed_size, ==, body_size + n_children * offset_size);
|
||
}
|
||
|
||
{
|
||
guchar *offset_ptr, *body_ptr;
|
||
gsize i;
|
||
|
||
body_ptr = data = align_malloc (needed_size);
|
||
offset_ptr = body_ptr + needed_size - offset_size * n_children;
|
||
|
||
for (i = 0; i < n_children; i++)
|
||
{
|
||
append_instance_data (instances[i], &body_ptr);
|
||
append_offset (&offset_ptr, body_ptr - data, offset_size);
|
||
}
|
||
|
||
g_assert_true (body_ptr == data + needed_size - offset_size * n_children);
|
||
g_assert_true (offset_ptr == data + needed_size);
|
||
}
|
||
|
||
{
|
||
guint alignment;
|
||
gsize flavour;
|
||
gsize i;
|
||
|
||
g_variant_type_info_query (array_info, &alignment, NULL);
|
||
alignment = (alignment & ALIGN_BITS) + 1;
|
||
|
||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||
{
|
||
GVariantSerialised serialised;
|
||
|
||
serialised.type_info = array_info;
|
||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||
serialised.size = needed_size;
|
||
serialised.depth = 0;
|
||
|
||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||
(gpointer *) instances, n_children);
|
||
|
||
if (serialised.size)
|
||
g_assert_cmpint (memcmp (serialised.data, data, serialised.size), ==, 0);
|
||
|
||
g_assert_cmpuint (g_variant_serialised_n_children (serialised), ==, n_children);
|
||
|
||
for (i = 0; i < n_children; i++)
|
||
{
|
||
GVariantSerialised child;
|
||
|
||
child = g_variant_serialised_get_child (serialised, i);
|
||
g_assert_true (child.type_info == instances[i]->type_info);
|
||
random_instance_assert (instances[i], child.data, child.size);
|
||
g_variant_type_info_unref (child.type_info);
|
||
}
|
||
|
||
flavoured_free (serialised.data, flavour);
|
||
}
|
||
}
|
||
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < n_children; i++)
|
||
random_instance_free (instances[i]);
|
||
g_free (instances);
|
||
}
|
||
|
||
g_variant_type_info_unref (element_info);
|
||
g_variant_type_info_unref (array_info);
|
||
align_free (data);
|
||
}
|
||
|
||
static void
|
||
test_arrays (void)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < 100; i++)
|
||
test_array ();
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static void
|
||
test_tuple (void)
|
||
{
|
||
GVariantTypeInfo *type_info;
|
||
RandomInstance **instances;
|
||
gboolean fixed_size;
|
||
gsize needed_size;
|
||
gsize offset_size;
|
||
guint n_children;
|
||
guint alignment;
|
||
guchar *data;
|
||
|
||
n_children = g_test_rand_int_range (0, MAX_TUPLE_CHILDREN);
|
||
instances = g_new (RandomInstance *, n_children);
|
||
|
||
{
|
||
GString *type_string;
|
||
gsize i;
|
||
|
||
fixed_size = TRUE;
|
||
alignment = 0;
|
||
|
||
type_string = g_string_new ("(");
|
||
for (i = 0; i < n_children; i++)
|
||
{
|
||
const gchar *str;
|
||
|
||
instances[i] = random_instance (NULL);
|
||
|
||
alignment |= instances[i]->alignment;
|
||
if (!instances[i]->is_fixed_sized)
|
||
fixed_size = FALSE;
|
||
|
||
str = g_variant_type_info_get_type_string (instances[i]->type_info);
|
||
g_string_append (type_string, str);
|
||
}
|
||
g_string_append_c (type_string, ')');
|
||
|
||
type_info = g_variant_type_info_get (G_VARIANT_TYPE (type_string->str));
|
||
g_string_free (type_string, TRUE);
|
||
}
|
||
|
||
needed_size = g_variant_serialiser_needed_size (type_info,
|
||
random_instance_filler,
|
||
(gpointer *) instances,
|
||
n_children);
|
||
{
|
||
gsize body_size = 0;
|
||
gsize offsets = 0;
|
||
gsize i;
|
||
|
||
for (i = 0; i < n_children; i++)
|
||
{
|
||
append_instance_size (instances[i], &body_size);
|
||
|
||
if (i != n_children - 1 && !instances[i]->is_fixed_sized)
|
||
offsets++;
|
||
}
|
||
|
||
if (fixed_size)
|
||
{
|
||
body_size += (-body_size) & alignment;
|
||
|
||
g_assert_true ((body_size == 0) == (n_children == 0));
|
||
if (n_children == 0)
|
||
body_size = 1;
|
||
}
|
||
|
||
offset_size = calculate_offset_size (body_size, offsets);
|
||
g_assert_cmpint (needed_size, ==, body_size + offsets * offset_size);
|
||
}
|
||
|
||
{
|
||
guchar *body_ptr;
|
||
guchar *ofs_ptr;
|
||
gsize i;
|
||
|
||
body_ptr = data = align_malloc (needed_size);
|
||
ofs_ptr = body_ptr + needed_size;
|
||
|
||
for (i = 0; i < n_children; i++)
|
||
{
|
||
append_instance_data (instances[i], &body_ptr);
|
||
|
||
if (i != n_children - 1 && !instances[i]->is_fixed_sized)
|
||
prepend_offset (&ofs_ptr, body_ptr - data, offset_size);
|
||
}
|
||
|
||
if (fixed_size)
|
||
{
|
||
while (((gsize) body_ptr) & alignment)
|
||
*body_ptr++ = '\0';
|
||
|
||
g_assert_true ((body_ptr == data) == (n_children == 0));
|
||
if (n_children == 0)
|
||
*body_ptr++ = '\0';
|
||
|
||
}
|
||
|
||
|
||
g_assert_true (body_ptr == ofs_ptr);
|
||
}
|
||
|
||
{
|
||
gsize flavour;
|
||
gsize i;
|
||
|
||
alignment = (alignment & ALIGN_BITS) + 1;
|
||
|
||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||
{
|
||
GVariantSerialised serialised;
|
||
|
||
serialised.type_info = type_info;
|
||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||
serialised.size = needed_size;
|
||
serialised.depth = 0;
|
||
|
||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||
(gpointer *) instances, n_children);
|
||
|
||
if (serialised.size)
|
||
g_assert_cmpint (memcmp (serialised.data, data, serialised.size), ==, 0);
|
||
|
||
g_assert_cmpuint (g_variant_serialised_n_children (serialised), ==, n_children);
|
||
|
||
for (i = 0; i < n_children; i++)
|
||
{
|
||
GVariantSerialised child;
|
||
|
||
child = g_variant_serialised_get_child (serialised, i);
|
||
g_assert_true (child.type_info == instances[i]->type_info);
|
||
random_instance_assert (instances[i], child.data, child.size);
|
||
g_variant_type_info_unref (child.type_info);
|
||
}
|
||
|
||
flavoured_free (serialised.data, flavour);
|
||
}
|
||
}
|
||
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < n_children; i++)
|
||
random_instance_free (instances[i]);
|
||
g_free (instances);
|
||
}
|
||
|
||
g_variant_type_info_unref (type_info);
|
||
align_free (data);
|
||
}
|
||
|
||
static void
|
||
test_tuples (void)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < 100; i++)
|
||
test_tuple ();
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static void
|
||
test_variant (void)
|
||
{
|
||
GVariantTypeInfo *type_info;
|
||
RandomInstance *instance;
|
||
const gchar *type_string;
|
||
gsize needed_size;
|
||
guchar *data;
|
||
gsize len;
|
||
|
||
type_info = g_variant_type_info_get (G_VARIANT_TYPE_VARIANT);
|
||
instance = random_instance (NULL);
|
||
|
||
type_string = g_variant_type_info_get_type_string (instance->type_info);
|
||
len = strlen (type_string);
|
||
|
||
needed_size = g_variant_serialiser_needed_size (type_info,
|
||
random_instance_filler,
|
||
(gpointer *) &instance, 1);
|
||
|
||
g_assert_cmpint (needed_size, ==, instance->size + 1 + len);
|
||
|
||
{
|
||
guchar *ptr;
|
||
|
||
ptr = data = align_malloc (needed_size);
|
||
append_instance_data (instance, &ptr);
|
||
*ptr++ = '\0';
|
||
memcpy (ptr, type_string, len);
|
||
ptr += len;
|
||
|
||
g_assert_true (data + needed_size == ptr);
|
||
}
|
||
|
||
{
|
||
gsize alignment;
|
||
gsize flavour;
|
||
|
||
/* variants are always 8-aligned */
|
||
alignment = ALIGN_BITS + 1;
|
||
|
||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||
{
|
||
GVariantSerialised serialised;
|
||
GVariantSerialised child;
|
||
|
||
serialised.type_info = type_info;
|
||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||
serialised.size = needed_size;
|
||
serialised.depth = 0;
|
||
|
||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||
(gpointer *) &instance, 1);
|
||
|
||
if (serialised.size)
|
||
g_assert_cmpint (memcmp (serialised.data, data, serialised.size), ==, 0);
|
||
|
||
g_assert_cmpuint (g_variant_serialised_n_children (serialised), ==, 1);
|
||
|
||
child = g_variant_serialised_get_child (serialised, 0);
|
||
g_assert_true (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);
|
||
align_free (data);
|
||
}
|
||
|
||
static void
|
||
test_variants (void)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < 100; i++)
|
||
test_variant ();
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static void
|
||
test_strings (void)
|
||
{
|
||
struct {
|
||
guint flags;
|
||
guint size;
|
||
gconstpointer data;
|
||
} test_cases[] = {
|
||
#define is_nval 0
|
||
#define is_string 1
|
||
#define is_objpath is_string | 2
|
||
#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" },
|
||
{ is_string, 3, "//" },
|
||
{ is_objpath, 11, "/some/path" },
|
||
{ is_string, 12, "/some/path/" },
|
||
{ is_nval, 11, "/some\0path" },
|
||
{ is_string, 11, "/some\\path" },
|
||
{ is_string, 12, "/some//path" },
|
||
{ is_string, 12, "/some-/path" },
|
||
|
||
{ is_sig, 2, "i" },
|
||
{ is_sig, 2, "s" },
|
||
{ is_sig, 5, "(si)" },
|
||
{ is_string, 4, "(si" },
|
||
{ is_string, 2, "*" },
|
||
{ is_sig, 3, "ai" },
|
||
{ is_string, 3, "mi" },
|
||
{ is_string, 2, "r" },
|
||
{ is_sig, 15, "(yyy{sv}ssiai)" },
|
||
{ is_string, 16, "(yyy{yv}ssiai))" },
|
||
{ is_string, 15, "(yyy{vv}ssiai)" },
|
||
{ is_string, 15, "(yyy{sv)ssiai}" }
|
||
};
|
||
gsize i;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (test_cases); i++)
|
||
{
|
||
guint flags;
|
||
|
||
flags = g_variant_serialiser_is_string (test_cases[i].data,
|
||
test_cases[i].size)
|
||
? 1 : 0;
|
||
|
||
flags |= g_variant_serialiser_is_object_path (test_cases[i].data,
|
||
test_cases[i].size)
|
||
? 2 : 0;
|
||
|
||
flags |= g_variant_serialiser_is_signature (test_cases[i].data,
|
||
test_cases[i].size)
|
||
? 4 : 0;
|
||
|
||
g_assert_cmpuint (flags, ==, test_cases[i].flags);
|
||
}
|
||
}
|
||
|
||
typedef struct _TreeInstance TreeInstance;
|
||
struct _TreeInstance
|
||
{
|
||
GVariantTypeInfo *info;
|
||
|
||
TreeInstance **children;
|
||
gsize n_children;
|
||
|
||
union {
|
||
guint64 integer;
|
||
gdouble floating;
|
||
gchar string[200];
|
||
} data;
|
||
gsize data_size;
|
||
};
|
||
|
||
static GVariantType *
|
||
make_random_definite_type (int depth)
|
||
{
|
||
GString *description;
|
||
GString *type_string;
|
||
GVariantType *type;
|
||
|
||
description = g_string_new (NULL);
|
||
type_string = g_string_new (NULL);
|
||
type = append_type_string (type_string, description, TRUE, depth);
|
||
g_string_free (description, TRUE);
|
||
g_string_free (type_string, TRUE);
|
||
|
||
return type;
|
||
}
|
||
|
||
static void
|
||
make_random_string (gchar *string,
|
||
gsize size,
|
||
const GVariantType *type)
|
||
{
|
||
gsize i;
|
||
|
||
/* create strings that are valid signature strings */
|
||
#define good_chars "bynqiuxthdsog"
|
||
|
||
for (i = 0; i < size - 1; i++)
|
||
string[i] = good_chars[g_test_rand_int_range (0, strlen (good_chars))];
|
||
string[i] = '\0';
|
||
|
||
/* in case we need an object path, prefix a '/' */
|
||
if (*g_variant_type_peek_string (type) == 'o')
|
||
string[0] = '/';
|
||
|
||
#undef good_chars
|
||
}
|
||
|
||
static TreeInstance *
|
||
tree_instance_new (const GVariantType *type,
|
||
int depth)
|
||
{
|
||
const GVariantType *child_type = NULL;
|
||
GVariantType *mytype = NULL;
|
||
TreeInstance *instance;
|
||
gboolean is_tuple_type;
|
||
|
||
if (type == NULL)
|
||
type = mytype = make_random_definite_type (depth);
|
||
|
||
instance = g_slice_new (TreeInstance);
|
||
instance->info = g_variant_type_info_get (type);
|
||
instance->children = NULL;
|
||
instance->n_children = 0;
|
||
instance->data_size = 0;
|
||
|
||
is_tuple_type = FALSE;
|
||
|
||
switch (*g_variant_type_peek_string (type))
|
||
{
|
||
case G_VARIANT_TYPE_INFO_CHAR_MAYBE:
|
||
instance->n_children = g_test_rand_int_range (0, 2);
|
||
child_type = g_variant_type_element (type);
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_ARRAY:
|
||
instance->n_children = g_test_rand_int_range (0, MAX_ARRAY_CHILDREN);
|
||
child_type = g_variant_type_element (type);
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY:
|
||
case G_VARIANT_TYPE_INFO_CHAR_TUPLE:
|
||
instance->n_children = g_variant_type_n_items (type);
|
||
child_type = g_variant_type_first (type);
|
||
is_tuple_type = TRUE;
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_VARIANT:
|
||
instance->n_children = 1;
|
||
child_type = NULL;
|
||
break;
|
||
|
||
case 'b':
|
||
instance->data.integer = g_test_rand_int_range (0, 2);
|
||
instance->data_size = 1;
|
||
break;
|
||
|
||
case 'y':
|
||
instance->data.integer = g_test_rand_int ();
|
||
instance->data_size = 1;
|
||
break;
|
||
|
||
case 'n': case 'q':
|
||
instance->data.integer = g_test_rand_int ();
|
||
instance->data_size = 2;
|
||
break;
|
||
|
||
case 'i': case 'u': case 'h':
|
||
instance->data.integer = g_test_rand_int ();
|
||
instance->data_size = 4;
|
||
break;
|
||
|
||
case 'x': case 't':
|
||
instance->data.integer = g_test_rand_int ();
|
||
instance->data.integer <<= 32;
|
||
instance->data.integer |= (guint32) g_test_rand_int ();
|
||
instance->data_size = 8;
|
||
break;
|
||
|
||
case 'd':
|
||
instance->data.floating = g_test_rand_double ();
|
||
instance->data_size = 8;
|
||
break;
|
||
|
||
case 's': case 'o': case 'g':
|
||
instance->data_size = g_test_rand_int_range (10, 200);
|
||
make_random_string (instance->data.string, instance->data_size, type);
|
||
break;
|
||
}
|
||
|
||
if (instance->data_size == 0)
|
||
/* no data -> it is a container */
|
||
{
|
||
gsize i;
|
||
|
||
instance->children = g_new (TreeInstance *, instance->n_children);
|
||
|
||
for (i = 0; i < instance->n_children; i++)
|
||
{
|
||
instance->children[i] = tree_instance_new (child_type, depth - 1);
|
||
|
||
if (is_tuple_type)
|
||
child_type = g_variant_type_next (child_type);
|
||
}
|
||
|
||
g_assert_true (!is_tuple_type || child_type == NULL);
|
||
}
|
||
|
||
g_variant_type_free (mytype);
|
||
|
||
return instance;
|
||
}
|
||
|
||
static void
|
||
tree_instance_free (TreeInstance *instance)
|
||
{
|
||
gsize i;
|
||
|
||
g_variant_type_info_unref (instance->info);
|
||
for (i = 0; i < instance->n_children; i++)
|
||
tree_instance_free (instance->children[i]);
|
||
g_free (instance->children);
|
||
g_slice_free (TreeInstance, instance);
|
||
}
|
||
|
||
static gboolean i_am_writing_byteswapped;
|
||
|
||
static void
|
||
tree_filler (GVariantSerialised *serialised,
|
||
gpointer data)
|
||
{
|
||
TreeInstance *instance = data;
|
||
|
||
if (serialised->type_info == NULL)
|
||
serialised->type_info = instance->info;
|
||
|
||
serialised->depth = 0;
|
||
|
||
if (instance->data_size == 0)
|
||
/* is a container */
|
||
{
|
||
if (serialised->size == 0)
|
||
serialised->size =
|
||
g_variant_serialiser_needed_size (instance->info, tree_filler,
|
||
(gpointer *) instance->children,
|
||
instance->n_children);
|
||
|
||
if (serialised->data)
|
||
g_variant_serialiser_serialise (*serialised, tree_filler,
|
||
(gpointer *) instance->children,
|
||
instance->n_children);
|
||
}
|
||
else
|
||
/* it is a leaf */
|
||
{
|
||
if (serialised->size == 0)
|
||
serialised->size = instance->data_size;
|
||
|
||
if (serialised->data)
|
||
{
|
||
switch (instance->data_size)
|
||
{
|
||
case 1:
|
||
*serialised->data = instance->data.integer;
|
||
break;
|
||
|
||
case 2:
|
||
{
|
||
guint16 value = instance->data.integer;
|
||
|
||
if (i_am_writing_byteswapped)
|
||
value = GUINT16_SWAP_LE_BE (value);
|
||
|
||
*(guint16 *) serialised->data = value;
|
||
}
|
||
break;
|
||
|
||
case 4:
|
||
{
|
||
guint32 value = instance->data.integer;
|
||
|
||
if (i_am_writing_byteswapped)
|
||
value = GUINT32_SWAP_LE_BE (value);
|
||
|
||
*(guint32 *) serialised->data = value;
|
||
}
|
||
break;
|
||
|
||
case 8:
|
||
{
|
||
guint64 value = instance->data.integer;
|
||
|
||
if (i_am_writing_byteswapped)
|
||
value = GUINT64_SWAP_LE_BE (value);
|
||
|
||
*(guint64 *) serialised->data = value;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
memcpy (serialised->data,
|
||
instance->data.string,
|
||
instance->data_size);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
check_tree (TreeInstance *instance,
|
||
GVariantSerialised serialised)
|
||
{
|
||
if (instance->info != serialised.type_info)
|
||
return FALSE;
|
||
|
||
if (instance->data_size == 0)
|
||
/* is a container */
|
||
{
|
||
gsize i;
|
||
|
||
if (g_variant_serialised_n_children (serialised) !=
|
||
instance->n_children)
|
||
return FALSE;
|
||
|
||
for (i = 0; i < instance->n_children; i++)
|
||
{
|
||
GVariantSerialised child;
|
||
gpointer data = NULL;
|
||
gboolean ok;
|
||
|
||
child = g_variant_serialised_get_child (serialised, i);
|
||
if (child.size && child.data == NULL)
|
||
child.data = data = g_malloc0 (child.size);
|
||
ok = check_tree (instance->children[i], child);
|
||
g_variant_type_info_unref (child.type_info);
|
||
g_free (data);
|
||
|
||
if (!ok)
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
else
|
||
/* it is a leaf */
|
||
{
|
||
switch (instance->data_size)
|
||
{
|
||
case 1:
|
||
g_assert_cmpuint (serialised.size, ==, 1);
|
||
return *(guint8 *) serialised.data ==
|
||
(guint8) instance->data.integer;
|
||
|
||
case 2:
|
||
g_assert_cmpuint (serialised.size, ==, 2);
|
||
return *(guint16 *) serialised.data ==
|
||
(guint16) instance->data.integer;
|
||
|
||
case 4:
|
||
g_assert_cmpuint (serialised.size, ==, 4);
|
||
return *(guint32 *) serialised.data ==
|
||
(guint32) instance->data.integer;
|
||
|
||
case 8:
|
||
g_assert_cmpuint (serialised.size, ==, 8);
|
||
return *(guint64 *) serialised.data ==
|
||
(guint64) instance->data.integer;
|
||
|
||
default:
|
||
if (serialised.size != instance->data_size)
|
||
return FALSE;
|
||
|
||
return memcmp (serialised.data,
|
||
instance->data.string,
|
||
instance->data_size) == 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
serialise_tree (TreeInstance *tree,
|
||
GVariantSerialised *serialised)
|
||
{
|
||
GVariantSerialised empty = {0, };
|
||
|
||
*serialised = empty;
|
||
tree_filler (serialised, tree);
|
||
serialised->data = g_malloc (serialised->size);
|
||
tree_filler (serialised, tree);
|
||
}
|
||
|
||
static void
|
||
test_byteswap (void)
|
||
{
|
||
GVariantSerialised one, two;
|
||
TreeInstance *tree;
|
||
|
||
tree = tree_instance_new (NULL, 3);
|
||
serialise_tree (tree, &one);
|
||
|
||
i_am_writing_byteswapped = TRUE;
|
||
serialise_tree (tree, &two);
|
||
i_am_writing_byteswapped = FALSE;
|
||
|
||
g_variant_serialised_byteswap (two);
|
||
|
||
g_assert_cmpmem (one.data, one.size, two.data, two.size);
|
||
g_assert_cmpuint (one.depth, ==, two.depth);
|
||
|
||
tree_instance_free (tree);
|
||
g_free (one.data);
|
||
g_free (two.data);
|
||
}
|
||
|
||
static void
|
||
test_byteswaps (void)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < 200; i++)
|
||
test_byteswap ();
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static void
|
||
test_serialiser_children (void)
|
||
{
|
||
GBytes *data1, *data2;
|
||
GVariant *child1, *child2;
|
||
GVariantType *mv_type = g_variant_type_new_maybe (G_VARIANT_TYPE_VARIANT);
|
||
GVariant *variant, *child;
|
||
|
||
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1865");
|
||
g_test_summary ("Test that getting a child variant before and after "
|
||
"serialisation of the parent works");
|
||
|
||
/* Construct a variable sized array containing a child which serialises to a
|
||
* zero-length bytestring. */
|
||
child = g_variant_new_maybe (G_VARIANT_TYPE_VARIANT, NULL);
|
||
variant = g_variant_new_array (mv_type, &child, 1);
|
||
|
||
/* Get the child before serialising. */
|
||
child1 = g_variant_get_child_value (variant, 0);
|
||
data1 = g_variant_get_data_as_bytes (child1);
|
||
|
||
/* Serialise the parent variant. */
|
||
g_variant_get_data (variant);
|
||
|
||
/* Get the child again after serialising — this uses a different code path. */
|
||
child2 = g_variant_get_child_value (variant, 0);
|
||
data2 = g_variant_get_data_as_bytes (child2);
|
||
|
||
/* Check things are equal. */
|
||
g_assert_cmpvariant (child1, child2);
|
||
g_assert_true (g_bytes_equal (data1, data2));
|
||
|
||
g_variant_unref (child2);
|
||
g_variant_unref (child1);
|
||
g_variant_unref (variant);
|
||
g_bytes_unref (data2);
|
||
g_bytes_unref (data1);
|
||
g_variant_type_free (mv_type);
|
||
}
|
||
|
||
static void
|
||
test_fuzz (gdouble *fuzziness)
|
||
{
|
||
GVariantSerialised serialised;
|
||
TreeInstance *tree;
|
||
|
||
/* make an instance */
|
||
tree = tree_instance_new (NULL, 3);
|
||
|
||
/* serialise it */
|
||
serialise_tree (tree, &serialised);
|
||
|
||
g_assert_true (g_variant_serialised_is_normal (serialised));
|
||
g_assert_true (check_tree (tree, serialised));
|
||
|
||
if (serialised.size)
|
||
{
|
||
gboolean fuzzed = FALSE;
|
||
gboolean a, b;
|
||
|
||
while (!fuzzed)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < serialised.size; i++)
|
||
if (randomly (*fuzziness))
|
||
{
|
||
serialised.data[i] += g_test_rand_int_range (1, 256);
|
||
fuzzed = TRUE;
|
||
}
|
||
}
|
||
|
||
/* at least one byte in the serialised data has changed.
|
||
*
|
||
* this means that at least one of the following is true:
|
||
*
|
||
* - the serialised data now represents a different value:
|
||
* check_tree() will return FALSE
|
||
*
|
||
* - the serialised data is in non-normal form:
|
||
* g_variant_serialiser_is_normal() will return FALSE
|
||
*
|
||
* we always do both checks to increase exposure of the serialiser
|
||
* to corrupt data.
|
||
*/
|
||
a = g_variant_serialised_is_normal (serialised);
|
||
b = check_tree (tree, serialised);
|
||
|
||
g_assert_true (!a || !b);
|
||
}
|
||
|
||
tree_instance_free (tree);
|
||
g_free (serialised.data);
|
||
}
|
||
|
||
|
||
static void
|
||
test_fuzzes (gpointer data)
|
||
{
|
||
gdouble fuzziness;
|
||
int i;
|
||
|
||
fuzziness = GPOINTER_TO_INT (data) / 100.;
|
||
|
||
for (i = 0; i < 200; i++)
|
||
test_fuzz (&fuzziness);
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static GVariant *
|
||
tree_instance_get_gvariant (TreeInstance *tree)
|
||
{
|
||
const GVariantType *type;
|
||
GVariant *result;
|
||
|
||
type = (GVariantType *) g_variant_type_info_get_type_string (tree->info);
|
||
|
||
switch (g_variant_type_info_get_type_char (tree->info))
|
||
{
|
||
case G_VARIANT_TYPE_INFO_CHAR_MAYBE:
|
||
{
|
||
const GVariantType *child_type;
|
||
GVariant *child;
|
||
|
||
if (tree->n_children)
|
||
child = tree_instance_get_gvariant (tree->children[0]);
|
||
else
|
||
child = NULL;
|
||
|
||
child_type = g_variant_type_element (type);
|
||
|
||
if (child != NULL && randomly (0.5))
|
||
child_type = NULL;
|
||
|
||
result = g_variant_new_maybe (child_type, child);
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_ARRAY:
|
||
{
|
||
const GVariantType *child_type;
|
||
GVariant **children;
|
||
gsize i;
|
||
|
||
children = g_new (GVariant *, tree->n_children);
|
||
for (i = 0; i < tree->n_children; i++)
|
||
children[i] = tree_instance_get_gvariant (tree->children[i]);
|
||
|
||
child_type = g_variant_type_element (type);
|
||
|
||
if (i > 0 && randomly (0.5))
|
||
child_type = NULL;
|
||
|
||
result = g_variant_new_array (child_type, children, tree->n_children);
|
||
g_free (children);
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_TUPLE:
|
||
{
|
||
GVariant **children;
|
||
gsize i;
|
||
|
||
children = g_new (GVariant *, tree->n_children);
|
||
for (i = 0; i < tree->n_children; i++)
|
||
children[i] = tree_instance_get_gvariant (tree->children[i]);
|
||
|
||
result = g_variant_new_tuple (children, tree->n_children);
|
||
g_free (children);
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY:
|
||
{
|
||
GVariant *key, *val;
|
||
|
||
g_assert_cmpuint (tree->n_children, ==, 2);
|
||
|
||
key = tree_instance_get_gvariant (tree->children[0]);
|
||
val = tree_instance_get_gvariant (tree->children[1]);
|
||
|
||
result = g_variant_new_dict_entry (key, val);
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_VARIANT:
|
||
{
|
||
GVariant *value;
|
||
|
||
g_assert_cmpuint (tree->n_children, ==, 1);
|
||
|
||
value = tree_instance_get_gvariant (tree->children[0]);
|
||
result = g_variant_new_variant (value);
|
||
}
|
||
break;
|
||
|
||
case 'b':
|
||
result = g_variant_new_boolean (tree->data.integer > 0);
|
||
break;
|
||
|
||
case 'y':
|
||
result = g_variant_new_byte (tree->data.integer);
|
||
break;
|
||
|
||
case 'n':
|
||
result = g_variant_new_int16 (tree->data.integer);
|
||
break;
|
||
|
||
case 'q':
|
||
result = g_variant_new_uint16 (tree->data.integer);
|
||
break;
|
||
|
||
case 'i':
|
||
result = g_variant_new_int32 (tree->data.integer);
|
||
break;
|
||
|
||
case 'u':
|
||
result = g_variant_new_uint32 (tree->data.integer);
|
||
break;
|
||
|
||
case 'x':
|
||
result = g_variant_new_int64 (tree->data.integer);
|
||
break;
|
||
|
||
case 't':
|
||
result = g_variant_new_uint64 (tree->data.integer);
|
||
break;
|
||
|
||
case 'h':
|
||
result = g_variant_new_handle (tree->data.integer);
|
||
break;
|
||
|
||
case 'd':
|
||
result = g_variant_new_double (tree->data.floating);
|
||
break;
|
||
|
||
case 's':
|
||
result = g_variant_new_string (tree->data.string);
|
||
break;
|
||
|
||
case 'o':
|
||
result = g_variant_new_object_path (tree->data.string);
|
||
break;
|
||
|
||
case 'g':
|
||
result = g_variant_new_signature (tree->data.string);
|
||
break;
|
||
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
static gboolean
|
||
tree_instance_check_gvariant (TreeInstance *tree,
|
||
GVariant *value)
|
||
{
|
||
const GVariantType *type;
|
||
|
||
type = (GVariantType *) g_variant_type_info_get_type_string (tree->info);
|
||
g_assert_true (g_variant_is_of_type (value, type));
|
||
|
||
switch (g_variant_type_info_get_type_char (tree->info))
|
||
{
|
||
case G_VARIANT_TYPE_INFO_CHAR_MAYBE:
|
||
{
|
||
GVariant *child;
|
||
gboolean equal;
|
||
|
||
child = g_variant_get_maybe (value);
|
||
|
||
if (child != NULL && tree->n_children == 1)
|
||
equal = tree_instance_check_gvariant (tree->children[0], child);
|
||
else if (child == NULL && tree->n_children == 0)
|
||
equal = TRUE;
|
||
else
|
||
equal = FALSE;
|
||
|
||
if (child != NULL)
|
||
g_variant_unref (child);
|
||
|
||
return equal;
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_ARRAY:
|
||
case G_VARIANT_TYPE_INFO_CHAR_TUPLE:
|
||
case G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY:
|
||
{
|
||
gsize i;
|
||
|
||
if (g_variant_n_children (value) != tree->n_children)
|
||
return FALSE;
|
||
|
||
for (i = 0; i < tree->n_children; i++)
|
||
{
|
||
GVariant *child;
|
||
gboolean equal;
|
||
|
||
child = g_variant_get_child_value (value, i);
|
||
equal = tree_instance_check_gvariant (tree->children[i], child);
|
||
g_variant_unref (child);
|
||
|
||
if (!equal)
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
break;
|
||
|
||
case G_VARIANT_TYPE_INFO_CHAR_VARIANT:
|
||
{
|
||
const gchar *str1, *str2;
|
||
GVariant *child;
|
||
gboolean equal;
|
||
|
||
child = g_variant_get_variant (value);
|
||
str1 = g_variant_get_type_string (child);
|
||
str2 = g_variant_type_info_get_type_string (tree->children[0]->info);
|
||
/* GVariant only keeps one copy of type strings around */
|
||
equal = str1 == str2 &&
|
||
tree_instance_check_gvariant (tree->children[0], child);
|
||
|
||
g_variant_unref (child);
|
||
|
||
return equal;
|
||
}
|
||
break;
|
||
|
||
case 'b':
|
||
return g_variant_get_boolean (value) == tree->data.integer;
|
||
|
||
case 'y':
|
||
return g_variant_get_byte (value) == (guchar) tree->data.integer;
|
||
|
||
case 'n':
|
||
return g_variant_get_int16 (value) == (gint16) tree->data.integer;
|
||
|
||
case 'q':
|
||
return g_variant_get_uint16 (value) == (guint16) tree->data.integer;
|
||
|
||
case 'i':
|
||
return g_variant_get_int32 (value) == (gint32) tree->data.integer;
|
||
|
||
case 'u':
|
||
return g_variant_get_uint32 (value) == (guint32) tree->data.integer;
|
||
|
||
case 'x':
|
||
return g_variant_get_int64 (value) == (gint64) tree->data.integer;
|
||
|
||
case 't':
|
||
return g_variant_get_uint64 (value) == (guint64) tree->data.integer;
|
||
|
||
case 'h':
|
||
return g_variant_get_handle (value) == (gint32) tree->data.integer;
|
||
|
||
case 'd':
|
||
{
|
||
gdouble floating = g_variant_get_double (value);
|
||
|
||
return memcmp (&floating, &tree->data.floating, sizeof floating) == 0;
|
||
}
|
||
|
||
case 's':
|
||
case 'o':
|
||
case 'g':
|
||
return strcmp (g_variant_get_string (value, NULL),
|
||
tree->data.string) == 0;
|
||
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
static void
|
||
tree_instance_build_gvariant (TreeInstance *tree,
|
||
GVariantBuilder *builder,
|
||
gboolean guess_ok)
|
||
{
|
||
const GVariantType *type;
|
||
|
||
type = (GVariantType *) g_variant_type_info_get_type_string (tree->info);
|
||
|
||
if (g_variant_type_is_container (type))
|
||
{
|
||
gsize i;
|
||
|
||
/* force GVariantBuilder to guess the type half the time */
|
||
if (guess_ok && randomly (0.5))
|
||
{
|
||
if (g_variant_type_is_array (type) && tree->n_children)
|
||
type = G_VARIANT_TYPE_ARRAY;
|
||
|
||
if (g_variant_type_is_maybe (type) && tree->n_children)
|
||
type = G_VARIANT_TYPE_MAYBE;
|
||
|
||
if (g_variant_type_is_tuple (type))
|
||
type = G_VARIANT_TYPE_TUPLE;
|
||
|
||
if (g_variant_type_is_dict_entry (type))
|
||
type = G_VARIANT_TYPE_DICT_ENTRY;
|
||
}
|
||
else
|
||
guess_ok = FALSE;
|
||
|
||
g_variant_builder_open (builder, type);
|
||
|
||
for (i = 0; i < tree->n_children; i++)
|
||
tree_instance_build_gvariant (tree->children[i], builder, guess_ok);
|
||
|
||
g_variant_builder_close (builder);
|
||
}
|
||
else
|
||
g_variant_builder_add_value (builder, tree_instance_get_gvariant (tree));
|
||
}
|
||
|
||
|
||
static gboolean
|
||
tree_instance_check_iter (TreeInstance *tree,
|
||
GVariantIter *iter)
|
||
{
|
||
GVariant *value;
|
||
|
||
value = g_variant_iter_next_value (iter);
|
||
|
||
if (g_variant_is_container (value))
|
||
{
|
||
gsize i;
|
||
|
||
iter = g_variant_iter_new (value);
|
||
g_variant_unref (value);
|
||
|
||
if (g_variant_iter_n_children (iter) != tree->n_children)
|
||
{
|
||
g_variant_iter_free (iter);
|
||
return FALSE;
|
||
}
|
||
|
||
for (i = 0; i < tree->n_children; i++)
|
||
if (!tree_instance_check_iter (tree->children[i], iter))
|
||
{
|
||
g_variant_iter_free (iter);
|
||
return FALSE;
|
||
}
|
||
|
||
g_assert_null (g_variant_iter_next_value (iter));
|
||
g_variant_iter_free (iter);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
else
|
||
{
|
||
gboolean equal;
|
||
|
||
equal = tree_instance_check_gvariant (tree, value);
|
||
g_variant_unref (value);
|
||
|
||
return equal;
|
||
}
|
||
}
|
||
|
||
static void
|
||
test_container (void)
|
||
{
|
||
TreeInstance *tree;
|
||
GVariant *value;
|
||
gchar *s1, *s2;
|
||
|
||
tree = tree_instance_new (NULL, 3);
|
||
value = g_variant_ref_sink (tree_instance_get_gvariant (tree));
|
||
|
||
s1 = g_variant_print (value, TRUE);
|
||
g_assert_true (tree_instance_check_gvariant (tree, value));
|
||
|
||
g_variant_get_data (value);
|
||
|
||
s2 = g_variant_print (value, TRUE);
|
||
g_assert_true (tree_instance_check_gvariant (tree, value));
|
||
|
||
g_assert_cmpstr (s1, ==, s2);
|
||
|
||
if (g_variant_is_container (value))
|
||
{
|
||
GVariantBuilder builder;
|
||
GVariantIter iter;
|
||
GVariant *built;
|
||
GVariant *val;
|
||
gchar *s3;
|
||
|
||
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARIANT);
|
||
tree_instance_build_gvariant (tree, &builder, TRUE);
|
||
built = g_variant_builder_end (&builder);
|
||
g_variant_ref_sink (built);
|
||
g_variant_get_data (built);
|
||
val = g_variant_get_variant (built);
|
||
|
||
s3 = g_variant_print (val, TRUE);
|
||
g_assert_cmpstr (s1, ==, s3);
|
||
|
||
g_variant_iter_init (&iter, built);
|
||
g_assert_true (tree_instance_check_iter (tree, &iter));
|
||
g_assert_null (g_variant_iter_next_value (&iter));
|
||
|
||
g_variant_unref (built);
|
||
g_variant_unref (val);
|
||
g_free (s3);
|
||
}
|
||
|
||
tree_instance_free (tree);
|
||
g_variant_unref (value);
|
||
g_free (s2);
|
||
g_free (s1);
|
||
}
|
||
|
||
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_false (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_true (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_true (g_variant_get_string (value, NULL) == invalid);
|
||
g_variant_unref (value);
|
||
}
|
||
|
||
static void
|
||
test_containers (void)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; i < 100; i++)
|
||
{
|
||
test_container ();
|
||
}
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static void
|
||
test_format_strings (void)
|
||
{
|
||
GVariantType *type;
|
||
const gchar *end;
|
||
|
||
g_assert_true (g_variant_format_string_scan ("i", NULL, &end) && *end == '\0');
|
||
g_assert_true (g_variant_format_string_scan ("@i", NULL, &end) && *end == '\0');
|
||
g_assert_true (g_variant_format_string_scan ("@ii", NULL, &end) && *end == 'i');
|
||
g_assert_true (g_variant_format_string_scan ("^a&s", NULL, &end) && *end == '\0');
|
||
g_assert_true (g_variant_format_string_scan ("(^as)", NULL, &end) &&
|
||
*end == '\0');
|
||
g_assert_false (g_variant_format_string_scan ("(^s)", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("(^a)", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("(z)", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("az", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("{**}", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("{@**}", NULL, &end));
|
||
g_assert_true (g_variant_format_string_scan ("{@y*}", NULL, &end) &&
|
||
*end == '\0');
|
||
g_assert_true (g_variant_format_string_scan ("{yv}", NULL, &end) &&
|
||
*end == '\0');
|
||
g_assert_false (g_variant_format_string_scan ("{&?v}", NULL, &end));
|
||
g_assert_true (g_variant_format_string_scan ("{@?v}", NULL, &end) &&
|
||
*end == '\0');
|
||
g_assert_false (g_variant_format_string_scan ("{&@sv}", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("{@&sv}", NULL, &end));
|
||
g_assert_true (g_variant_format_string_scan ("{&sv}", NULL, &end) &&
|
||
*end == '\0');
|
||
g_assert_false (g_variant_format_string_scan ("{vv}", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("{y}", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("{yyy}", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("{ya}", NULL, &end));
|
||
g_assert_true (g_variant_format_string_scan ("&s", NULL, &end) && *end == '\0');
|
||
g_assert_false (g_variant_format_string_scan ("&as", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("@z", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("az", NULL, &end));
|
||
g_assert_false (g_variant_format_string_scan ("a&s", NULL, &end));
|
||
|
||
type = g_variant_format_string_scan_type ("mm(@xy^a&s*?@?)", NULL, &end);
|
||
g_assert_true (type && *end == '\0');
|
||
g_assert_true (g_variant_type_equal (type, G_VARIANT_TYPE ("mm(xyas*?\?)")));
|
||
g_variant_type_free (type);
|
||
|
||
type = g_variant_format_string_scan_type ("mm(@xy^a&*?@?)", NULL, NULL);
|
||
g_assert_null (type);
|
||
}
|
||
|
||
static void
|
||
do_failed_test (const char *test,
|
||
const gchar *pattern)
|
||
{
|
||
g_test_trap_subprocess (test, 1000000, 0);
|
||
g_test_trap_assert_failed ();
|
||
g_test_trap_assert_stderr (pattern);
|
||
}
|
||
|
||
static void
|
||
test_invalid_varargs (void)
|
||
{
|
||
GVariant *value;
|
||
const gchar *end;
|
||
|
||
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_null (value);
|
||
|
||
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_null (value);
|
||
|
||
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
|
||
check_and_free (GVariant *value,
|
||
const gchar *str)
|
||
{
|
||
gchar *valstr = g_variant_print (value, FALSE);
|
||
g_assert_cmpstr (str, ==, valstr);
|
||
g_variant_unref (value);
|
||
g_free (valstr);
|
||
}
|
||
|
||
static void
|
||
test_varargs_empty_array (void)
|
||
{
|
||
g_variant_new ("(a{s*})", NULL);
|
||
|
||
g_assert_not_reached ();
|
||
}
|
||
|
||
static void
|
||
assert_cmpstrv (const gchar **strv1, const gchar **strv2)
|
||
{
|
||
gsize i;
|
||
|
||
for (i = 0; strv1[i] != NULL && strv2[i] != NULL; i++)
|
||
g_assert_cmpstr (strv1[i], ==, strv2[i]);
|
||
|
||
g_assert_null (strv1[i]);
|
||
g_assert_null (strv2[i]);
|
||
}
|
||
|
||
static void
|
||
test_varargs (void)
|
||
{
|
||
{
|
||
GVariantBuilder array;
|
||
|
||
g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
|
||
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,
|
||
g_variant_new_dict_entry (g_variant_new_string ("temperature"),
|
||
g_variant_new_variant (
|
||
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)>, "
|
||
"'title': <'Test case'>, "
|
||
"'temperature': <37.5>}, "
|
||
"7777, 8888)");
|
||
|
||
check_and_free (g_variant_new ("(imimimmimmimmi)",
|
||
123,
|
||
FALSE, 321,
|
||
TRUE, 123,
|
||
FALSE, TRUE, 321,
|
||
TRUE, FALSE, 321,
|
||
TRUE, TRUE, 123),
|
||
"(123, nothing, 123, nothing, just nothing, 123)");
|
||
|
||
check_and_free (g_variant_new ("(ybnixd)",
|
||
'a', 1, 22, 33, (guint64) 44, 5.5),
|
||
"(0x61, true, 22, 33, 44, 5.5)");
|
||
|
||
check_and_free (g_variant_new ("(@y?*rv)",
|
||
g_variant_new ("y", 'a'),
|
||
g_variant_new ("y", 'b'),
|
||
g_variant_new ("y", 'c'),
|
||
g_variant_new ("(y)", 'd'),
|
||
g_variant_new ("y", 'e')),
|
||
"(0x61, 0x62, 0x63, (0x64,), <byte 0x65>)");
|
||
}
|
||
|
||
{
|
||
GVariantBuilder array;
|
||
GVariantIter iter;
|
||
GVariant *value;
|
||
gchar *number;
|
||
gboolean just;
|
||
guint i;
|
||
gint val;
|
||
|
||
g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
|
||
for (i = 0; i < 100; i++)
|
||
{
|
||
number = g_strdup_printf ("%u", i);
|
||
g_variant_builder_add (&array, "s", number);
|
||
g_free (number);
|
||
}
|
||
|
||
value = g_variant_builder_end (&array);
|
||
g_variant_iter_init (&iter, value);
|
||
|
||
i = 0;
|
||
while (g_variant_iter_loop (&iter, "s", &number))
|
||
{
|
||
gchar *check = g_strdup_printf ("%u", i++);
|
||
g_assert_cmpstr (number, ==, check);
|
||
g_free (check);
|
||
}
|
||
g_assert_null (number);
|
||
g_assert_cmpuint (i, ==, 100);
|
||
|
||
g_variant_unref (value);
|
||
|
||
g_variant_builder_init (&array, G_VARIANT_TYPE_ARRAY);
|
||
for (i = 0; i < 100; i++)
|
||
g_variant_builder_add (&array, "mi", i % 2 == 0, i);
|
||
value = g_variant_builder_end (&array);
|
||
|
||
i = 0;
|
||
g_variant_iter_init (&iter, value);
|
||
while (g_variant_iter_loop (&iter, "mi", NULL, &val))
|
||
g_assert_true (val == i++ || val == 0);
|
||
g_assert_cmpuint (i, ==, 100);
|
||
|
||
i = 0;
|
||
g_variant_iter_init (&iter, value);
|
||
while (g_variant_iter_loop (&iter, "mi", &just, &val))
|
||
{
|
||
gint this = i++;
|
||
|
||
if (this % 2 == 0)
|
||
{
|
||
g_assert_true (just);
|
||
g_assert_cmpint (val, ==, this);
|
||
}
|
||
else
|
||
{
|
||
g_assert_false (just);
|
||
g_assert_cmpint (val, ==, 0);
|
||
}
|
||
}
|
||
g_assert_cmpuint (i, ==, 100);
|
||
|
||
g_variant_unref (value);
|
||
}
|
||
|
||
{
|
||
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;
|
||
gsize i;
|
||
|
||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
|
||
g_variant_builder_add (&builder, "s", test_strs[0]);
|
||
g_variant_builder_add (&builder, "s", test_strs[1]);
|
||
g_variant_builder_add (&builder, "s", test_strs[2]);
|
||
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_cmpuint (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_cmpuint (i, ==, 3);
|
||
|
||
g_variant_iter_free (array);
|
||
|
||
g_variant_iter_next (&tuple, "^a&s", &strv);
|
||
g_variant_iter_next (&tuple, "^as", &my_strv);
|
||
|
||
assert_cmpstrv (strv, strvector);
|
||
assert_cmpstrv ((const char **)my_strv, strvector);
|
||
|
||
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;
|
||
gsize i;
|
||
|
||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aaay"));
|
||
g_variant_builder_add (&builder, "^aay", strvector);
|
||
g_variant_builder_add (&builder, "^aay", strvector);
|
||
g_variant_builder_add (&builder, "^aay", strvector);
|
||
value = g_variant_new ("aaay", &builder);
|
||
array = g_variant_iter_new (value);
|
||
i = 0;
|
||
while (g_variant_iter_loop (array, "^aay", &my_strv))
|
||
i++;
|
||
g_assert_cmpuint (i, ==, 3);
|
||
|
||
/* start over */
|
||
g_variant_iter_init (array, value);
|
||
i = 0;
|
||
while (g_variant_iter_loop (array, "^a&ay", &strv))
|
||
i++;
|
||
g_assert_cmpuint (i, ==, 3);
|
||
g_variant_unref (value);
|
||
g_variant_iter_free (array);
|
||
|
||
/* next test */
|
||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
|
||
g_variant_builder_add (&builder, "^ay", test_strs[0]);
|
||
g_variant_builder_add (&builder, "^ay", test_strs[1]);
|
||
g_variant_builder_add (&builder, "^ay", test_strs[2]);
|
||
value = g_variant_new ("(aay^aay^a&ay)", &builder, strvector, strvector);
|
||
g_variant_iter_init (&tuple, value);
|
||
g_variant_iter_next (&tuple, "aay", &array);
|
||
|
||
i = 0;
|
||
while (g_variant_iter_loop (array, "^ay", &str))
|
||
g_assert_cmpstr (str, ==, test_strs[i++]);
|
||
g_assert_cmpuint (i, ==, 3);
|
||
|
||
g_variant_iter_free (array);
|
||
|
||
/* start over */
|
||
g_variant_iter_init (&tuple, value);
|
||
g_variant_iter_next (&tuple, "aay", &array);
|
||
|
||
i = 0;
|
||
while (g_variant_iter_loop (array, "^&ay", &str))
|
||
g_assert_cmpstr (str, ==, test_strs[i++]);
|
||
g_assert_cmpuint (i, ==, 3);
|
||
|
||
g_variant_iter_free (array);
|
||
|
||
g_variant_iter_next (&tuple, "^a&ay", &strv);
|
||
g_variant_iter_next (&tuple, "^aay", &my_strv);
|
||
|
||
assert_cmpstrv (strv, strvector);
|
||
assert_cmpstrv ((const char **)my_strv, strvector);
|
||
|
||
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;
|
||
gsize i;
|
||
|
||
g_variant_builder_init (&builder, G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
|
||
g_variant_builder_add (&builder, "o", test_strs[0]);
|
||
g_variant_builder_add (&builder, "o", test_strs[1]);
|
||
g_variant_builder_add (&builder, "o", test_strs[2]);
|
||
value = g_variant_new ("(ao^ao^a&o)", &builder, strvector, strvector);
|
||
g_variant_iter_init (&tuple, value);
|
||
g_variant_iter_next (&tuple, "ao", &array);
|
||
|
||
i = 0;
|
||
while (g_variant_iter_loop (array, "o", &str))
|
||
g_assert_cmpstr (str, ==, test_strs[i++]);
|
||
g_assert_cmpuint (i, ==, 3);
|
||
|
||
g_variant_iter_free (array);
|
||
|
||
/* start over */
|
||
g_variant_iter_init (&tuple, value);
|
||
g_variant_iter_next (&tuple, "ao", &array);
|
||
|
||
i = 0;
|
||
while (g_variant_iter_loop (array, "&o", &str))
|
||
g_assert_cmpstr (str, ==, test_strs[i++]);
|
||
g_assert_cmpuint (i, ==, 3);
|
||
|
||
g_variant_iter_free (array);
|
||
|
||
g_variant_iter_next (&tuple, "^a&o", &strv);
|
||
g_variant_iter_next (&tuple, "^ao", &my_strv);
|
||
|
||
assert_cmpstrv (strv, strvector);
|
||
assert_cmpstrv ((const char **)my_strv, strvector);
|
||
|
||
g_variant_unref (value);
|
||
g_strfreev (my_strv);
|
||
g_free (strv);
|
||
}
|
||
|
||
{
|
||
const gchar *strvector[] = { "i", "ii", "iii", "iv", "v", "vi", NULL };
|
||
GVariantBuilder builder;
|
||
GVariantIter iter;
|
||
GVariantIter *i2;
|
||
GVariantIter *i3;
|
||
GVariant *value;
|
||
GVariant *sub;
|
||
gchar **strv;
|
||
gsize i;
|
||
|
||
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, "s", strvector[i]);
|
||
else
|
||
g_variant_builder_add (&builder, "&s", strvector[i]);
|
||
g_variant_builder_close (&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, "^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&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, "as", &i2))
|
||
{
|
||
gchar *str;
|
||
|
||
i = 0;
|
||
while (g_variant_iter_loop (i2, "s", &str))
|
||
g_assert_cmpstr (str, ==, strvector[i++]);
|
||
g_assert_cmpuint (i, ==, 6);
|
||
}
|
||
|
||
g_variant_iter_init (&iter, value);
|
||
i3 = g_variant_iter_copy (&iter);
|
||
while (g_variant_iter_loop (&iter, "@as", &sub))
|
||
{
|
||
gchar *str = g_variant_print (sub, TRUE);
|
||
g_assert_cmpstr (str, ==,
|
||
"['i', 'ii', 'iii', 'iv', 'v', 'vi']");
|
||
g_free (str);
|
||
}
|
||
|
||
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, ==,
|
||
"['i', 'ii', 'iii', 'iv', 'v', 'vi']");
|
||
g_free (str);
|
||
}
|
||
|
||
g_variant_iter_free (i3);
|
||
|
||
for (i = 0; i < g_variant_n_children (value); i++)
|
||
{
|
||
gsize j;
|
||
|
||
g_variant_get_child (value, i, "*", &sub);
|
||
|
||
for (j = 0; j < g_variant_n_children (sub); j++)
|
||
{
|
||
const gchar *str = NULL;
|
||
GVariant *cval;
|
||
|
||
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, "&s", &str);
|
||
g_assert_cmpstr (str, ==, strvector[j]);
|
||
g_variant_unref (cval);
|
||
}
|
||
|
||
g_variant_unref (sub);
|
||
}
|
||
|
||
g_variant_unref (value);
|
||
}
|
||
|
||
{
|
||
gboolean justs[10];
|
||
GVariant *value;
|
||
|
||
GVariant *vval;
|
||
guchar byteval;
|
||
gboolean bval;
|
||
gint16 i16val;
|
||
guint16 u16val;
|
||
gint32 i32val;
|
||
guint32 u32val;
|
||
gint64 i64val;
|
||
guint64 u64val;
|
||
gdouble dval;
|
||
gint32 hval;
|
||
|
||
/* test all 'nothing' */
|
||
value = g_variant_new ("(mymbmnmqmimumxmtmhmdmv)",
|
||
FALSE, 'a',
|
||
FALSE, TRUE,
|
||
FALSE, (gint16) 123,
|
||
FALSE, (guint16) 123,
|
||
FALSE, (gint32) 123,
|
||
FALSE, (guint32) 123,
|
||
FALSE, (gint64) 123,
|
||
FALSE, (guint64) 123,
|
||
FALSE, (gint32) -1,
|
||
FALSE, (gdouble) 37.5,
|
||
NULL);
|
||
|
||
/* both NULL */
|
||
g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL);
|
||
|
||
/* NULL values */
|
||
memset (justs, 1, sizeof justs);
|
||
g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
|
||
&justs[0], NULL,
|
||
&justs[1], NULL,
|
||
&justs[2], NULL,
|
||
&justs[3], NULL,
|
||
&justs[4], NULL,
|
||
&justs[5], NULL,
|
||
&justs[6], NULL,
|
||
&justs[7], NULL,
|
||
&justs[8], NULL,
|
||
&justs[9], NULL,
|
||
NULL);
|
||
g_assert_true (!(justs[0] || justs[1] || justs[2] || justs[3] || justs[4] ||
|
||
justs[5] || justs[6] || justs[7] || justs[8] || justs[9]));
|
||
|
||
/* both non-NULL */
|
||
memset (justs, 1, sizeof justs);
|
||
byteval = i16val = u16val = i32val = u32val = i64val = u64val = hval = 88;
|
||
vval = (void *) 1;
|
||
bval = TRUE;
|
||
dval = 88.88;
|
||
g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
|
||
&justs[0], &byteval,
|
||
&justs[1], &bval,
|
||
&justs[2], &i16val,
|
||
&justs[3], &u16val,
|
||
&justs[4], &i32val,
|
||
&justs[5], &u32val,
|
||
&justs[6], &i64val,
|
||
&justs[7], &u64val,
|
||
&justs[8], &hval,
|
||
&justs[9], &dval,
|
||
&vval);
|
||
g_assert_true (!(justs[0] || justs[1] || justs[2] || justs[3] || justs[4] ||
|
||
justs[5] || justs[6] || justs[7] || justs[8] || justs[9]));
|
||
g_assert_true (byteval == '\0' && bval == FALSE);
|
||
g_assert_true (i16val == 0 && u16val == 0 && i32val == 0 &&
|
||
u32val == 0 && i64val == 0 && u64val == 0 &&
|
||
hval == 0 && dval == 0.0);
|
||
g_assert_null (vval);
|
||
|
||
/* NULL justs */
|
||
byteval = i16val = u16val = i32val = u32val = i64val = u64val = hval = 88;
|
||
vval = (void *) 1;
|
||
bval = TRUE;
|
||
dval = 88.88;
|
||
g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
|
||
NULL, &byteval,
|
||
NULL, &bval,
|
||
NULL, &i16val,
|
||
NULL, &u16val,
|
||
NULL, &i32val,
|
||
NULL, &u32val,
|
||
NULL, &i64val,
|
||
NULL, &u64val,
|
||
NULL, &hval,
|
||
NULL, &dval,
|
||
&vval);
|
||
g_assert_true (byteval == '\0' && bval == FALSE);
|
||
g_assert_true (i16val == 0 && u16val == 0 && i32val == 0 &&
|
||
u32val == 0 && i64val == 0 && u64val == 0 &&
|
||
hval == 0 && dval == 0.0);
|
||
g_assert_null (vval);
|
||
|
||
g_variant_unref (value);
|
||
|
||
|
||
/* test all 'just' */
|
||
value = g_variant_new ("(mymbmnmqmimumxmtmhmdmv)",
|
||
TRUE, 'a',
|
||
TRUE, TRUE,
|
||
TRUE, (gint16) 123,
|
||
TRUE, (guint16) 123,
|
||
TRUE, (gint32) 123,
|
||
TRUE, (guint32) 123,
|
||
TRUE, (gint64) 123,
|
||
TRUE, (guint64) 123,
|
||
TRUE, (gint32) -1,
|
||
TRUE, (gdouble) 37.5,
|
||
g_variant_new ("()"));
|
||
|
||
/* both NULL */
|
||
g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL);
|
||
|
||
/* NULL values */
|
||
memset (justs, 0, sizeof justs);
|
||
g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
|
||
&justs[0], NULL,
|
||
&justs[1], NULL,
|
||
&justs[2], NULL,
|
||
&justs[3], NULL,
|
||
&justs[4], NULL,
|
||
&justs[5], NULL,
|
||
&justs[6], NULL,
|
||
&justs[7], NULL,
|
||
&justs[8], NULL,
|
||
&justs[9], NULL,
|
||
NULL);
|
||
g_assert_true (justs[0] && justs[1] && justs[2] && justs[3] && justs[4] &&
|
||
justs[5] && justs[6] && justs[7] && justs[8] && justs[9]);
|
||
|
||
/* both non-NULL */
|
||
memset (justs, 0, sizeof justs);
|
||
byteval = i16val = u16val = i32val = u32val = i64val = u64val = hval = 88;
|
||
vval = (void *) 1;
|
||
bval = FALSE;
|
||
dval = 88.88;
|
||
g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
|
||
&justs[0], &byteval,
|
||
&justs[1], &bval,
|
||
&justs[2], &i16val,
|
||
&justs[3], &u16val,
|
||
&justs[4], &i32val,
|
||
&justs[5], &u32val,
|
||
&justs[6], &i64val,
|
||
&justs[7], &u64val,
|
||
&justs[8], &hval,
|
||
&justs[9], &dval,
|
||
&vval);
|
||
g_assert_true (justs[0] && justs[1] && justs[2] && justs[3] && justs[4] &&
|
||
justs[5] && justs[6] && justs[7] && justs[8] && justs[9]);
|
||
g_assert_true (byteval == 'a' && bval == TRUE);
|
||
g_assert_true (i16val == 123 && u16val == 123 && i32val == 123 &&
|
||
u32val == 123 && i64val == 123 && u64val == 123 &&
|
||
hval == -1 && dval == 37.5);
|
||
g_assert_true (g_variant_is_of_type (vval, G_VARIANT_TYPE_UNIT));
|
||
g_variant_unref (vval);
|
||
|
||
/* NULL justs */
|
||
byteval = i16val = u16val = i32val = u32val = i64val = u64val = hval = 88;
|
||
vval = (void *) 1;
|
||
bval = TRUE;
|
||
dval = 88.88;
|
||
g_variant_get (value, "(mymbmnmqmimumxmtmhmdmv)",
|
||
NULL, &byteval,
|
||
NULL, &bval,
|
||
NULL, &i16val,
|
||
NULL, &u16val,
|
||
NULL, &i32val,
|
||
NULL, &u32val,
|
||
NULL, &i64val,
|
||
NULL, &u64val,
|
||
NULL, &hval,
|
||
NULL, &dval,
|
||
&vval);
|
||
g_assert_true (byteval == 'a' && bval == TRUE);
|
||
g_assert_true (i16val == 123 && u16val == 123 && i32val == 123 &&
|
||
u32val == 123 && i64val == 123 && u64val == 123 &&
|
||
hval == -1 && dval == 37.5);
|
||
g_assert_true (g_variant_is_of_type (vval, G_VARIANT_TYPE_UNIT));
|
||
g_variant_unref (vval);
|
||
|
||
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 ();
|
||
}
|
||
|
||
static void
|
||
hash_get (GVariant *value,
|
||
const gchar *format,
|
||
...)
|
||
{
|
||
const gchar *endptr = NULL;
|
||
gboolean hash;
|
||
va_list ap;
|
||
|
||
hash = g_str_has_suffix (format, "#");
|
||
|
||
va_start (ap, format);
|
||
g_variant_get_va (value, format, hash ? &endptr : NULL, &ap);
|
||
va_end (ap);
|
||
|
||
if (hash)
|
||
g_assert_cmpint (*endptr, ==, '#');
|
||
}
|
||
|
||
static GVariant *
|
||
hash_new (const gchar *format,
|
||
...)
|
||
{
|
||
const gchar *endptr = NULL;
|
||
GVariant *value;
|
||
gboolean hash;
|
||
va_list ap;
|
||
|
||
hash = g_str_has_suffix (format, "#");
|
||
|
||
va_start (ap, format);
|
||
value = g_variant_new_va (format, hash ? &endptr : NULL, &ap);
|
||
va_end (ap);
|
||
|
||
if (hash)
|
||
g_assert_cmpint (*endptr, ==, '#');
|
||
|
||
return value;
|
||
}
|
||
|
||
static void
|
||
test_valist (void)
|
||
{
|
||
GVariant *value;
|
||
gint32 x;
|
||
|
||
x = 0;
|
||
value = hash_new ("i", 234);
|
||
hash_get (value, "i", &x);
|
||
g_assert_cmpint (x, ==, 234);
|
||
g_variant_unref (value);
|
||
|
||
x = 0;
|
||
value = hash_new ("i#", 234);
|
||
hash_get (value, "i#", &x);
|
||
g_assert_cmpint (x, ==, 234);
|
||
g_variant_unref (value);
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static void
|
||
test_builder_memory (void)
|
||
{
|
||
GVariantBuilder *hb;
|
||
GVariantBuilder sb;
|
||
|
||
hb = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_open (hb, G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_open (hb, G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_open (hb, G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_add (hb, "s", "some value");
|
||
g_variant_builder_ref (hb);
|
||
g_variant_builder_unref (hb);
|
||
g_variant_builder_unref (hb);
|
||
|
||
hb = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_unref (hb);
|
||
|
||
hb = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_clear (hb);
|
||
g_variant_builder_unref (hb);
|
||
|
||
g_variant_builder_init (&sb, G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_open (&sb, G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_open (&sb, G_VARIANT_TYPE_ARRAY);
|
||
g_variant_builder_add (&sb, "s", "some value");
|
||
g_variant_builder_clear (&sb);
|
||
|
||
g_variant_type_info_assert_no_infos ();
|
||
}
|
||
|
||
static void
|
||
test_hashing (void)
|
||
{
|
||
GVariant *items[4096];
|
||
GHashTable *table;
|
||
gsize i;
|
||
|
||
table = g_hash_table_new_full (g_variant_hash, g_variant_equal,
|
||
(GDestroyNotify ) g_variant_unref,
|
||
NULL);
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (items); i++)
|
||
{
|
||
TreeInstance *tree;
|
||
gsize j;
|
||
|
||
again:
|
||
tree = tree_instance_new (NULL, 0);
|
||
items[i] = tree_instance_get_gvariant (tree);
|
||
tree_instance_free (tree);
|
||
|
||
for (j = 0; j < i; j++)
|
||
if (g_variant_equal (items[i], items[j]))
|
||
{
|
||
g_variant_unref (items[i]);
|
||
goto again;
|
||
}
|
||
|
||
g_hash_table_insert (table,
|
||
g_variant_ref_sink (items[i]),
|
||
GINT_TO_POINTER (i));
|
||
}
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (items); i++)
|
||
{
|
||
gpointer result;
|
||
|
||
result = g_hash_table_lookup (table, items[i]);
|
||
g_assert_cmpint (GPOINTER_TO_INT (result), ==, i);
|
||
}
|
||
|
||
g_hash_table_unref (table);
|
||
|
||
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_cmpuint (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_cmpuint (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_cmpuint (g_variant_get_size (swapped), ==, 13);
|
||
value = g_variant_byteswap (swapped);
|
||
g_variant_unref (swapped);
|
||
string2 = g_variant_print (value, FALSE);
|
||
g_assert_cmpuint (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)
|
||
{
|
||
gsize 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);
|
||
}
|
||
|
||
/* pattern coalese of `MN` and `*` is `MN` */
|
||
{
|
||
GVariant *value = NULL;
|
||
GError *error = NULL;
|
||
|
||
value = g_variant_parse (NULL, "[[0], [], [nothing]]", NULL, NULL, &error);
|
||
g_assert_no_error (error);
|
||
g_assert_cmpstr (g_variant_get_type_string (value), ==, "aami");
|
||
g_variant_unref (value);
|
||
}
|
||
|
||
#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;
|
||
gsize 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_true (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",
|
||
"\x0a", "1:", "expected value",
|
||
"((", "2:", "expected value",
|
||
"(b", "1:", "expected value",
|
||
"b'", "0-2:", "unterminated string constant",
|
||
"b\"", "0-2:", "unterminated string constant",
|
||
"b'a", "0-3:", "unterminated string constant",
|
||
"b\"a", "0-3:", "unterminated string constant",
|
||
"b'\\", "0-3:", "unterminated string constant",
|
||
"b\"\\", "0-3:", "unterminated string constant",
|
||
"b'\\'", "0-4:", "unterminated string constant",
|
||
"b\"\\\"", "0-4:", "unterminated string constant",
|
||
"b'\\'a", "0-5:", "unterminated string constant",
|
||
"b\"\\\"a", "0-5:", "unterminated string constant",
|
||
"'\\u-ff4'", "3:", "invalid 4-character unicode escape",
|
||
"'\\u+ff4'", "3:", "invalid 4-character unicode escape",
|
||
"'\\u'", "3:", "invalid 4-character unicode escape",
|
||
"'\\u0'", "3-4:", "invalid 4-character unicode escape",
|
||
"'\\uHELLO'", "3:", "invalid 4-character unicode escape",
|
||
"'\\u ff4'", "3:", "invalid 4-character unicode escape",
|
||
"'\\u012'", "3-6:", "invalid 4-character unicode escape",
|
||
"'\\u0xff4'", "3-4:", "invalid 4-character unicode escape",
|
||
"'\\U-ff4'", "3:", "invalid 8-character unicode escape",
|
||
"'\\U+ff4'", "3:", "invalid 8-character unicode escape",
|
||
"'\\U'", "3:", "invalid 8-character unicode escape",
|
||
"'\\U0'", "3-4:", "invalid 8-character unicode escape",
|
||
"'\\UHELLO'", "3:", "invalid 8-character unicode escape",
|
||
"'\\U ff4'", "3:", "invalid 8-character unicode escape",
|
||
"'\\U0123456'", "3-10:", "invalid 8-character unicode escape",
|
||
"'\\U0xff4'", "3-4:", "invalid 8-character unicode escape",
|
||
};
|
||
guint i;
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (test); i += 3)
|
||
{
|
||
GError *error1 = NULL, *error2 = NULL;
|
||
GVariant *value;
|
||
|
||
/* Copy the test string and drop its nul terminator, then use the @limit
|
||
* parameter of g_variant_parse() to set the length. This allows valgrind
|
||
* to catch 1-byte heap buffer overflows. */
|
||
gsize test_len = MAX (strlen (test[i]), 1);
|
||
gchar *test_blob = g_malloc0 (test_len); /* no nul terminator */
|
||
|
||
memcpy (test_blob, test[i], test_len);
|
||
value = g_variant_parse (NULL, test_blob, test_blob + test_len, NULL, &error1);
|
||
g_assert_null (value);
|
||
|
||
g_free (test_blob);
|
||
|
||
if (!strstr (error1->message, test[i+2]))
|
||
g_error ("test %u: Can't find '%s' in '%s'", i / 3,
|
||
test[i+2], error1->message);
|
||
|
||
if (!g_str_has_prefix (error1->message, test[i+1]))
|
||
g_error ("test %u: Expected location '%s' in '%s'", i / 3,
|
||
test[i+1], error1->message);
|
||
|
||
/* Test again with the nul terminator this time. The behaviour should be
|
||
* the same. */
|
||
value = g_variant_parse (NULL, test[i], NULL, NULL, &error2);
|
||
g_assert_null (value);
|
||
|
||
g_assert_cmpint (error1->domain, ==, error2->domain);
|
||
g_assert_cmpint (error1->code, ==, error2->code);
|
||
g_assert_cmpstr (error1->message, ==, error2->message);
|
||
|
||
g_clear_error (&error1);
|
||
g_clear_error (&error2);
|
||
}
|
||
}
|
||
|
||
/* Test that parsing GVariant text format integers works at the boundaries of
|
||
* those integer types. We’re especially interested in the handling of the most
|
||
* negative numbers, since those can’t be represented in sign + absolute value
|
||
* form. */
|
||
static void
|
||
test_parser_integer_bounds (void)
|
||
{
|
||
GVariant *value = NULL;
|
||
GError *local_error = NULL;
|
||
|
||
#define test_bound(TYPE, type, text, expected_value) \
|
||
value = g_variant_parse (G_VARIANT_TYPE_##TYPE, text, NULL, NULL, &local_error); \
|
||
g_assert_no_error (local_error); \
|
||
g_assert_nonnull (value); \
|
||
g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE_##TYPE)); \
|
||
g_assert_cmpint (g_variant_get_##type (value), ==, expected_value); \
|
||
g_variant_unref (value)
|
||
|
||
test_bound (BYTE, byte, "0", 0);
|
||
test_bound (BYTE, byte, "255", G_MAXUINT8);
|
||
test_bound (INT16, int16, "-32768", G_MININT16);
|
||
test_bound (INT16, int16, "32767", G_MAXINT16);
|
||
test_bound (INT32, int32, "-2147483648", G_MININT32);
|
||
test_bound (INT32, int32, "2147483647", G_MAXINT32);
|
||
test_bound (INT64, int64, "-9223372036854775808", G_MININT64);
|
||
test_bound (INT64, int64, "9223372036854775807", G_MAXINT64);
|
||
test_bound (HANDLE, handle, "-2147483648", G_MININT32);
|
||
test_bound (HANDLE, handle, "2147483647", G_MAXINT32);
|
||
|
||
#undef test_bound
|
||
}
|
||
|
||
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_true (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_true (g_variant_is_floating (value));
|
||
g_variant_ref_sink (value);
|
||
g_assert_true (!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_true (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" }
|
||
};
|
||
gsize 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_true (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_true (ok);
|
||
g_assert_cmpint (num, ==, 5);
|
||
|
||
ok = g_variant_lookup (dict, "a", "&s", &str);
|
||
g_assert_false (ok);
|
||
|
||
ok = g_variant_lookup (dict, "q", "&s", &str);
|
||
g_assert_false (ok);
|
||
|
||
ok = g_variant_lookup (dict, "b", "i", &num);
|
||
g_assert_false (ok);
|
||
|
||
ok = g_variant_lookup (dict, "b", "&s", &str);
|
||
g_assert_true (ok);
|
||
g_assert_cmpstr (str, ==, "c");
|
||
|
||
ok = g_variant_lookup (dict, "q", "&s", &str);
|
||
g_assert_false (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpint (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_cmpvariant (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_cmpvariant (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_cmpvariant (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_cmpvariant (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_cmpvariant (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_cmpvariant (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_cmpvariant (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_cmpvariant (a, b);
|
||
g_variant_unref (a);
|
||
g_variant_unref (b);
|
||
a = untrusted (g_variant_new_string ("abc"));
|
||
g_assert_cmpvariant (a, a);
|
||
b = g_variant_get_normal_form (a);
|
||
g_assert_cmpvariant (a, b);
|
||
g_variant_unref (a);
|
||
g_variant_unref (b);
|
||
a = untrusted (g_variant_new_object_path ("/abc"));
|
||
g_assert_cmpvariant (a, a);
|
||
b = g_variant_get_normal_form (a);
|
||
a = untrusted (a);
|
||
g_assert_cmpvariant (a, b);
|
||
g_variant_unref (a);
|
||
g_variant_unref (b);
|
||
a = untrusted (g_variant_new_signature ("g"));
|
||
g_assert_cmpvariant (a, a);
|
||
b = g_variant_get_normal_form (a);
|
||
a = untrusted (a);
|
||
g_assert_cmpvariant (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_cmpvariant (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;
|
||
gsize 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_cmpuint (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_cmpuint (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_true (g_variant_check_format_string (value, "(s*)", TRUE));
|
||
g_assert_true (g_variant_check_format_string (value, "(s*)", FALSE));
|
||
g_assert_false (g_variant_check_format_string (value, "(u*)", TRUE));
|
||
g_assert_false (g_variant_check_format_string (value, "(u*)", FALSE));
|
||
|
||
g_assert_true (g_variant_check_format_string (value, "(&s*)", FALSE));
|
||
g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "*contains a '&' character*");
|
||
g_assert_false (g_variant_check_format_string (value, "(&s*)", TRUE));
|
||
g_test_assert_expected_messages ();
|
||
|
||
g_assert_true (g_variant_check_format_string (value, "(s^as)", TRUE));
|
||
g_assert_true (g_variant_check_format_string (value, "(s^as)", FALSE));
|
||
|
||
g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "*contains a '&' character*");
|
||
g_assert_false (g_variant_check_format_string (value, "(s^a&s)", TRUE));
|
||
g_test_assert_expected_messages ();
|
||
g_assert_true (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_true (g_variant_check_format_string (value, "(s*)", TRUE));
|
||
g_assert_true (g_variant_check_format_string (value, "(s*)", FALSE));
|
||
g_assert_false (g_variant_check_format_string (value, "(u*)", TRUE));
|
||
g_assert_false (g_variant_check_format_string (value, "(u*)", FALSE));
|
||
|
||
g_assert_true (g_variant_check_format_string (value, "(&s*)", FALSE));
|
||
g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "*contains a '&' character*");
|
||
g_assert_false (g_variant_check_format_string (value, "(&s*)", TRUE));
|
||
g_test_assert_expected_messages ();
|
||
|
||
g_assert_true (g_variant_check_format_string (value, "(s^ay)", TRUE));
|
||
g_assert_true (g_variant_check_format_string (value, "(s^ay)", FALSE));
|
||
|
||
g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL, "*contains a '&' character*");
|
||
g_assert_false (g_variant_check_format_string (value, "(s^&ay)", TRUE));
|
||
g_test_assert_expected_messages ();
|
||
g_assert_true (g_variant_check_format_string (value, "(s^&ay)", FALSE));
|
||
|
||
g_assert_true (g_variant_check_format_string (value, "r", FALSE));
|
||
g_assert_true (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;
|
||
gsize 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_cmpuint (n_elts, ==, 5);
|
||
for (i = 0; i < 5; i++)
|
||
g_assert_cmpuint (elts[i], ==, i + 1);
|
||
|
||
bytes2 = g_variant_get_data_as_bytes (a);
|
||
g_variant_unref (a);
|
||
|
||
bytes = g_bytes_new (&values, 5);
|
||
g_assert_true (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_false (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;
|
||
gsize 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_nonnull (strstr (s, tests[i].out));
|
||
g_free (s);
|
||
g_clear_error (&error);
|
||
}
|
||
}
|
||
|
||
static void
|
||
test_error_quark (void)
|
||
{
|
||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||
g_assert_cmpuint (g_variant_parser_get_error_quark (), ==, g_variant_parse_error_quark ());
|
||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||
}
|
||
|
||
static void
|
||
test_stack_builder_init (void)
|
||
{
|
||
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_BYTESTRING);
|
||
GVariant *variant;
|
||
|
||
g_variant_builder_add_value (&builder, g_variant_new_byte ('g'));
|
||
g_variant_builder_add_value (&builder, g_variant_new_byte ('l'));
|
||
g_variant_builder_add_value (&builder, g_variant_new_byte ('i'));
|
||
g_variant_builder_add_value (&builder, g_variant_new_byte ('b'));
|
||
g_variant_builder_add_value (&builder, g_variant_new_byte ('\0'));
|
||
|
||
variant = g_variant_ref_sink (g_variant_builder_end (&builder));
|
||
g_assert_nonnull (variant);
|
||
g_assert_true (g_variant_type_equal (g_variant_get_type (variant),
|
||
G_VARIANT_TYPE_BYTESTRING));
|
||
g_assert_cmpuint (g_variant_n_children (variant), ==, 5);
|
||
g_assert_cmpstr (g_variant_get_bytestring (variant), ==, "glib");
|
||
g_variant_unref (variant);
|
||
}
|
||
|
||
static GVariant *
|
||
get_asv (void)
|
||
{
|
||
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
|
||
|
||
g_variant_builder_add (&builder, "{s@v}", "foo", g_variant_new_variant (g_variant_new_string ("FOO")));
|
||
g_variant_builder_add (&builder, "{s@v}", "bar", g_variant_new_variant (g_variant_new_string ("BAR")));
|
||
|
||
return g_variant_ref_sink (g_variant_builder_end (&builder));
|
||
}
|
||
|
||
static void
|
||
test_stack_dict_init (void)
|
||
{
|
||
GVariant *asv = get_asv ();
|
||
GVariantDict dict = G_VARIANT_DICT_INIT (asv);
|
||
GVariant *variant;
|
||
GVariantIter iter;
|
||
gchar *key;
|
||
GVariant *value;
|
||
|
||
g_variant_dict_insert_value (&dict, "baz", g_variant_new_string ("BAZ"));
|
||
g_variant_dict_insert_value (&dict, "quux", g_variant_new_string ("QUUX"));
|
||
|
||
variant = g_variant_ref_sink (g_variant_dict_end (&dict));
|
||
g_assert_nonnull (variant);
|
||
g_assert_true (g_variant_type_equal (g_variant_get_type (variant),
|
||
G_VARIANT_TYPE_VARDICT));
|
||
g_assert_cmpuint (g_variant_n_children (variant), ==, 4);
|
||
|
||
g_variant_iter_init (&iter, variant);
|
||
while (g_variant_iter_next (&iter, "{sv}", &key, &value))
|
||
{
|
||
gchar *strup = g_ascii_strup (key, -1);
|
||
|
||
g_assert_cmpstr (strup, ==, g_variant_get_string (value, NULL));
|
||
g_free (key);
|
||
g_free (strup);
|
||
g_variant_unref (value);
|
||
}
|
||
|
||
g_variant_unref (asv);
|
||
g_variant_unref (variant);
|
||
}
|
||
|
||
/* Test checking arbitrary binary data for normal form. This time, it’s a tuple
|
||
* with invalid element ends. */
|
||
static void
|
||
test_normal_checking_tuples (void)
|
||
{
|
||
const guint8 data[] = {
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
|
||
'a', '(', 'a', 'o', 'a', 'o', 'a', 'a', 'o', 'a', 'a', 'o', ')'
|
||
};
|
||
gsize size = sizeof (data);
|
||
GVariant *variant = NULL;
|
||
GVariant *normal_variant = NULL;
|
||
|
||
variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size,
|
||
FALSE, NULL, NULL);
|
||
g_assert_nonnull (variant);
|
||
|
||
normal_variant = g_variant_get_normal_form (variant);
|
||
g_assert_nonnull (normal_variant);
|
||
|
||
g_variant_unref (normal_variant);
|
||
g_variant_unref (variant);
|
||
}
|
||
|
||
/* Check that deeply nested variants are not considered in normal form when
|
||
* deserialised from untrusted data.*/
|
||
static void
|
||
test_recursion_limits_variant_in_variant (void)
|
||
{
|
||
GVariant *wrapper_variant = NULL;
|
||
gsize i;
|
||
GBytes *bytes = NULL;
|
||
GVariant *deserialised_variant = NULL;
|
||
|
||
/* Construct a hierarchy of variants, containing a single string. This is just
|
||
* below the maximum recursion level, as a series of nested variant types. */
|
||
wrapper_variant = g_variant_new_string ("hello");
|
||
|
||
for (i = 0; i < G_VARIANT_MAX_RECURSION_DEPTH - 1; i++)
|
||
wrapper_variant = g_variant_new_variant (g_steal_pointer (&wrapper_variant));
|
||
|
||
/* Serialise and deserialise it as untrusted data, to force normalisation. */
|
||
bytes = g_variant_get_data_as_bytes (wrapper_variant);
|
||
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
|
||
bytes, FALSE);
|
||
g_assert_nonnull (deserialised_variant);
|
||
g_assert_true (g_variant_is_normal_form (deserialised_variant));
|
||
|
||
g_bytes_unref (bytes);
|
||
g_variant_unref (deserialised_variant);
|
||
|
||
/* Wrap it once more. Normalisation should now fail. */
|
||
wrapper_variant = g_variant_new_variant (g_steal_pointer (&wrapper_variant));
|
||
|
||
bytes = g_variant_get_data_as_bytes (wrapper_variant);
|
||
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
|
||
bytes, FALSE);
|
||
g_assert_nonnull (deserialised_variant);
|
||
g_assert_false (g_variant_is_normal_form (deserialised_variant));
|
||
|
||
g_variant_unref (deserialised_variant);
|
||
|
||
/* Deserialise it again, but trusted this time. This should succeed. */
|
||
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
|
||
bytes, TRUE);
|
||
g_assert_nonnull (deserialised_variant);
|
||
g_assert_true (g_variant_is_normal_form (deserialised_variant));
|
||
|
||
g_bytes_unref (bytes);
|
||
g_variant_unref (deserialised_variant);
|
||
g_variant_unref (wrapper_variant);
|
||
}
|
||
|
||
/* Check that deeply nested arrays are not considered in normal form when
|
||
* deserialised from untrusted data after being wrapped in a variant. This is
|
||
* worth testing, because neither the deeply nested array, nor the variant,
|
||
* have a static #GVariantType which is too deep — only when nested together do
|
||
* they become too deep. */
|
||
static void
|
||
test_recursion_limits_array_in_variant (void)
|
||
{
|
||
GVariant *child_variant = NULL;
|
||
GVariant *wrapper_variant = NULL;
|
||
gsize i;
|
||
GBytes *bytes = NULL;
|
||
GVariant *deserialised_variant = NULL;
|
||
|
||
/* Construct a hierarchy of arrays, containing a single string. This is just
|
||
* below the maximum recursion level, all in a single definite type. */
|
||
child_variant = g_variant_new_string ("hello");
|
||
|
||
for (i = 0; i < G_VARIANT_MAX_RECURSION_DEPTH - 1; i++)
|
||
child_variant = g_variant_new_array (NULL, &child_variant, 1);
|
||
|
||
/* Serialise and deserialise it as untrusted data, to force normalisation. */
|
||
bytes = g_variant_get_data_as_bytes (child_variant);
|
||
deserialised_variant = g_variant_new_from_bytes (g_variant_get_type (child_variant),
|
||
bytes, FALSE);
|
||
g_assert_nonnull (deserialised_variant);
|
||
g_assert_true (g_variant_is_normal_form (deserialised_variant));
|
||
|
||
g_bytes_unref (bytes);
|
||
g_variant_unref (deserialised_variant);
|
||
|
||
/* Wrap it in a variant. Normalisation should now fail. */
|
||
wrapper_variant = g_variant_new_variant (g_steal_pointer (&child_variant));
|
||
|
||
bytes = g_variant_get_data_as_bytes (wrapper_variant);
|
||
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
|
||
bytes, FALSE);
|
||
g_assert_nonnull (deserialised_variant);
|
||
g_assert_false (g_variant_is_normal_form (deserialised_variant));
|
||
|
||
g_variant_unref (deserialised_variant);
|
||
|
||
/* Deserialise it again, but trusted this time. This should succeed. */
|
||
deserialised_variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT,
|
||
bytes, TRUE);
|
||
g_assert_nonnull (deserialised_variant);
|
||
g_assert_true (g_variant_is_normal_form (deserialised_variant));
|
||
|
||
g_bytes_unref (bytes);
|
||
g_variant_unref (deserialised_variant);
|
||
g_variant_unref (wrapper_variant);
|
||
}
|
||
|
||
/* Test that an array with invalidly large values in its offset table is
|
||
* normalised successfully without looping infinitely. */
|
||
static void
|
||
test_normal_checking_array_offsets (void)
|
||
{
|
||
const guint8 data[] = {
|
||
0x07, 0xe5, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00,
|
||
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'g',
|
||
};
|
||
gsize size = sizeof (data);
|
||
GVariant *variant = NULL;
|
||
GVariant *normal_variant = NULL;
|
||
|
||
variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size,
|
||
FALSE, NULL, NULL);
|
||
g_assert_nonnull (variant);
|
||
|
||
normal_variant = g_variant_get_normal_form (variant);
|
||
g_assert_nonnull (normal_variant);
|
||
|
||
g_variant_unref (normal_variant);
|
||
g_variant_unref (variant);
|
||
}
|
||
|
||
/* Test that a tuple with invalidly large values in its offset table is
|
||
* normalised successfully without looping infinitely. */
|
||
static void
|
||
test_normal_checking_tuple_offsets (void)
|
||
{
|
||
const guint8 data[] = {
|
||
0x07, 0xe5, 0x00, 0x07, 0x00, 0x07,
|
||
'(', 'a', 's', 'a', 's', 'a', 's', 'a', 's', 'a', 's', 'a', 's', ')',
|
||
};
|
||
gsize size = sizeof (data);
|
||
GVariant *variant = NULL;
|
||
GVariant *normal_variant = NULL;
|
||
|
||
variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size,
|
||
FALSE, NULL, NULL);
|
||
g_assert_nonnull (variant);
|
||
|
||
normal_variant = g_variant_get_normal_form (variant);
|
||
g_assert_nonnull (normal_variant);
|
||
|
||
g_variant_unref (normal_variant);
|
||
g_variant_unref (variant);
|
||
}
|
||
|
||
/* Test that an empty object path is normalised successfully to the base object
|
||
* path, ‘/’. */
|
||
static void
|
||
test_normal_checking_empty_object_path (void)
|
||
{
|
||
const guint8 data[] = {
|
||
0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
|
||
'(', 'h', '(', 'a', 'i', 'a', 'b', 'i', 'o', ')', ')',
|
||
};
|
||
gsize size = sizeof (data);
|
||
GVariant *variant = NULL;
|
||
GVariant *normal_variant = NULL;
|
||
|
||
variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT, data, size,
|
||
FALSE, NULL, NULL);
|
||
g_assert_nonnull (variant);
|
||
|
||
normal_variant = g_variant_get_normal_form (variant);
|
||
g_assert_nonnull (normal_variant);
|
||
|
||
g_variant_unref (normal_variant);
|
||
g_variant_unref (variant);
|
||
}
|
||
|
||
/* Test that constructing a #GVariant from data which is not correctly aligned
|
||
* for the variant type is OK, by loading a variant from data at various offsets
|
||
* which are aligned and unaligned. When unaligned, a slow construction path
|
||
* should be taken. */
|
||
static void
|
||
test_unaligned_construction (void)
|
||
{
|
||
const guint8 data[] = {
|
||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||
};
|
||
GVariant *variant = NULL;
|
||
GVariant *normal_variant = NULL;
|
||
gsize i, offset;
|
||
const struct {
|
||
const GVariantType *type;
|
||
gsize size;
|
||
gsize max_offset;
|
||
} vectors[] = {
|
||
{ G_VARIANT_TYPE_UINT64, sizeof (guint64), sizeof (guint64) },
|
||
{ G_VARIANT_TYPE_UINT32, sizeof (guint32), sizeof (guint32) },
|
||
{ G_VARIANT_TYPE_UINT16, sizeof (guint16), sizeof (guint16) },
|
||
{ G_VARIANT_TYPE_BYTE, sizeof (guint8), 3 },
|
||
};
|
||
|
||
G_STATIC_ASSERT (sizeof (guint64) * 2 <= sizeof (data));
|
||
|
||
for (i = 0; i < G_N_ELEMENTS (vectors); i++)
|
||
{
|
||
for (offset = 0; offset < vectors[i].max_offset; offset++)
|
||
{
|
||
variant = g_variant_new_from_data (vectors[i].type, data + offset,
|
||
vectors[i].size,
|
||
FALSE, NULL, NULL);
|
||
g_assert_nonnull (variant);
|
||
|
||
normal_variant = g_variant_get_normal_form (variant);
|
||
g_assert_nonnull (normal_variant);
|
||
|
||
g_variant_unref (normal_variant);
|
||
g_variant_unref (variant);
|
||
}
|
||
}
|
||
}
|
||
|
||
int
|
||
main (int argc, char **argv)
|
||
{
|
||
guint i;
|
||
|
||
g_test_init (&argc, &argv, NULL);
|
||
g_test_bug_base ("");
|
||
|
||
g_test_add_func ("/gvariant/type", test_gvarianttype);
|
||
g_test_add_func ("/gvariant/type/string-scan/recursion/tuple",
|
||
test_gvarianttype_string_scan_recursion_tuple);
|
||
g_test_add_func ("/gvariant/type/string-scan/recursion/array",
|
||
test_gvarianttype_string_scan_recursion_array);
|
||
g_test_add_func ("/gvariant/typeinfo", test_gvarianttypeinfo);
|
||
g_test_add_func ("/gvariant/serialiser/maybe", test_maybes);
|
||
g_test_add_func ("/gvariant/serialiser/array", test_arrays);
|
||
g_test_add_func ("/gvariant/serialiser/tuple", test_tuples);
|
||
g_test_add_func ("/gvariant/serialiser/variant", test_variants);
|
||
g_test_add_func ("/gvariant/serialiser/strings", test_strings);
|
||
g_test_add_func ("/gvariant/serialiser/byteswap", test_byteswaps);
|
||
g_test_add_func ("/gvariant/serialiser/children", test_serialiser_children);
|
||
|
||
for (i = 1; i <= 20; i += 4)
|
||
{
|
||
char *testname;
|
||
|
||
testname = g_strdup_printf ("/gvariant/serialiser/fuzz/%u%%", i);
|
||
g_test_add_data_func (testname, GINT_TO_POINTER (i),
|
||
(gpointer) test_fuzzes);
|
||
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/parser/integer-bounds", test_parser_integer_bounds);
|
||
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/stack-builder-init", test_stack_builder_init);
|
||
g_test_add_func ("/gvariant/stack-dict-init", test_stack_dict_init);
|
||
|
||
g_test_add_func ("/gvariant/normal-checking/tuples",
|
||
test_normal_checking_tuples);
|
||
g_test_add_func ("/gvariant/normal-checking/array-offsets",
|
||
test_normal_checking_array_offsets);
|
||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
|
||
test_normal_checking_tuple_offsets);
|
||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||
test_normal_checking_empty_object_path);
|
||
|
||
g_test_add_func ("/gvariant/recursion-limits/variant-in-variant",
|
||
test_recursion_limits_variant_in_variant);
|
||
g_test_add_func ("/gvariant/recursion-limits/array-in-variant",
|
||
test_recursion_limits_array_in_variant);
|
||
|
||
g_test_add_func ("/gvariant/unaligned-construction",
|
||
test_unaligned_construction);
|
||
|
||
return g_test_run ();
|
||
}
|