summaryrefslogtreecommitdiffstats
path: root/tests/test-list_util.c
diff options
context:
space:
mode:
authorChris Hall <GMCH@hestia.halldom.com>2010-03-16 01:35:19 +0000
committerChris Hall <GMCH@hestia.halldom.com>2010-03-16 01:35:19 +0000
commitd87a9d74eab06082ea49313083ffa0aa41f666f9 (patch)
tree7c6f7ae0be39683b7c90ea298454ec28d49406cb /tests/test-list_util.c
parent05fb7fd0421b395c089bb08dd0e0d78d3746b8cf (diff)
downloadquagga-d87a9d74eab06082ea49313083ffa0aa41f666f9.tar.bz2
quagga-d87a9d74eab06082ea49313083ffa0aa41f666f9.tar.xz
Major update
bgpd/bgp_advertise.c bgpd/bgp_advertise.h The adj_in and adj_out objects are now put on a list based on the peer to whom the route belongs. The adj_in and adj_out objects also now point to the bgp_node which they are routes for. This substantially reduces the work needed to shut down a peer. bgpd/bgp_damp.c Changes to adj_in and adj_out forced small change to macros used in bgp_damp.c to manage its lists. bgpd/bgp_debug.c Replaced direct access to vty->node by the required vty_get_node(). bgpd/bgp_dump.c Changes to the names of fields in bgp_info structures. bgpd/bgp_engine.h Modified the debug and trace functions. bgpd/bgp_fsm.c Make use of sockunion2str() consistent with common usage. Improved some documentation. bgpd/bgp_main.c Use the newly extended qpn_add_hook_function() facility. bgpd/bgp_mplsvpn.c Changes to the names of fields in bgp_info structures. bgpd/bgp_msg_read.c Bug fix: correct handling of capability code length. Improvement: better casting in calculation of message length. bgpd/bgp_msg_write.c Bug fix: correct byte ordering of bgp_id in open message. bgpd/bgp_network.c Bug fix: correct handling of incoming connections. Takes advantage of improvements in sockunion.c. bgpd/bgp_nexthop.c Changes to the names of fields in bgp_info structures. bgpd/bgp_open_state.c Remove mistaken #include of memtypes.h bgpd/bgp_packet.c Improvements to handling of withdrawing routes for peers. bgpd/bgp_peer.c Tidying up the state of peers as they are enabled and disabled. Improvements to handling of withdrawing routes for peers. bgpd/bgp_peer.h Adding list bases for lists of routes originated by the peer. bgpd/bgp_peer_index.c Bug fix: correct freeing of peer indexes. bgpd/bgp_route.c Implement lists of bgp_info based in the owning peer. Adjust for name changes to bgp_info fields. Reimplemented all the clearing functions to use the lists of items that belong to the peer -- rather than searching route tables for stuff to withdraw. Changed work queue handling for added/changed routes, so that queues run through existing items, rather than having queues of auxiliary items -- lower memory overhead. bgpd/bgp_route.h Added fields to bgp_info to allow all bgp_info originated by each peer to live on lists based in the peer. And changed the name of existing fields to avoid confusion. bgpd/bgp_routemap.c Removing redundant code and fixing a memory leak. bgpd/bgp_table.h Based work queue for added/changed routes directly in the table, rather than having auxiliary structures. bgpd/bgp_vty.c Use vty_get_node() and vty_set_node() rather than direct access to the vty field. bgpd/bgpd.c Implement changes to route clearing. bgpd/bgpd.h Changes to work queue handling. lib/buffer.c Changes to allow embedded buffer structures. lib/buffer.h Moved struct buffer here so that could have embedded buffer structurs. lib/command.c Substantial tidy up and document exercise. Restructured the top level command processing and finding of descriptions and command completion. Removal of unpleasant messing around with the insides of vector structures. Movement of some command actions to vty.c. Uses uty.h to pick up the "private" functions from vty.c et al. lib/command.h Moved the "node" values to node_type.h, so that can use an enum node_type in places where cannot include command.h. lib/command_queue.c Updated to cope with the called command changing the node value. Improved handling of revoked commands, so the the command line handler does not get stuck waiting for a command to complete which has been revoked ! lib/command_queue.h Improved message format. lib/if.c Use vty_set_node(). lib/keychain.c Use vty_set_node(). new lib/keystroke.c new lib/keystroke.h New code to implement a keystroke FIFO. This moves some complexity out of the command handler. The handling of mixtures of escapes and Telnet IACs is tightened up. It would be possible to extend this to, say, UTF-8. Regularises the "stealing" of keystrokes for the "--more--" output handling... which was a bit hit and miss. new lib/list_util.c new lib/list_util.h New code to implement various forms of linked list, where the list pointers are embedded in structures. lib/log.c Changed the handling of log messages, so that all types of log output (except syslog) use the same message buffer scheme, and the message is constructed once and once only. Changes to the handling of VTY_LOCK() etc. Uses uty.h to pick up the "private" functions from vty.c et al. lib/log.h Changes to the buffering of log messages. new lib/mem_tracker.c New code to track memory allocation/deallocation, for debug purposes. lib/memory.c lib/memory.h Updated to allow the use of the mem_tracker. lib/memtypes.awk Made the memtypes into a named enum MTYPE. lib/memtypes.c Various new memory types. lib/mqueue.c lib/mqueue.h Add mqueue_finish function for close-down. lib/network.c lib/network.h Added non-blocking read_nb() and write_nb(). new lib/node_type.h As above. lib/plist.c Remove vty_puts() which wasn't a good idea. lib/qlib_init.c Added qps_init() to first stage and mqueue_finish to finish. lib/qpnexus.c lib/qpnexus.h More flexible hooks for in_thread_init and in_thread_final. lib/qpselect.c lib/qpselect.h Added qps_start_up() to build the required maps once and for all. Added qdebug to control the debug checks and validation. Improved validation and test functions. new lib/qstring.c new lib/qstring.h New code for limited flexible string handling. lib/qtimers.c Added qdebug to control the debug checks and validation. lib/routemap.c Use vty_set_node(). lib/sockunion.c lib/sockunion.h Tidied up and regularised the handling of sin_len and sin6_len. Created common function for setting port into socket. Created common function for initialisation/allocation of new sockunion. Reduced various functions by using common sub-functions. Rationalised some code. Added sockunion_listen() and sockunion_new_sockaddr(). Renamed sockunion_new() to sockunion_new_prefix(). Improved some logging messages. Added documentation. new lib/uty.h Functions etc. used only by vty/command/log/vty_io and vty_cli. lib/vector.c lib/vector.h Added vector_t type. Removed VECTOR_INDEX, vector_only_wrapper_free() and vector_only_index_free() -- following improvement of code in command.c. Added vector_set_min_length(), vector_set_new_min_length() and vector_length() functions. new lib/vio_fifo.c new lib/vio_fifo.h New code to manage simple FIFO of indefinite length. lib/vty.c lib/vty.h Reworked. Broken into vty.c, vty_io.c and vty_cli.c. new lib/vty_cli.c new lib/vty_cli.h CLI handling parts of the vty family. new lib/vty_io.c new lib/vty_io.h I/O parts of the vty family. lib/workqueue.h Introduced tyedefs for the various call-back entries. new tests/test-list_util.c Tests for the list-util stuff. vtysh/vtysh.c Small change to interface for cmd_execute_command()
Diffstat (limited to 'tests/test-list_util.c')
-rw-r--r--tests/test-list_util.c2112
1 files changed, 2112 insertions, 0 deletions
diff --git a/tests/test-list_util.c b/tests/test-list_util.c
new file mode 100644
index 00000000..fc81a562
--- /dev/null
+++ b/tests/test-list_util.c
@@ -0,0 +1,2112 @@
+#include <zebra.h>
+#include <list_util.h>
+#include <string.h>
+
+/* List util torture tests
+ *
+ */
+
+struct thread_master *master; /* allow lib/zebra.c to link ! */
+
+/* prototypes */
+int main(int argc, char **argv);
+
+static void test_ssl(void);
+static void test_sdl(void);
+static void test_ddl(void);
+
+#define test_assert(true, message) \
+ do { if (!(true)) test_assert_fail(#true, message, __func__, __LINE__) ; \
+ } while (0)
+
+static void
+test_assert_fail(const char* true, const char* message, const char* func,
+ int line)
+{
+ printf("*** %s %d: (%s) not true: %s\n", func, line, true, message) ;
+
+} ;
+
+/*==============================================================================
+ * The tests.
+ */
+int
+main(int argc, char **argv)
+{
+ printf("Starting list_util tests -- %s\n",
+#ifdef __GNUC__LIST_UTIL
+ "GNUC version"
+#else
+ "not GNUC version"
+#endif
+ " -- v0.01 26-Feb-2010") ;
+
+ srand(22) ; /* Consistent testing required */
+
+ test_ssl() ;
+ test_sdl() ;
+ test_ddl() ;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * Construct a majic mark from two addresses
+ */
+static unsigned majic(void* a, void* b)
+{
+ uintptr_t z = (uintptr_t)a ^ (uintptr_t)b ^ (uintptr_t)&majic ;
+ return z ;
+} ;
+
+/*==============================================================================
+ * Single Base, Single Link
+ *
+ * ssl_push(base, item, next) -- add at head of list
+ * ssl_del(base, item, next) -- delete from list
+ * ssl_del_head(base, next) -- delete head of list
+ * ssl_pop(dst, base, next) -- pop head of list, if any
+ * ssl_head(base) -- return head of list
+ * ssl_next(item, next) -- step to next item, if any
+ *
+ * Cases to cover:
+ *
+ * a) adding when list is empty
+ * b) adding when list is not empty
+ * c) deletion of first item and more than one item on list
+ * d) deletion of first item when only one item on the list
+ * e) deletion of arbitrary item (list implicitly not empty)
+ * f) deletion when item not on list and list not empty
+ * g) deletion when item not on list and list is empty
+ * h) deletion of NULL item and list not empty
+ * i) deletion of NULL item and list empty
+ * j) pop of head when list not empty
+ * k) pop of head when list contains one item
+ * l) pop of head when list empty
+ * m) deletion of head when list not empty
+ * n) deletion of head when contains one item
+ * o) deletion of head when list empty
+ * p) head when list not empty
+ * q) head when list is empty
+ * r) next when not last
+ * s) next when last
+ */
+
+typedef struct ssl_test* ssl_test ;
+struct ssl_test
+{
+ ssl_test next ; /* pointer at start of structure */
+
+ unsigned majic ;
+ char dummy ;
+
+ ssl_test other_next ; /* pointer elsewhere in structure */
+} ;
+
+struct ssl_test_parent
+{
+ unsigned rubbish ;
+ char fred ;
+
+ ssl_test base ;
+
+ int z[5] ;
+} ;
+
+static struct ssl_test_parent ssl_parent ;
+
+static void
+test_ssl(void)
+{
+ ssl_test base = NULL ;
+
+ ssl_test del = NULL ;
+ ssl_test other_del = NULL ;
+ ssl_test last = NULL ;
+ ssl_test first = NULL ;
+
+ struct ssl_test dummy ;
+
+ int n = 47 ;
+
+ int i_del = 7 ; /* NB: neither of these may be 0 or 1 */
+ int i_other_del = 37 ;
+
+ ssl_test prev ;
+ ssl_test this ;
+ ssl_test other_this ;
+ ssl_test take ;
+ ssl_test temp ;
+
+ int i ;
+ int ret ;
+
+ static struct ssl_test_parent* other = &ssl_parent ;
+
+ memset(other, 77, sizeof(struct ssl_test_parent)) ;
+ other->base = NULL ;
+
+ /* Repeated insertion, starting from empty list
+ *
+ * a) adding when list is empty
+ * b) adding when list is not empty
+ *
+ * Creates lists for following tests.
+ */
+ printf("=== Testing ssl -- Single Base, Single Link -- stuff\n") ;
+
+ printf(" Creating list of items") ;
+ for (i = 1 ; i <= n ; ++i)
+ {
+ ssl_test this = calloc(1, sizeof(struct ssl_test)) ;
+
+ if (last == NULL)
+ last = this ;
+
+ this->majic = majic(this, &base) ;
+
+ this->dummy = i ;
+
+ ssl_push(base, this, next) ;
+ if (i & 1)
+ ssl_push(other->base, this, other_next) ;
+ else
+ ssl_push(ssl_parent.base, this, other_next) ;
+
+ if (i == i_del)
+ del = this ;
+ if (i == i_other_del)
+ other_del = this ;
+
+ first = this ;
+
+ printf("+") ;
+ } ;
+
+ test_assert((base == first) && (other->base == first),
+ "Failed to create consistent lists") ;
+ printf("\n") ;
+
+ passert((del != base) && (del != last)) ;
+ passert((other_del != base) && (other_del != last)) ;
+
+ /* Walk to check that have the expected items
+ *
+ * l) head when list not empty
+ * r) next when not last
+ * s) next when last
+ */
+ printf(" Walking list of items") ;
+
+ this = ssl_head(base) ;
+ test_assert(this == first, "ssl_head failed") ;
+
+ this = ssl_head(other->base) ;
+ test_assert(this == first, "ssl_head failed") ;
+
+ this = ssl_head(ssl_parent.base) ;
+ test_assert(this == first, "ssl_head failed") ;
+
+ this = ssl_next(del, next) ;
+ test_assert((this == del->next) && (this != NULL), "ssl_next failed") ;
+ this = ssl_next(last, next) ;
+ test_assert((this == last->next) && (this == NULL),
+ "ssl_next failed at end") ;
+
+ this = ssl_next(other_del, other_next) ;
+ test_assert((this == other_del->other_next) && (this != NULL),
+ "ssl_next failed") ;
+ this = ssl_next(last, other_next) ;
+ test_assert((this == last->other_next) && (this == NULL),
+ "ssl_next failed at end") ;
+
+ this = base ;
+ other_this = other->base ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ test_assert(this == other_this, "this and other_this not in step") ;
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf(".") ;
+ this = ssl_next(this, next) ;
+ other_this = ssl_next(other_this, other_next) ;
+ } ;
+ printf("\n") ;
+
+ /* Deletion specifically at the start of the list
+ *
+ * c) deletion of first item and more than one item on list
+ */
+ printf(" Deleting the first item") ;
+
+ this = base ;
+ first = base->next ;
+ ret = ssl_del(base, this, next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(first == base, "ssl_del of first item failed") ;
+
+ this = other->base ;
+ ret = ssl_del(other->base, this, other_next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(first == other->base, "ssl_del of first item failed") ;
+
+ printf("\n") ;
+
+ --n ; /* one less on the lists ! */
+
+ /* Deletion of items from arbitrary place in list
+ *
+ * e) deletion of arbitrary item (list implicitly not empty)
+ */
+ printf(" Deleting arbitrary items") ;
+ ret = ssl_del(base, del, next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ ret = ssl_del(ssl_parent.base, other_del, other_next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ printf("\n") ;
+
+ /* Deletion of items from arbitrary place in list
+ *
+ * f) deletion when item not on list and list not empty
+ */
+ printf(" Deleting non-existant items") ;
+ ret = ssl_del(base, &dummy, next) ;
+ test_assert(ret == -1, "ssl_del did not return -1") ;
+ ret = ssl_del(other->base, &dummy, other_next) ;
+ test_assert(ret == -1, "ssl_del did not return -1") ;
+ printf("\n") ;
+
+ /* Deletion of NULL items
+ *
+ * h) deletion of NULL item and list not empty
+ */
+ printf(" Deleting NULL items") ;
+
+ this = NULL ;
+
+ ret = ssl_del(base, this, next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+
+ ret = ssl_del(ssl_parent.base, this, other_next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+
+ printf("\n") ;
+
+ /* Scan lists to check after deletion */
+ printf(" Base list scan") ;
+
+ this = base ;
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf("*") ;
+ this = ssl_next(this, next) ;
+ } ;
+ printf("\n") ;
+
+ printf(" Other list scan") ;
+
+ other_this = other->base ;
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ if (other_this == NULL)
+ break ;
+
+ test_assert(other_this != prev, "this is same as prev") ;
+ prev = other_this ;
+
+ if (i == i_other_del)
+ --i ;
+
+ test_assert(other_this->dummy == i--, "don't have the right dummy") ;
+ test_assert(other_this->majic == majic(other_this, &base),
+ "don't have the right majic") ;
+
+ printf("*") ;
+ other_this = ssl_next(other_this, other_next) ;
+ } ;
+ printf("\n") ;
+
+ /* Dismantle lists
+ *
+ * j) pop of head when list not empty
+ * k) pop of head when list contains one item
+ * l) pop of head when list empty
+ * p) head when list not empty
+ * q) head when list is empty
+ */
+ printf(" Popping the head until list is empty\n") ;
+ printf(" Base list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = base ;
+ test_assert(this == ssl_head(base), "this is not head !") ;
+
+ temp = ssl_pop(&take, base, next) ;
+ test_assert(this == take, "this is not same as deleted head !") ;
+ test_assert(temp == take, "temp is not same as deleted head !") ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(base == this->next, "ssl_pop broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(base == NULL, "Base list should be empty") ;
+
+ this = ssl_head(base) ;
+ test_assert(this == NULL, "ssl_head of empty list failed") ;
+
+ printf("\n") ;
+
+ printf(" Other list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = other->base ;
+ test_assert(this == ssl_head(other->base), "this is not head !") ;
+
+ if (i & 1)
+ temp = ssl_pop(&take, other->base, other_next) ;
+ else
+ temp = ssl_pop(&take, ssl_parent.base, other_next) ;
+
+ test_assert(this == take, "this is not same as deleted head !") ;
+ test_assert(temp == take, "temp is not same as deleted head !") ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(other->base == this->other_next, "ssl_pop broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_other_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(other->base == NULL, "Other list should be empty") ;
+
+ this = ssl_head(other->base) ;
+ test_assert(this == NULL, "ssl_head of empty list failed") ;
+
+ printf("\n") ;
+
+ /* Rebuild lists to do:
+ *
+ * m) deletion of head when list not empty
+ * n) deletion of head when contains one item
+ * o) deletion of head when list empty
+ */
+ passert((base == NULL) && (other->base == NULL)) ;
+
+ last = NULL ;
+ first = NULL ;
+ prev = NULL ;
+ printf(" Building list of items again") ;
+ for (i = 1 ; i <= n ; ++i)
+ {
+ ssl_test this = calloc(1, sizeof(struct ssl_test)) ;
+
+ if (last == NULL)
+ last = this ;
+
+ this->majic = majic(this, &base) ;
+ this->dummy = i ;
+
+ ssl_push(base, this, next) ;
+ if (i & 1)
+ ssl_push(ssl_parent.base, this, other_next) ;
+ else
+ ssl_push(other->base, this, other_next) ;
+
+ test_assert(this->next == prev, "broken ssl_push") ;
+ test_assert(this->other_next == prev, "broken ssl_push") ;
+
+ test_assert(base == this, "broken ssl_push") ;
+ test_assert(other->base == this, "broken ssl_push") ;
+
+ first = this ;
+ prev = this ;
+
+ printf("+") ;
+ } ;
+
+ printf("\n") ;
+
+ printf(" Deleting the head until list is empty\n") ;
+ printf(" Base list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = base ;
+
+ ssl_del_head(base, next) ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(base == this->next, "ssl_del_head broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(base == NULL, "Base list should be empty") ;
+
+ printf("\n") ;
+
+ printf(" Other list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = other->base ;
+
+ if (i & 1)
+ ssl_del_head(ssl_parent.base, other_next) ;
+ else
+ ssl_del_head(other->base, other_next) ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(other->base == this->other_next, "ssl_del_head broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(other->base == NULL, "Other list should be empty") ;
+
+ printf("\n") ;
+
+ /* Final few tests
+ *
+ * d) deletion of first item when only one item on the list
+ * g) deletion when item not on list and list is empty
+ * i) deletion of NULL item and list empty
+ */
+
+ /* Deletion of items from arbitrary place in list */
+ printf(" Miscellaneous") ;
+
+ del->next = &dummy ;
+ ssl_push(base, del, next) ;
+ test_assert((base == del) && (del->next == NULL), "ssl_push failed ??") ;
+ ssl_del(base, del, next) ;
+ test_assert(base == NULL, "ssl_del of first and only item failed") ;
+
+ other_del->other_next = &dummy ;
+ ssl_push(other->base, other_del, other_next) ;
+ test_assert((other->base == other_del) && (other_del->other_next == NULL),
+ "ssl_push failed ??") ;
+ ssl_del(other->base, other_del, other_next) ;
+ test_assert(other->base == NULL, "ssl_del of first and only item failed") ;
+
+ ret = ssl_del(base, del, next) ;
+ test_assert(ret == -1, "ssl_del did not return -1") ;
+ test_assert(base == NULL, "ssl_del on empty list") ;
+
+ ret = ssl_del(other->base, other_del, other_next) ;
+ test_assert(ret == -1, "ssl_del did not return -1") ;
+ test_assert(other->base == NULL, "ssl_del on empty list") ;
+
+ this = NULL ;
+
+ ret = ssl_del(base, this, next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(base == NULL, "ssl_del on empty list") ;
+
+ ret = ssl_del(other->base, this, other_next) ;
+ test_assert(ret == 0, "ssl_del did not return 0") ;
+ test_assert(other->base == NULL, "ssl_del on empty list") ;
+
+ printf("\n") ;
+
+} ;
+
+/*==============================================================================
+ * Single Base, Double Link
+ *
+ * sdl_push(base, item, list) -- add at head of list
+ * sdl_del(base, item, list) -- delete from list
+ * sdl_pop(&dst, base, next) -- pop head of list, if any
+ * sdl_head(base) -- return head of list
+ * sdl_next(item, next) -- step to next item, if any
+ * sdl_prev(item, next) -- step to prev item, if any
+ *
+ * Cases to cover:
+ *
+ * a) adding when list is empty
+ * b) adding when list is not empty
+ * c) deletion of first item and more than one item on list
+ * d) deletion of first item when only one item on the list
+ * e) deletion of arbitrary item (list implicitly not empty)
+ * f) deletion of NULL item and list not empty
+ * g) deletion of NULL item and list empty
+ * h) pop of head when list not empty
+ * i) pop of head when list contains one item
+ * j) pop of head when list empty
+ * k) deletion of head when list not empty
+ * l) deletion of head when list contains one item
+ * m) deletion of head when list empty
+ * n) head when list not empty
+ * o) head when list is empty
+ * p) next when not last
+ * q) next when last
+ * r) prev when not first
+ * s) prev when first
+ *
+ * NB: unlike single link stuff, cannot attempt to remove item which is
+ * not on the list !
+ */
+
+typedef struct sdl_test* sdl_test ;
+struct sdl_test
+{
+ struct dl_list_pair(sdl_test) list ;
+
+ unsigned majic ;
+ char dummy ;
+
+ struct dl_list_pair(sdl_test) other_list ;
+};
+
+struct sdl_test_parent
+{
+ long rubbish ;
+ char fred ;
+
+ sdl_test base ;
+
+ int z[7] ;
+} ;
+
+static struct sdl_test_parent sdl_parent ;
+
+static void
+test_sdl(void)
+{
+ sdl_test base = NULL ;
+
+ sdl_test del = NULL ;
+ sdl_test other_del = NULL ;
+ sdl_test last = NULL ;
+ sdl_test first = NULL ;
+
+ struct sdl_test dummy ;
+
+ int n = 57 ;
+
+ int i_del = 9 ; /* NB: neither of these may be 0 or 1 */
+ int i_other_del = 49 ;
+
+ sdl_test prev ;
+ sdl_test this ;
+ sdl_test other_this ;
+ sdl_test take ;
+ sdl_test temp ;
+
+ int i ;
+
+ static struct sdl_test_parent* other = &sdl_parent ;
+
+ memset(other, 99, sizeof(struct sdl_test_parent)) ;
+ other->base = NULL ;
+
+ /* Repeated insertion, starting from empty list
+ *
+ * a) adding when list is empty
+ * b) adding when list is not empty
+ *
+ * Creates lists for following tests.
+ */
+ printf("=== Testing sdl -- Single Base, Double Link -- stuff\n") ;
+
+ printf(" Creating list of items") ;
+ for (i = 1 ; i <= n ; ++i)
+ {
+ sdl_test this = calloc(1, sizeof(struct sdl_test)) ;
+
+ if (last == NULL)
+ last = this ;
+
+ this->majic = majic(this, &base) ;
+
+ this->dummy = i ;
+
+ sdl_push(base, this, list) ;
+ if (i & 1)
+ sdl_push(other->base, this, other_list) ;
+ else
+ sdl_push(sdl_parent.base, this, other_list) ;
+
+ if (i == i_del)
+ del = this ;
+ if (i == i_other_del)
+ other_del = this ;
+
+ first = this ;
+
+ printf("+") ;
+ } ;
+
+ test_assert((base == first) && (other->base == first),
+ "Failed to create consistent lists") ;
+
+ printf("\n") ;
+
+ passert((del != base) && (del != last)) ;
+ passert((other_del != base) && (other_del != last)) ;
+
+ /* Walk to check that have the expected items
+ *
+ * n) head when list not empty
+ * p) next when not last
+ * q) next when last
+ * r) prev when not first
+ * s) prev when first
+ */
+ printf(" Walking list of items") ;
+
+ this = sdl_head(base) ;
+ test_assert(this == first, "sdl_head failed") ;
+
+ this = sdl_head(other->base) ;
+ test_assert(this == first, "sdl_head failed") ;
+
+ this = sdl_head(sdl_parent.base) ;
+ test_assert(this == first, "sdl_head failed") ;
+
+ /* next on both lists */
+ this = sdl_next(first, list) ;
+ test_assert((this == first->list.next) && (this != NULL),
+ "sdl_next failed at start") ;
+ this = sdl_next(del, list) ;
+ test_assert((this == del->list.next) && (this != NULL), "sdl_next failed") ;
+ this = sdl_next(last, list) ;
+ test_assert((this == last->list.next) && (this == NULL),
+ "sdl_next failed at end") ;
+
+ this = sdl_next(first, other_list) ;
+ test_assert((this == first->other_list.next) && (this != NULL),
+ "sdl_next failed at start") ;
+ this = sdl_next(other_del, other_list) ;
+ test_assert((this == other_del->other_list.next) && (this != NULL),
+ "sdl_next failed") ;
+ this = sdl_next(last, other_list) ;
+ test_assert((this == last->other_list.next) && (this == NULL),
+ "sdl_next failed at end") ;
+
+ /* prev on both lists */
+ this = sdl_prev(first, list) ;
+ test_assert((this == first->list.prev) && (this == NULL),
+ "sdl_prev failed at start") ;
+ this = sdl_prev(del, list) ;
+ test_assert((this == del->list.prev) && (this != NULL), "sdl_prev failed") ;
+ this = sdl_prev(last, list) ;
+ test_assert((this == last->list.prev) && (this != NULL),
+ "sdl_prev failed at end") ;
+
+ this = sdl_prev(first, other_list) ;
+ test_assert((this == first->other_list.prev) && (this == NULL),
+ "sdl_prev failed at start") ;
+ this = sdl_prev(other_del, other_list) ;
+ test_assert((this == other_del->other_list.prev) && (this != NULL),
+ "sdl_prev failed") ;
+ this = sdl_prev(last, other_list) ;
+ test_assert((this == last->other_list.prev) && (this != NULL),
+ "sdl_prev failed at end") ;
+
+ this = base ;
+ other_this = other->base ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ test_assert(this == other_this, "this and other_this not in step") ;
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf(".") ;
+ this = sdl_next(this, list) ;
+ other_this = sdl_next(other_this, other_list) ;
+ } ;
+ printf("\n") ;
+
+ /* Deletion specifically at the start of the list
+ *
+ * c) deletion of first item and more than one item on list
+ */
+ printf(" Deleting the first item") ;
+
+ this = base ;
+ first = base->list.next ;
+ sdl_del(base, this, list) ;
+ test_assert(first == base, "sdl_del of first item failed") ;
+ test_assert((base == NULL) || (base->list.prev == NULL), "sdl_del failed") ;
+
+ this = other->base ;
+ sdl_del(sdl_parent.base, this, other_list) ;
+ test_assert(first == other->base, "sdl_del of first item failed") ;
+ test_assert((base == NULL) || (base->other_list.prev == NULL),
+ "sdl_del failed") ;
+
+ printf("\n") ;
+
+ --n ; /* one less on the lists ! */
+
+ /* Deletion of items from arbitrary place in list
+ *
+ * e) deletion of arbitrary item (list implicitly not empty)
+ */
+ printf(" Deleting arbitrary items") ;
+ sdl_del(base, del, list) ;
+ test_assert((base == NULL) || (base->list.prev == NULL), "sdl_del failed") ;
+ sdl_del(sdl_parent.base, other_del, other_list) ;
+ test_assert((base == NULL) || (base->other_list.prev == NULL),
+ "sdl_del failed") ;
+ printf("\n") ;
+
+ /* Deletion of NULL items
+ *
+ * f) deletion of NULL item and list not empty
+ */
+ printf(" Deleting NULL items") ;
+ this = NULL ;
+ sdl_del(base, this, list) ;
+ test_assert((base == NULL) || (base->list.prev == NULL), "sdl_del failed") ;
+ sdl_del(other->base, this, other_list) ;
+ test_assert((base == NULL) || (base->other_list.prev == NULL),
+ "sdl_del failed") ;
+ printf("\n") ;
+
+ /* Scan lists to check after deletion */
+ printf(" Base list scan") ;
+
+ this = base ;
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ test_assert(this->list.prev == prev, "broken prev pointer") ;
+
+ if (i == i_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("*") ;
+
+ prev = this ;
+ this = sdl_next(this, list) ;
+
+ test_assert(this == prev->list.next, "broken sdl_next") ;
+ if (this != NULL)
+ test_assert(prev == sdl_prev(this, list), "broken sdl_prev") ;
+ } ;
+ printf("\n") ;
+
+ printf(" Other list scan") ;
+
+ this = other->base ;
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ test_assert(this->other_list.prev == prev, "broken prev pointer") ;
+
+ if (i == i_other_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf("*") ;
+
+ prev = this ;
+ this = sdl_next(this, other_list) ;
+
+ test_assert(this == prev->other_list.next, "broken sdl_next") ;
+ if (this != NULL)
+ test_assert(prev == sdl_prev(this, other_list), "broken sdl_prev") ;
+ } ;
+ printf("\n") ;
+
+ /* Dismantle lists
+ *
+ * h) pop of head when list not empty
+ * i) pop of head when list contains one item
+ * j) pop of head when list empty
+ * o) head when list is empty
+ */
+ printf(" Popping the head until list is empty\n") ;
+ printf(" Base list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = sdl_head(base) ;
+ test_assert(this == base, "broken sdl_head !") ;
+
+ temp = sdl_pop(&take, base, list) ;
+ test_assert(this == take, "take is not same as deleted head !") ;
+ test_assert(this == temp, "temp is not same as deleted head !") ;
+ if (base != NULL)
+ test_assert(base->list.prev == NULL, "sdl_pop failed") ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(base == NULL, "Base list should be empty") ;
+
+ this = sdl_head(base) ;
+ test_assert(this == NULL, "sdl_head of empty list failed") ;
+
+ printf("\n") ;
+
+ printf(" Other list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = sdl_head(other->base) ;
+ test_assert(this == other->base, "broken sdl_head !") ;
+
+ if (i & 1)
+ temp = sdl_pop(&take, sdl_parent.base, other_list) ;
+ else
+ temp = sdl_pop(&take, other->base, other_list) ;
+
+ test_assert(this == take, "take is not same as deleted head !") ;
+ test_assert(this == temp, "temp is not same as deleted head !") ;
+ if (other->base != NULL)
+ test_assert(other->base->other_list.prev == NULL,
+ "sdl_pop failed") ;
+ if (this == NULL)
+ break ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ if (i == i_other_del)
+ --i ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(other->base == NULL, "Other list should be empty") ;
+
+ this = sdl_head(other->base) ;
+ test_assert(this == NULL, "sdl_head of empty list failed") ;
+
+ printf("\n") ;
+
+ /* Rebuild lists to do:
+ *
+ * k) deletion of head when list not empty
+ * l) deletion of head when list contains one item
+ * m) deletion of head when list empty
+ */
+ passert((base == NULL) && (other->base == NULL)) ;
+
+ last = NULL ;
+ first = NULL ;
+ prev = NULL ;
+ printf(" Building list of items again") ;
+ for (i = 1 ; i <= n ; ++i)
+ {
+ sdl_test this = calloc(1, sizeof(struct sdl_test)) ;
+
+ if (last == NULL)
+ last = this ;
+
+ this->majic = majic(this, &base) ;
+ this->dummy = i ;
+
+ sdl_push(base, this, list) ;
+ if (i & 1)
+ sdl_push(sdl_parent.base, this, other_list) ;
+ else
+ sdl_push(other->base, this, other_list) ;
+
+ test_assert(this->list.next == prev, "broken sdl_push") ;
+ test_assert(this->other_list.next == prev, "broken sdl_push") ;
+
+ test_assert(base == this, "broken sdl_push") ;
+ test_assert(other->base == this, "broken sdl_push") ;
+
+ first = this ;
+ prev = this ;
+
+ printf("+") ;
+ } ;
+
+ printf("\n") ;
+
+ printf(" Deleting the head until list is empty\n") ;
+ printf(" Base list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = base ;
+
+ sdl_del_head(base, list) ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(base == this->list.next, "sdl_del_head broken") ;
+ if (base != NULL)
+ test_assert(base->list.prev == NULL, "sdl_del_head broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(base == NULL, "Base list should be empty") ;
+
+ printf("\n") ;
+
+ printf(" Other list") ;
+
+ prev = NULL ;
+ i = n ;
+ while (1)
+ {
+ this = other->base ;
+
+ if (i & 1)
+ sdl_del_head(other->base, other_list) ;
+ else
+ sdl_del_head(sdl_parent.base, other_list) ;
+
+ if (this == NULL)
+ break ;
+
+ test_assert(other->base == this->other_list.next, "sdl_del_head broken") ;
+ if (other->base != NULL)
+ test_assert(other->base->other_list.prev == NULL,
+ "sdl_del_head broken") ;
+
+ test_assert(this != prev, "this is same as prev") ;
+ prev = this ;
+
+ test_assert(this->dummy == i--, "don't have the right dummy") ;
+ test_assert(this->majic == majic(this, &base),
+ "don't have the right majic") ;
+ printf("-") ;
+ } ;
+ test_assert(i == 0, "not the expected final value of 'i'") ;
+ test_assert(other->base == NULL, "Other list should be empty") ;
+
+ printf("\n") ;
+
+ /* Final few tests
+ *
+ * d) deletion of first item when only one item on the list
+ * g) deletion of NULL item and list empty
+ */
+
+ /* Deletion of items from arbitrary place in list */
+ printf(" Miscellaneous") ;
+
+ del->list.next = &dummy ;
+ sdl_push(base, del, list) ;
+ test_assert((base == del) && (del->list.next == NULL), "sdl_push failed ??") ;
+ sdl_del(base, del, list) ;
+ test_assert(base == NULL, "sdl_del of first and only item failed") ;
+
+ other_del->other_list.next = &dummy ;
+ sdl_push(other->base, other_del, other_list) ;
+ test_assert((other->base == other_del) && (other_del->other_list.next == NULL),
+ "sdl_push failed ??") ;
+ sdl_del(other->base, other_del, other_list) ;
+ test_assert(other->base == NULL, "sdl_del of first and only item failed") ;
+
+ this = NULL ;
+ sdl_del(base, this, list) ;
+ test_assert(base == NULL, "sdl_del of NULL item with empty list") ;
+
+ sdl_del(other->base, this, other_list) ;
+ test_assert(other->base == NULL, "sdl_del of NULL item with empty list") ;
+
+ printf("\n") ;
+} ;
+
+/*==============================================================================
+ * Double Base, Double Link
+ *
+ * ddl_init(base) -- initialise base
+ * ddl_push(base, item, list) -- insert at head of list
+ * ddl_append(base, item, list) -- insert at tail of list
+ * ddl_in_after(after, base, item, list) -- insert after
+ * ddl_in_before(before, base, item, list) -- insert before
+ * ddl_pop(&dst, base, next) -- pop head of list, if any
+ * ddl_crop(&dst, base, next) -- crop tail of list, if any
+ * ddl_del(base, item, list) -- delete from list
+ * ddl_del_head(base, next) -- delete head of list
+ * ddl_del_tail(base, next) -- delete tail of list
+ * ddl_head(base) -- return head of list
+ * ddl_tail(base) -- return tail of list
+ * ddl_next(item, next) -- step to next item, if any
+ * ddl_prev(item, next) -- step to prev item, if any
+ *
+ * Cases to cover:
+ */
+
+/* Testing runs two lists through struct ddt_item objects. */
+
+enum list { a_list, b_list, list_count } ;
+
+typedef struct ddt_item* ddt_item ;
+
+struct ddt_list_pair dl_list_pair(ddt_item) ; /* Example struct constructor */
+struct ddt_base_pair dl_base_pair(ddt_item) ;
+
+typedef struct dl_base_pair(ddt_item) ddt_base_pair_t ;
+ /* Example typedef constructor */
+typedef struct dl_base_pair(ddt_item)* p_ddt_base_pair ;
+ /* Example typedef constructor */
+
+typedef struct ddt_list_pair* ddt_list_pair ;
+typedef struct ddt_base_pair* ddt_base_pair ;
+
+typedef struct ddt_rank* ddt_rank ;
+
+struct ddt_rank /* information for each list */
+{
+ struct ddt_list_pair list ; /* the thing we are testing */
+
+ int lister ;
+ int list_found ;
+ unsigned majic ;
+
+ int ordinal ;
+};
+
+struct ddt_item /* the test items */
+{
+ struct ddt_rank a ;
+
+ char a_rubbish[21] ;
+
+ struct ddt_rank b ;
+
+ char b_rubbish[19] ;
+} ;
+
+/* The test list base pairs, and pointers to the actual bases, for use in
+ * the verification code.
+ */
+static ddt_base_pair ddt_bases[list_count] ;
+
+/* For some tests want to construct lists and know the first, last and
+ * somewhere between items.
+ */
+
+enum where { first, middle, last, where_count } ;
+
+struct ddt_test_list_items
+{
+ struct
+ {
+ ddt_item where[where_count] ;
+ } list[list_count] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The test list items -- keep track here also for use in verification.
+ */
+enum { ddt_max_items = 1000 } ;
+
+static unsigned ddt_item_count = 0 ;
+static unsigned ddt_item_alloc = 0 ;
+static ddt_item ddt_items[ddt_max_items] ;
+
+static inline ddt_item
+ddt_new_item(void)
+{
+ ddt_item item ;
+
+ assert(ddt_item_count <= ddt_item_alloc) ;
+
+ if (ddt_item_count == ddt_item_alloc)
+ {
+ assert(ddt_item_alloc < ddt_max_items) ;
+ ddt_items[ddt_item_alloc++] = malloc(sizeof(struct ddt_item)) ;
+ } ;
+
+ item = ddt_items[ddt_item_count++] ;
+
+ memset(item, ddt_item_count & 0xFF, sizeof(struct ddt_item)) ;
+
+ item->a.lister = 0 ;
+ item->b.lister = 0 ;
+
+ item->a.ordinal = 0 ;
+ item->b.ordinal = 0 ;
+
+ return item ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Given an item and a list ordinal, return pointer to "rank" for item.
+ */
+static inline ddt_rank
+ddt_get_rank(ddt_item item, enum list l)
+{
+ if (item == NULL)
+ return NULL ;
+
+ if (l == a_list)
+ return &item->a ;
+ if (l == b_list)
+ return &item->b ;
+
+ assert(0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Keep track of what should be found on the lists, and majic marker to check
+ * that don't get lost and point into space.
+ */
+static inline unsigned
+ddt_get_majic(ddt_item item, enum list l)
+{
+ return majic(item, ddt_bases[l]) ;
+} ;
+
+static void
+ddt_test_list_add(ddt_item item, enum list l)
+{
+ ddt_rank rank = ddt_get_rank(item, l) ;
+
+ test_assert(rank->lister == 0, "already on list") ;
+
+ rank->lister = 1 ;
+ rank->majic = ddt_get_majic(item, l) ;
+ rank->ordinal = 0 ; /* unknown ordinal */
+} ;
+
+static void
+ddt_test_list_del(ddt_item item, enum list l)
+{
+ ddt_rank rank ;
+
+ if (item == NULL)
+ return ;
+
+ rank = ddt_get_rank(item, l) ;
+
+ test_assert(rank->lister == 1, "not on list") ;
+
+ rank->lister = 0 ;
+ rank->majic = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Verification code.
+ *
+ * Blunt instrument to check that all known lists are valid. Checks:
+ *
+ * * bases are both NULL together, or both not NULL.
+ *
+ * * first and last items on the list have suitable prev/next pointers.
+ *
+ * * walk list to confirm, for each item:
+ *
+ * -- prev pointer valid for each item
+ * -- item majic is correct (so not pointing somewhere invalid)
+ * -- item is supposed to be on the list
+ * -- item has not already been seen on list (list bent)
+ * -- ordinal, if not zero, is bigger than any previous non-zero ordinal
+ *
+ * * last item visited on walk is what the tail points to
+ *
+ * * for any items which are supposed to be on list, but were not found
+ */
+static void
+ddt_verify_lists(void)
+{
+ ddt_base_pair base ;
+ ddt_rank r ;
+ ddt_item this ;
+ ddt_item prev ;
+ int l ;
+ int i ;
+
+ /* Wash the found flags */
+ for (l = 0 ; l < list_count ; ++l)
+ for (i = 0 ; i < (int)ddt_item_count ; ++i)
+ ddt_get_rank(ddt_items[i], l)->list_found = 0 ;
+
+ /* Walk the lists */
+ for (l = 0 ; l < list_count ; ++l)
+ {
+ int ordinal = 0 ;
+
+ base = ddt_bases[l] ;
+ if (base == NULL)
+ continue ;
+
+ if ((base->head == NULL) || (base->tail == NULL))
+ test_assert(base->head == base->tail, "broken list bases") ;
+ else
+ {
+ r = ddt_get_rank(base->head, l) ;
+ test_assert(r->list.prev == NULL, "broken list first item->prev") ;
+ r = ddt_get_rank(base->tail, l) ;
+ test_assert(r->list.next == NULL, "broken list last item->next") ;
+
+ this = base->head ;
+ prev = NULL ;
+
+ while (this != NULL)
+ {
+ r = ddt_get_rank(this, l) ;
+
+ test_assert(r->list.prev == prev, "broken item->prev") ;
+
+ test_assert(r->lister, "should not be on this list") ;
+
+ test_assert(!r->list_found, "circular list") ;
+ r->list_found = 1 ;
+
+ if (r->ordinal != 0)
+ {
+ test_assert(r->ordinal > ordinal, "list out of order") ;
+ ordinal = r->ordinal ;
+ }
+
+ test_assert(r->majic == ddt_get_majic(this, l),
+ "wrong sort of majic") ;
+ prev = this ;
+ this = r->list.next ;
+ } ;
+
+ test_assert(base->tail == prev, "broken tail pointer") ;
+ } ;
+ } ;
+
+ /* Verify that did not miss anything should have found */
+ /* Wash the found flags */
+ for (l = 0 ; l < list_count ; ++l)
+ for (i = 0 ; i < (int)ddt_item_count ; ++i)
+ {
+ r = ddt_get_rank(ddt_items[i], l) ;
+
+ if (r->lister)
+ test_assert(r->list_found, "should have found this on list") ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset the test list handling
+ *
+ */
+static void
+ddt_reset_lists(void)
+{
+ int l ;
+
+ for (l = 0 ; l < list_count ; ++l)
+ ddl_init(*(ddt_bases[l])) ;
+
+ ddt_item_count = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make lists with 'n' items each.
+ *
+ * If 'n' 0..3, makes lists with exactly that many items.
+ *
+ * Otherwise, list length has +/- 25% jitter.
+ */
+static void
+ddt_test_make_lists(struct ddt_test_list_items* test, int n)
+{
+ ddt_base_pair base ;
+ ddt_item item ;
+ ddt_rank rank ;
+ enum list l ;
+ int t ;
+ int m ;
+
+ int req[list_count] ;
+ int mid[list_count] ;
+
+ /* Capture the requirements */
+ t = 0 ;
+ m = 1 ;
+ for (l = 0 ; l < list_count ; ++l)
+ {
+ m += m ;
+ int j = (n + 1) / 4 ;
+
+ if (n <= 3)
+ req[l] = n ;
+ else
+ req[l] = n - j + (rand() % (j + j + 1)) ;
+
+ mid[l] = req[l] / 2 ;
+
+ t += req[l] ;
+
+ test->list[l].where[first] = NULL ;
+ test->list[l].where[middle] = NULL ;
+ test->list[l].where[last] = NULL ;
+ } ;
+
+ ddt_reset_lists() ;
+
+ /* Have t = total number of items still required
+ * m = 2^n -- where there are 'n' lists
+ */
+ while (t != 0)
+ {
+ int b ;
+ int r ;
+ r = (rand() % (m - 1)) + 1 ; /* bit pattern, at least one set */
+
+ item = ddt_new_item() ;
+
+ b = 1 ;
+ for (l = 0 ; l < list_count ; ++l)
+ {
+ if ((req[l] != 0) && ((r & b) != 0))
+ {
+ --req[l] ;
+ --t ;
+
+ ddt_test_list_add(item, l) ;
+
+ if (test->list[l].where[first] == NULL)
+ test->list[l].where[first] = item ;
+
+ if (mid[l]-- == 0)
+ test->list[l].where[middle] = item ;
+
+ test->list[l].where[last] = item ;
+
+ base = ddt_bases[l] ;
+ rank = ddt_get_rank(item, l) ;
+
+ if (base->head == NULL)
+ {
+ base->head = item ;
+ base->tail = item ;
+ rank->list.next = NULL ;
+ rank->list.prev = NULL ;
+ }
+ else if (rand() & 1)
+ {
+ rank->list.next = base->head ;
+ rank->list.prev = NULL ;
+ ddt_get_rank(base->head, l)->list.prev = item ;
+ base->head = item ;
+ }
+ else
+ {
+ rank->list.next = NULL ;
+ rank->list.prev = base->tail ;
+ ddt_get_rank(base->tail, l)->list.next = item ;
+ base->tail = item ;
+ }
+ } ;
+ b <<= 1 ;
+ }
+ } ;
+
+ /* Number the items */
+ for (l = 0 ; l < list_count ; ++l)
+ {
+ int o = 0 ;
+
+ base = ddt_bases[l] ;
+
+ item = base->head ;
+ while (item != NULL)
+ {
+ rank = ddt_get_rank(item, l) ;
+ rank->ordinal = ++o ; /* first is 1 */
+ item = rank->list.next ;
+ } ;
+ } ;
+
+ ddt_verify_lists() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * ddl_init(base) -- initialise base
+ * ddl_push(base, item, list) -- insert at head of list
+ * ddl_append(base, item, list) -- insert at tail of list
+ * ddl_in_after(after, base, item, list) -- insert after
+ * ddl_in_before(before, base, item, list) -- insert before
+ * ddl_pop(&dst, base, next) -- pop head of list, if any
+ * ddl_crop(&dst, base, next) -- crop tail of list, if any
+ * ddl_del(base, item, list) -- delete from list
+ * ddl_del_head(base, next) -- delete head of list
+ * ddl_del_tail(base, next) -- delete tail of list
+ * ddl_head(base) -- return head of list
+ * ddl_tail(base) -- return tail of list
+ * ddl_next(item, next) -- step to next item, if any
+ * ddl_prev(item, next) -- step to prev item, if any
+ */
+
+static struct ddt_parent
+{
+ char zlxq[37] ;
+
+ struct ddt_base_pair base ;
+
+ char qxlz[45] ;
+} ddt_parent ;
+
+static void
+test_ddl(void)
+{
+ struct ddt_base_pair a_base ;
+ struct ddt_parent* b ;
+
+ struct ddt_test_list_items test ;
+ int n ;
+ int o ;
+
+ int base_n = 23 ;
+ int rand_n = 17 ;
+
+ printf("=== Testing ddl -- Double Base, Double Link -- stuff\n") ;
+
+ /* Initialise the test support */
+ ddt_bases[a_list] = &a_base ;
+ ddt_bases[b_list] = &ddt_parent.base ;
+
+ ddt_item_count = 0 ;
+ ddt_item_alloc = 0 ;
+
+ /* Initialise the list bases */
+ b = &ddt_parent ;
+ memset(b, 42, sizeof(struct ddt_parent)) ;
+
+ ddl_init(a_base) ;
+ ddl_init(b->base) ;
+
+ ddt_verify_lists() ; /* start as mean to go on */
+
+
+ /* ddl_push(base, item, list) -- insert at head of list
+ *
+ * Cases: (a) empty list
+ * (b) list with one item
+ * (c) list with multiple items
+ */
+ printf(" ddl_push test") ;
+ ddt_reset_lists() ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+ int r ;
+
+ printf(".") ;
+
+ item = ddt_new_item() ;
+ r = (rand() % 3) + 1 ;
+
+ if (r & 1)
+ {
+ ddl_push(a_base, item, a.list) ;
+ test_assert(a_base.head == item, "ddl_push broken") ;
+ ddt_test_list_add(item, a_list) ;
+ item->a.ordinal = n ;
+ } ;
+ ddt_verify_lists() ;
+
+ if (r & 2)
+ {
+ ddl_push(b->base, item, b.list) ;
+ test_assert(b->base.head == item, "ddl_push broken") ;
+ ddt_test_list_add(item, b_list) ;
+ item->b.ordinal = n ;
+ } ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_append(base, item, list) -- insert at tail of list
+ *
+ * Cases: (a) empty list
+ * (b) list with one item
+ * (c) list with multiple items
+ */
+ printf(" ddl_append test") ;
+ ddt_reset_lists() ;
+
+ n = base_n + (rand() % rand_n) ;
+ o = 0 ;
+ while (n)
+ {
+ ddt_item item ;
+ int r ;
+
+ printf(".") ;
+ ++o ;
+
+ item = ddt_new_item() ;
+ r = (rand() % 3) + 1 ;
+
+ if (r & 1)
+ {
+ ddl_append(a_base, item, a.list) ;
+ test_assert(a_base.tail == item, "ddl_append broken") ;
+ ddt_test_list_add(item, a_list) ;
+ item->a.ordinal = o ;
+ } ;
+ ddt_verify_lists() ;
+
+ if (r & 2)
+ {
+ ddl_append(b->base, item, b.list) ;
+ test_assert(b->base.tail == item, "ddl_append broken") ;
+ ddt_test_list_add(item, b_list) ;
+ item->b.ordinal = o ;
+ } ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_in_after(after, base, item, list) -- insert after
+ *
+ * Cases: (a) after first and only (so is also last)
+ * (b) after first when more than one
+ * (c) after last when more than one
+ * (d) after something between
+ */
+ printf(" ddl_in_after test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+ ddt_item after ;
+
+ printf(".") ;
+ ddt_test_make_lists(&test, n) ;
+
+ item = ddt_new_item() ;
+ after = test.list[a_list].where[n % where_count] ;
+ ddl_in_after(after, a_base, item, a.list) ;
+ test_assert(after->a.list.next == item, "ddl_in_after broken") ;
+ test_assert(item->a.list.prev == after, "ddl_in_after broken") ;
+ ddt_test_list_add(item, a_list) ;
+ ddt_verify_lists() ;
+
+ item = ddt_new_item() ;
+ after = test.list[b_list].where[n % where_count] ;
+ ddl_in_after(after, b->base, item, b.list) ;
+ test_assert(after->b.list.next == item, "ddl_in_after broken") ;
+ test_assert(item->b.list.prev == after, "ddl_in_after broken") ;
+ ddt_test_list_add(item, b_list) ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_in_before(before, base, item, list) -- insert before
+ *
+ * Cases: (a) before first and only (so is also last)
+ * (b) before first when more than one
+ * (c) before last when more than one
+ * (d) before something between
+ */
+ printf(" ddl_in_before test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+ ddt_item before ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ item = ddt_new_item() ;
+ before = test.list[a_list].where[n % where_count] ;
+ ddl_in_before(before, a_base, item, a.list) ;
+ test_assert(before->a.list.prev == item, "ddl_in_before broken") ;
+ test_assert(item->a.list.next == before, "ddl_in_before broken") ;
+ ddt_test_list_add(item, a_list) ;
+ ddt_verify_lists() ;
+
+ item = ddt_new_item() ;
+ before = test.list[b_list].where[n % where_count] ;
+ ddl_in_before(before, b->base, item, b.list) ;
+ test_assert(before->b.list.prev == item, "ddl_in_before broken") ;
+ test_assert(item->b.list.next == before, "ddl_in_before broken") ;
+ ddt_test_list_add(item, b_list) ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+ printf(" ddl_pop test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ ddt_item item ;
+ ddt_item temp ;
+ ddt_item peek ;
+ int ordinal ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ ordinal = 0 ;
+ while (1)
+ {
+ peek = a_base.head ;
+ temp = ddl_pop(&item, a_base, a.list) ;
+ test_assert(temp == item, "ddl_pop broken") ;
+ test_assert(peek == item, "ddl_pop broken") ;
+
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+
+ ++ordinal ;
+ test_assert(item->a.ordinal == ordinal, "ddl_pop not in order") ;
+ } ;
+
+ ordinal = 0 ;
+ while (1)
+ {
+ peek = b->base.head ;
+ temp = ddl_pop(&item, b->base, b.list) ;
+ test_assert(temp == item, "ddl_pop broken") ;
+ test_assert(peek == item, "ddl_pop broken") ;
+
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+
+ ++ordinal ;
+ test_assert(item->b.ordinal == ordinal, "ddl_pop not in order") ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_crop(&dst, base, next) -- crop tail of list, if any
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+
+ printf(" ddl_crop test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ ddt_item item ;
+ ddt_item temp ;
+ ddt_item peek ;
+ int ordinal ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ ordinal = 0 ;
+ while (1)
+ {
+ peek = a_base.tail ;
+ temp = ddl_crop(&item, a_base, a.list) ;
+ test_assert(temp == item, "ddl_crop broken") ;
+ test_assert(peek == item, "ddl_crop broken") ;
+
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+
+ if (ordinal == 0)
+ ordinal = item->a.ordinal ;
+ else
+ {
+ test_assert(ordinal > 0, "ordinal out of whack") ;
+ --ordinal ;
+ test_assert(item->a.ordinal == ordinal, "ddl_crop not in order") ;
+ } ;
+ } ;
+
+ ordinal = 0 ;
+ while (1)
+ {
+ peek = b->base.tail ;
+ temp = ddl_crop(&item, b->base, b.list) ;
+ test_assert(temp == item, "ddl_crop broken") ;
+ test_assert(peek == item, "ddl_crop broken") ;
+
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+
+ if (ordinal == 0)
+ ordinal = item->b.ordinal ;
+ else
+ {
+ test_assert(ordinal > 0, "ordinal out of whack") ;
+ --ordinal ;
+ test_assert(item->b.ordinal == ordinal, "ddl_crop not in order") ;
+ } ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_del(base, item, list) -- delete from list
+ *
+ * Cases: (a) first and only (so is also last)
+ * (b) first when more than one
+ * (c) last when more than one
+ * (d) something between
+ */
+ printf(" ddl_del test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ item = test.list[a_list].where[n % where_count] ;
+ ddl_del(a_base, item, a.list) ;
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ item = test.list[b_list].where[n % where_count] ;
+ ddl_del(b->base, item, b.list) ;
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_del_head(base, next) -- delete head of list
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+ printf(" ddl_del_head test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ ddt_item item ;
+ ddt_item peek ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ while (1)
+ {
+ item = a_base.head ;
+ peek = (item != NULL) ? item->a.list.next : NULL ;
+
+ ddl_del_head(a_base, a.list) ;
+
+ test_assert(a_base.head == peek, "ddl_del_head broken") ;
+
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+ } ;
+
+ while (1)
+ {
+ item = b->base.head ;
+ peek = (item != NULL) ? item->b.list.next : NULL ;
+
+ ddl_del_head(b->base, b.list) ;
+
+ test_assert(b->base.head == peek, "ddl_del_head broken") ;
+
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_del_tail(base, next) -- delete tail of list
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+ printf(" ddl_del_tail test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ ddt_item item ;
+ ddt_item peek ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ while (1)
+ {
+ item = a_base.tail ;
+ peek = (item != NULL) ? item->a.list.prev : NULL ;
+
+ ddl_del_tail(a_base, a.list) ;
+
+ test_assert(a_base.tail == peek, "ddl_del_tail broken") ;
+
+ ddt_test_list_del(item, a_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+ } ;
+
+ while (1)
+ {
+ item = b->base.tail ;
+ peek = (item != NULL) ? item->b.list.prev : NULL ;
+
+ ddl_del_tail(b->base, b.list) ;
+
+ test_assert(b->base.tail == peek, "ddl_del_tail broken") ;
+
+ ddt_test_list_del(item, b_list) ;
+ ddt_verify_lists() ;
+
+ if (item == NULL)
+ break ;
+ } ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+ /* ddl_head(base) -- return head of list
+ * ddl_tail(base) -- return tail of list
+ *
+ * Cases: (a) list with more than one item
+ * (b) list with one item
+ * (c) empty list
+ */
+ printf(" ddl_head & ddl_tail test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n >= 0)
+ {
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ test_assert(ddl_head(a_base) == a_base.head, "ddl_head broken") ;
+ test_assert(ddl_tail(a_base) == a_base.tail, "ddl_head broken") ;
+ test_assert(ddl_head(b->base) == b->base.head, "ddl_head broken") ;
+ test_assert(ddl_tail(b->base) == b->base.tail, "ddl_head broken") ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+
+ /* ddl_next(item, next) -- step to next item, if any
+ * ddl_prev(item, next) -- step to prev item, if any
+ *
+ * Cases: (a) at first and only (so is also last)
+ * (b) at first when more than one
+ * (c) at last when more than one
+ * (d) at something between
+ */
+ printf(" ddl_next and ddl_prev test") ;
+
+ n = base_n + (rand() % rand_n) ;
+ while (n)
+ {
+ ddt_item item ;
+ ddt_item where ;
+
+ printf(".") ;
+
+ ddt_test_make_lists(&test, n) ;
+
+ where = test.list[a_list].where[n % where_count] ;
+ item = ddl_next(where, a.list) ;
+ test_assert(item == where->a.list.next, "ddl_next broken") ;
+
+ where = test.list[b_list].where[n % where_count] ;
+ item = ddl_next(where, b.list) ;
+ test_assert(item == where->b.list.next, "ddl_next broken") ;
+
+ where = test.list[a_list].where[n % where_count] ;
+ item = ddl_prev(where, a.list) ;
+ test_assert(item == where->a.list.prev, "ddl_prev broken") ;
+
+ where = test.list[b_list].where[n % where_count] ;
+ item = ddl_prev(where, b.list) ;
+ test_assert(item == where->b.list.prev, "ddl_prev broken") ;
+
+ --n ;
+ } ;
+ printf("\n") ;
+
+
+}
+
+/*
+ * TODO
+ *
+ */