blob: 95124140ab9a90a9e914f73380091a665445cd66 [file] [log] [blame]
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <config.h>
#ifdef DBUS_BUILD_TESTS
#include "dbus-marshal-recursive.h"
#include "dbus-marshal-basic.h"
#include "dbus-signature.h"
#include "dbus-internals.h"
#include <string.h>
static void
basic_value_zero (DBusBasicValue *value)
{
#ifdef DBUS_HAVE_INT64
value->u64 = 0;
#else
value->eight.first32 = 0;
value->eight.second32 = 0;
#endif
}
static dbus_bool_t
basic_value_equal (int type,
DBusBasicValue *lhs,
DBusBasicValue *rhs)
{
if (type == DBUS_TYPE_STRING ||
type == DBUS_TYPE_SIGNATURE ||
type == DBUS_TYPE_OBJECT_PATH)
{
return strcmp (lhs->str, rhs->str) == 0;
}
else
{
#ifdef DBUS_HAVE_INT64
return lhs->u64 == rhs->u64;
#else
return lhs->eight.first32 == rhs->eight.first32 &&
lhs->eight.second32 == rhs->eight.second32;
#endif
}
}
static dbus_bool_t
equal_values_helper (DBusTypeReader *lhs,
DBusTypeReader *rhs)
{
int lhs_type;
int rhs_type;
lhs_type = _dbus_type_reader_get_current_type (lhs);
rhs_type = _dbus_type_reader_get_current_type (rhs);
if (lhs_type != rhs_type)
return FALSE;
if (lhs_type == DBUS_TYPE_INVALID)
return TRUE;
if (dbus_type_is_basic (lhs_type))
{
DBusBasicValue lhs_value;
DBusBasicValue rhs_value;
basic_value_zero (&lhs_value);
basic_value_zero (&rhs_value);
_dbus_type_reader_read_basic (lhs, &lhs_value);
_dbus_type_reader_read_basic (rhs, &rhs_value);
return basic_value_equal (lhs_type, &lhs_value, &rhs_value);
}
else
{
DBusTypeReader lhs_sub;
DBusTypeReader rhs_sub;
_dbus_type_reader_recurse (lhs, &lhs_sub);
_dbus_type_reader_recurse (rhs, &rhs_sub);
return equal_values_helper (&lhs_sub, &rhs_sub);
}
}
/**
* See whether the two readers point to identical data blocks.
*
* @param lhs reader 1
* @param rhs reader 2
* @returns #TRUE if the data blocks have the same values
*/
dbus_bool_t
_dbus_type_reader_equal_values (const DBusTypeReader *lhs,
const DBusTypeReader *rhs)
{
DBusTypeReader copy_lhs = *lhs;
DBusTypeReader copy_rhs = *rhs;
return equal_values_helper (&copy_lhs, &copy_rhs);
}
/* TESTS */
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#include "dbus-test.h"
#include "dbus-list.h"
#include <stdio.h>
#include <stdlib.h>
/* 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 int16_write_value (TestTypeNode *node,
DataBlock *block,
DBusTypeWriter *writer,
int seed);
static dbus_bool_t int16_read_value (TestTypeNode *node,
DBusTypeReader *reader,
int seed);
static dbus_bool_t int16_set_value (TestTypeNode *node,
DBusTypeReader *reader,
DBusTypeReader *realign_root,
int seed);
static dbus_bool_t int16_write_multi (TestTypeNode *node,
DataBlock *block,
DBusTypeWriter *writer,
int seed,
int count);
static dbus_bool_t int16_read_multi (TestTypeNode *node,
DBusTypeReader *reader,
int seed,
int count);
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 dict_write_value (TestTypeNode *node,
DataBlock *block,
DBusTypeWriter *writer,
int seed);
static dbus_bool_t dict_read_value (TestTypeNode *node,
DBusTypeReader *reader,
int seed);
static dbus_bool_t dict_set_value (TestTypeNode *node,
DBusTypeReader *reader,
DBusTypeReader *realign_root,
int seed);
static dbus_bool_t dict_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 int16_class = {
DBUS_TYPE_INT16,
sizeof (TestTypeNode),
0,
NULL,
NULL,
int16_write_value,
int16_read_value,
int16_set_value,
NULL,
int16_write_multi,
int16_read_multi
};
static const TestTypeNodeClass uint16_class = {
DBUS_TYPE_UINT16,
sizeof (TestTypeNode),
0,
NULL,
NULL,
int16_write_value, /* recycle from int16 */
int16_read_value, /* recycle from int16 */
int16_set_value, /* recycle from int16 */
NULL,
int16_write_multi, /* recycle from int16 */
int16_read_multi /* recycle from int16 */
};
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 const TestTypeNodeClass dict_1_class = {
DBUS_TYPE_ARRAY, /* this is correct, a dict is an array of dict entry */
sizeof (TestTypeNodeContainer),
1, /* number of entries */
NULL,
container_destroy,
dict_write_value,
dict_read_value,
dict_set_value,
dict_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[] = {
&int16_class,
&uint16_class,
&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,
&dict_1_class /* last since we want struct and array before it */
/* 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 NULL;
}
}
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)
{
/* DBusTypeReader restored; */
if (!(* node->klass->read_value) (node, reader, 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 ("\n");
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 ("\n");
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 ("\n");
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 ("\n");
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
build_body (TestTypeNode **nodes,
int n_nodes,
int byte_order,
DBusString *signature,
DBusString *body)
{
int i;
DataBlock block;
DBusTypeReader reader;
DBusTypeWriter writer;
i = 0;
while (i < n_nodes)
{
if (! node_build_signature (nodes[i], signature))
_dbus_assert_not_reached ("no memory");
++i;
}
if (!data_block_init (&block, byte_order, 0))
_dbus_assert_not_reached ("no memory");
data_block_init_reader_writer (&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 (&block.signature,
0, '\0'))
_dbus_assert_not_reached ("no memory");
i = 0;
while (i < n_nodes)
{
if (!node_write_value (nodes[i], &block, &writer, i))
_dbus_assert_not_reached ("no memory");
++i;
}
if (!_dbus_string_copy_len (&block.body, 0,
_dbus_string_get_length (&block.body) - N_FENCE_BYTES,
body, 0))
_dbus_assert_not_reached ("oom");
data_block_free (&block);
}
dbus_bool_t
dbus_internal_do_not_use_generate_bodies (int sequence,
int byte_order,
DBusString *signature,
DBusString *body)
{
TestTypeNode *nodes[1];
int i;
int n_nodes;
nodes[0] = value_generator (&sequence);
if (nodes[0] == NULL)
return FALSE;
n_nodes = 1;
build_body (nodes, n_nodes, byte_order, signature, body);
i = 0;
while (i < n_nodes)
{
node_destroy (nodes[i]);
++i;
}
return TRUE;
}
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);
}
if (_dbus_getenv ("DBUS_TEST_SLOW") == NULL ||
atoi (_dbus_getenv ("DBUS_TEST_SLOW")) < 1)
{
fprintf (stderr, "skipping remaining marshal-recursive tests, "
"run with DBUS_TEST_SLOW=1 (or more) to enable\n");
goto out;
}
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);
}
/* This one takes a really long time (10 minutes on a Core2), so only enable
* it if you're really sure */
if (atoi (_dbus_getenv ("DBUS_TEST_SLOW")) < 2)
{
fprintf (stderr, "skipping really slow marshal-recursive test, "
"run with DBUS_TEST_SLOW=2 (or more) to enable\n");
goto out;
}
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]);
}
}
out:
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_INT16 1234
#define SAMPLE_INT16_ALTERNATE 6785
static dbus_int16_t
int16_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_int16_t v;
v = 42; /* just to quiet compiler afaik */
switch (seed % 5)
{
case 0:
v = SAMPLE_INT16;
break;
case 1:
v = SAMPLE_INT16_ALTERNATE;
break;
case 2:
v = -1;
break;
case 3:
v = _DBUS_INT16_MAX;
break;
case 4:
v = 1;
break;
}
if (seed > 1)
v *= seed; /* wraps around eventually, which is fine */
return v;
}
static dbus_bool_t
int16_write_value (TestTypeNode *node,
DataBlock *block,
DBusTypeWriter *writer,
int seed)
{
/* also used for uint16 */
dbus_int16_t v;
v = int16_from_seed (seed);
return _dbus_type_writer_write_basic (writer,
node->klass->typecode,
&v);
}
static dbus_bool_t
int16_read_value (TestTypeNode *node,
DBusTypeReader *reader,
int seed)
{
/* also used for uint16 */
dbus_int16_t v;
check_expected_type (reader, node->klass->typecode);
_dbus_type_reader_read_basic (reader,
(dbus_int16_t*) &v);
_dbus_assert (v == int16_from_seed (seed));
return TRUE;
}
static dbus_bool_t
int16_set_value (TestTypeNode *node,
DBusTypeReader *reader,
DBusTypeReader *realign_root,
int seed)
{
/* also used for uint16 */
dbus_int16_t v;
v = int16_from_seed (seed);
return _dbus_type_reader_set_basic (reader,
&v,
realign_root);
}
static dbus_bool_t
int16_write_multi (TestTypeNode *node,
DataBlock *block,
DBusTypeWriter *writer,
int seed,
int count)
{
/* also used for uint16 */
dbus_int16_t values[MAX_MULTI_COUNT];
dbus_int16_t *v_ARRAY_INT16 = values;
int i;
for (i = 0; i < count; ++i)
values[i] = int16_from_seed (seed + i);
return _dbus_type_writer_write_fixed_multi (writer,
node->klass->typecode,
&v_ARRAY_INT16, count);
}
static dbus_bool_t
int16_read_multi (TestTypeNode *node,
DBusTypeReader *reader,
int seed,
int count)
{
/* also used for uint16 */
dbus_int16_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 (((dbus_int16_t)_dbus_unpack_uint16 (reader->byte_order,
(const unsigned char*)values + (i * 2))) ==
int16_from_seed (seed + i));
return TRUE;
}
#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 + 1]="";
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 + 1];
v = buf;
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 + 1];
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) ((dbus_bool_t)((seed) % 2))
static dbus_bool_t
bool_write_value (TestTypeNode *node,
DataBlock *block,
DBusTypeWriter *writer,
int seed)
{
dbus_bool_t 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)
{
dbus_bool_t 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)
{
dbus_bool_t 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_INT64_PRINTF_MODIFIER
_dbus_warn ("Expected double %g got %g\n bits = 0x%" DBUS_INT64_PRINTF_MODIFIER "x vs.\n bits = 0x%" DBUS_INT64_PRINTF_MODIFIER "x)\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);
if (len < 2)
{
buf[0] = '/';
i = 1;
}
else
{
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 + 1];
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 + 1];
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 + 1];
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)
{
/* try to avoid ascending, descending, or alternating length to help find bugs */
const char *sample_signatures[] = {
"asax"
"",
"asau(xxxx)",
"x",
"ai",
"a(ii)"
};
strcpy (buf, sample_signatures[seed % _DBUS_N_ELEMENTS(sample_signatures)]);
}
static dbus_bool_t
signature_write_value (TestTypeNode *node,
DataBlock *block,
DBusTypeWriter *writer,
int seed)
{
char buf[MAX_SAMPLE_SIGNATURE_LEN + 1];
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 + 1];
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 + 1];
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)
{