blob: 67b633b9b4fb8f74a9b423e8172f402ae17c96c7 [file] [log] [blame]
/* Regression test for DBusVariant
*
* Copyright © 2017 Collabora Ltd.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <config.h>
#include <string.h>
#include <glib.h>
#include <glib-object.h>
#include <dbus/dbus.h>
#include <dbus/dbus-internals.h>
#include <dbus/dbus-string.h>
#include <dbus/dbus-message-internal.h>
#include "test-utils-glib.h"
typedef struct
{
DBusMessage *original;
DBusMessage *copy;
} Fixture;
/* Return TRUE on success, FALSE on OOM, abort on failure. */
static gboolean
setup (Fixture *f)
{
dbus_int32_t fortytwo = 42;
dbus_int64_t twentythree = 23;
const char *s = "Hello, world!";
DBusMessageIter iter;
DBusMessageIter arr_iter;
DBusMessageIter struct_iter;
DBusMessageIter pair_iter;
f->original = dbus_message_new_signal ("/", "a.b", "c");
if (f->original == NULL)
return FALSE;
/* It ends up as:
* (
* int32 42,
* "Hello, world!",
* int64 23,
* [int32 42, int32 42],
* (int32 42, "Hello, world!", int64 23),
* {int32 42: int64 23},
* )
*/
if (!dbus_message_append_args (f->original,
DBUS_TYPE_INT32, &fortytwo,
DBUS_TYPE_STRING, &s,
DBUS_TYPE_INT64, &twentythree,
DBUS_TYPE_INVALID))
return FALSE;
dbus_message_iter_init_append (f->original, &iter);
if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_INT32_AS_STRING, &arr_iter))
return FALSE;
{
if (!dbus_message_iter_append_basic (&arr_iter, DBUS_TYPE_INT32, &fortytwo) ||
!dbus_message_iter_append_basic (&arr_iter, DBUS_TYPE_INT32, &fortytwo))
{
dbus_message_iter_abandon_container (&iter, &arr_iter);
return FALSE;
}
}
if (!dbus_message_iter_close_container (&iter, &arr_iter))
return FALSE;
if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_STRUCT, NULL,
&struct_iter))
return FALSE;
{
if (!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT32,
&fortytwo) ||
!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_STRING, &s) ||
!dbus_message_iter_append_basic (&struct_iter, DBUS_TYPE_INT64,
&twentythree))
{
dbus_message_iter_abandon_container (&iter, &struct_iter);
return FALSE;
}
}
if (!dbus_message_iter_close_container (&iter, &struct_iter))
return FALSE;
if (!dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{ix}",
&arr_iter))
return FALSE;
{
if (!dbus_message_iter_open_container (&arr_iter, DBUS_TYPE_DICT_ENTRY,
NULL, &pair_iter))
{
dbus_message_iter_abandon_container (&iter, &arr_iter);
return FALSE;
}
{
if (!dbus_message_iter_append_basic (&pair_iter, DBUS_TYPE_INT32,
&fortytwo) ||
!dbus_message_iter_append_basic (&pair_iter, DBUS_TYPE_INT64,
&twentythree))
{
dbus_message_iter_abandon_container (&arr_iter, &pair_iter);
dbus_message_iter_abandon_container (&iter, &arr_iter);
return FALSE;
}
}
if (!dbus_message_iter_close_container (&arr_iter, &pair_iter))
{
dbus_message_iter_abandon_container (&iter, &arr_iter);
return FALSE;
}
}
if (!dbus_message_iter_close_container (&iter, &arr_iter))
return FALSE;
return TRUE;
}
/* Assert that item_iter points to an int32 equal to expected_value.
* Copy it into a DBusVariant and assert that the copy is done correctly.
* Return TRUE on success, FALSE if libdbus pretends to run out of memory,
* or abort on failure. */
static gboolean
assert_int32 (DBusMessageIter *item_iter,
dbus_int32_t expected_value)
{
DBusVariant *v;
const DBusString *s;
const void *value_p;
dbus_int32_t value;
g_assert_cmpint (dbus_message_iter_get_arg_type (item_iter), ==,
DBUS_TYPE_INT32);
dbus_message_iter_get_basic (item_iter, &value);
g_assert_cmpint (value, ==, expected_value);
v = _dbus_variant_read (item_iter);
if (v == NULL)
return FALSE;
s = _dbus_variant_peek (v);
g_assert (s != NULL);
g_assert_cmpstr (_dbus_variant_get_signature (v), ==,
DBUS_TYPE_INT32_AS_STRING);
/* Variant serialization of <int32 something> at offset 0:
* 01 'i' 00 signature
* 00 padding
* vv vv vv vv bytes of value
*/
g_assert_cmpint (_dbus_variant_get_length (v), ==, 8);
g_assert_cmpint (_dbus_string_get_length (s), ==, 8);
g_assert_cmpint (_dbus_string_get_byte (s, 0), ==, 1);
g_assert_cmpint (_dbus_string_get_byte (s, 1), ==, DBUS_TYPE_INT32);
g_assert_cmpint (_dbus_string_get_byte (s, 2), ==, '\0');
g_assert_cmpint (_dbus_string_get_byte (s, 3), ==, 0); /* padding */
value_p = _dbus_string_get_const_data_len (s, 4, 4);
memcpy (&value, value_p, 4);
g_assert_cmpint (value, ==, expected_value);
_dbus_variant_free (v);
return TRUE;
}
/* Assert that item_iter points to an int64 equal to expected_value.
* Copy it into a DBusVariant and assert that the copy is done correctly.
* Return TRUE on success, FALSE if libdbus pretends to run out of memory,
* or abort on failure. */
static gboolean
assert_int64 (DBusMessageIter *item_iter,
dbus_int64_t expected_value)
{
DBusVariant *v;
const DBusString *s;
const void *value_p;
dbus_int64_t value;
int i;
g_assert_cmpint (dbus_message_iter_get_arg_type (item_iter), ==,
DBUS_TYPE_INT64);
dbus_message_iter_get_basic (item_iter, &value);
g_assert_cmpint (value, ==, expected_value);
v = _dbus_variant_read (item_iter);
if (v == NULL)
return FALSE;
s = _dbus_variant_peek (v);
g_assert (s != NULL);
g_assert_cmpstr (_dbus_variant_get_signature (v), ==,
DBUS_TYPE_INT64_AS_STRING);
/* Variant serialization of <int64 something> at offset 0:
* 01 'i' 00 signature
* 00 00 00 00 00 padding
* vv vv vv vv vv vv vv vv bytes of value
*/
g_assert_cmpint (_dbus_variant_get_length (v), ==, 16);
g_assert_cmpint (_dbus_string_get_length (s), ==, 16);
g_assert_cmpint (_dbus_string_get_byte (s, 0), ==, 1);
g_assert_cmpint (_dbus_string_get_byte (s, 1), ==, DBUS_TYPE_INT64);
g_assert_cmpint (_dbus_string_get_byte (s, 2), ==, '\0');
for (i = 3; i < 8; i++)
g_assert_cmpint (_dbus_string_get_byte (s, i), ==, 0); /* padding */
value_p = _dbus_string_get_const_data_len (s, 8, 8);
memcpy (&value, value_p, 8);
g_assert_cmpint (value, ==, expected_value);
_dbus_variant_free (v);
return TRUE;
}
/* Assert that item_iter points to a string equal to expected_value.
* Copy it into a DBusVariant and assert that the copy is done correctly.
* Return TRUE on success, FALSE if libdbus pretends to run out of memory,
* or abort on failure. */
static gboolean
assert_string (DBusMessageIter *item_iter,
const char *expected_value)
{
DBusVariant *v;
const DBusString *s;
const char *value;
dbus_int32_t length;
g_assert_cmpint (dbus_message_iter_get_arg_type (item_iter), ==,
DBUS_TYPE_STRING);
dbus_message_iter_get_basic (item_iter, &value);
g_assert_cmpstr (value, ==, expected_value);
v = _dbus_variant_read (item_iter);
if (v == NULL)
return FALSE;
s = _dbus_variant_peek (v);
g_assert (s != NULL);
g_assert_cmpstr (_dbus_variant_get_signature (v), ==,
DBUS_TYPE_STRING_AS_STRING);
/* Variant serialization of <"something"> at offset 0:
* 01 's' 00 signature
* 00 padding
* ll ll ll ll bytes of length excluding \0
* vv vv vv ... 00 bytes of value
*/
g_assert_cmpint (_dbus_variant_get_length (v), ==,
(int) strlen (expected_value) + 9);
g_assert_cmpint (_dbus_string_get_length (s), ==,
_dbus_variant_get_length (v));
g_assert_cmpint (_dbus_string_get_byte (s, 0), ==, 1);
g_assert_cmpint (_dbus_string_get_byte (s, 1), ==, DBUS_TYPE_STRING);
g_assert_cmpint (_dbus_string_get_byte (s, 2), ==, '\0');
g_assert_cmpint (_dbus_string_get_byte (s, 3), ==, 0); /* padding */
value = _dbus_string_get_const_data_len (s, 4, 4);
memcpy (&length, value, 4);
g_assert_cmpuint (length, ==, (int) strlen (expected_value));
value = _dbus_string_get_const_data_len (s, 8, length + 1);
g_assert_cmpstr (value, ==, expected_value);
_dbus_variant_free (v);
return TRUE;
}
/* Assert that item_iter points to an array of n_values repetitions of the
* int32 expected_value. Copy it into a DBusVariant and assert that the
* copy is done correctly.
* Return TRUE on success, FALSE if libdbus pretends to run out of memory,
* or abort on failure. */
static gboolean
assert_array_of_int32 (DBusMessageIter *item_iter,
int n_values,
dbus_int32_t expected_value)
{
DBusMessageIter arr_iter;
DBusVariant *v;
const DBusString *s;
const void *value_p;
dbus_int32_t value;
int i;
g_assert_cmpint (dbus_message_iter_get_arg_type (item_iter), ==,
DBUS_TYPE_ARRAY);
dbus_message_iter_recurse (item_iter, &arr_iter);
for (i = 0; i < n_values; i++)
{
assert_int32 (&arr_iter, expected_value);
if (i == n_values - 1)
g_assert_false (dbus_message_iter_next (&arr_iter));
else
g_assert_true (dbus_message_iter_next (&arr_iter));
}
v = _dbus_variant_read (item_iter);
if (v == NULL)
return FALSE;
s = _dbus_variant_peek (v);
g_assert (s != NULL);
g_assert_cmpstr (_dbus_variant_get_signature (v), ==,
DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT32_AS_STRING);
/* Variant serialization of <[int32 something, ...]> at offset 0:
* 02 'a' 'i' 00 signature
* ll ll ll ll total number of bytes in values
* vv vv vv vv ... bytes of values
*/
g_assert_cmpint (_dbus_variant_get_length (v), ==, 8 + (4 * n_values));
g_assert_cmpint (_dbus_string_get_length (s), ==, 8 + (4 * n_values));
g_assert_cmpint (_dbus_string_get_byte (s, 0), ==, 2);
g_assert_cmpint (_dbus_string_get_byte (s, 1), ==, DBUS_TYPE_ARRAY);
g_assert_cmpint (_dbus_string_get_byte (s, 2), ==, DBUS_TYPE_INT32);
g_assert_cmpint (_dbus_string_get_byte (s, 3), ==, '\0');
value_p = _dbus_string_get_const_data_len (s, 4, 4);
memcpy (&value, value_p, 4);
g_assert_cmpint (value, ==, n_values * 4);
for (i = 0; i < n_values; i++)
{
value_p = _dbus_string_get_const_data_len (s, 8 + (4 * (n_values - 1)),
4);
memcpy (&value, value_p, 4);
g_assert_cmpint (value, ==, expected_value);
}
_dbus_variant_free (v);
return TRUE;
}
/* Assert that m is (in GVariant notation):
* (
* int32 42,
* "Hello, world!",
* int64 23,
* [int32 42, int32 42],
* (int32 42, "Hello, world!", int64 23),
* {int32 42: int64 23},
* )
*
* Serialize some of those values into DBusVariants and assert that it is
* done correctly.
*
* Return TRUE on success, FALSE if libdbus pretends to run out of memory,
* or abort on failure. */
static gboolean
assert_message_as_expected (DBusMessage *m)
{
DBusMessageIter item_iter;
DBusMessageIter arr_iter;
DBusMessageIter struct_iter;
DBusMessageIter pair_iter;
g_assert_cmpstr (dbus_message_get_signature (m), ==, "isxai(isx)a{ix}");
dbus_message_iter_init (m, &item_iter);
{
if (!assert_int32 (&item_iter, 42))
return FALSE;
g_assert_true (dbus_message_iter_next (&item_iter));
if (!assert_string (&item_iter, "Hello, world!"))
return FALSE;
g_assert_true (dbus_message_iter_next (&item_iter));
if (!assert_int64 (&item_iter, 23))
return FALSE;
g_assert_true (dbus_message_iter_next (&item_iter));
g_assert_cmpint (dbus_message_iter_get_arg_type (&item_iter), ==,
DBUS_TYPE_ARRAY);
if (!assert_array_of_int32 (&item_iter, 2, 42))
return FALSE;
g_assert_true (dbus_message_iter_next (&item_iter));
g_assert_cmpint (dbus_message_iter_get_arg_type (&item_iter), ==,
DBUS_TYPE_STRUCT);
dbus_message_iter_recurse (&item_iter, &struct_iter);
{
if (!assert_int32 (&struct_iter, 42))
return FALSE;
g_assert_true (dbus_message_iter_next (&struct_iter));
if (!assert_string (&struct_iter, "Hello, world!"))
return FALSE;
g_assert_true (dbus_message_iter_next (&struct_iter));
if (!assert_int64 (&struct_iter, 23))
return FALSE;
g_assert_false (dbus_message_iter_next (&struct_iter));
}
g_assert_true (dbus_message_iter_next (&item_iter));
g_assert_cmpint (dbus_message_iter_get_arg_type (&item_iter), ==,
DBUS_TYPE_ARRAY);
dbus_message_iter_recurse (&item_iter, &arr_iter);
{
g_assert_cmpint (dbus_message_iter_get_arg_type (&arr_iter), ==,
DBUS_TYPE_DICT_ENTRY);
dbus_message_iter_recurse (&arr_iter, &pair_iter);
{
if (!assert_int32 (&pair_iter, 42))
return FALSE;
g_assert_true (dbus_message_iter_next (&pair_iter));
if (!assert_int64 (&pair_iter, 23))
return FALSE;
g_assert_false (dbus_message_iter_next (&pair_iter));
}
g_assert_false (dbus_message_iter_next (&arr_iter));
}
}
g_assert_false (dbus_message_iter_next (&item_iter));
return TRUE;
}
/* Return TRUE on success or OOM, as per DBusTestMemoryFunction signature */
static dbus_bool_t
test_once (void *data)
{
gboolean *really_succeeded = data;
Fixture fixture = { NULL, NULL };
Fixture *f = &fixture;
DBusMessageIter item_iter;
DBusMessageIter appender;
int i;
if (really_succeeded != NULL)
*really_succeeded = FALSE;
if (!setup (f))
goto out;
if (!assert_message_as_expected (f->original))
goto out;
dbus_message_iter_init (f->original, &item_iter);
f->copy = dbus_message_new_signal ("/", "a.b", "c");
if (f->copy == NULL)
goto out;
dbus_message_iter_init_append (f->copy, &appender);
for (i = 0; i < 6; i++)
{
DBusVariant *var = _dbus_variant_read (&item_iter);
if (var == NULL)
goto out;
if (!_dbus_variant_write (var, &appender))
{
_dbus_variant_free (var);
goto out;
}
_dbus_variant_free (var);
if (i == 5)
g_assert_false (dbus_message_iter_next (&item_iter));
else
g_assert_true (dbus_message_iter_next (&item_iter));
}
if (!assert_message_as_expected (f->copy))
goto out;
if (really_succeeded != NULL)
*really_succeeded = TRUE;
out:
if (f->original)
dbus_message_unref (f->original);
if (f->copy)
dbus_message_unref (f->copy);
dbus_shutdown ();
g_assert_cmpint (_dbus_get_malloc_blocks_outstanding (), ==, 0);
return !g_test_failed ();
}
static void
test_simple (void)
{
gboolean really_succeeded = FALSE;
if (!test_once (&really_succeeded))
g_error ("Test failed");
if (!really_succeeded)
g_error ("Out of memory");
}
static void
test_oom (void)
{
if (!_dbus_test_oom_handling ("DBusVariant", test_once, NULL))
g_error ("Test failed");
}
int
main (int argc,
char **argv)
{
test_init (&argc, &argv);
g_test_add_func ("/variant/simple", test_simple);
g_test_add_func ("/variant/oom", test_oom);
return g_test_run ();
}