mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-02 09:16:17 +01:00
be5d48fe77
glib/tests/gvariant.c: In function ‘append_tuple_type_string’: glib/tests/gvariant.c:206:17: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘gint’ {aka ‘int’} 206 | for (i = 0; i < size; i++) | ^ glib/tests/gvariant.c:210:13: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘int’ 210 | if (i < size - 1) | ^ glib/tests/gvariant.c:223:17: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘gint’ {aka ‘int’} 223 | for (i = 0; i < size; i++) | ^ glib/tests/gvariant.c: In function ‘describe_type’: glib/tests/gvariant.c:386:29: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘gint’ {aka ‘int’} 386 | for (i = 0; i < length; i++) | ^ glib/tests/gvariant.c: In function ‘describe_info’: glib/tests/gvariant.c:882:23: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘gint’ {aka ‘int’} 882 | for (i = 0; i < length; i++) | ^ glib/tests/gvariant.c: In function ‘check_offsets’: glib/tests/gvariant.c:962:21: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘gint’ {aka ‘int’} 962 | for (i = 0; i < length; i++) | ^ glib/tests/gvariant.c: In function ‘tree_instance_check_gvariant’: glib/tests/gvariant.c:2636:44: error: comparison of integer expressions of different signedness: ‘gboolean’ {aka ‘int’} and ‘guint64’ {aka ‘long unsigned int’} 2636 | return g_variant_get_boolean (value) == tree->data.integer; | ^~ glib/tests/gvariant.c: In function ‘test_varargs’: glib/tests/gvariant.c:3090:26: error: comparison of integer expressions of different signedness: ‘gint’ {aka ‘int’} and ‘guint’ {aka ‘unsigned int’} 3090 | g_assert_true (val == i++ || val == 0); | ^~
5220 lines
153 KiB
C
5220 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;
|
||
gsize i, size;
|
||
|
||
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;
|
||
gsize i, length;
|
||
|
||
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;
|
||
gsize i, length;
|
||
|
||
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, length;
|
||
|
||
length = g_variant_type_info_n_members (info);
|
||
g_assert_cmpuint (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) == (gboolean) 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 == (gint) 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
|
||
}
|
||
|
||
/* Test that #GVariants which recurse too deeply are rejected. */
|
||
static void
|
||
test_parser_recursion (void)
|
||
{
|
||
GVariant *value = NULL;
|
||
GError *local_error = NULL;
|
||
const guint recursion_depth = G_VARIANT_MAX_RECURSION_DEPTH + 1;
|
||
gchar *silly_dict = g_malloc0 (recursion_depth * 2 + 1);
|
||
gsize i;
|
||
|
||
for (i = 0; i < recursion_depth; i++)
|
||
{
|
||
silly_dict[i] = '{';
|
||
silly_dict[recursion_depth * 2 - i - 1] = '}';
|
||
}
|
||
|
||
value = g_variant_parse (NULL, silly_dict, NULL, NULL, &local_error);
|
||
g_assert_error (local_error, G_VARIANT_PARSE_ERROR, G_VARIANT_PARSE_ERROR_RECURSION);
|
||
g_assert_null (value);
|
||
g_error_free (local_error);
|
||
g_free (silly_dict);
|
||
}
|
||
|
||
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/parser/recursion", test_parser_recursion);
|
||
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 ();
|
||
}
|