glib/gio/tests/gdbus-serialization.c
Simon McVittie f42d2c1b54 gdbus-serialization: use check_serialization() instead of dbus-daemon
This test originally did not connect to the bus, which meant it was
omitted from commits like 415a8d81 that made sure none of GLib tests
rely on the presence of an existing session bus. (In particular,
Debian autobuilders don't have a session bus.)

When test_double_array() was added, environments like the Debian
autobuilders didn't catch the fact that this test relied on having a
session bus, because it is often skipped in minimal environments
due to its libdbus-1 dependency.

We don't actually need to connect to a dbus-daemon here: it's enough
to convert the message from GVariant to D-Bus serialization, and
back into an in-memory representation through libdbus. That's what
check_serialization() does, and I've verified that when I re-introduce
bug #732754 by reverting commits 627b49b and 2268628 locally, this
test still fails.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=744895
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Reviewed-by: Colin Walters <walters@verbum.org>
2015-05-08 16:03:57 +01:00

1106 lines
36 KiB
C

/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2010 Red Hat, Inc.
*
* 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 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
*/
#include <locale.h>
#include <gio/gio.h>
#include <string.h>
#include <unistd.h>
#include <dbus/dbus.h>
/* ---------------------------------------------------------------------------------------------------- */
static void
hexdump (const guchar *str, gsize len)
{
const guchar *data = (const guchar *) str;
guint n, m;
for (n = 0; n < len; n += 16)
{
g_printerr ("%04x: ", n);
for (m = n; m < n + 16; m++)
{
if (m > n && (m%4) == 0)
g_printerr (" ");
if (m < len)
g_printerr ("%02x ", data[m]);
else
g_printerr (" ");
}
g_printerr (" ");
for (m = n; m < len && m < n + 16; m++)
g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.');
g_printerr ("\n");
}
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
append_gv_to_dbus_iter (DBusMessageIter *iter,
GVariant *value,
GError **error)
{
const GVariantType *type;
type = g_variant_get_type (value);
if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
{
dbus_bool_t v = g_variant_get_boolean (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE))
{
guint8 v = g_variant_get_byte (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16))
{
gint16 v = g_variant_get_int16 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16))
{
guint16 v = g_variant_get_uint16 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
{
gint32 v = g_variant_get_int32 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32))
{
guint32 v = g_variant_get_uint32 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64))
{
gint64 v = g_variant_get_int64 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64))
{
guint64 v = g_variant_get_uint64 (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
{
gdouble v = g_variant_get_double (value);
dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v);
}
else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
{
const gchar *v = g_variant_get_string (value, NULL);
dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v);
}
else if (g_variant_type_is_variant (type))
{
DBusMessageIter sub;
GVariant *child;
child = g_variant_get_child_value (value, 0);
dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT,
g_variant_get_type_string (child),
&sub);
if (!append_gv_to_dbus_iter (&sub, child, error))
{
g_variant_unref (child);
goto fail;
}
dbus_message_iter_close_container (iter, &sub);
g_variant_unref (child);
}
else if (g_variant_type_is_array (type))
{
DBusMessageIter dbus_iter;
const gchar *type_string;
GVariantIter gv_iter;
GVariant *item;
type_string = g_variant_get_type_string (value);
type_string++; /* skip the 'a' */
dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
type_string, &dbus_iter);
g_variant_iter_init (&gv_iter, value);
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
{
goto fail;
}
}
dbus_message_iter_close_container (iter, &dbus_iter);
}
else if (g_variant_type_is_tuple (type))
{
DBusMessageIter dbus_iter;
GVariantIter gv_iter;
GVariant *item;
dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT,
NULL, &dbus_iter);
g_variant_iter_init (&gv_iter, value);
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&dbus_iter, item, error))
goto fail;
}
dbus_message_iter_close_container (iter, &dbus_iter);
}
else if (g_variant_type_is_dict_entry (type))
{
DBusMessageIter dbus_iter;
GVariant *key, *val;
dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY,
NULL, &dbus_iter);
key = g_variant_get_child_value (value, 0);
if (!append_gv_to_dbus_iter (&dbus_iter, key, error))
{
g_variant_unref (key);
goto fail;
}
g_variant_unref (key);
val = g_variant_get_child_value (value, 1);
if (!append_gv_to_dbus_iter (&dbus_iter, val, error))
{
g_variant_unref (val);
goto fail;
}
g_variant_unref (val);
dbus_message_iter_close_container (iter, &dbus_iter);
}
else
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Error serializing GVariant with type-string '%s' to a D-Bus message",
g_variant_get_type_string (value));
goto fail;
}
return TRUE;
fail:
return FALSE;
}
static gboolean
append_gv_to_dbus_message (DBusMessage *message,
GVariant *value,
GError **error)
{
gboolean ret;
guint n;
ret = FALSE;
if (value != NULL)
{
DBusMessageIter iter;
GVariantIter gv_iter;
GVariant *item;
dbus_message_iter_init_append (message, &iter);
g_variant_iter_init (&gv_iter, value);
n = 0;
while ((item = g_variant_iter_next_value (&gv_iter)))
{
if (!append_gv_to_dbus_iter (&iter, item, error))
{
g_prefix_error (error,
"Error encoding in-arg %d: ",
n);
goto out;
}
n++;
}
}
ret = TRUE;
out:
return ret;
}
static void
print_gv_dbus_message (GVariant *value)
{
DBusMessage *message;
char *blob;
int blob_len;
GError *error;
message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
dbus_message_set_serial (message, 0x41);
dbus_message_set_path (message, "/foo/bar");
dbus_message_set_member (message, "Member");
error = NULL;
if (!append_gv_to_dbus_message (message, value, &error))
{
g_printerr ("Error printing GVariant as DBusMessage: %s", error->message);
g_error_free (error);
goto out;
}
dbus_message_marshal (message, &blob, &blob_len);
g_printerr ("\n");
hexdump ((guchar *) blob, blob_len);
out:
dbus_message_unref (message);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
dbus_1_message_append (GString *s,
guint indent,
DBusMessageIter *iter)
{
gint arg_type;
DBusMessageIter sub;
g_string_append_printf (s, "%*s", indent, "");
arg_type = dbus_message_iter_get_arg_type (iter);
switch (arg_type)
{
case DBUS_TYPE_BOOLEAN:
{
dbus_bool_t value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "bool: %s\n", value ? "true" : "false");
break;
}
case DBUS_TYPE_BYTE:
{
guchar value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "byte: 0x%02x\n", (guint) value);
break;
}
case DBUS_TYPE_INT16:
{
gint16 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT16:
{
guint16 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value);
break;
}
case DBUS_TYPE_INT32:
{
gint32 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT32:
{
guint32 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value);
break;
}
case DBUS_TYPE_INT64:
{
gint64 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value);
break;
}
case DBUS_TYPE_UINT64:
{
guint64 value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value);
break;
}
case DBUS_TYPE_DOUBLE:
{
gdouble value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "double: %f\n", value);
break;
}
case DBUS_TYPE_STRING:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "string: '%s'\n", value);
break;
}
case DBUS_TYPE_OBJECT_PATH:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "object_path: '%s'\n", value);
break;
}
case DBUS_TYPE_SIGNATURE:
{
const gchar *value;
dbus_message_iter_get_basic (iter, &value);
g_string_append_printf (s, "signature: '%s'\n", value);
break;
}
#ifdef DBUS_TYPE_UNIX_FD
case DBUS_TYPE_UNIX_FD:
{
/* unfortunately there's currently no way to get just the
* protocol value, since dbus_message_iter_get_basic() wants
* to be 'helpful' and dup the fd for the user...
*/
g_string_append (s, "unix-fd: (not extracted)\n");
break;
}
#endif
case DBUS_TYPE_VARIANT:
g_string_append_printf (s, "variant:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_ARRAY:
g_string_append_printf (s, "array:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_STRUCT:
g_string_append_printf (s, "struct:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
case DBUS_TYPE_DICT_ENTRY:
g_string_append_printf (s, "dict_entry:\n");
dbus_message_iter_recurse (iter, &sub);
while (dbus_message_iter_get_arg_type (&sub))
{
dbus_1_message_append (s, indent + 2, &sub);
dbus_message_iter_next (&sub);
}
break;
default:
g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type '%c' (%d)",
arg_type,
arg_type);
g_assert_not_reached ();
break;
}
}
static gchar *
dbus_1_message_print (DBusMessage *message)
{
GString *s;
guint n;
DBusMessageIter iter;
s = g_string_new (NULL);
n = 0;
dbus_message_iter_init (message, &iter);
while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID)
{
g_string_append_printf (s, "value %d: ", n);
dbus_1_message_append (s, 2, &iter);
dbus_message_iter_next (&iter);
n++;
}
return g_string_free (s, FALSE);
}
/* ---------------------------------------------------------------------------------------------------- */
static gchar *
get_body_signature (GVariant *value)
{
const gchar *s;
gsize len;
gchar *ret;
if (value == NULL)
{
ret = g_strdup ("");
goto out;
}
s = g_variant_get_type_string (value);
len = strlen (s);
g_assert (len >= 2);
ret = g_strndup (s + 1, len - 2);
out:
return ret;
}
/* If @value is floating, this assumes ownership. */
static void
check_serialization (GVariant *value,
const gchar *expected_dbus_1_output)
{
guchar *blob;
gsize blob_size;
DBusMessage *dbus_1_message;
GDBusMessage *message;
GDBusMessage *recovered_message;
GError *error;
DBusError dbus_error;
gchar *s;
gchar *s1;
guint n;
message = g_dbus_message_new ();
g_dbus_message_set_body (message, value);
g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL);
g_dbus_message_set_serial (message, 0x41);
s = get_body_signature (value);
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member"));
g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s));
g_free (s);
/* First check that the serialization to the D-Bus wire format is correct - do this for both byte orders */
for (n = 0; n < 2; n++)
{
GDBusMessageByteOrder byte_order;
switch (n)
{
case 0:
byte_order = G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN;
break;
case 1:
byte_order = G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN;
break;
case 2:
g_assert_not_reached ();
break;
}
g_dbus_message_set_byte_order (message, byte_order);
error = NULL;
blob = g_dbus_message_to_blob (message,
&blob_size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&error);
g_assert_no_error (error);
g_assert (blob != NULL);
switch (byte_order)
{
case G_DBUS_MESSAGE_BYTE_ORDER_BIG_ENDIAN:
g_assert_cmpint (blob[0], ==, 'B');
break;
case G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN:
g_assert_cmpint (blob[0], ==, 'l');
break;
}
dbus_error_init (&dbus_error);
dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error);
if (dbus_error_is_set (&dbus_error))
{
g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n",
dbus_error.name,
dbus_error.message);
hexdump (blob, blob_size);
dbus_error_free (&dbus_error);
s = g_variant_print (value, TRUE);
g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s);
g_free (s);
g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n");
print_gv_dbus_message (value);
g_assert_not_reached ();
}
s = dbus_1_message_print (dbus_1_message);
dbus_message_unref (dbus_1_message);
g_assert_cmpstr (s, ==, expected_dbus_1_output);
g_free (s);
/* Then serialize back and check that the body is identical */
error = NULL;
recovered_message = g_dbus_message_new_from_blob (blob,
blob_size,
G_DBUS_CAPABILITY_FLAGS_NONE,
&error);
g_assert_no_error (error);
g_assert (recovered_message != NULL);
if (value == NULL)
{
g_assert (g_dbus_message_get_body (recovered_message) == NULL);
}
else
{
g_assert (g_dbus_message_get_body (recovered_message) != NULL);
if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value))
{
s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE);
s1 = g_variant_print (value, TRUE);
g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n",
s,
s1);
g_free (s);
g_free (s1);
g_assert_not_reached ();
}
}
g_object_unref (recovered_message);
g_free (blob);
}
g_object_unref (message);
}
static void
message_serialize_basic (void)
{
check_serialization (NULL, "");
check_serialization (g_variant_new ("(sogybnqiuxtd)",
"this is a string",
"/this/is/a/path",
"sad",
42,
TRUE,
-42,
60000,
-44,
100000,
-G_GINT64_CONSTANT(2)<<34,
G_GUINT64_CONSTANT(0xffffffffffffffff),
42.5),
"value 0: string: 'this is a string'\n"
"value 1: object_path: '/this/is/a/path'\n"
"value 2: signature: 'sad'\n"
"value 3: byte: 0x2a\n"
"value 4: bool: true\n"
"value 5: int16: -42\n"
"value 6: uint16: 60000\n"
"value 7: int32: -44\n"
"value 8: uint32: 100000\n"
"value 9: int64: -34359738368\n"
"value 10: uint64: 18446744073709551615\n"
"value 11: double: 42.500000\n");
}
/* ---------------------------------------------------------------------------------------------------- */
static void
message_serialize_complex (void)
{
GError *error;
GVariant *value;
error = NULL;
value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"),
"([1, 2, 3], {'one': 'white', 'two': 'black'})",
NULL, NULL, &error);
g_assert_no_error (error);
g_assert (value != NULL);
check_serialization (value,
"value 0: array:\n"
" int32: 1\n"
" int32: 2\n"
" int32: 3\n"
"value 1: array:\n"
" dict_entry:\n"
" string: 'one'\n"
" string: 'white'\n"
" dict_entry:\n"
" string: 'two'\n"
" string: 'black'\n");
g_variant_unref (value);
value = g_variant_parse (G_VARIANT_TYPE ("(sa{sv}as)"),
"('01234567890123456', {}, ['Something'])",
NULL, NULL, &error);
g_assert_no_error (error);
g_assert (value != NULL);
check_serialization (value,
"value 0: string: '01234567890123456'\n"
"value 1: array:\n"
"value 2: array:\n"
" string: 'Something'\n");
g_variant_unref (value);
/* https://bugzilla.gnome.org/show_bug.cgi?id=621838 */
check_serialization (g_variant_new_parsed ("(@aay [], {'cwd': <'/home/davidz/Hacking/glib/gio/tests'>})"),
"value 0: array:\n"
"value 1: array:\n"
" dict_entry:\n"
" string: 'cwd'\n"
" variant:\n"
" string: '/home/davidz/Hacking/glib/gio/tests'\n");
#ifdef DBUS_TYPE_UNIX_FD
value = g_variant_parse (G_VARIANT_TYPE ("(hah)"),
"(42, [43, 44])",
NULL, NULL, &error);
g_assert_no_error (error);
g_assert (value != NULL);
/* about (not extracted), see comment in DBUS_TYPE_UNIX_FD case in
* dbus_1_message_append() above.
*/
check_serialization (value,
"value 0: unix-fd: (not extracted)\n"
"value 1: array:\n"
" unix-fd: (not extracted)\n"
" unix-fd: (not extracted)\n");
g_variant_unref (value);
#endif
}
/* ---------------------------------------------------------------------------------------------------- */
static void
replace (char *blob,
gsize len,
const char *before,
const char *after)
{
gsize i;
gsize slen = strlen (before) + 1;
g_assert_cmpuint (strlen (before), ==, strlen (after));
g_assert_cmpuint (len, >=, slen);
for (i = 0; i < (len - slen + 1); i++)
{
if (memcmp (blob + i, before, slen) == 0)
memcpy (blob + i, after, slen);
}
}
static void
message_serialize_invalid (void)
{
guint n;
/* Other things we could check (note that GDBus _does_ check for all
* these things - we just don't have test-suit coverage for it)
*
* - array exceeding 64 MiB (2^26 bytes) - unfortunately libdbus-1 checks
* this, e.g.
*
* process 19620: arguments to dbus_message_iter_append_fixed_array() were incorrect,
* assertion "n_elements <= DBUS_MAXIMUM_ARRAY_LENGTH / _dbus_type_get_alignment (element_type)"
* failed in file dbus-message.c line 2344.
* This is normally a bug in some application using the D-Bus library.
* D-Bus not built with -rdynamic so unable to print a backtrace
* Aborted (core dumped)
*
* - message exceeding 128 MiB (2^27 bytes)
*
* - endianness, message type, flags, protocol version
*/
for (n = 0; n < 3; n++)
{
GDBusMessage *message;
GError *error;
DBusMessage *dbus_message;
char *blob;
int blob_len;
/* these are in pairs with matching length */
const gchar *valid_utf8_str = "this is valid...";
const gchar *invalid_utf8_str = "this is invalid\xff";
const gchar *valid_signature = "a{sv}a{sv}a{sv}aiai";
const gchar *invalid_signature = "not valid signature";
const gchar *valid_object_path = "/this/is/a/valid/dbus/object/path";
const gchar *invalid_object_path = "/this/is/not a valid object path!";
dbus_message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
dbus_message_set_serial (dbus_message, 0x41);
dbus_message_set_path (dbus_message, "/foo/bar");
dbus_message_set_member (dbus_message, "Member");
switch (n)
{
case 0:
/* invalid UTF-8 */
dbus_message_append_args (dbus_message,
DBUS_TYPE_STRING, &valid_utf8_str,
DBUS_TYPE_INVALID);
break;
case 1:
/* invalid object path */
dbus_message_append_args (dbus_message,
DBUS_TYPE_OBJECT_PATH, &valid_object_path,
DBUS_TYPE_INVALID);
break;
case 2:
/* invalid signature */
dbus_message_append_args (dbus_message,
DBUS_TYPE_SIGNATURE, &valid_signature,
DBUS_TYPE_INVALID);
break;
default:
g_assert_not_reached ();
break;
}
dbus_message_marshal (dbus_message, &blob, &blob_len);
/* hack up the message to be invalid by replacing each valid string
* with its invalid counterpart */
replace (blob, blob_len, valid_utf8_str, invalid_utf8_str);
replace (blob, blob_len, valid_object_path, invalid_object_path);
replace (blob, blob_len, valid_signature, invalid_signature);
error = NULL;
message = g_dbus_message_new_from_blob ((guchar *) blob,
blob_len,
G_DBUS_CAPABILITY_FLAGS_NONE,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_error_free (error);
g_assert (message == NULL);
dbus_free (blob);
dbus_message_unref (dbus_message);
}
}
/* ---------------------------------------------------------------------------------------------------- */
static void
message_serialize_header_checks (void)
{
GDBusMessage *message;
GDBusMessage *reply;
GError *error;
guchar *blob;
gsize blob_size;
/*
* check we can't serialize messages with INVALID type
*/
message = g_dbus_message_new ();
error = NULL;
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: type is INVALID");
g_error_free (error);
g_assert (blob == NULL);
g_object_unref (message);
/*
* check we can't serialize signal messages with INTERFACE, PATH or MEMBER unset / set to reserved value
*/
message = g_dbus_message_new_signal ("/the/path", "The.Interface", "TheMember");
/* ----- */
/* interface NULL => error */
g_dbus_message_set_interface (message, NULL);
error = NULL;
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing");
g_error_free (error);
g_assert (blob == NULL);
/* interface reserved value => error */
g_dbus_message_set_interface (message, "org.freedesktop.DBus.Local");
error = NULL;
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: The INTERFACE header field is using the reserved value org.freedesktop.DBus.Local");
g_error_free (error);
g_assert (blob == NULL);
/* reset interface */
g_dbus_message_set_interface (message, "The.Interface");
/* ----- */
/* path NULL => error */
g_dbus_message_set_path (message, NULL);
error = NULL;
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing");
g_error_free (error);
g_assert (blob == NULL);
/* path reserved value => error */
g_dbus_message_set_path (message, "/org/freedesktop/DBus/Local");
error = NULL;
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: The PATH header field is using the reserved value /org/freedesktop/DBus/Local");
g_error_free (error);
g_assert (blob == NULL);
/* reset path */
g_dbus_message_set_path (message, "/the/path");
/* ----- */
/* member NULL => error */
g_dbus_message_set_member (message, NULL);
error = NULL;
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: SIGNAL message: PATH, INTERFACE or MEMBER header field is missing");
g_error_free (error);
g_assert (blob == NULL);
/* reset member */
g_dbus_message_set_member (message, "TheMember");
/* ----- */
/* done */
g_object_unref (message);
/*
* check that we can't serialize method call messages with PATH or MEMBER unset
*/
message = g_dbus_message_new_method_call (NULL, "/the/path", NULL, "TheMember");
/* ----- */
/* path NULL => error */
g_dbus_message_set_path (message, NULL);
error = NULL;
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing");
g_error_free (error);
g_assert (blob == NULL);
/* reset path */
g_dbus_message_set_path (message, "/the/path");
/* ----- */
/* member NULL => error */
g_dbus_message_set_member (message, NULL);
error = NULL;
blob = g_dbus_message_to_blob (message, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_CALL message: PATH or MEMBER header field is missing");
g_error_free (error);
g_assert (blob == NULL);
/* reset member */
g_dbus_message_set_member (message, "TheMember");
/* ----- */
/* done */
g_object_unref (message);
/*
* check that we can't serialize method reply messages with REPLY_SERIAL unset
*/
message = g_dbus_message_new_method_call (NULL, "/the/path", NULL, "TheMember");
g_dbus_message_set_serial (message, 42);
/* method reply */
reply = g_dbus_message_new_method_reply (message);
g_assert_cmpint (g_dbus_message_get_reply_serial (reply), ==, 42);
g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL);
error = NULL;
blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: METHOD_RETURN message: REPLY_SERIAL header field is missing");
g_error_free (error);
g_assert (blob == NULL);
g_object_unref (reply);
/* method error - first nuke ERROR_NAME, then REPLY_SERIAL */
reply = g_dbus_message_new_method_error (message, "Some.Error.Name", "the message");
g_assert_cmpint (g_dbus_message_get_reply_serial (reply), ==, 42);
/* nuke ERROR_NAME */
g_dbus_message_set_error_name (reply, NULL);
error = NULL;
blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing");
g_error_free (error);
g_assert (blob == NULL);
/* reset ERROR_NAME */
g_dbus_message_set_error_name (reply, "Some.Error.Name");
/* nuke REPLY_SERIAL */
g_dbus_message_set_header (reply, G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL, NULL);
error = NULL;
blob = g_dbus_message_to_blob (reply, &blob_size, G_DBUS_CAPABILITY_FLAGS_NONE, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
g_assert_cmpstr (error->message, ==, "Cannot serialize message: ERROR message: REPLY_SERIAL or ERROR_NAME header field is missing");
g_error_free (error);
g_assert (blob == NULL);
g_object_unref (reply);
g_object_unref (message);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
message_parse_empty_arrays_of_arrays (void)
{
GVariant *body;
GError *error = NULL;
g_test_bug ("673612");
/* These three-element array of empty arrays were previously read back as a
* two-element array of empty arrays, due to sometimes erroneously skipping
* four bytes to align for the eight-byte-aligned grandchild types (x and
* dict_entry).
*/
body = g_variant_parse (G_VARIANT_TYPE ("(aaax)"),
"([@aax [], [], []],)", NULL, NULL, &error);
g_assert_no_error (error);
check_serialization (body,
"value 0: array:\n"
" array:\n"
" array:\n"
" array:\n");
g_variant_unref (body);
body = g_variant_parse (G_VARIANT_TYPE ("(aaa{uu})"),
"([@aa{uu} [], [], []],)", NULL, NULL, &error);
g_assert_no_error (error);
check_serialization (body,
"value 0: array:\n"
" array:\n"
" array:\n"
" array:\n");
g_variant_unref (body);
/* Due to the same bug, g_dbus_message_new_from_blob() would fail for this
* message because it would try to read past the end of the string. Hence,
* sending this to an application would make it fall off the bus. */
body = g_variant_parse (G_VARIANT_TYPE ("(a(aa{sv}as))"),
"([ ([], []),"
" ([], []),"
" ([], [])],)", NULL, NULL, &error);
g_assert_no_error (error);
check_serialization (body,
"value 0: array:\n"
" struct:\n"
" array:\n"
" array:\n"
" struct:\n"
" array:\n"
" array:\n"
" struct:\n"
" array:\n"
" array:\n");
g_variant_unref (body);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
test_double_array (void)
{
GVariantBuilder builder;
GVariant *body;
g_test_bug ("732754");
g_variant_builder_init (&builder, G_VARIANT_TYPE ("ad"));
g_variant_builder_add (&builder, "d", (gdouble)0.0);
g_variant_builder_add (&builder, "d", (gdouble)8.0);
g_variant_builder_add (&builder, "d", (gdouble)22.0);
g_variant_builder_add (&builder, "d", (gdouble)0.0);
body = g_variant_new ("(@ad)", g_variant_builder_end (&builder));
check_serialization (body,
"value 0: array:\n"
" double: 0.000000\n"
" double: 8.000000\n"
" double: 22.000000\n"
" double: 0.000000\n");
}
/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
char *argv[])
{
setlocale (LC_ALL, "C");
g_test_init (&argc, &argv, NULL);
g_test_bug_base ("https://bugzilla.gnome.org/show_bug.cgi?id=");
g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic);
g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex);
g_test_add_func ("/gdbus/message-serialize-invalid", message_serialize_invalid);
g_test_add_func ("/gdbus/message-serialize-header-checks", message_serialize_header_checks);
g_test_add_func ("/gdbus/message-parse-empty-arrays-of-arrays",
message_parse_empty_arrays_of_arrays);
g_test_add_func ("/gdbus/message-serialize/double-array", test_double_array);
return g_test_run();
}