summaryrefslogtreecommitdiff
path: root/dbus/dbus-marshal-recursive-util.c
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2005-01-17 03:53:40 +0000
committerHavoc Pennington <hp@redhat.com>2005-01-17 03:53:40 +0000
commit7bf62e31a3c820852271768fafc04ba95c31a19f (patch)
tree660d6e210d04143773da2e86d3b3e8dae8be5cc0 /dbus/dbus-marshal-recursive-util.c
parent4c1a2a760b67b4600db3e5b9c2ad0056b5cf32b6 (diff)
downloaddbus-7bf62e31a3c820852271768fafc04ba95c31a19f.tar.gz
2005-01-16 Havoc Pennington <hp@redhat.com>
This is about it on what can be disabled/deleted from libdbus easily, back below 150K anyhow. Deeper cuts are more work than just turning the code off as I've done here. * dbus/dbus-marshal-basic.c (_dbus_pack_int32): we don't need the signed int convenience funcs * dbus/dbus-internals.c (_dbus_verbose_real): omit when not in verbose mode * dbus/dbus-string-util.c, dbus/dbus-string.c: more breaking things out of libdbus * dbus/dbus-sysdeps.c, dbus/dbus-sysdeps-util.c: same * dbus/dbus-hash.c: purge the TWO_STRINGS crap (well, make it tests-enabled-only, though it should probably be deleted) * dbus/dbus-message-util.c: same stuff * dbus/dbus-auth-util.c: same stuff
Diffstat (limited to 'dbus/dbus-marshal-recursive-util.c')
-rw-r--r--dbus/dbus-marshal-recursive-util.c2963
1 files changed, 2963 insertions, 0 deletions
diff --git a/dbus/dbus-marshal-recursive-util.c b/dbus/dbus-marshal-recursive-util.c
new file mode 100644
index 00000000..cd80ea07
--- /dev/null
+++ b/dbus/dbus-marshal-recursive-util.c
@@ -0,0 +1,2963 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-marshal-recursive-util.c Would be in dbus-marshal-recursive.c, but only used in bus/tests
+ *
+ * Copyright (C) 2004, 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "dbus-marshal-recursive.h"
+#include "dbus-marshal-basic.h"
+#include "dbus-internals.h"
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include "dbus-list.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static int
+first_type_in_signature (const DBusString *str,
+ int pos)
+{
+ unsigned char t;
+
+ t = _dbus_string_get_byte (str, pos);
+
+ if (t == DBUS_STRUCT_BEGIN_CHAR)
+ return DBUS_TYPE_STRUCT;
+ else
+ return t;
+}
+
+/* Whether to do the OOM stuff (only with other expensive tests) */
+#define TEST_OOM_HANDLING 0
+/* We do start offset 0 through 9, to get various alignment cases. Still this
+ * obviously makes the test suite run 10x as slow.
+ */
+#define MAX_INITIAL_OFFSET 9
+
+/* Largest iteration count to test copying, realignment,
+ * etc. with. i.e. we only test this stuff with some of the smaller
+ * data sets.
+ */
+#define MAX_ITERATIONS_FOR_EXPENSIVE_TESTS 1000
+
+typedef struct
+{
+ int byte_order;
+ int initial_offset;
+ DBusString signature;
+ DBusString body;
+} DataBlock;
+
+typedef struct
+{
+ int saved_sig_len;
+ int saved_body_len;
+} DataBlockState;
+
+#define N_FENCE_BYTES 5
+#define FENCE_BYTES_STR "abcde"
+#define INITIAL_PADDING_BYTE '\0'
+
+static dbus_bool_t
+data_block_init (DataBlock *block,
+ int byte_order,
+ int initial_offset)
+{
+ if (!_dbus_string_init (&block->signature))
+ return FALSE;
+
+ if (!_dbus_string_init (&block->body))
+ {
+ _dbus_string_free (&block->signature);
+ return FALSE;
+ }
+
+ if (!_dbus_string_insert_bytes (&block->signature, 0, initial_offset,
+ INITIAL_PADDING_BYTE) ||
+ !_dbus_string_insert_bytes (&block->body, 0, initial_offset,
+ INITIAL_PADDING_BYTE) ||
+ !_dbus_string_append (&block->signature, FENCE_BYTES_STR) ||
+ !_dbus_string_append (&block->body, FENCE_BYTES_STR))
+ {
+ _dbus_string_free (&block->signature);
+ _dbus_string_free (&block->body);
+ return FALSE;
+ }
+
+ block->byte_order = byte_order;
+ block->initial_offset = initial_offset;
+
+ return TRUE;
+}
+
+static void
+data_block_save (DataBlock *block,
+ DataBlockState *state)
+{
+ state->saved_sig_len = _dbus_string_get_length (&block->signature) - N_FENCE_BYTES;
+ state->saved_body_len = _dbus_string_get_length (&block->body) - N_FENCE_BYTES;
+}
+
+static void
+data_block_restore (DataBlock *block,
+ DataBlockState *state)
+{
+ _dbus_string_delete (&block->signature,
+ state->saved_sig_len,
+ _dbus_string_get_length (&block->signature) - state->saved_sig_len - N_FENCE_BYTES);
+ _dbus_string_delete (&block->body,
+ state->saved_body_len,
+ _dbus_string_get_length (&block->body) - state->saved_body_len - N_FENCE_BYTES);
+}
+
+static void
+data_block_verify (DataBlock *block)
+{
+ if (!_dbus_string_ends_with_c_str (&block->signature,
+ FENCE_BYTES_STR))
+ {
+ int offset;
+
+ offset = _dbus_string_get_length (&block->signature) - N_FENCE_BYTES - 8;
+ if (offset < 0)
+ offset = 0;
+
+ _dbus_verbose_bytes_of_string (&block->signature,
+ offset,
+ _dbus_string_get_length (&block->signature) - offset);
+ _dbus_assert_not_reached ("block did not verify: bad bytes at end of signature");
+ }
+ if (!_dbus_string_ends_with_c_str (&block->body,
+ FENCE_BYTES_STR))
+ {
+ int offset;
+
+ offset = _dbus_string_get_length (&block->body) - N_FENCE_BYTES - 8;
+ if (offset < 0)
+ offset = 0;
+
+ _dbus_verbose_bytes_of_string (&block->body,
+ offset,
+ _dbus_string_get_length (&block->body) - offset);
+ _dbus_assert_not_reached ("block did not verify: bad bytes at end of body");
+ }
+
+ _dbus_assert (_dbus_string_validate_nul (&block->signature,
+ 0, block->initial_offset));
+ _dbus_assert (_dbus_string_validate_nul (&block->body,
+ 0, block->initial_offset));
+}
+
+static void
+data_block_free (DataBlock *block)
+{
+ data_block_verify (block);
+
+ _dbus_string_free (&block->signature);
+ _dbus_string_free (&block->body);
+}
+
+static void
+data_block_reset (DataBlock *block)
+{
+ data_block_verify (block);
+
+ _dbus_string_delete (&block->signature,
+ block->initial_offset,
+ _dbus_string_get_length (&block->signature) - N_FENCE_BYTES - block->initial_offset);
+ _dbus_string_delete (&block->body,
+ block->initial_offset,
+ _dbus_string_get_length (&block->body) - N_FENCE_BYTES - block->initial_offset);
+
+ data_block_verify (block);
+}
+
+static void
+data_block_init_reader_writer (DataBlock *block,
+ DBusTypeReader *reader,
+ DBusTypeWriter *writer)
+{
+ if (reader)
+ _dbus_type_reader_init (reader,
+ block->byte_order,
+ &block->signature,
+ block->initial_offset,
+ &block->body,
+ block->initial_offset);
+
+ if (writer)
+ _dbus_type_writer_init (writer,
+ block->byte_order,
+ &block->signature,
+ _dbus_string_get_length (&block->signature) - N_FENCE_BYTES,
+ &block->body,
+ _dbus_string_get_length (&block->body) - N_FENCE_BYTES);
+}
+
+static void
+real_check_expected_type (DBusTypeReader *reader,
+ int expected,
+ const char *funcname,
+ int line)
+{
+ int t;
+
+ t = _dbus_type_reader_get_current_type (reader);
+
+ if (t != expected)
+ {
+ _dbus_warn ("Read type %s while expecting %s at %s line %d\n",
+ _dbus_type_to_string (t),
+ _dbus_type_to_string (expected),
+ funcname, line);
+
+ _dbus_assert_not_reached ("read wrong type");
+ }
+}
+
+#define check_expected_type(reader, expected) real_check_expected_type (reader, expected, _DBUS_FUNCTION_NAME, __LINE__)
+
+#define NEXT_EXPECTING_TRUE(reader) do { if (!_dbus_type_reader_next (reader)) \
+ { \
+ _dbus_warn ("_dbus_type_reader_next() should have returned TRUE at %s %d\n", \
+ _DBUS_FUNCTION_NAME, __LINE__); \
+ _dbus_assert_not_reached ("test failed"); \
+ } \
+} while (0)
+
+#define NEXT_EXPECTING_FALSE(reader) do { if (_dbus_type_reader_next (reader)) \
+ { \
+ _dbus_warn ("_dbus_type_reader_next() should have returned FALSE at %s %d\n", \
+ _DBUS_FUNCTION_NAME, __LINE__); \
+ _dbus_assert_not_reached ("test failed"); \
+ } \
+ check_expected_type (reader, DBUS_TYPE_INVALID); \
+} while (0)
+
+typedef struct TestTypeNode TestTypeNode;
+typedef struct TestTypeNodeClass TestTypeNodeClass;
+typedef struct TestTypeNodeContainer TestTypeNodeContainer;
+typedef struct TestTypeNodeContainerClass TestTypeNodeContainerClass;
+
+struct TestTypeNode
+{
+ const TestTypeNodeClass *klass;
+};
+
+struct TestTypeNodeContainer
+{
+ TestTypeNode base;
+ DBusList *children;
+};
+
+struct TestTypeNodeClass
+{
+ int typecode;
+
+ int instance_size;
+
+ int subclass_detail; /* a bad hack to avoid a bunch of subclass casting */
+
+ dbus_bool_t (* construct) (TestTypeNode *node);
+ void (* destroy) (TestTypeNode *node);
+
+ dbus_bool_t (* write_value) (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+ dbus_bool_t (* read_value) (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+ dbus_bool_t (* set_value) (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+ dbus_bool_t (* build_signature) (TestTypeNode *node,
+ DBusString *str);
+ dbus_bool_t (* write_multi) (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed,
+ int count);
+ dbus_bool_t (* read_multi) (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed,
+ int count);
+};
+
+struct TestTypeNodeContainerClass
+{
+ TestTypeNodeClass base;
+};
+
+/* FIXME this could be chilled out substantially by unifying
+ * the basic types into basic_write_value/basic_read_value
+ * and by merging read_value and set_value into one function
+ * taking a flag argument.
+ */
+static dbus_bool_t int32_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t int32_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t int32_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t int32_write_multi (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed,
+ int count);
+static dbus_bool_t int32_read_multi (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed,
+ int count);
+static dbus_bool_t int64_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t int64_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t int64_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t string_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t string_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t string_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t bool_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t bool_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t bool_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t byte_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t byte_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t byte_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t double_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t double_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t double_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t object_path_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t object_path_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t object_path_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t signature_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t signature_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t signature_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t struct_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t struct_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t struct_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t struct_build_signature (TestTypeNode *node,
+ DBusString *str);
+static dbus_bool_t array_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t array_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t array_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static dbus_bool_t array_build_signature (TestTypeNode *node,
+ DBusString *str);
+static dbus_bool_t variant_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed);
+static dbus_bool_t variant_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed);
+static dbus_bool_t variant_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed);
+static void container_destroy (TestTypeNode *node);
+
+
+static const TestTypeNodeClass int32_class = {
+ DBUS_TYPE_INT32,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ int32_write_value,
+ int32_read_value,
+ int32_set_value,
+ NULL,
+ int32_write_multi,
+ int32_read_multi
+};
+
+static const TestTypeNodeClass uint32_class = {
+ DBUS_TYPE_UINT32,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ int32_write_value, /* recycle from int32 */
+ int32_read_value, /* recycle from int32 */
+ int32_set_value, /* recycle from int32 */
+ NULL,
+ int32_write_multi, /* recycle from int32 */
+ int32_read_multi /* recycle from int32 */
+};
+
+static const TestTypeNodeClass int64_class = {
+ DBUS_TYPE_INT64,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ int64_write_value,
+ int64_read_value,
+ int64_set_value,
+ NULL,
+ NULL, /* FIXME */
+ NULL /* FIXME */
+};
+
+static const TestTypeNodeClass uint64_class = {
+ DBUS_TYPE_UINT64,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ int64_write_value, /* recycle from int64 */
+ int64_read_value, /* recycle from int64 */
+ int64_set_value, /* recycle from int64 */
+ NULL,
+ NULL, /* FIXME */
+ NULL /* FIXME */
+};
+
+static const TestTypeNodeClass string_0_class = {
+ DBUS_TYPE_STRING,
+ sizeof (TestTypeNode),
+ 0, /* string length */
+ NULL,
+ NULL,
+ string_write_value,
+ string_read_value,
+ string_set_value,
+ NULL,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass string_1_class = {
+ DBUS_TYPE_STRING,
+ sizeof (TestTypeNode),
+ 1, /* string length */
+ NULL,
+ NULL,
+ string_write_value,
+ string_read_value,
+ string_set_value,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* with nul, a len 3 string should fill 4 bytes and thus is "special" */
+static const TestTypeNodeClass string_3_class = {
+ DBUS_TYPE_STRING,
+ sizeof (TestTypeNode),
+ 3, /* string length */
+ NULL,
+ NULL,
+ string_write_value,
+ string_read_value,
+ string_set_value,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* with nul, a len 8 string should fill 9 bytes and thus is "special" (far-fetched I suppose) */
+static const TestTypeNodeClass string_8_class = {
+ DBUS_TYPE_STRING,
+ sizeof (TestTypeNode),
+ 8, /* string length */
+ NULL,
+ NULL,
+ string_write_value,
+ string_read_value,
+ string_set_value,
+ NULL,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass bool_class = {
+ DBUS_TYPE_BOOLEAN,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ bool_write_value,
+ bool_read_value,
+ bool_set_value,
+ NULL,
+ NULL, /* FIXME */
+ NULL /* FIXME */
+};
+
+static const TestTypeNodeClass byte_class = {
+ DBUS_TYPE_BYTE,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ byte_write_value,
+ byte_read_value,
+ byte_set_value,
+ NULL,
+ NULL, /* FIXME */
+ NULL /* FIXME */
+};
+
+static const TestTypeNodeClass double_class = {
+ DBUS_TYPE_DOUBLE,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ double_write_value,
+ double_read_value,
+ double_set_value,
+ NULL,
+ NULL, /* FIXME */
+ NULL /* FIXME */
+};
+
+static const TestTypeNodeClass object_path_class = {
+ DBUS_TYPE_OBJECT_PATH,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ object_path_write_value,
+ object_path_read_value,
+ object_path_set_value,
+ NULL,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass signature_class = {
+ DBUS_TYPE_SIGNATURE,
+ sizeof (TestTypeNode),
+ 0,
+ NULL,
+ NULL,
+ signature_write_value,
+ signature_read_value,
+ signature_set_value,
+ NULL,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass struct_1_class = {
+ DBUS_TYPE_STRUCT,
+ sizeof (TestTypeNodeContainer),
+ 1, /* number of times children appear as fields */
+ NULL,
+ container_destroy,
+ struct_write_value,
+ struct_read_value,
+ struct_set_value,
+ struct_build_signature,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass struct_2_class = {
+ DBUS_TYPE_STRUCT,
+ sizeof (TestTypeNodeContainer),
+ 2, /* number of times children appear as fields */
+ NULL,
+ container_destroy,
+ struct_write_value,
+ struct_read_value,
+ struct_set_value,
+ struct_build_signature,
+ NULL,
+ NULL
+};
+
+static dbus_bool_t arrays_write_fixed_in_blocks = FALSE;
+
+static const TestTypeNodeClass array_0_class = {
+ DBUS_TYPE_ARRAY,
+ sizeof (TestTypeNodeContainer),
+ 0, /* number of array elements */
+ NULL,
+ container_destroy,
+ array_write_value,
+ array_read_value,
+ array_set_value,
+ array_build_signature,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass array_1_class = {
+ DBUS_TYPE_ARRAY,
+ sizeof (TestTypeNodeContainer),
+ 1, /* number of array elements */
+ NULL,
+ container_destroy,
+ array_write_value,
+ array_read_value,
+ array_set_value,
+ array_build_signature,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass array_2_class = {
+ DBUS_TYPE_ARRAY,
+ sizeof (TestTypeNodeContainer),
+ 2, /* number of array elements */
+ NULL,
+ container_destroy,
+ array_write_value,
+ array_read_value,
+ array_set_value,
+ array_build_signature,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass array_9_class = {
+ DBUS_TYPE_ARRAY,
+ sizeof (TestTypeNodeContainer),
+ 9, /* number of array elements */
+ NULL,
+ container_destroy,
+ array_write_value,
+ array_read_value,
+ array_set_value,
+ array_build_signature,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass variant_class = {
+ DBUS_TYPE_VARIANT,
+ sizeof (TestTypeNodeContainer),
+ 0,
+ NULL,
+ container_destroy,
+ variant_write_value,
+ variant_read_value,
+ variant_set_value,
+ NULL,
+ NULL,
+ NULL
+};
+
+static const TestTypeNodeClass* const
+basic_nodes[] = {
+ &int32_class,
+ &uint32_class,
+ &int64_class,
+ &uint64_class,
+ &bool_class,
+ &byte_class,
+ &double_class,
+ &string_0_class,
+ &string_1_class,
+ &string_3_class,
+ &string_8_class,
+ &object_path_class,
+ &signature_class
+};
+#define N_BASICS (_DBUS_N_ELEMENTS (basic_nodes))
+
+static const TestTypeNodeClass* const
+container_nodes[] = {
+ &struct_1_class,
+ &array_1_class,
+ &struct_2_class,
+ &array_0_class,
+ &array_2_class,
+ &variant_class
+ /* array_9_class is omitted on purpose, it's too slow;
+ * we only use it in one hardcoded test below
+ */
+};
+#define N_CONTAINERS (_DBUS_N_ELEMENTS (container_nodes))
+
+static TestTypeNode*
+node_new (const TestTypeNodeClass *klass)
+{
+ TestTypeNode *node;
+
+ node = dbus_malloc0 (klass->instance_size);
+ if (node == NULL)
+ return NULL;
+
+ node->klass = klass;
+
+ if (klass->construct)
+ {
+ if (!(* klass->construct) (node))
+ {
+ dbus_free (node);
+ return FALSE;
+ }
+ }
+
+ return node;
+}
+
+static void
+node_destroy (TestTypeNode *node)
+{
+ if (node->klass->destroy)
+ (* node->klass->destroy) (node);
+ dbus_free (node);
+}
+
+static dbus_bool_t
+node_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ dbus_bool_t retval;
+
+ retval = (* node->klass->write_value) (node, block, writer, seed);
+
+#if 0
+ /* Handy to see where things break, but too expensive to do all the time */
+ data_block_verify (block);
+#endif
+
+ return retval;
+}
+
+static dbus_bool_t
+node_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ DBusTypeMark mark;
+ DBusTypeReader restored;
+
+ _dbus_type_reader_save_mark (reader, &mark);
+
+ if (!(* node->klass->read_value) (node, reader, seed))
+ return FALSE;
+
+ _dbus_type_reader_init_from_mark (&restored,
+ reader->byte_order,
+ reader->type_str,
+ reader->value_str,
+ &mark);
+
+ if (!(* node->klass->read_value) (node, &restored, seed))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Warning: if this one fails due to OOM, it has side effects (can
+ * modify only some of the sub-values). OK in a test suite, but we
+ * never do this in real code.
+ */
+static dbus_bool_t
+node_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ if (!(* node->klass->set_value) (node, reader, realign_root, seed))
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+node_build_signature (TestTypeNode *node,
+ DBusString *str)
+{
+ if (node->klass->build_signature)
+ return (* node->klass->build_signature) (node, str);
+ else
+ return _dbus_string_append_byte (str, node->klass->typecode);
+}
+
+static dbus_bool_t
+node_append_child (TestTypeNode *node,
+ TestTypeNode *child)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+
+ _dbus_assert (node->klass->instance_size >= (int) sizeof (TestTypeNodeContainer));
+
+ if (!_dbus_list_append (&container->children, child))
+ _dbus_assert_not_reached ("no memory"); /* we never check the return value on node_append_child anyhow - it's run from outside the malloc-failure test code */
+
+ return TRUE;
+}
+
+static dbus_bool_t
+node_write_multi (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed,
+ int n_copies)
+{
+ dbus_bool_t retval;
+
+ _dbus_assert (node->klass->write_multi != NULL);
+ retval = (* node->klass->write_multi) (node, block, writer, seed, n_copies);
+
+#if 0
+ /* Handy to see where things break, but too expensive to do all the time */
+ data_block_verify (block);
+#endif
+
+ return retval;
+}
+
+static dbus_bool_t
+node_read_multi (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed,
+ int n_copies)
+{
+ _dbus_assert (node->klass->read_multi != NULL);
+
+ if (!(* node->klass->read_multi) (node, reader, seed, n_copies))
+ return FALSE;
+
+ return TRUE;
+}
+
+static int n_iterations_completed_total = 0;
+static int n_iterations_completed_this_test = 0;
+static int n_iterations_expected_this_test = 0;
+
+typedef struct
+{
+ const DBusString *signature;
+ DataBlock *block;
+ int type_offset;
+ TestTypeNode **nodes;
+ int n_nodes;
+} NodeIterationData;
+
+static dbus_bool_t
+run_test_copy (NodeIterationData *nid)
+{
+ DataBlock *src;
+ DataBlock dest;
+ dbus_bool_t retval;
+ DBusTypeReader reader;
+ DBusTypeWriter writer;
+
+ _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+
+ src = nid->block;
+
+ retval = FALSE;
+
+ if (!data_block_init (&dest, src->byte_order, src->initial_offset))
+ return FALSE;
+
+ data_block_init_reader_writer (src, &reader, NULL);
+ data_block_init_reader_writer (&dest, NULL, &writer);
+
+ /* DBusTypeWriter assumes it's writing into an existing signature,
+ * so doesn't add nul on its own. We have to do that.
+ */
+ if (!_dbus_string_insert_byte (&dest.signature,
+ dest.initial_offset, '\0'))
+ goto out;
+
+ if (!_dbus_type_writer_write_reader (&writer, &reader))
+ goto out;
+
+ /* Data blocks should now be identical */
+ if (!_dbus_string_equal (&src->signature, &dest.signature))
+ {
+ _dbus_verbose ("SOURCE\n");
+ _dbus_verbose_bytes_of_string (&src->signature, 0,
+ _dbus_string_get_length (&src->signature));
+ _dbus_verbose ("DEST\n");
+ _dbus_verbose_bytes_of_string (&dest.signature, 0,
+ _dbus_string_get_length (&dest.signature));
+ _dbus_assert_not_reached ("signatures did not match");
+ }
+
+ if (!_dbus_string_equal (&src->body, &dest.body))
+ {
+ _dbus_verbose ("SOURCE\n");
+ _dbus_verbose_bytes_of_string (&src->body, 0,
+ _dbus_string_get_length (&src->body));
+ _dbus_verbose ("DEST\n");
+ _dbus_verbose_bytes_of_string (&dest.body, 0,
+ _dbus_string_get_length (&dest.body));
+ _dbus_assert_not_reached ("bodies did not match");
+ }
+
+ retval = TRUE;
+
+ out:
+
+ data_block_free (&dest);
+
+ return retval;
+}
+
+static dbus_bool_t
+run_test_values_only_write (NodeIterationData *nid)
+{
+ DBusTypeReader reader;
+ DBusTypeWriter writer;
+ int i;
+ dbus_bool_t retval;
+ int sig_len;
+
+ _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+
+ retval = FALSE;
+
+ data_block_reset (nid->block);
+
+ sig_len = _dbus_string_get_length (nid->signature);
+
+ _dbus_type_writer_init_values_only (&writer,
+ nid->block->byte_order,
+ nid->signature, 0,
+ &nid->block->body,
+ _dbus_string_get_length (&nid->block->body) - N_FENCE_BYTES);
+ _dbus_type_reader_init (&reader,
+ nid->block->byte_order,
+ nid->signature, 0,
+ &nid->block->body,
+ nid->block->initial_offset);
+
+ i = 0;
+ while (i < nid->n_nodes)
+ {
+ if (!node_write_value (nid->nodes[i], nid->block, &writer, i))
+ goto out;
+
+ ++i;
+ }
+
+ /* if we wrote any typecodes then this would fail */
+ _dbus_assert (sig_len == _dbus_string_get_length (nid->signature));
+
+ /* But be sure we wrote out the values correctly */
+ i = 0;
+ while (i < nid->n_nodes)
+ {
+ if (!node_read_value (nid->nodes[i], &reader, i))
+ goto out;
+
+ if (i + 1 == nid->n_nodes)
+ NEXT_EXPECTING_FALSE (&reader);
+ else
+ NEXT_EXPECTING_TRUE (&reader);
+
+ ++i;
+ }
+
+ retval = TRUE;
+
+ out:
+ data_block_reset (nid->block);
+ return retval;
+}
+
+/* offset the seed for setting, so we set different numbers than
+ * we originally wrote. Don't offset by a huge number since in
+ * some cases it's value = possibilities[seed % n_possibilities]
+ * and we don't want to wrap around. bool_from_seed
+ * is just seed % 2 even.
+ */
+#define SET_SEED 1
+static dbus_bool_t
+run_test_set_values (NodeIterationData *nid)
+{
+ DBusTypeReader reader;
+ DBusTypeReader realign_root;
+ dbus_bool_t retval;
+ int i;
+
+ _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+
+ retval = FALSE;
+
+ data_block_init_reader_writer (nid->block,
+ &reader, NULL);
+
+ realign_root = reader;
+
+ i = 0;
+ while (i < nid->n_nodes)
+ {
+ if (!node_set_value (nid->nodes[i],
+ &reader, &realign_root,
+ i + SET_SEED))
+ goto out;
+
+ if (i + 1 == nid->n_nodes)
+ NEXT_EXPECTING_FALSE (&reader);
+ else
+ NEXT_EXPECTING_TRUE (&reader);
+
+ ++i;
+ }
+
+ /* Check that the new values were set */
+
+ reader = realign_root;
+
+ i = 0;
+ while (i < nid->n_nodes)
+ {
+ if (!node_read_value (nid->nodes[i], &reader,
+ i + SET_SEED))
+ goto out;
+
+ if (i + 1 == nid->n_nodes)
+ NEXT_EXPECTING_FALSE (&reader);
+ else
+ NEXT_EXPECTING_TRUE (&reader);
+
+ ++i;
+ }
+
+ retval = TRUE;
+
+ out:
+ return retval;
+}
+
+static dbus_bool_t
+run_test_delete_values (NodeIterationData *nid)
+{
+ DBusTypeReader reader;
+ dbus_bool_t retval;
+ int t;
+
+ _dbus_verbose ("%s\n", _DBUS_FUNCTION_NAME);
+
+ retval = FALSE;
+
+ data_block_init_reader_writer (nid->block,
+ &reader, NULL);
+
+ while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID)
+ {
+ /* Right now, deleting only works on array elements. We delete
+ * all array elements, and then verify that there aren't any
+ * left.
+ */
+ if (t == DBUS_TYPE_ARRAY)
+ {
+ DBusTypeReader array;
+ int n_elements;
+ int elem_type;
+
+ _dbus_type_reader_recurse (&reader, &array);
+ n_elements = 0;
+ while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID)
+ {
+ n_elements += 1;
+ _dbus_type_reader_next (&array);
+ }
+
+ /* reset to start of array */
+ _dbus_type_reader_recurse (&reader, &array);
+ _dbus_verbose ("recursing into deletion loop reader.value_pos = %d array.value_pos = %d array.u.start_pos = %d\n",
+ reader.value_pos, array.value_pos, array.u.array.start_pos);
+ while ((elem_type = _dbus_type_reader_get_current_type (&array)) != DBUS_TYPE_INVALID)
+ {
+ /* We don't want to always delete from the same part of the array. */
+ static int cycle = 0;
+ int elem;
+
+ _dbus_assert (n_elements > 0);
+
+ elem = cycle;
+ if (elem == 3 || elem >= n_elements) /* end of array */
+ elem = n_elements - 1;
+
+ _dbus_verbose ("deleting array element %d of %d type %s cycle %d reader pos %d elem pos %d\n",
+ elem, n_elements, _dbus_type_to_string (elem_type),
+ cycle, reader.value_pos, array.value_pos);
+ while (elem > 0)
+ {
+ if (!_dbus_type_reader_next (&array))
+ _dbus_assert_not_reached ("should have had another element\n");
+ --elem;
+ }
+
+ if (!_dbus_type_reader_delete (&array, &reader))
+ goto out;
+
+ n_elements -= 1;
+
+ /* reset */
+ _dbus_type_reader_recurse (&reader, &array);
+
+ if (cycle > 2)
+ cycle = 0;
+ else
+ cycle += 1;
+ }
+ }
+ _dbus_type_reader_next (&reader);
+ }
+
+ /* Check that there are no array elements left */
+ data_block_init_reader_writer (nid->block,
+ &reader, NULL);
+
+ while ((t = _dbus_type_reader_get_current_type (&reader)) != DBUS_TYPE_INVALID)
+ {
+ _dbus_type_reader_next (&reader);
+ }
+
+ retval = TRUE;
+
+ out:
+ return retval;
+}
+
+static dbus_bool_t
+run_test_nodes_iteration (void *data)
+{
+ NodeIterationData *nid = data;
+ DBusTypeReader reader;
+ DBusTypeWriter writer;
+ int i;
+ dbus_bool_t retval;
+
+ /* Stuff to do:
+ * 1. write the value
+ * 2. strcmp-compare with the signature we built
+ * 3. read the value
+ * 4. type-iterate the signature and the value and see if they are the same type-wise
+ */
+ retval = FALSE;
+
+ data_block_init_reader_writer (nid->block,
+ &reader, &writer);
+
+ /* DBusTypeWriter assumes it's writing into an existing signature,
+ * so doesn't add nul on its own. We have to do that.
+ */
+ if (!_dbus_string_insert_byte (&nid->block->signature,
+ nid->type_offset, '\0'))
+ goto out;
+
+ i = 0;
+ while (i < nid->n_nodes)
+ {
+ if (!node_write_value (nid->nodes[i], nid->block, &writer, i))
+ goto out;
+
+ ++i;
+ }
+
+ if (!_dbus_string_equal_substring (nid->signature, 0, _dbus_string_get_length (nid->signature),
+ &nid->block->signature, nid->type_offset))
+ {
+ _dbus_warn ("Expected signature '%s' and got '%s' with initial offset %d\n",
+ _dbus_string_get_const_data (nid->signature),
+ _dbus_string_get_const_data_len (&nid->block->signature, nid->type_offset, 0),
+ nid->type_offset);
+ _dbus_assert_not_reached ("wrong signature");
+ }
+
+ i = 0;
+ while (i < nid->n_nodes)
+ {
+ if (!node_read_value (nid->nodes[i], &reader, i))
+ goto out;
+
+ if (i + 1 == nid->n_nodes)
+ NEXT_EXPECTING_FALSE (&reader);
+ else
+ NEXT_EXPECTING_TRUE (&reader);
+
+ ++i;
+ }
+
+ if (n_iterations_expected_this_test <= MAX_ITERATIONS_FOR_EXPENSIVE_TESTS)
+ {
+ /* this set values test uses code from copy and
+ * values_only_write so would ideally be last so you get a
+ * simpler test case for problems with copying or values_only
+ * writing; but it also needs an already-written DataBlock so it
+ * has to go first. Comment it out if it breaks, and see if the
+ * later tests also break - debug them first if so.
+ */
+ if (!run_test_set_values (nid))
+ goto out;
+
+ if (!run_test_delete_values (nid))
+ goto out;
+
+ if (!run_test_copy (nid))
+ goto out;
+
+ if (!run_test_values_only_write (nid))
+ goto out;
+ }
+
+ /* FIXME type-iterate both signature and value and compare the resulting
+ * tree to the node tree perhaps
+ */
+
+ retval = TRUE;
+
+ out:
+
+ data_block_reset (nid->block);
+
+ return retval;
+}
+
+static void
+run_test_nodes_in_one_configuration (TestTypeNode **nodes,
+ int n_nodes,
+ const DBusString *signature,
+ int byte_order,
+ int initial_offset)
+{
+ DataBlock block;
+ NodeIterationData nid;
+
+ if (!data_block_init (&block, byte_order, initial_offset))
+ _dbus_assert_not_reached ("no memory");
+
+ nid.signature = signature;
+ nid.block = &block;
+ nid.type_offset = initial_offset;
+ nid.nodes = nodes;
+ nid.n_nodes = n_nodes;
+
+ if (TEST_OOM_HANDLING &&
+ n_iterations_expected_this_test <= MAX_ITERATIONS_FOR_EXPENSIVE_TESTS)
+ {
+ _dbus_test_oom_handling ("running test node",
+ run_test_nodes_iteration,
+ &nid);
+ }
+ else
+ {
+ if (!run_test_nodes_iteration (&nid))
+ _dbus_assert_not_reached ("no memory");
+ }
+
+ data_block_free (&block);
+}
+
+static void
+run_test_nodes (TestTypeNode **nodes,
+ int n_nodes)
+{
+ int i;
+ DBusString signature;
+
+ if (!_dbus_string_init (&signature))
+ _dbus_assert_not_reached ("no memory");
+
+ i = 0;
+ while (i < n_nodes)
+ {
+ if (! node_build_signature (nodes[i], &signature))
+ _dbus_assert_not_reached ("no memory");
+
+ ++i;
+ }
+
+ _dbus_verbose (">>> test nodes with signature '%s'\n",
+ _dbus_string_get_const_data (&signature));
+
+ i = 0;
+ while (i <= MAX_INITIAL_OFFSET)
+ {
+ run_test_nodes_in_one_configuration (nodes, n_nodes, &signature,
+ DBUS_LITTLE_ENDIAN, i);
+ run_test_nodes_in_one_configuration (nodes, n_nodes, &signature,
+ DBUS_BIG_ENDIAN, i);
+
+ ++i;
+ }
+
+ n_iterations_completed_this_test += 1;
+ n_iterations_completed_total += 1;
+
+ if (n_iterations_completed_this_test == n_iterations_expected_this_test)
+ {
+ fprintf (stderr, " 100%% %d this test (%d cumulative)\n",
+ n_iterations_completed_this_test,
+ n_iterations_completed_total);
+ }
+ /* this happens to turn out well with mod == 1 */
+ else if ((n_iterations_completed_this_test %
+ (int)(n_iterations_expected_this_test / 10.0)) == 1)
+ {
+ fprintf (stderr, " %d%% ", (int) (n_iterations_completed_this_test / (double) n_iterations_expected_this_test * 100));
+ }
+
+ _dbus_string_free (&signature);
+}
+
+#define N_VALUES (N_BASICS * N_CONTAINERS + N_BASICS)
+
+static TestTypeNode*
+value_generator (int *ip)
+{
+ int i = *ip;
+ const TestTypeNodeClass *child_klass;
+ const TestTypeNodeClass *container_klass;
+ TestTypeNode *child;
+ TestTypeNode *node;
+
+ _dbus_assert (i <= N_VALUES);
+
+ if (i == N_VALUES)
+ {
+ return NULL;
+ }
+ else if (i < N_BASICS)
+ {
+ node = node_new (basic_nodes[i]);
+ }
+ else
+ {
+ /* imagine an array:
+ * container 0 of basic 0
+ * container 0 of basic 1
+ * container 0 of basic 2
+ * container 1 of basic 0
+ * container 1 of basic 1
+ * container 1 of basic 2
+ */
+ i -= N_BASICS;
+
+ container_klass = container_nodes[i / N_BASICS];
+ child_klass = basic_nodes[i % N_BASICS];
+
+ node = node_new (container_klass);
+ child = node_new (child_klass);
+
+ node_append_child (node, child);
+ }
+
+ *ip += 1; /* increment the generator */
+
+ return node;
+}
+
+static void
+make_and_run_values_inside_container (const TestTypeNodeClass *container_klass,
+ int n_nested)
+{
+ TestTypeNode *root;
+ TestTypeNode *container;
+ TestTypeNode *child;
+ int i;
+
+ root = node_new (container_klass);
+ container = root;
+ for (i = 1; i < n_nested; i++)
+ {
+ child = node_new (container_klass);
+ node_append_child (container, child);
+ container = child;
+ }
+
+ /* container should now be the most-nested container */
+
+ i = 0;
+ while ((child = value_generator (&i)))
+ {
+ node_append_child (container, child);
+
+ run_test_nodes (&root, 1);
+
+ _dbus_list_clear (&((TestTypeNodeContainer*)container)->children);
+ node_destroy (child);
+ }
+
+ node_destroy (root);
+}
+
+static void
+start_next_test (const char *format,
+ int expected)
+{
+ n_iterations_completed_this_test = 0;
+ n_iterations_expected_this_test = expected;
+
+ fprintf (stderr, ">>> >>> ");
+ fprintf (stderr, format,
+ n_iterations_expected_this_test);
+}
+
+static void
+make_and_run_test_nodes (void)
+{
+ int i, j, k, m;
+
+ /* We try to do this in order of "complicatedness" so that test
+ * failures tend to show up in the simplest test case that
+ * demonstrates the failure. There are also some tests that run
+ * more than once for this reason, first while going through simple
+ * cases, second while going through a broader range of complex
+ * cases.
+ */
+ /* Each basic node. The basic nodes should include:
+ *
+ * - each fixed-size type (in such a way that it has different values each time,
+ * so we can tell if we mix two of them up)
+ * - strings of various lengths
+ * - object path
+ * - signature
+ */
+ /* Each container node. The container nodes should include:
+ *
+ * struct with 1 and 2 copies of the contained item
+ * array with 0, 1, 2 copies of the contained item
+ * variant
+ */
+ /* Let a "value" be a basic node, or a container containing a single basic node.
+ * Let n_values be the number of such values i.e. (n_container * n_basic + n_basic)
+ * When iterating through all values to make combinations, do the basic types
+ * first and the containers second.
+ */
+ /* Each item is shown with its number of iterations to complete so
+ * we can keep a handle on this unit test
+ */
+
+ /* FIXME test just an empty body, no types at all */
+
+ start_next_test ("Each value by itself %d iterations\n", N_VALUES);
+ {
+ TestTypeNode *node;
+ i = 0;
+ while ((node = value_generator (&i)))
+ {
+ run_test_nodes (&node, 1);
+
+ node_destroy (node);
+ }
+ }
+
+ start_next_test ("Each value by itself with arrays as blocks %d iterations\n", N_VALUES);
+ arrays_write_fixed_in_blocks = TRUE;
+ {
+ TestTypeNode *node;
+ i = 0;
+ while ((node = value_generator (&i)))
+ {
+ run_test_nodes (&node, 1);
+
+ node_destroy (node);
+ }
+ }
+ arrays_write_fixed_in_blocks = FALSE;
+
+ start_next_test ("All values in one big toplevel %d iteration\n", 1);
+ {
+ TestTypeNode *nodes[N_VALUES];
+
+ i = 0;
+ while ((nodes[i] = value_generator (&i)))
+ ;
+
+ run_test_nodes (nodes, N_VALUES);
+
+ for (i = 0; i < N_VALUES; i++)
+ node_destroy (nodes[i]);
+ }
+
+ start_next_test ("Each value,value pair combination as toplevel, in both orders %d iterations\n",
+ N_VALUES * N_VALUES);
+ {
+ TestTypeNode *nodes[2];
+
+ i = 0;
+ while ((nodes[0] = value_generator (&i)))
+ {
+ j = 0;
+ while ((nodes[1] = value_generator (&j)))
+ {
+ run_test_nodes (nodes, 2);
+
+ node_destroy (nodes[1]);
+ }
+
+ node_destroy (nodes[0]);
+ }
+ }
+
+ start_next_test ("Each container containing each value %d iterations\n",
+ N_CONTAINERS * N_VALUES);
+ for (i = 0; i < N_CONTAINERS; i++)
+ {
+ const TestTypeNodeClass *container_klass = container_nodes[i];
+
+ make_and_run_values_inside_container (container_klass, 1);
+ }
+
+ start_next_test ("Each container containing each value with arrays as blocks %d iterations\n",
+ N_CONTAINERS * N_VALUES);
+ arrays_write_fixed_in_blocks = TRUE;
+ for (i = 0; i < N_CONTAINERS; i++)
+ {
+ const TestTypeNodeClass *container_klass = container_nodes[i];
+
+ make_and_run_values_inside_container (container_klass, 1);
+ }
+ arrays_write_fixed_in_blocks = FALSE;
+
+ start_next_test ("Each container of same container of each value %d iterations\n",
+ N_CONTAINERS * N_VALUES);
+ for (i = 0; i < N_CONTAINERS; i++)
+ {
+ const TestTypeNodeClass *container_klass = container_nodes[i];
+
+ make_and_run_values_inside_container (container_klass, 2);
+ }
+
+ start_next_test ("Each container of same container of same container of each value %d iterations\n",
+ N_CONTAINERS * N_VALUES);
+ for (i = 0; i < N_CONTAINERS; i++)
+ {
+ const TestTypeNodeClass *container_klass = container_nodes[i];
+
+ make_and_run_values_inside_container (container_klass, 3);
+ }
+
+ start_next_test ("Each value,value pair inside a struct %d iterations\n",
+ N_VALUES * N_VALUES);
+ {
+ TestTypeNode *val1, *val2;
+ TestTypeNode *node;
+
+ node = node_new (&struct_1_class);
+
+ i = 0;
+ while ((val1 = value_generator (&i)))
+ {
+ j = 0;
+ while ((val2 = value_generator (&j)))
+ {
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+
+ node_append_child (node, val1);
+ node_append_child (node, val2);
+
+ run_test_nodes (&node, 1);
+
+ _dbus_list_clear (&container->children);
+ node_destroy (val2);
+ }
+ node_destroy (val1);
+ }
+ node_destroy (node);
+ }
+
+ start_next_test ("All values in one big struct %d iteration\n",
+ 1);
+ {
+ TestTypeNode *node;
+ TestTypeNode *child;
+
+ node = node_new (&struct_1_class);
+
+ i = 0;
+ while ((child = value_generator (&i)))
+ node_append_child (node, child);
+
+ run_test_nodes (&node, 1);
+
+ node_destroy (node);
+ }
+
+ start_next_test ("Each value in a large array %d iterations\n",
+ N_VALUES);
+ {
+ TestTypeNode *val;
+ TestTypeNode *node;
+
+ node = node_new (&array_9_class);
+
+ i = 0;
+ while ((val = value_generator (&i)))
+ {
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+
+ node_append_child (node, val);
+
+ run_test_nodes (&node, 1);
+
+ _dbus_list_clear (&container->children);
+ node_destroy (val);
+ }
+
+ node_destroy (node);
+ }
+
+ start_next_test ("Each container of each container of each value %d iterations\n",
+ N_CONTAINERS * N_CONTAINERS * N_VALUES);
+ for (i = 0; i < N_CONTAINERS; i++)
+ {
+ const TestTypeNodeClass *outer_container_klass = container_nodes[i];
+ TestTypeNode *outer_container = node_new (outer_container_klass);
+
+ for (j = 0; j < N_CONTAINERS; j++)
+ {
+ TestTypeNode *child;
+ const TestTypeNodeClass *inner_container_klass = container_nodes[j];
+ TestTypeNode *inner_container = node_new (inner_container_klass);
+
+ node_append_child (outer_container, inner_container);
+
+ m = 0;
+ while ((child = value_generator (&m)))
+ {
+ node_append_child (inner_container, child);
+
+ run_test_nodes (&outer_container, 1);
+
+ _dbus_list_clear (&((TestTypeNodeContainer*)inner_container)->children);
+ node_destroy (child);
+ }
+ _dbus_list_clear (&((TestTypeNodeContainer*)outer_container)->children);
+ node_destroy (inner_container);
+ }
+ node_destroy (outer_container);
+ }
+
+ start_next_test ("Each container of each container of each container of each value %d iterations\n",
+ N_CONTAINERS * N_CONTAINERS * N_CONTAINERS * N_VALUES);
+ for (i = 0; i < N_CONTAINERS; i++)
+ {
+ const TestTypeNodeClass *outer_container_klass = container_nodes[i];
+ TestTypeNode *outer_container = node_new (outer_container_klass);
+
+ for (j = 0; j < N_CONTAINERS; j++)
+ {
+ const TestTypeNodeClass *inner_container_klass = container_nodes[j];
+ TestTypeNode *inner_container = node_new (inner_container_klass);
+
+ node_append_child (outer_container, inner_container);
+
+ for (k = 0; k < N_CONTAINERS; k++)
+ {
+ TestTypeNode *child;
+ const TestTypeNodeClass *center_container_klass = container_nodes[k];
+ TestTypeNode *center_container = node_new (center_container_klass);
+
+ node_append_child (inner_container, center_container);
+
+ m = 0;
+ while ((child = value_generator (&m)))
+ {
+ node_append_child (center_container, child);
+
+ run_test_nodes (&outer_container, 1);
+
+ _dbus_list_clear (&((TestTypeNodeContainer*)center_container)->children);
+ node_destroy (child);
+ }
+ _dbus_list_clear (&((TestTypeNodeContainer*)inner_container)->children);
+ node_destroy (center_container);
+ }
+ _dbus_list_clear (&((TestTypeNodeContainer*)outer_container)->children);
+ node_destroy (inner_container);
+ }
+ node_destroy (outer_container);
+ }
+
+#if 0
+ /* This one takes a really long time, so comment it out for now */
+ start_next_test ("Each value,value,value triplet combination as toplevel, in all orders %d iterations\n",
+ N_VALUES * N_VALUES * N_VALUES);
+ {
+ TestTypeNode *nodes[3];
+
+ i = 0;
+ while ((nodes[0] = value_generator (&i)))
+ {
+ j = 0;
+ while ((nodes[1] = value_generator (&j)))
+ {
+ k = 0;
+ while ((nodes[2] = value_generator (&k)))
+ {
+ run_test_nodes (nodes, 3);
+
+ node_destroy (nodes[2]);
+ }
+ node_destroy (nodes[1]);
+ }
+ node_destroy (nodes[0]);
+ }
+ }
+#endif /* #if 0 expensive test */
+
+ fprintf (stderr, "%d total iterations of recursive marshaling tests\n",
+ n_iterations_completed_total);
+ fprintf (stderr, "each iteration ran at initial offsets 0 through %d in both big and little endian\n",
+ MAX_INITIAL_OFFSET);
+ fprintf (stderr, "out of memory handling %s tested\n",
+ TEST_OOM_HANDLING ? "was" : "was not");
+}
+
+dbus_bool_t
+_dbus_marshal_recursive_test (void)
+{
+ make_and_run_test_nodes ();
+
+ return TRUE;
+}
+
+/*
+ *
+ *
+ * Implementations of each type node class
+ *
+ *
+ *
+ */
+#define MAX_MULTI_COUNT 5
+
+
+#define SAMPLE_INT32 12345678
+#define SAMPLE_INT32_ALTERNATE 53781429
+static dbus_int32_t
+int32_from_seed (int seed)
+{
+ /* Generate an integer value that's predictable from seed. We could
+ * just use seed itself, but that would only ever touch one byte of
+ * the int so would miss some kinds of bug.
+ */
+ dbus_int32_t v;
+
+ v = 42; /* just to quiet compiler afaik */
+ switch (seed % 5)
+ {
+ case 0:
+ v = SAMPLE_INT32;
+ break;
+ case 1:
+ v = SAMPLE_INT32_ALTERNATE;
+ break;
+ case 2:
+ v = -1;
+ break;
+ case 3:
+ v = _DBUS_INT_MAX;
+ break;
+ case 4:
+ v = 1;
+ break;
+ }
+
+ if (seed > 1)
+ v *= seed; /* wraps around eventually, which is fine */
+
+ return v;
+}
+
+static dbus_bool_t
+int32_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ /* also used for uint32 */
+ dbus_int32_t v;
+
+ v = int32_from_seed (seed);
+
+ return _dbus_type_writer_write_basic (writer,
+ node->klass->typecode,
+ &v);
+}
+
+static dbus_bool_t
+int32_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ /* also used for uint32 */
+ dbus_int32_t v;
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_basic (reader,
+ (dbus_int32_t*) &v);
+
+ _dbus_assert (v == int32_from_seed (seed));
+
+ return TRUE;
+}
+
+static dbus_bool_t
+int32_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ /* also used for uint32 */
+ dbus_int32_t v;
+
+ v = int32_from_seed (seed);
+
+ return _dbus_type_reader_set_basic (reader,
+ &v,
+ realign_root);
+}
+
+static dbus_bool_t
+int32_write_multi (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed,
+ int count)
+{
+ /* also used for uint32 */
+ dbus_int32_t values[MAX_MULTI_COUNT];
+ dbus_int32_t *v_ARRAY_INT32 = values;
+ int i;
+
+ for (i = 0; i < count; ++i)
+ values[i] = int32_from_seed (seed + i);
+
+ return _dbus_type_writer_write_fixed_multi (writer,
+ node->klass->typecode,
+ &v_ARRAY_INT32, count);
+}
+
+static dbus_bool_t
+int32_read_multi (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed,
+ int count)
+{
+ /* also used for uint32 */
+ dbus_int32_t *values;
+ int n_elements;
+ int i;
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_fixed_multi (reader,
+ &values,
+ &n_elements);
+
+ if (n_elements != count)
+ _dbus_warn ("got %d elements expected %d\n", n_elements, count);
+ _dbus_assert (n_elements == count);
+
+ for (i = 0; i < count; i++)
+ _dbus_assert (((int)_dbus_unpack_uint32 (reader->byte_order,
+ (const unsigned char*)values + (i * 4))) ==
+ int32_from_seed (seed + i));
+
+ return TRUE;
+}
+
+#ifdef DBUS_HAVE_INT64
+static dbus_int64_t
+int64_from_seed (int seed)
+{
+ dbus_int32_t v32;
+ dbus_int64_t v;
+
+ v32 = int32_from_seed (seed);
+
+ v = - (dbus_int32_t) ~ v32;
+ v |= (((dbus_int64_t)v32) << 32);
+
+ return v;
+}
+#endif
+
+static dbus_bool_t
+int64_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+#ifdef DBUS_HAVE_INT64
+ /* also used for uint64 */
+ dbus_int64_t v;
+
+ v = int64_from_seed (seed);
+
+ return _dbus_type_writer_write_basic (writer,
+ node->klass->typecode,
+ &v);
+#else
+ return TRUE;
+#endif
+}
+
+static dbus_bool_t
+int64_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+#ifdef DBUS_HAVE_INT64
+ /* also used for uint64 */
+ dbus_int64_t v;
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_basic (reader,
+ (dbus_int64_t*) &v);
+
+ _dbus_assert (v == int64_from_seed (seed));
+
+ return TRUE;
+#else
+ return TRUE;
+#endif
+}
+
+static dbus_bool_t
+int64_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+#ifdef DBUS_HAVE_INT64
+ /* also used for uint64 */
+ dbus_int64_t v;
+
+ v = int64_from_seed (seed);
+
+ return _dbus_type_reader_set_basic (reader,
+ &v,
+ realign_root);
+#else
+ return TRUE;
+#endif
+}
+
+#define MAX_SAMPLE_STRING_LEN 10
+static void
+string_from_seed (char *buf,
+ int len,
+ int seed)
+{
+ int i;
+ unsigned char v;
+
+ _dbus_assert (len < MAX_SAMPLE_STRING_LEN);
+
+ /* vary the length slightly, though we also have multiple string
+ * value types for this, varying it here tests the set_value code
+ */
+ switch (seed % 3)
+ {
+ case 1:
+ len += 2;
+ break;
+ case 2:
+ len -= 2;
+ break;
+ }
+ if (len < 0)
+ len = 0;
+
+ v = (unsigned char) ('A' + seed);
+
+ i = 0;
+ while (i < len)
+ {
+ if (v < 'A' || v > 'z')
+ v = 'A';
+
+ buf[i] = v;
+
+ v += 1;
+ ++i;
+ }
+
+ buf[i] = '\0';
+}
+
+static dbus_bool_t
+string_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ char buf[MAX_SAMPLE_STRING_LEN];
+ const char *v_string = buf;
+
+ string_from_seed (buf, node->klass->subclass_detail,
+ seed);
+
+ return _dbus_type_writer_write_basic (writer,
+ node->klass->typecode,
+ &v_string);
+}
+
+static dbus_bool_t
+string_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ const char *v;
+ char buf[MAX_SAMPLE_STRING_LEN];
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_basic (reader,
+ (const char **) &v);
+
+ string_from_seed (buf, node->klass->subclass_detail,
+ seed);
+
+ if (strcmp (buf, v) != 0)
+ {
+ _dbus_warn ("read string '%s' expected '%s'\n",
+ v, buf);
+ _dbus_assert_not_reached ("test failed");
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+string_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ char buf[MAX_SAMPLE_STRING_LEN];
+ const char *v_string = buf;
+
+ string_from_seed (buf, node->klass->subclass_detail,
+ seed);
+
+#if RECURSIVE_MARSHAL_WRITE_TRACE
+ {
+ const char *old;
+ _dbus_type_reader_read_basic (reader, &old);
+ _dbus_verbose ("SETTING new string '%s' len %d in place of '%s' len %d\n",
+ v_string, strlen (v_string), old, strlen (old));
+ }
+#endif
+
+ return _dbus_type_reader_set_basic (reader,
+ &v_string,
+ realign_root);
+}
+
+#define BOOL_FROM_SEED(seed) (seed % 2)
+
+static dbus_bool_t
+bool_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ unsigned char v;
+
+ v = BOOL_FROM_SEED (seed);
+
+ return _dbus_type_writer_write_basic (writer,
+ node->klass->typecode,
+ &v);
+}
+
+static dbus_bool_t
+bool_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ unsigned char v;
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_basic (reader,
+ (unsigned char*) &v);
+
+ _dbus_assert (v == BOOL_FROM_SEED (seed));
+
+ return TRUE;
+}
+
+static dbus_bool_t
+bool_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ unsigned char v;
+
+ v = BOOL_FROM_SEED (seed);
+
+ return _dbus_type_reader_set_basic (reader,
+ &v,
+ realign_root);
+}
+
+#define BYTE_FROM_SEED(seed) ((unsigned char) int32_from_seed (seed))
+
+static dbus_bool_t
+byte_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ unsigned char v;
+
+ v = BYTE_FROM_SEED (seed);
+
+ return _dbus_type_writer_write_basic (writer,
+ node->klass->typecode,
+ &v);
+}
+
+static dbus_bool_t
+byte_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ unsigned char v;
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_basic (reader,
+ (unsigned char*) &v);
+
+ _dbus_assert (v == BYTE_FROM_SEED (seed));
+
+ return TRUE;
+}
+
+
+static dbus_bool_t
+byte_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ unsigned char v;
+
+ v = BYTE_FROM_SEED (seed);
+
+ return _dbus_type_reader_set_basic (reader,
+ &v,
+ realign_root);
+}
+
+static double
+double_from_seed (int seed)
+{
+ return SAMPLE_INT32 * (double) seed + 0.3;
+}
+
+static dbus_bool_t
+double_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ double v;
+
+ v = double_from_seed (seed);
+
+ return _dbus_type_writer_write_basic (writer,
+ node->klass->typecode,
+ &v);
+}
+
+static dbus_bool_t
+double_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ double v;
+ double expected;
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_basic (reader,
+ (double*) &v);
+
+ expected = double_from_seed (seed);
+
+ if (!_DBUS_DOUBLES_BITWISE_EQUAL (v, expected))
+ {
+#ifdef DBUS_HAVE_INT64
+ _dbus_warn ("Expected double %g got %g\n bits = 0x%llx vs.\n bits = 0x%llx)\n",
+ expected, v,
+ *(dbus_uint64_t*)(char*)&expected,
+ *(dbus_uint64_t*)(char*)&v);
+#endif
+ _dbus_assert_not_reached ("test failed");
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+double_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ double v;
+
+ v = double_from_seed (seed);
+
+ return _dbus_type_reader_set_basic (reader,
+ &v,
+ realign_root);
+}
+
+#define MAX_SAMPLE_OBJECT_PATH_LEN 10
+static void
+object_path_from_seed (char *buf,
+ int seed)
+{
+ int i;
+ unsigned char v;
+ int len;
+
+ len = seed % 9;
+ _dbus_assert (len < MAX_SAMPLE_OBJECT_PATH_LEN);
+
+ v = (unsigned char) ('A' + seed);
+
+ i = 0;
+ while (i + 1 < len)
+ {
+ if (v < 'A' || v > 'z')
+ v = 'A';
+
+ buf[i] = '/';
+ ++i;
+ buf[i] = v;
+ ++i;
+
+ v += 1;
+ }
+
+ buf[i] = '\0';
+}
+
+static dbus_bool_t
+object_path_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ char buf[MAX_SAMPLE_OBJECT_PATH_LEN];
+ const char *v_string = buf;
+
+ object_path_from_seed (buf, seed);
+
+ return _dbus_type_writer_write_basic (writer,
+ node->klass->typecode,
+ &v_string);
+}
+
+static dbus_bool_t
+object_path_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ const char *v;
+ char buf[MAX_SAMPLE_OBJECT_PATH_LEN];
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_basic (reader,
+ (const char **) &v);
+
+ object_path_from_seed (buf, seed);
+
+ if (strcmp (buf, v) != 0)
+ {
+ _dbus_warn ("read object path '%s' expected '%s'\n",
+ v, buf);
+ _dbus_assert_not_reached ("test failed");
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+object_path_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ char buf[MAX_SAMPLE_OBJECT_PATH_LEN];
+ const char *v_string = buf;
+
+ object_path_from_seed (buf, seed);
+
+ return _dbus_type_reader_set_basic (reader,
+ &v_string,
+ realign_root);
+}
+
+#define MAX_SAMPLE_SIGNATURE_LEN 10
+static void
+signature_from_seed (char *buf,
+ int seed)
+{
+ int i;
+ const char *s;
+ /* try to avoid ascending, descending, or alternating length to help find bugs */
+ const char *sample_signatures[] = {
+ "asax"
+ "",
+ "asau(xxxx)",
+ "x",
+ "ai",
+ "a(ii)"
+ };
+
+ s = sample_signatures[seed % _DBUS_N_ELEMENTS(sample_signatures)];
+
+ for (i = 0; s[i]; i++)
+ {
+ buf[i] = s[i];
+ }
+ buf[i] = '\0';
+}
+
+static dbus_bool_t
+signature_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ char buf[MAX_SAMPLE_SIGNATURE_LEN];
+ const char *v_string = buf;
+
+ signature_from_seed (buf, seed);
+
+ return _dbus_type_writer_write_basic (writer,
+ node->klass->typecode,
+ &v_string);
+}
+
+static dbus_bool_t
+signature_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ const char *v;
+ char buf[MAX_SAMPLE_SIGNATURE_LEN];
+
+ check_expected_type (reader, node->klass->typecode);
+
+ _dbus_type_reader_read_basic (reader,
+ (const char **) &v);
+
+ signature_from_seed (buf, seed);
+
+ if (strcmp (buf, v) != 0)
+ {
+ _dbus_warn ("read signature value '%s' expected '%s'\n",
+ v, buf);
+ _dbus_assert_not_reached ("test failed");
+ }
+
+ return TRUE;
+}
+
+
+static dbus_bool_t
+signature_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ char buf[MAX_SAMPLE_SIGNATURE_LEN];
+ const char *v_string = buf;
+
+ signature_from_seed (buf, seed);
+
+ return _dbus_type_reader_set_basic (reader,
+ &v_string,
+ realign_root);
+}
+
+static dbus_bool_t
+struct_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ DataBlockState saved;
+ DBusTypeWriter sub;
+ int i;
+ int n_copies;
+
+ n_copies = node->klass->subclass_detail;
+
+ _dbus_assert (container->children != NULL);
+
+ data_block_save (block, &saved);
+
+ if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_STRUCT,
+ NULL, 0,
+ &sub))
+ return FALSE;
+
+ i = 0;
+ while (i < n_copies)
+ {
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&container->children);
+ while (link != NULL)
+ {
+ TestTypeNode *child = link->data;
+ DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+ if (!node_write_value (child, block, &sub, seed + i))
+ {
+ data_block_restore (block, &saved);
+ return FALSE;
+ }
+
+ link = next;
+ }
+
+ ++i;
+ }
+
+ if (!_dbus_type_writer_unrecurse (writer, &sub))
+ {
+ data_block_restore (block, &saved);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+struct_read_or_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ DBusTypeReader sub;
+ int i;
+ int n_copies;
+
+ n_copies = node->klass->subclass_detail;
+
+ check_expected_type (reader, DBUS_TYPE_STRUCT);
+
+ _dbus_type_reader_recurse (reader, &sub);
+
+ i = 0;
+ while (i < n_copies)
+ {
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&container->children);
+ while (link != NULL)
+ {
+ TestTypeNode *child = link->data;
+ DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+ if (realign_root == NULL)
+ {
+ if (!node_read_value (child, &sub, seed + i))
+ return FALSE;
+ }
+ else
+ {
+ if (!node_set_value (child, &sub, realign_root, seed + i))
+ return FALSE;
+ }
+
+ if (i == (n_copies - 1) && next == NULL)
+ NEXT_EXPECTING_FALSE (&sub);
+ else
+ NEXT_EXPECTING_TRUE (&sub);
+
+ link = next;
+ }
+
+ ++i;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+struct_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ return struct_read_or_set_value (node, reader, NULL, seed);
+}
+
+static dbus_bool_t
+struct_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ return struct_read_or_set_value (node, reader, realign_root, seed);
+}
+
+static dbus_bool_t
+struct_build_signature (TestTypeNode *node,
+ DBusString *str)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ int i;
+ int orig_len;
+ int n_copies;
+
+ n_copies = node->klass->subclass_detail;
+
+ orig_len = _dbus_string_get_length (str);
+
+ if (!_dbus_string_append_byte (str, DBUS_STRUCT_BEGIN_CHAR))
+ goto oom;
+
+ i = 0;
+ while (i < n_copies)
+ {
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&container->children);
+ while (link != NULL)
+ {
+ TestTypeNode *child = link->data;
+ DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+ if (!node_build_signature (child, str))
+ goto oom;
+
+ link = next;
+ }
+
+ ++i;
+ }
+
+ if (!_dbus_string_append_byte (str, DBUS_STRUCT_END_CHAR))
+ goto oom;
+
+ return TRUE;
+
+ oom:
+ _dbus_string_set_length (str, orig_len);
+ return FALSE;
+}
+
+static dbus_bool_t
+array_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ DataBlockState saved;
+ DBusTypeWriter sub;
+ DBusString element_signature;
+ int i;
+ int n_copies;
+ int element_type;
+ TestTypeNode *child;
+
+ n_copies = node->klass->subclass_detail;
+
+ _dbus_assert (container->children != NULL);
+
+ data_block_save (block, &saved);
+
+ if (!_dbus_string_init (&element_signature))
+ return FALSE;
+
+ child = _dbus_list_get_first (&container->children);
+
+ if (!node_build_signature (child,
+ &element_signature))
+ goto oom;
+
+ element_type = first_type_in_signature (&element_signature, 0);
+
+ if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_ARRAY,
+ &element_signature, 0,
+ &sub))
+ goto oom;
+
+ if (arrays_write_fixed_in_blocks &&
+ _dbus_type_is_fixed (element_type) &&
+ child->klass->write_multi)
+ {
+ if (!node_write_multi (child, block, &sub, seed, n_copies))
+ goto oom;
+ }
+ else
+ {
+ i = 0;
+ while (i < n_copies)
+ {
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&container->children);
+ while (link != NULL)
+ {
+ TestTypeNode *child = link->data;
+ DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+ if (!node_write_value (child, block, &sub, seed + i))
+ goto oom;
+
+ link = next;
+ }
+
+ ++i;
+ }
+ }
+
+ if (!_dbus_type_writer_unrecurse (writer, &sub))
+ goto oom;
+
+ _dbus_string_free (&element_signature);
+ return TRUE;
+
+ oom:
+ data_block_restore (block, &saved);
+ _dbus_string_free (&element_signature);
+ return FALSE;
+}
+
+static dbus_bool_t
+array_read_or_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ DBusTypeReader sub;
+ int i;
+ int n_copies;
+ TestTypeNode *child;
+
+ n_copies = node->klass->subclass_detail;
+
+ check_expected_type (reader, DBUS_TYPE_ARRAY);
+
+ child = _dbus_list_get_first (&container->children);
+
+ if (n_copies > 0)
+ {
+ _dbus_type_reader_recurse (reader, &sub);
+
+ if (realign_root == NULL && arrays_write_fixed_in_blocks &&
+ _dbus_type_is_fixed (_dbus_type_reader_get_element_type (reader)) &&
+ child->klass->read_multi)
+ {
+ if (!node_read_multi (child, &sub, seed, n_copies))
+ return FALSE;
+ }
+ else
+ {
+ i = 0;
+ while (i < n_copies)
+ {
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&container->children);
+ while (link != NULL)
+ {
+ TestTypeNode *child = link->data;
+ DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+ _dbus_assert (child->klass->typecode ==
+ _dbus_type_reader_get_element_type (reader));
+
+ if (realign_root == NULL)
+ {
+ if (!node_read_value (child, &sub, seed + i))
+ return FALSE;
+ }
+ else
+ {
+ if (!node_set_value (child, &sub, realign_root, seed + i))
+ return FALSE;
+ }
+
+ if (i == (n_copies - 1) && next == NULL)
+ NEXT_EXPECTING_FALSE (&sub);
+ else
+ NEXT_EXPECTING_TRUE (&sub);
+
+ link = next;
+ }
+
+ ++i;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+array_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ return array_read_or_set_value (node, reader, NULL, seed);
+}
+
+static dbus_bool_t
+array_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ return array_read_or_set_value (node, reader, realign_root, seed);
+}
+
+static dbus_bool_t
+array_build_signature (TestTypeNode *node,
+ DBusString *str)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ int orig_len;
+
+ orig_len = _dbus_string_get_length (str);
+
+ if (!_dbus_string_append_byte (str, DBUS_TYPE_ARRAY))
+ goto oom;
+
+ if (!node_build_signature (_dbus_list_get_first (&container->children),
+ str))
+ goto oom;
+
+ return TRUE;
+
+ oom:
+ _dbus_string_set_length (str, orig_len);
+ return FALSE;
+}
+
+ /* 10 is random just to add another seed that we use in the suite */
+#define VARIANT_SEED 10
+
+static dbus_bool_t
+variant_write_value (TestTypeNode *node,
+ DataBlock *block,
+ DBusTypeWriter *writer,
+ int seed)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ DataBlockState saved;
+ DBusTypeWriter sub;
+ DBusString content_signature;
+ TestTypeNode *child;
+
+ _dbus_assert (container->children != NULL);
+ _dbus_assert (_dbus_list_length_is_one (&container->children));
+
+ child = _dbus_list_get_first (&container->children);
+
+ data_block_save (block, &saved);
+
+ if (!_dbus_string_init (&content_signature))
+ return FALSE;
+
+ if (!node_build_signature (child,
+ &content_signature))
+ goto oom;
+
+ if (!_dbus_type_writer_recurse (writer, DBUS_TYPE_VARIANT,
+ &content_signature, 0,
+ &sub))
+ goto oom;
+
+ if (!node_write_value (child, block, &sub, seed + VARIANT_SEED))
+ goto oom;
+
+ if (!_dbus_type_writer_unrecurse (writer, &sub))
+ goto oom;
+
+ _dbus_string_free (&content_signature);
+ return TRUE;
+
+ oom:
+ data_block_restore (block, &saved);
+ _dbus_string_free (&content_signature);
+ return FALSE;
+}
+
+static dbus_bool_t
+variant_read_or_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ DBusTypeReader sub;
+ TestTypeNode *child;
+
+ _dbus_assert (container->children != NULL);
+ _dbus_assert (_dbus_list_length_is_one (&container->children));
+
+ child = _dbus_list_get_first (&container->children);
+
+ check_expected_type (reader, DBUS_TYPE_VARIANT);
+
+ _dbus_type_reader_recurse (reader, &sub);
+
+ if (realign_root == NULL)
+ {
+ if (!node_read_value (child, &sub, seed + VARIANT_SEED))
+ return FALSE;
+ }
+ else
+ {
+ if (!node_set_value (child, &sub, realign_root, seed + VARIANT_SEED))
+ return FALSE;
+ }
+
+ NEXT_EXPECTING_FALSE (&sub);
+
+ return TRUE;
+}
+
+static dbus_bool_t
+variant_read_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ int seed)
+{
+ return variant_read_or_set_value (node, reader, NULL, seed);
+}
+
+static dbus_bool_t
+variant_set_value (TestTypeNode *node,
+ DBusTypeReader *reader,
+ DBusTypeReader *realign_root,
+ int seed)
+{
+ return variant_read_or_set_value (node, reader, realign_root, seed);
+}
+
+static void
+container_destroy (TestTypeNode *node)
+{
+ TestTypeNodeContainer *container = (TestTypeNodeContainer*) node;
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&container->children);
+ while (link != NULL)
+ {
+ TestTypeNode *child = link->data;
+ DBusList *next = _dbus_list_get_next_link (&container->children, link);
+
+ node_destroy (child);
+
+ _dbus_list_free_link (link);
+
+ link = next;
+ }
+}
+
+#endif /* DBUS_BUILD_TESTS */