| /* 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 (); |
| } |