From 7ae17be411c9c9097112afd4a978a8f3f716c3a9 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Fri, 31 Dec 2004 21:08:06 +0000 Subject: new test framework in place --- dbus/dbus-marshal-recursive.c | 649 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 611 insertions(+), 38 deletions(-) diff --git a/dbus/dbus-marshal-recursive.c b/dbus/dbus-marshal-recursive.c index 727fbfc3..ec35cf9b 100644 --- a/dbus/dbus-marshal-recursive.c +++ b/dbus/dbus-marshal-recursive.c @@ -642,7 +642,7 @@ _dbus_type_reader_recurse (DBusTypeReader *reader, break; case DBUS_TYPE_VARIANT: if (reader->klass->types_only) - _dbus_assert_not_reached ("variant types are inside the variant value, not in the signature"); + _dbus_assert_not_reached ("can't recurse into variant typecode"); else sub->klass = &variant_reader_class; break; @@ -881,6 +881,9 @@ _dbus_type_writer_recurse_struct (DBusTypeWriter *writer, /* Ensure that we'll be able to add alignment padding and the typecode */ if (!_dbus_string_alloc_space (sub->value_str, 8)) return FALSE; + + if (!_dbus_string_alloc_space (sub->type_str, 1)) + return FALSE; if (!write_or_verify_typecode (sub, DBUS_STRUCT_BEGIN_CHAR)) _dbus_assert_not_reached ("failed to insert struct typecode after prealloc"); @@ -908,30 +911,22 @@ _dbus_type_writer_recurse_array (DBusTypeWriter *writer, DBusString str; writer_recurse_init_and_check (writer, DBUS_TYPE_ARRAY, sub); + + _dbus_string_init_const (&element_type_str, element_type); + element_type_len = _dbus_string_get_length (&element_type_str); #ifndef DBUS_DISABLE_CHECKS if (writer->container_type == DBUS_TYPE_ARRAY) { - DBusString parent_elements; - - _dbus_assert (element_type != NULL); - - _dbus_string_init_const (&parent_elements, - _dbus_string_get_const_data_len (writer->type_str, - writer->u.array.element_type_pos + 1, - 0)); - - if (!_dbus_string_starts_with_c_str (&parent_elements, element_type)) + if (!_dbus_string_equal_substring (&element_type_str, 0, element_type_len, + writer->type_str, writer->u.array.element_type_pos + 1)) { _dbus_warn ("Writing an array of '%s' but this is incompatible with the expected type of elements in the parent array\n", element_type); _dbus_assert_not_reached ("incompatible type for child array"); } } -#endif /* DBUS_DISABLE_CHECKS */ - - _dbus_string_init_const (&element_type_str, element_type); - element_type_len = _dbus_string_get_length (&element_type_str); +#endif /* DBUS_DISABLE_CHECKS */ /* 4 bytes for the array length and 4 bytes possible padding */ if (!_dbus_string_alloc_space (sub->value_str, 8)) @@ -1229,6 +1224,7 @@ _dbus_type_writer_write_array (DBusTypeWriter *writer, #ifdef DBUS_BUILD_TESTS #include "dbus-test.h" +#include "dbus-list.h" #include #include @@ -1310,6 +1306,29 @@ data_block_init_reader_writer (DataBlock *block, _dbus_string_get_length (&block->body)); } +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); + + exit (1); + } +} + +#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", \ @@ -1340,29 +1359,6 @@ write_int32 (DataBlock *block, &v); } -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); - - exit (1); - } -} - -#define check_expected_type(reader, expected) real_check_expected_type (reader, expected, _DBUS_FUNCTION_NAME, __LINE__) - static dbus_bool_t read_int32 (DataBlock *block, DBusTypeReader *reader) @@ -2656,14 +2652,375 @@ recursive_marshal_test_iteration (void *data) return TRUE; } +typedef struct TestTypeNode TestTypeNode; +typedef struct TestTypeNodeClass TestTypeNodeClass; +typedef struct TestTypeNodeContainer TestTypeNodeContainer; +typedef struct TestTypeNodeContainerClass TestTypeNodeContainerClass; + +struct TestTypeNode +{ + const TestTypeNodeClass *klass; + void *data; /* some data, such as the particular value we wrote that we expect to read again */ +}; + +struct TestTypeNodeContainer +{ + TestTypeNode base; + DBusList *children; +}; + +struct TestTypeNodeClass +{ + int typecode; + + int instance_size; + + dbus_bool_t (* construct) (TestTypeNode *node); + void (* destroy) (TestTypeNode *node); + + dbus_bool_t (* write_value) (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer); + dbus_bool_t (* read_value) (TestTypeNode *node, + DataBlock *block, + DBusTypeReader *reader); + dbus_bool_t (* build_signature) (TestTypeNode *node, + DBusString *str); +}; + +struct TestTypeNodeContainerClass +{ + TestTypeNodeClass base; +}; + +static dbus_bool_t int32_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer); +static dbus_bool_t int32_read_value (TestTypeNode *node, + DataBlock *block, + DBusTypeReader *reader); +static dbus_bool_t struct_1_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer); +static dbus_bool_t struct_1_read_value (TestTypeNode *node, + DataBlock *block, + DBusTypeReader *reader); +static dbus_bool_t struct_1_build_signature (TestTypeNode *node, + DBusString *str); + +static void container_destroy (TestTypeNode *node); + +static const TestTypeNodeClass int32_class = { + DBUS_TYPE_INT32, + sizeof (TestTypeNode), + NULL, + NULL, + int32_write_value, + int32_read_value, + NULL +}; + +static const TestTypeNodeClass struct_1_class = { + DBUS_TYPE_STRUCT, + sizeof (TestTypeNodeContainer), + NULL, + container_destroy, + struct_1_write_value, + struct_1_read_value, + struct_1_build_signature +}; + +static const TestTypeNodeClass* const +basic_nodes[] = { + &int32_class +}; + +static const TestTypeNodeClass* const +container_nodes[] = { + &struct_1_class +}; + +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) +{ + return (* node->klass->write_value) (node, block, writer); +} + +static dbus_bool_t +node_read_value (TestTypeNode *node, + DataBlock *block, + DBusTypeReader *reader) +{ + return (* node->klass->read_value) (node, block, reader); +} + +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)); + + return _dbus_list_append (&container->children, child); +} + +typedef struct +{ + const DBusString *signature; + DataBlock *block; + int type_offset; + int byte_order; + TestTypeNode *node; +} NodeIterationData; + +static dbus_bool_t +run_test_node_iteration (void *data) +{ + DBusTypeReader reader; + DBusTypeWriter writer; + NodeIterationData *nid = data; + + /* 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 + */ + data_block_init_reader_writer (nid->block, + nid->byte_order, + &reader, &writer); + + if (!node_write_value (nid->node, nid->block, &writer)) + return FALSE; + + 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"); + } + + if (!node_read_value (nid->node, nid->block, &reader)) + return FALSE; + + /* FIXME type-iterate both signature and value */ + + return TRUE; +} + +static void +run_test_node_in_one_configuration (TestTypeNode *node, + int byte_order, + int initial_offset, + const DBusString *signature) +{ + DataBlock block; + NodeIterationData nid; + + if (!data_block_init (&block)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_lengthen (&block.signature, initial_offset)) + _dbus_assert_not_reached ("no memory"); + + if (!_dbus_string_lengthen (&block.body, initial_offset)) + _dbus_assert_not_reached ("no memory"); + + nid.signature = signature; + nid.block = █ + nid.type_offset = initial_offset; + nid.node = node; + nid.byte_order = byte_order; + + _dbus_test_oom_handling ("running test node", + run_test_node_iteration, + &nid); + + data_block_free (&block); +} + +static void +run_test_node (TestTypeNode *node) +{ + int i; + DBusString signature; + + if (!_dbus_string_init (&signature)) + _dbus_assert_not_reached ("no memory"); + + if (! node_build_signature (node, &signature)) + _dbus_assert_not_reached ("no memory"); + + _dbus_verbose (">>> test node with signature '%s'\n", + _dbus_string_get_const_data (&signature)); + + i = 0; + while (i < 18) + { + run_test_node_in_one_configuration (node, DBUS_LITTLE_ENDIAN, i, &signature); + run_test_node_in_one_configuration (node, DBUS_BIG_ENDIAN, i, &signature); + + ++i; + } + + _dbus_string_free (&signature); +} + +static void +make_and_run_test_nodes (void) +{ + int i, j; + + /* 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 length 0-9 + * - 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 so we can keep a handle + * on this unit test + */ + + /* n_basic iterations */ + _dbus_verbose (">>> >>> Each basic node by itself\n"); + for (i = 0; i < _DBUS_N_ELEMENTS (basic_nodes); i++) + { + const TestTypeNodeClass *klass = basic_nodes[i]; + TestTypeNode *node; + + node = node_new (klass); + + run_test_node (node); + + node_destroy (node); + } + + /* n_container * n_basic iterations */ + _dbus_verbose (">>> >>> - Each container of each basic (redundant with later tests)\n"); + for (i = 0; i < _DBUS_N_ELEMENTS (container_nodes); i++) + { + const TestTypeNodeClass *container_klass = container_nodes[i]; + for (j = 0; j < _DBUS_N_ELEMENTS (basic_nodes); j++) + { + const TestTypeNodeClass *child_klass = basic_nodes[j]; + TestTypeNode *child; + TestTypeNode *container; + + container = node_new (container_klass); + child = node_new (child_klass); + + node_append_child (container, child); + + run_test_node (container); + + node_destroy (container); + } + } + + /* n_values * n_values * 2 - each value,value pair combination as toplevel, in both orders */ + + /* 1 - all values in one big toplevel */ + + /* n_container * n_values - container of values */ + + /* n_container * n_values - container of same container of values */ + + /* n_container * n_values - container of same container of same container of values */ + + /* n_container * n_container * n_values - container of container of values */ + /* n_container * n_container * n_container * n_values - container of container of container of values */ + + /* n_values * n_values * n_values * 6 - each trio of value,value,value in all orders */ + + /* n_values * n_values * 2 - each value,value pair inside STRUCT, in both orders */ + + /* 1 - all values in one big STRUCT */ +} + dbus_bool_t _dbus_marshal_recursive_test (void); dbus_bool_t _dbus_marshal_recursive_test (void) { + /* The new comprehensive tests */ + +#if 1 + make_and_run_test_nodes (); +#endif + +#if 1 + /* The old tests */ _dbus_test_oom_handling ("recursive marshaling", recursive_marshal_test_iteration, NULL); +#endif return TRUE; } @@ -2678,4 +3035,220 @@ main (int argc, char **argv) } #endif /* main() */ + +/* + * + * + * Implementations of each type node class + * + * + * + */ + +static dbus_bool_t +int32_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer) +{ + /* also used for uint32 */ + dbus_int32_t v = _DBUS_POINTER_TO_INT (node->data); + + return _dbus_type_writer_write_basic (writer, + node->klass->typecode, + &v); +} + +static dbus_bool_t +int32_read_value (TestTypeNode *node, + DataBlock *block, + DBusTypeReader *reader) +{ + /* 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 == _DBUS_POINTER_TO_INT (node->data)); + + return TRUE; +} + +static dbus_bool_t +struct_N_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer, + int n_copies) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DataBlockState saved; + DBusTypeWriter sub; + int i; + + _dbus_assert (container->children != NULL); + + data_block_save (block, &saved); + + if (!_dbus_type_writer_recurse_struct (writer, + &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); + + node_write_value (child, block, &sub); + + link = next; + } + + ++i; + } + + if (!_dbus_type_writer_unrecurse (writer, &sub)) + { + data_block_restore (block, &saved); + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +struct_N_read_value (TestTypeNode *node, + DataBlock *block, + DBusTypeReader *reader, + int n_copies) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + DBusTypeReader sub; + int i; + + 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); + + node_read_value (child, block, &sub); + + 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_N_build_signature (TestTypeNode *node, + DBusString *str, + int n_copies) +{ + TestTypeNodeContainer *container = (TestTypeNodeContainer*) node; + int i; + int orig_len; + + 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 +struct_1_write_value (TestTypeNode *node, + DataBlock *block, + DBusTypeWriter *writer) +{ + return struct_N_write_value (node, block, writer, 1); +} + +static dbus_bool_t +struct_1_read_value (TestTypeNode *node, + DataBlock *block, + DBusTypeReader *reader) +{ + return struct_N_read_value (node, block, reader, 1); +} + +static dbus_bool_t +struct_1_build_signature (TestTypeNode *node, + DBusString *str) +{ + return struct_N_build_signature (node, str, 1); +} + +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 */ -- cgit