]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[v9_10] Add various RBT unit tests
authorEvan Hunt <each@isc.org>
Thu, 29 May 2014 19:29:33 +0000 (12:29 -0700)
committerEvan Hunt <each@isc.org>
Thu, 29 May 2014 19:30:33 +0000 (12:30 -0700)
3865. [test] Improved testability of the red-black tree
implementation and added unit tests. [RT #35904]

CHANGES
bin/tests/rbt_test.c
lib/dns/include/dns/rbt.h
lib/dns/rbt.c
lib/dns/tests/Makefile.in
lib/dns/tests/rbt_serialize_test.c [new file with mode: 0644]
lib/dns/tests/rbt_test.c
lib/dns/win32/libdns.def.in
lib/isc/hash.c
lib/isc/include/isc/hash.h

diff --git a/CHANGES b/CHANGES
index f1718489d3d0a4bb454c1a626de47cabc79bfb99..a0154152ec29441f89b6da34528717d77f256a0b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+3865.  [test]          Improved testability of the red-black tree
+                       implementation and added unit tests. [RT #35904]
+
 3864.  [bug]           RPZ didn't work well when being used as forwarder.
                        [RT #36060]
 
index f98c1e968ef8e45e1ccbd454bc952e3a37febdb2..66e5dd7cb9e9b7c09ffba3a80e0f144727124c5b 100644 (file)
@@ -430,7 +430,7 @@ main(int argc, char **argv) {
 
                        } else if (CMDCHECK("print")) {
                                if (arg == NULL || *arg == '\0')
-                                       dns_rbt_printall(rbt, NULL);
+                                       dns_rbt_printtext(rbt, NULL, stdout);
                                else
                                        printf("usage: print\n");
 
index f1a4ee89e9b43eaff3aa5d782d58017da54475c4..34c7f7ef961489f8bbfb24bc721bdb56d37c8585 100644 (file)
@@ -726,10 +726,14 @@ dns_rbt_deserialize_tree(void *base_address, size_t filesize,
  */
 
 void
-dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *));
+dns_rbt_printtext(dns_rbt_t *rbt,
+                 void (*data_printer)(FILE *, void *), FILE *f);
 /*%<
  * Print an ASCII representation of the internal structure of the red-black
- * tree of trees.
+ * tree of trees to the passed stream.
+ *
+ * data_printer is a callback function that is called to print the data
+ * in a node. It should print it to the passed FILE stream.
  *
  * Notes:
  * \li  The name stored at each node, along with the node's color, is printed.
@@ -739,9 +743,69 @@ dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *));
  */
 
 void
-dns_rbt_printnodeinfo(dns_rbtnode_t *n);
+dns_rbt_printdot(dns_rbt_t *rbt, isc_boolean_t show_pointers, FILE *f);
+/*%<
+ * Print a GraphViz dot representation of the internal structure of the
+ * red-black tree of trees to the passed stream.
+ *
+ * If show_pointers is TRUE, pointers are also included in the generated
+ * graph.
+ *
+ * Notes:
+ * \li The name stored at each node, along with the node's color is displayed.
+ *     Then the down pointer, left and right pointers are displayed
+ *     recursively in turn.  NULL left, right and down pointers are
+ *     silently omitted.
+ */
+
+void
+dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f);
 /*%<
  * Print out various information about a node
+ *
+ * Requires:
+ *\li  'n' is a valid pointer.
+ *
+ *\li  'f' points to a valid open FILE structure that allows writing.
+ */
+
+
+size_t
+dns__rbt_getheight(dns_rbt_t *rbt);
+/*%<
+ * Return the maximum height of sub-root nodes found in the red-black
+ * forest.
+ *
+ * The height of a node is defined as the number of nodes in the longest
+ * path from the node to a leaf. For each subtree in the forest, this
+ * function determines the height of its root node. Then it returns the
+ * maximum such height in the forest.
+ *
+ * Note: This function exists for testing purposes. Non-test code must
+ * not use it.
+ *
+ * Requires:
+ * \li  rbt is a valid rbt manager.
+ */
+
+isc_boolean_t
+dns__rbt_checkproperties(dns_rbt_t *rbt);
+/*%<
+ * Check red-black properties of the forest.
+ *
+ * Note: This function exists for testing purposes. Non-test code must
+ * not use it.
+ *
+ * Requires:
+ * \li  rbt is a valid rbt manager.
+ */
+
+size_t
+dns__rbtnode_getdistance(dns_rbtnode_t *node);
+/*%<
+ * Return the distance (in nodes) from the node to its upper node of its
+ * subtree. The root node has a distance of 1. A child of the root node
+ * has a distance of 2.
  */
 
 /*****
@@ -1036,6 +1100,12 @@ dns_rbtnode_refdecrement(dns_rbtnode_t *node, unsigned int *refs) {
 #endif
 #endif /* DNS_RBT_USEISCREFCOUNT */
 
+void
+dns_rbtnode_nodename(dns_rbtnode_t *node, dns_name_t *name);
+
+dns_rbtnode_t *
+dns_rbt_root(dns_rbt_t *rbt);
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_RBT_H */
index 4e3b51fd1645f66c28336478edaa8a95759c1d50..4cefa8833e5ce2b62fb585778546e971f64ee9ea 100644 (file)
@@ -209,6 +209,7 @@ getdata(dns_rbtnode_t *node, file_header_t *header) {
 #define RIGHT(node)             ((node)->right)
 #define DOWN(node)              ((node)->down)
 #define DATA(node)              ((node)->data)
+#define IS_EMPTY(node)          ((node)->data == NULL)
 #define HASHNEXT(node)          ((node)->hashnext)
 #define HASHVAL(node)           ((node)->hashval)
 #define COLOR(node)             ((node)->color)
@@ -280,6 +281,21 @@ NODENAME(dns_rbtnode_t *node, dns_name_t *name) {
        name->attributes |= DNS_NAMEATTR_READONLY;
 }
 
+void
+dns_rbtnode_nodename(dns_rbtnode_t *node, dns_name_t *name) {
+       name->length = NAMELEN(node);
+       name->labels = OFFSETLEN(node);
+       name->ndata = NAME(node);
+       name->offsets = OFFSETS(node);
+       name->attributes = ATTRS(node);
+       name->attributes |= DNS_NAMEATTR_READONLY;
+}
+
+dns_rbtnode_t *
+dns_rbt_root(dns_rbt_t *rbt) {
+  return rbt->root;
+}
+
 #ifdef DNS_RBT_USEHASH
 static isc_result_t
 inithash(dns_rbt_t *rbt);
@@ -302,8 +318,6 @@ Name(dns_rbtnode_t *node) {
        return (name);
 }
 
-static void printnodename(dns_rbtnode_t *node);
-
 static void
 hexdump(const char *desc, unsigned char *data, size_t size) {
        char hexdump[BUFSIZ * 2 + 1];
@@ -326,23 +340,47 @@ hexdump(const char *desc, unsigned char *data, size_t size) {
        } while (size > 0);
        fprintf(stderr, "\n");
 }
-#endif
+#endif /* DEBUG */
+
+/* The passed node must not be NULL. */
+static inline dns_rbtnode_t *
+get_subtree_root(dns_rbtnode_t *node) {
+       while (!IS_ROOT(node)) {
+               node = PARENT(node);
+       }
+
+       return (node);
+}
 
+/* Upper node is the parent of the root of the passed node's
+ * subtree. The passed node must not be NULL.
+ */
 static inline dns_rbtnode_t *
-find_up(dns_rbtnode_t *node) {
-       dns_rbtnode_t *root;
+get_upper_node(dns_rbtnode_t *node) {
+       dns_rbtnode_t *root = get_subtree_root(node);
 
        /*
         * Return the node in the level above the argument node that points
         * to the level the argument node is in.  If the argument node is in
         * the top level, the return value is NULL.
         */
-       for (root = node; ! IS_ROOT(root); root = PARENT(root))
-               ; /* Nothing. */
-
        return (PARENT(root));
 }
 
+size_t
+dns__rbtnode_getdistance(dns_rbtnode_t *node) {
+       size_t nodes = 1;
+
+       while (node != NULL) {
+               if (IS_ROOT(node))
+                       break;
+               nodes++;
+               node = PARENT(node);
+       }
+
+       return (nodes);
+}
+
 /*
  * Forward declarations.
  */
@@ -387,7 +425,7 @@ static void
 deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep);
 
 static void
-printnodename(dns_rbtnode_t *node);
+printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f);
 
 static void
 freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep);
@@ -1371,15 +1409,14 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
 
        if (rbt->root == NULL)
                return (ISC_R_NOTFOUND);
-       else {
-               /*
-                * Appease GCC about variables it incorrectly thinks are
-                * possibly used uninitialized.
-                */
-               compared = dns_namereln_none;
-               last_compared = NULL;
-               order = 0;
-       }
+
+       /*
+        * Appease GCC about variables it incorrectly thinks are
+        * possibly used uninitialized.
+        */
+       compared = dns_namereln_none;
+       last_compared = NULL;
+       order = 0;
 
        dns_fixedname_init(&fixedcallbackname);
        callback_name = dns_fixedname_name(&fixedcallbackname);
@@ -1405,6 +1442,12 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
                NODENAME(current, &current_name);
                compared = dns_name_fullcompare(search_name, &current_name,
                                                &order, &common_labels);
+               /*
+                * last_compared is used as a shortcut to start (or
+                * continue rather) finding the stop-node of the search
+                * when hashing was used (see much below in this
+                * function).
+                */
                last_compared = current;
 
                if (compared == dns_namereln_equal)
@@ -1412,6 +1455,16 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
 
                if (compared == dns_namereln_none) {
 #ifdef DNS_RBT_USEHASH
+                       /*
+                        * Here, current is pointing at a subtree root
+                        * node. We try to find a matching node using
+                        * the hashtable. We can get one of 3 results
+                        * here: (a) we locate the matching node, (b) we
+                        * find a node to which the current node has a
+                        * subdomain relation, (c) we fail to find (a)
+                        * or (b).
+                        */
+
                        dns_name_t hash_name;
                        dns_rbtnode_t *hnode;
                        dns_rbtnode_t *up_current;
@@ -1446,7 +1499,12 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
 
                hashagain:
                        /*
-                        * Hash includes tail.
+                        * Compute the hash over the full absolute
+                        * name. Look for the smallest suffix match at
+                        * this tree level (hlevel), and then at every
+                        * iteration, look for the next smallest suffix
+                        * match (add another subdomain label to the
+                        * absolute name being hashed).
                         */
                        dns_name_getlabelsequence(name,
                                                  nlabels - tlabels,
@@ -1457,6 +1515,10 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
                                                  nlabels - tlabels,
                                                  tlabels, &hash_name);
 
+                       /*
+                        * Walk all the nodes in the hash bucket pointed
+                        * by the computed hash value.
+                        */
                        for (hnode = rbt->hashtable[hash % rbt->hashsize];
                             hnode != NULL;
                             hnode = hnode->hashnext)
@@ -1465,8 +1527,16 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
 
                                if (hash != HASHVAL(hnode))
                                        continue;
-                               if (find_up(hnode) != up_current)
+                               /*
+                                * This checks that the hashed label
+                                * sequence being looked up is at the
+                                * same tree level, so that we don't
+                                * match a labelsequence from some other
+                                * subdomain.
+                                */
+                               if (get_upper_node(hnode) != up_current)
                                        continue;
+
                                dns_name_init(&hnode_name, NULL);
                                NODENAME(hnode, &hnode_name);
                                if (dns_name_equal(&hnode_name, &hash_name))
@@ -1971,7 +2041,7 @@ dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
         * deleted.  If the deleted node is the top level, parent will be set
         * to NULL.
         */
-       parent = find_up(node);
+       parent = get_upper_node(node);
 
        /*
         * This node now has no down pointer (either because it didn't
@@ -2051,7 +2121,7 @@ dns_rbt_fullnamefromnode(dns_rbtnode_t *node, dns_name_t *name) {
                if (result != ISC_R_SUCCESS)
                        break;
 
-               node = find_up(node);
+               node = get_upper_node(node);
        } while (! dns_name_isabsolute(name));
 
        return (result);
@@ -2799,42 +2869,140 @@ deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep) {
        goto again;
 }
 
+static size_t
+getheight_helper(dns_rbtnode_t *node) {
+       size_t dl, dr;
+       size_t this_height, down_height;
+
+       if (node == NULL)
+               return (0);
+
+       dl = getheight_helper(LEFT(node));
+       dr = getheight_helper(RIGHT(node));
+
+       this_height = ISC_MAX(dl + 1, dr + 1);
+       down_height = getheight_helper(DOWN(node));
+
+       return (ISC_MAX(this_height, down_height));
+}
+
+size_t
+dns__rbt_getheight(dns_rbt_t *rbt) {
+       return (getheight_helper(rbt->root));
+}
+
+static isc_boolean_t
+check_properties_helper(dns_rbtnode_t *node) {
+       if (node == NULL)
+               return (ISC_TRUE);
+
+       if (IS_RED(node)) {
+               /* Root nodes must be BLACK. */
+               if (IS_ROOT(node))
+                       return (ISC_FALSE);
+
+               /* Both children of RED nodes must be BLACK. */
+               if (IS_RED(LEFT(node)) || IS_RED(RIGHT(node)))
+                       return (ISC_FALSE);
+       }
+
+       /* If node is assigned to the down_ pointer of its parent, it is
+        * a subtree root and must have the flag set.
+        */
+       if (((!PARENT(node)) ||
+            (DOWN(PARENT(node)) == node)) &&
+           (!IS_ROOT(node)))
+       {
+               return (ISC_FALSE);
+       }
+
+       /* Repeat tests with this node's children. */
+       return (check_properties_helper(LEFT(node)) &&
+               check_properties_helper(RIGHT(node)) &&
+               check_properties_helper(DOWN(node)));
+}
+
+static isc_boolean_t
+check_black_distance_helper(dns_rbtnode_t *node, size_t *distance) {
+       size_t dl, dr, dd;
+
+       if (node == NULL) {
+               *distance = 1;
+               return (ISC_TRUE);
+       }
+
+       if (!check_black_distance_helper(LEFT(node), &dl))
+               return (ISC_FALSE);
+
+       if (!check_black_distance_helper(RIGHT(node), &dr))
+               return (ISC_FALSE);
+
+       if (!check_black_distance_helper(DOWN(node), &dd))
+               return (ISC_FALSE);
+
+       /* Left and right side black node counts must match. */
+       if (dl != dr)
+               return (ISC_FALSE);
+
+       if (IS_BLACK(node))
+               dl++;
+
+       *distance = dl;
+
+       return (ISC_TRUE);
+}
+
+isc_boolean_t
+dns__rbt_checkproperties(dns_rbt_t *rbt) {
+       size_t dd;
+
+       if (!check_properties_helper(rbt->root))
+               return (ISC_FALSE);
+
+       /* Path from a given node to all its leaves must contain the
+        * same number of BLACK child nodes. This is done separately
+        * here instead of inside check_properties_helper() as
+        * it would take (n log n) complexity otherwise.
+        */
+       return (check_black_distance_helper(rbt->root, &dd));
+}
+
 static void
-dns_rbt_indent(int depth) {
+dns_rbt_indent(FILE *f, int depth) {
        int i;
 
-       printf("%4d ", depth);
+       fprintf(f, "%4d ", depth);
 
        for (i = 0; i < depth; i++)
-               printf("- ");
+               fprintf(f, "- ");
 }
 
 void
-dns_rbt_printnodeinfo(dns_rbtnode_t *n) {
-       printf("Node info for nodename: ");
-       printnodename(n);
-       printf("\n");
+dns_rbt_printnodeinfo(dns_rbtnode_t *n, FILE *f) {
+       fprintf(f, "Node info for nodename: ");
+       printnodename(n, ISC_TRUE, f);
+       fprintf(f, "\n");
 
-       printf("n = %p\n", n);
+       fprintf(f, "n = %p\n", n);
 
-       printf("Relative pointers: %s%s%s%s%s\n",
+       fprintf(f, "Relative pointers: %s%s%s%s%s\n",
                        (n->parent_is_relative == 1 ? " P" : ""),
                        (n->right_is_relative == 1 ? " R" : ""),
                        (n->left_is_relative == 1 ? " L" : ""),
                        (n->down_is_relative == 1 ? " D" : ""),
                        (n->data_is_relative == 1 ? " T" : ""));
 
-       printf("node lock address = %d\n", n->locknum);
+       fprintf(f, "node lock address = %d\n", n->locknum);
 
-       printf("Parent: %p\n", n->parent);
-       printf("Right: %p\n", n->right);
-       printf("Left: %p\n", n->left);
-       printf("Down: %p\n", n->down);
-       printf("daTa: %p\n", n->data);
+       fprintf(f, "Parent: %p\n", n->parent);
+       fprintf(f, "Right: %p\n", n->right);
+       fprintf(f, "Left: %p\n", n->left);
+       fprintf(f, "Down: %p\n", n->down);
+       fprintf(f, "daTa: %p\n", n->data);
 }
 
 static void
-printnodename(dns_rbtnode_t *node) {
+printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f) {
        isc_region_t r;
        dns_name_t name;
        char buffer[DNS_NAME_FORMATSIZE];
@@ -2848,65 +3016,135 @@ printnodename(dns_rbtnode_t *node) {
 
        dns_name_format(&name, buffer, sizeof(buffer));
 
-       printf("\"%s\"", buffer);
+       if (quoted)
+               fprintf(f, "\"%s\"", buffer);
+       else
+               fprintf(f, "%s", buffer);
 }
 
 static void
-dns_rbt_printtree(dns_rbtnode_t *root, dns_rbtnode_t *parent,
+print_text_helper(dns_rbtnode_t *root, dns_rbtnode_t *parent,
                  int depth, const char *direction,
-                 void (*data_printer)(FILE *, void *))
+                 void (*data_printer)(FILE *, void *), FILE *f)
 {
-       dns_rbt_indent(depth);
+       dns_rbt_indent(f, depth);
 
        if (root != NULL) {
-               printnodename(root);
-               printf(" (%s, %s", direction, IS_RED(root) ? "RED" : "BLACK");
+               printnodename(root, ISC_TRUE, f);
+               fprintf(f, " (%s, %s", direction,
+                       IS_RED(root) ? "RED" : "BLACK");
 
                if ((! IS_ROOT(root) && PARENT(root) != parent) ||
                    (  IS_ROOT(root) && depth > 0 &&
                       DOWN(PARENT(root)) != root)) {
 
-                       printf(" (BAD parent pointer! -> ");
+                       fprintf(f, " (BAD parent pointer! -> ");
                        if (PARENT(root) != NULL)
-                               printnodename(PARENT(root));
+                               printnodename(PARENT(root), ISC_TRUE, f);
                        else
-                               printf("NULL");
-                       printf(")");
+                               fprintf(f, "NULL");
+                       fprintf(f, ")");
                }
 
-               printf(")");
+               fprintf(f, ")");
 
                if (root->data != NULL && data_printer != NULL) {
-                       printf(" data@%p: ", root->data);
-                       data_printer(stdout, root->data);
+                       fprintf(f, " data@%p: ", root->data);
+                       data_printer(f, root->data);
                }
-               printf("\n");
+               fprintf(f, "\n");
 
                depth++;
 
                if (IS_RED(root) && IS_RED(LEFT(root)))
-                       printf("** Red/Red color violation on left\n");
-               dns_rbt_printtree(LEFT(root), root, depth, "left",
-                                 data_printer);
+                       fprintf(f, "** Red/Red color violation on left\n");
+               print_text_helper(LEFT(root), root, depth, "left",
+                                         data_printer, f);
 
                if (IS_RED(root) && IS_RED(RIGHT(root)))
-                       printf("** Red/Red color violation on right\n");
-               dns_rbt_printtree(RIGHT(root), root, depth, "right",
-                                 data_printer);
+                       fprintf(f, "** Red/Red color violation on right\n");
+               print_text_helper(RIGHT(root), root, depth, "right",
+                                         data_printer, f);
 
-               dns_rbt_printtree(DOWN(root), NULL, depth, "down",
-                                 data_printer);
+               print_text_helper(DOWN(root), NULL, depth, "down",
+                                         data_printer, f);
        } else {
-               printf("NULL (%s)\n", direction);
+               fprintf(f, "NULL (%s)\n", direction);
        }
 }
 
 void
-dns_rbt_printall(dns_rbt_t *rbt, void (*data_printer)(FILE *, void *)) {
+dns_rbt_printtext(dns_rbt_t *rbt,
+                 void (*data_printer)(FILE *, void *), FILE *f)
+{
+       REQUIRE(VALID_RBT(rbt));
+
+       print_text_helper(rbt->root, NULL, 0, "root", data_printer, f);
+}
+
+static int
+print_dot_helper(dns_rbtnode_t *node, unsigned int *nodecount,
+                isc_boolean_t show_pointers, FILE *f)
+{
+       unsigned int l, r, d;
+
+       if (node == NULL)
+               return (0);
+
+       l = print_dot_helper(LEFT(node), nodecount, show_pointers, f);
+       r = print_dot_helper(RIGHT(node), nodecount, show_pointers, f);
+       d = print_dot_helper(DOWN(node), nodecount, show_pointers, f);
+
+       *nodecount += 1;
+
+       fprintf(f, "node%u[label = \"<f0> |<f1> ", *nodecount);
+       printnodename(node, ISC_FALSE, f);
+       fprintf(f, "|<f2>");
+
+       if (show_pointers)
+               fprintf(f, "|<f3> n=%p|<f4> p=%p", node, PARENT(node));
+
+       fprintf(f, "\"] [");
+
+       if (IS_RED(node))
+               fprintf(f, "color=red");
+       else
+               fprintf(f, "color=black");
+
+       /* XXXMUKS: verify that IS_ROOT() indicates subtree root and not
+        * forest root.
+        */
+       if (IS_ROOT(node))
+               fprintf(f, ",penwidth=3");
+
+       if (IS_EMPTY(node))
+               fprintf(f, ",style=filled,fillcolor=lightgrey");
+
+       fprintf(f, "];\n");
+
+       if (LEFT(node) != NULL)
+               fprintf(f, "\"node%u\":f0 -> \"node%u\":f1;\n", *nodecount, l);
+
+       if (DOWN(node) != NULL)
+               fprintf(f, "\"node%u\":f1 -> \"node%u\":f1 [penwidth=5];\n",
+                       *nodecount, d);
+
+       if (RIGHT(node) != NULL)
+               fprintf(f, "\"node%u\":f2 -> \"node%u\":f1;\n", *nodecount, r);
+
+       return (*nodecount);
+}
+
+void
+dns_rbt_printdot(dns_rbt_t *rbt, isc_boolean_t show_pointers, FILE *f) {
+       unsigned int nodecount = 0;
 
        REQUIRE(VALID_RBT(rbt));
 
-       dns_rbt_printtree(rbt->root, NULL, 0, "root", data_printer);
+       fprintf(f, "digraph g {\n");
+       fprintf(f, "node [shape = record,height=.1];\n");
+       print_dot_helper(rbt->root, &nodecount, show_pointers, f);
+       fprintf(f, "}\n");
 }
 
 /*
@@ -3384,3 +3622,13 @@ dns_rbtnodechain_invalidate(dns_rbtnodechain_t *chain) {
 
        chain->magic = 0;
 }
+
+/* XXXMUKS:
+ *
+ * - worth removing inline as static functions are inlined automatically
+ *   where suitable by modern compilers.
+ * - bump the size of dns_rbt.nodecount to size_t.
+ * - the dumpfile header also contains a nodecount that is unsigned
+ *   int. If large files (> 2^32 nodes) are to be supported, the
+ *   allocation for this field should be increased.
+ */
index f89dc84c6531efcb82328f7d8c8afac9f6642e71..824476714f3296ba9525837bdae917c49379f435 100644 (file)
@@ -49,6 +49,7 @@ SRCS =                db_test.c \
                nsec3_test.c \
                private_test.c \
                rbt_test.c \
+               rbt_serialize_test.c \
                rdata_test.c \
                rdataset_test.c \
                rdatasetstats_test.c \
@@ -69,6 +70,7 @@ TARGETS =     db_test@EXEEXT@ \
                nsec3_test@EXEEXT@ \
                private_test@EXEEXT@ \
                rbt_test@EXEEXT@ \
+               rbt_serialize_test@EXEEXT@ \
                rdata_test@EXEEXT@ \
                rdataset_test@EXEEXT@ \
                rdatasetstats_test@EXEEXT@ \
@@ -158,6 +160,11 @@ rbt_test@EXEEXT@: rbt_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
                        rbt_test.@O@ dnstest.@O@ ${DNSLIBS} \
                                ${ISCLIBS} ${LIBS}
 
+rbt_serialize_test@EXEEXT@: rbt_serialize_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
+       ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+                       rbt_serialize_test.@O@ dnstest.@O@ ${DNSLIBS} \
+                               ${ISCLIBS} ${LIBS}
+
 rdata_test@EXEEXT@: rdata_test.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS}
        ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
                        rdata_test.@O@ ${DNSLIBS} \
diff --git a/lib/dns/tests/rbt_serialize_test.c b/lib/dns/tests/rbt_serialize_test.c
new file mode 100644 (file)
index 0000000..80b5125
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2012-2014  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: rbt_test.c,v 1.1.14.8 2012/02/10 16:24:37 ckb Exp $ */
+
+/* ! \file */
+
+#include <config.h>
+#include <atf-c.h>
+#include <isc/mem.h>
+#include <isc/random.h>
+#include <isc/string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h> /* uintptr_t */
+#endif
+
+#include <dns/rbt.h>
+#include <dns/fixedname.h>
+#include <dns/result.h>
+#include <dns/compress.h>
+#include "dnstest.h"
+
+#include <isc/app.h>
+#include <isc/buffer.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/string.h>
+#include <isc/socket.h>
+#include <isc/stdio.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/result.h>
+
+#include <dst/dst.h>
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+typedef struct data_holder {
+       int len;
+       const char *data;
+} data_holder_t;
+
+typedef struct rbt_testdata {
+       const char *name;
+       size_t name_len;
+       data_holder_t data;
+} rbt_testdata_t;
+
+#define DATA_ITEM(name) { (name), sizeof(name) - 1, { sizeof(name), (name) } }
+
+rbt_testdata_t testdata[] = {
+       DATA_ITEM("first.com."),
+       DATA_ITEM("one.net."),
+       DATA_ITEM("two.com."),
+       DATA_ITEM("three.org."),
+       DATA_ITEM("asdf.com."),
+       DATA_ITEM("ghjkl.com."),
+       DATA_ITEM("1.edu."),
+       DATA_ITEM("2.edu."),
+       DATA_ITEM("3.edu."),
+       DATA_ITEM("123.edu."),
+       DATA_ITEM("1236.com."),
+       DATA_ITEM("and_so_forth.com."),
+       DATA_ITEM("thisisalongname.com."),
+       DATA_ITEM("a.b."),
+       DATA_ITEM("test.net."),
+       DATA_ITEM("whoknows.org."),
+       DATA_ITEM("blargh.com."),
+       DATA_ITEM("www.joe.com."),
+       DATA_ITEM("test.com."),
+       DATA_ITEM("isc.org."),
+       DATA_ITEM("uiop.mil."),
+       DATA_ITEM("last.fm."),
+       { NULL, 0, { 0, NULL } }
+};
+
+static void
+delete_data(void *data, void *arg) {
+       UNUSED(arg);
+       UNUSED(data);
+}
+
+static isc_result_t
+write_data(FILE *file, unsigned char *datap, void *arg, isc_uint64_t *crc) {
+       isc_result_t result;
+       size_t ret = 0;
+       data_holder_t *data = (data_holder_t *)datap;
+       data_holder_t temp;
+       off_t where;
+
+       UNUSED(arg);
+
+       REQUIRE(file != NULL);
+       REQUIRE(crc != NULL);
+       REQUIRE(data != NULL);
+       REQUIRE((data->len == 0 && data->data == NULL) ||
+               (data->len != 0 && data->data != NULL));
+
+       result = isc_stdio_tell(file, &where);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       temp = *data;
+       temp.data = (data->len == 0
+                    ? NULL
+                    : (char *)((uintptr_t)where + sizeof(data_holder_t)));
+
+       isc_crc64_update(crc, (void *)&temp, sizeof(temp));
+       ret = fwrite(&temp, sizeof(data_holder_t), 1, file);
+       if (ret != 1)
+               return (ISC_R_FAILURE);
+       if (data->len > 0) {
+               isc_crc64_update(crc, (const void *)data->data, data->len);
+               ret = fwrite(data->data, data->len, 1, file);
+               if (ret != 1)
+                       return (ISC_R_FAILURE);
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+fix_data(dns_rbtnode_t *p, void *base, size_t max, void *arg,
+        isc_uint64_t *crc)
+{
+       data_holder_t *data = p->data;
+       size_t size;
+
+       UNUSED(base);
+       UNUSED(max);
+       UNUSED(arg);
+
+       REQUIRE(crc != NULL);
+       REQUIRE(p != NULL);
+
+
+       if (data == NULL)
+               printf("fixing data: data NULL\n");
+       else
+               printf("fixing data: len %d, data %p\n", data->len, data->data);
+
+       if (data == NULL ||
+           (data->len == 0 && data->data != NULL) ||
+           (data->len != 0 && data->data == NULL))
+               return (ISC_R_INVALIDFILE);
+
+       size = max - ((char *)p - (char *)base);
+
+       if (data->len > (int) size || data->data > (const char *) max) {
+               printf("data invalid\n");
+               return (ISC_R_INVALIDFILE);
+       }
+
+       isc_crc64_update(crc, (void *)data, sizeof(*data));
+
+       data->data = (data->len == 0)
+               ? NULL
+               : (char *)data + sizeof(data_holder_t);
+
+       if (data->len > 0)
+               isc_crc64_update(crc, (const void *)data->data, data->len);
+
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Load test data into the RBT.
+ */
+static void
+add_test_data(isc_mem_t *mctx, dns_rbt_t *rbt) {
+       char buffer[1024];
+       isc_buffer_t b;
+       isc_result_t result;
+       dns_fixedname_t fname;
+       dns_name_t *name;
+       dns_compress_t cctx;
+       rbt_testdata_t *testdatap = testdata;
+
+       dns_compress_init(&cctx, -1, mctx);
+
+       while (testdatap->name != NULL && testdatap->data.data != NULL) {
+               memmove(buffer, testdatap->name, testdatap->name_len);
+
+               isc_buffer_init(&b, buffer, testdatap->name_len);
+               isc_buffer_add(&b, testdatap->name_len);
+               dns_fixedname_init(&fname);
+               name = dns_fixedname_name(&fname);
+               result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+               if (result != ISC_R_SUCCESS) {
+                       testdatap++;
+                       continue;
+               }
+
+               if (name != NULL) {
+                       result = dns_rbt_addname(rbt, name, &testdatap->data);
+                       ATF_CHECK_STREQ(dns_result_totext(result), "success");
+               }
+               testdatap++;
+       }
+
+       dns_compress_invalidate(&cctx);
+}
+
+/*
+ * Walk the tree and ensure that all the test nodes are present.
+ */
+static void
+check_test_data(dns_rbt_t *rbt) {
+       char buffer[1024];
+       char *arg;
+       dns_fixedname_t fname;
+       dns_fixedname_t fixed;
+       dns_name_t *name;
+       isc_buffer_t b;
+       data_holder_t *data;
+       isc_result_t result;
+       dns_name_t *foundname;
+       rbt_testdata_t *testdatap = testdata;
+
+       dns_fixedname_init(&fixed);
+       foundname = dns_fixedname_name(&fixed);
+
+       while (testdatap->name != NULL && testdatap->data.data != NULL) {
+               memmove(buffer, testdatap->name, testdatap->name_len + 1);
+               arg = buffer;
+
+               isc_buffer_init(&b, arg, testdatap->name_len);
+               isc_buffer_add(&b, testdatap->name_len);
+               dns_fixedname_init(&fname);
+               name = dns_fixedname_name(&fname);
+               result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+               if (result != ISC_R_SUCCESS) {
+                       testdatap++;
+                       continue;
+               }
+
+               data = NULL;
+               result = dns_rbt_findname(rbt, name, 0, foundname,
+                                         (void *) &data);
+               ATF_CHECK_STREQ(dns_result_totext(result), "success");
+
+               testdatap++;
+       }
+}
+
+static void
+data_printer(FILE *out, void *datap)
+{
+       data_holder_t *data = (data_holder_t *)datap;
+
+       fprintf(out, "%d bytes, %s", data->len, data->data);
+}
+
+ATF_TC(serialize);
+ATF_TC_HEAD(serialize, tc) {
+       atf_tc_set_md_var(tc, "descr", "Test writing an rbt to file");
+}
+ATF_TC_BODY(serialize, tc) {
+       dns_rbt_t *rbt = NULL;
+       isc_result_t result;
+       FILE *rbtfile = NULL;
+       dns_rbt_t *rbt_deserialized = NULL;
+       off_t offset;
+       int fd;
+       off_t filesize = 0;
+       char *base;
+
+       UNUSED(tc);
+
+       isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_CHECK_STREQ(dns_result_totext(result), "success");
+       result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
+       ATF_CHECK_STREQ(dns_result_totext(result), "success");
+
+       add_test_data(mctx, rbt);
+
+       dns_rbt_printtext(rbt, data_printer, stdout);
+
+       /*
+        * Serialize the tree.
+        */
+       printf("serialization begins.\n");
+       rbtfile = fopen("./zone.bin", "w+b");
+       ATF_REQUIRE(rbtfile != NULL);
+       result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
+                                       &offset);
+       ATF_REQUIRE(result == ISC_R_SUCCESS);
+       dns_rbt_destroy(&rbt);
+
+       /*
+        * Deserialize the tree
+        */
+       printf("deserialization begins.\n");
+
+       /*
+        * Map in the whole file in one go
+        */
+       fd = open("zone.bin", O_RDWR);
+       isc_file_getsizefd(fd, &filesize);
+       base = mmap(NULL, filesize,
+                   PROT_READ|PROT_WRITE,
+                   MAP_FILE|MAP_PRIVATE, fd, 0);
+       ATF_REQUIRE(base != NULL && base != MAP_FAILED);
+       close(fd);
+
+       result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
+                                         delete_data, NULL, fix_data, NULL,
+                                         NULL, &rbt_deserialized);
+
+       /* Test to make sure we have a valid tree */
+       ATF_REQUIRE(result == ISC_R_SUCCESS);
+       if (rbt_deserialized == NULL)
+               atf_tc_fail("deserialized rbt is null!"); /* Abort execution. */
+
+       check_test_data(rbt_deserialized);
+
+       dns_rbt_printtext(rbt_deserialized, data_printer, stdout);
+
+       dns_rbt_destroy(&rbt_deserialized);
+       munmap(base, filesize);
+       unlink("zone.bin");
+       dns_test_end();
+}
+
+ATF_TC(deserialize_corrupt);
+ATF_TC_HEAD(deserialize_corrupt, tc) {
+       atf_tc_set_md_var(tc, "descr", "Test reading a corrupt map file");
+}
+ATF_TC_BODY(deserialize_corrupt, tc) {
+       dns_rbt_t *rbt = NULL;
+       isc_result_t result;
+       FILE *rbtfile = NULL;
+       off_t offset;
+       int fd;
+       off_t filesize = 0;
+       char *base, *p, *q;
+       isc_uint32_t r;
+       int i;
+
+       UNUSED(tc);
+
+       isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       /* Set up map file */
+       result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       add_test_data(mctx, rbt);
+       rbtfile = fopen("./zone.bin", "w+b");
+       ATF_REQUIRE(rbtfile != NULL);
+       result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
+                                       &offset);
+       ATF_REQUIRE(result == ISC_R_SUCCESS);
+       dns_rbt_destroy(&rbt);
+
+       /* Read back with random fuzzing */
+       for (i = 0; i < 256; i++) {
+               dns_rbt_t *rbt_deserialized = NULL;
+
+               fd = open("zone.bin", O_RDWR);
+               isc_file_getsizefd(fd, &filesize);
+               base = mmap(NULL, filesize,
+                           PROT_READ|PROT_WRITE,
+                           MAP_FILE|MAP_PRIVATE, fd, 0);
+               ATF_REQUIRE(base != NULL && base != MAP_FAILED);
+               close(fd);
+
+               /* Randomly fuzz a portion of the memory */
+               isc_random_get(&r);
+               p = base + (r % filesize);
+               q = base + filesize;
+               isc_random_get(&r);
+               q -= (r % (q - p));
+               while (p++ < q) {
+                       isc_random_get(&r);
+                       *p = r & 0xff;
+               }
+
+               result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
+                                                 delete_data, NULL,
+                                                 fix_data, NULL,
+                                                 NULL, &rbt_deserialized);
+               printf("%d: %s\n", i, isc_result_totext(result));
+
+               /* Test to make sure we have a valid tree */
+               ATF_REQUIRE(result == ISC_R_SUCCESS ||
+                           result == ISC_R_INVALIDFILE);
+               if (result != ISC_R_SUCCESS)
+                       ATF_REQUIRE(rbt_deserialized == NULL);
+
+               if (rbt_deserialized != NULL)
+                       dns_rbt_destroy(&rbt_deserialized);
+
+               munmap(base, filesize);
+       }
+
+       unlink("zone.bin");
+       dns_test_end();
+}
+
+
+ATF_TC(serialize_align);
+ATF_TC_HEAD(serialize_align, tc) {
+       atf_tc_set_md_var(tc, "descr",
+                         "Test the dns_rbt_serialize_align() function.");
+}
+ATF_TC_BODY(serialize_align, tc) {
+       UNUSED(tc);
+
+       ATF_CHECK(dns_rbt_serialize_align(0) == 0);
+       ATF_CHECK(dns_rbt_serialize_align(1) == 8);
+       ATF_CHECK(dns_rbt_serialize_align(2) == 8);
+       ATF_CHECK(dns_rbt_serialize_align(3) == 8);
+       ATF_CHECK(dns_rbt_serialize_align(4) == 8);
+       ATF_CHECK(dns_rbt_serialize_align(5) == 8);
+       ATF_CHECK(dns_rbt_serialize_align(6) == 8);
+       ATF_CHECK(dns_rbt_serialize_align(7) == 8);
+       ATF_CHECK(dns_rbt_serialize_align(8) == 8);
+       ATF_CHECK(dns_rbt_serialize_align(9) == 16);
+       ATF_CHECK(dns_rbt_serialize_align(0xff) == 0x100);
+       ATF_CHECK(dns_rbt_serialize_align(0x301) == 0x308);
+}
+
+/*
+ * Main
+ */
+ATF_TP_ADD_TCS(tp) {
+       ATF_TP_ADD_TC(tp, serialize);
+       ATF_TP_ADD_TC(tp, deserialize_corrupt);
+       ATF_TP_ADD_TC(tp, serialize_align);
+
+       return (atf_no_error());
+}
index 9b1500c057c6e5388d9d42736902040384e018e0..d2df4e511684e91634a2d7a87c7af21a538c89e9 100644 (file)
@@ -25,7 +25,6 @@
 #include <isc/string.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <sys/mman.h>
 
 #ifdef HAVE_INTTYPES_H
 #include <inttypes.h> /* uintptr_t */
@@ -50,6 +49,7 @@
 #include <isc/task.h>
 #include <isc/timer.h>
 #include <isc/util.h>
+#include <isc/print.h>
 
 #include <dns/log.h>
 #include <dns/name.h>
 
 #include <dst/dst.h>
 
-#ifndef MAP_FILE
-#define MAP_FILE 0
-#endif
+#include <ctype.h>
+
+typedef struct {
+       dns_rbt_t *rbt;
+       dns_rbt_t *rbt_distances;
+} test_context_t;
+
+/* The initial structure of domain tree will be as follows:
+ *
+ *            .
+ *            |
+ *            b
+ *          /   \
+ *         a    d.e.f
+ *             /  |   \
+ *            c   |    g.h
+ *                |     |
+ *               w.y    i
+ *             /  |  \   \
+ *            x   |   z   k
+ *                |   |
+ *                p   j
+ *              /   \
+ *             o     q
+ */
 
-typedef struct data_holder {
-       int len;
-       const char *data;
-} data_holder_t;
-
-typedef struct rbt_testdata {
-       const char *name;
-       size_t name_len;
-       data_holder_t data;
-} rbt_testdata_t;
-
-#define DATA_ITEM(name) { (name), sizeof(name) - 1, { sizeof(name), (name) } }
-
-rbt_testdata_t testdata[] = {
-       DATA_ITEM("first.com."),
-       DATA_ITEM("one.net."),
-       DATA_ITEM("two.com."),
-       DATA_ITEM("three.org."),
-       DATA_ITEM("asdf.com."),
-       DATA_ITEM("ghjkl.com."),
-       DATA_ITEM("1.edu."),
-       DATA_ITEM("2.edu."),
-       DATA_ITEM("3.edu."),
-       DATA_ITEM("123.edu."),
-       DATA_ITEM("1236.com."),
-       DATA_ITEM("and_so_forth.com."),
-       DATA_ITEM("thisisalongname.com."),
-       DATA_ITEM("a.b."),
-       DATA_ITEM("test.net."),
-       DATA_ITEM("whoknows.org."),
-       DATA_ITEM("blargh.com."),
-       DATA_ITEM("www.joe.com."),
-       DATA_ITEM("test.com."),
-       DATA_ITEM("isc.org."),
-       DATA_ITEM("uiop.mil."),
-       DATA_ITEM("last.fm."),
-       { NULL, 0, { 0, NULL } }
+/* The full absolute names of the nodes in the tree (the tree also
+ * contains "." which is not included in this list).
+ */
+static const char * const domain_names[] = {
+    "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+    "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"
 };
 
+static const size_t domain_names_count = (sizeof(domain_names) /
+                                         sizeof(domain_names[0]));
+
+/* These are set as the node data for the tree used in distances check
+ * (for the names in domain_names[] above).
+ */
+static const int node_distances[] = {
+    3, 1, 2, 2, 2, 3, 1, 2, 1, 1, 2, 2
+};
+
+/*
+ * The domain order should be:
+ * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
+ * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
+ *            . (no data, can't be found)
+ *            |
+ *            b
+ *          /   \
+ *         a    d.e.f
+ *             /  |   \
+ *            c   |    g.h
+ *                |     |
+ *               w.y    i
+ *             /  |  \   \
+ *            x   |   z   k
+ *                |   |
+ *                p   j
+ *              /   \
+ *             o     q
+ */
+
+static const char * const ordered_names[] = {
+    "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+    "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
+    "g.h", "i.g.h", "k.g.h"};
+
+static const size_t ordered_names_count = (sizeof(ordered_names) /
+                                          sizeof(*ordered_names));
+
 static void
 delete_data(void *data, void *arg) {
        UNUSED(arg);
-       UNUSED(data);
+
+       isc_mem_put(mctx, data, sizeof(size_t));
 }
 
-static isc_result_t
-write_data(FILE *file, unsigned char *datap, void *arg, isc_uint64_t *crc) {
+static void
+build_name_from_str(const char *namestr, dns_fixedname_t *fname) {
+       size_t length;
+       isc_buffer_t *b = NULL;
        isc_result_t result;
-       size_t ret = 0;
-       data_holder_t *data = (data_holder_t *)datap;
-       data_holder_t temp;
-       off_t where;
+       dns_name_t *name;
 
-       UNUSED(arg);
+       length = strlen(namestr);
 
-       REQUIRE(file != NULL);
-       REQUIRE(crc != NULL);
-       REQUIRE(data != NULL);
-       REQUIRE((data->len == 0 && data->data == NULL) ||
-               (data->len != 0 && data->data != NULL));
-
-       result = isc_stdio_tell(file, &where);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-
-       temp = *data;
-       temp.data = (data->len == 0
-                    ? NULL
-                    : (char *)((uintptr_t)where + sizeof(data_holder_t)));
-
-       isc_crc64_update(crc, (void *)&temp, sizeof(temp));
-       ret = fwrite(&temp, sizeof(data_holder_t), 1, file);
-       if (ret != 1)
-               return (ISC_R_FAILURE);
-       if (data->len > 0) {
-               isc_crc64_update(crc, (const void *)data->data, data->len);
-               ret = fwrite(data->data, data->len, 1, file);
-               if (ret != 1)
-                       return (ISC_R_FAILURE);
-       }
+       result = isc_buffer_allocate(mctx, &b, length);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       isc_buffer_putmem(b, (const unsigned char *) namestr, length);
+
+       dns_fixedname_init(fname);
+       name = dns_fixedname_name(fname);
+       ATF_REQUIRE(name != NULL);
+       result = dns_name_fromtext(name, b, dns_rootname, 0, NULL);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
 
-       return (ISC_R_SUCCESS);
+       isc_buffer_free(&b);
 }
 
-static isc_result_t
-fix_data(dns_rbtnode_t *p, void *base, size_t max, void *arg,
-        isc_uint64_t *crc)
-{
-       data_holder_t *data = p->data;
-       size_t size;
+static test_context_t *
+test_context_setup(void) {
+       test_context_t *ctx;
+       isc_result_t result;
+       size_t i;
 
-       UNUSED(base);
-       UNUSED(max);
-       UNUSED(arg);
+       ctx = isc_mem_get(mctx, sizeof(*ctx));
 
-       REQUIRE(crc != NULL);
-       REQUIRE(p != NULL);
+       ctx->rbt = NULL;
+       result = dns_rbt_create(mctx, delete_data, NULL, &ctx->rbt);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
 
+       ctx->rbt_distances = NULL;
+       result = dns_rbt_create(mctx, delete_data, NULL, &ctx->rbt_distances);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
 
-       if (data == NULL)
-               printf("fixing data: data NULL\n");
-       else
-               printf("fixing data: len %d, data %p\n", data->len, data->data);
+       for (i = 0; i < domain_names_count; i++) {
+               size_t *n;
+               dns_fixedname_t fname;
+               dns_name_t *name;
 
-       if (data == NULL ||
-           (data->len == 0 && data->data != NULL) ||
-           (data->len != 0 && data->data == NULL))
-               return (ISC_R_INVALIDFILE);
+               build_name_from_str(domain_names[i], &fname);
 
-       size = max - ((char *)p - (char *)base);
+               name = dns_fixedname_name(&fname);
 
-       if (data->len > (int) size || data->data > (const char *) max) {
-               printf("data invalid\n");
-               return (ISC_R_INVALIDFILE);
-       }
+               n = isc_mem_get(mctx, sizeof(size_t));
+               *n = i + 1;
+               result = dns_rbt_addname(ctx->rbt, name, n);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
 
-       isc_crc64_update(crc, (void *)data, sizeof(*data));
+               n = isc_mem_get(mctx, sizeof(size_t));
+               *n = node_distances[i];
+               result = dns_rbt_addname(ctx->rbt_distances, name, n);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       }
 
-       data->data = (data->len == 0)
-               ? NULL
-               : (char *)data + sizeof(data_holder_t);
+       return (ctx);
+}
 
-       if (data->len > 0)
-               isc_crc64_update(crc, (const void *)data->data, data->len);
+static void
+test_context_teardown(test_context_t *ctx) {
+       dns_rbt_destroy(&ctx->rbt);
+       dns_rbt_destroy(&ctx->rbt_distances);
 
-       return (ISC_R_SUCCESS);
+       isc_mem_put(mctx, ctx, sizeof(*ctx));
 }
 
 /*
- * Load test data into the RBT.
+ * Walk the tree and ensure that all the test nodes are present.
  */
 static void
-add_test_data(isc_mem_t *mctx, dns_rbt_t *rbt) {
-       char buffer[1024];
-       isc_buffer_t b;
+check_test_data(dns_rbt_t *rbt) {
+       dns_fixedname_t fixed;
        isc_result_t result;
-       dns_fixedname_t fname;
-       dns_name_t *name;
-       dns_compress_t cctx;
-       rbt_testdata_t *testdatap = testdata;
+       dns_name_t *foundname;
+       size_t i;
+
+       dns_fixedname_init(&fixed);
+       foundname = dns_fixedname_name(&fixed);
 
-       dns_compress_init(&cctx, -1, mctx);
+       for (i = 0; i < domain_names_count; i++) {
+               dns_fixedname_t fname;
+               dns_name_t *name;
+               size_t *n;
 
-       while (testdatap->name != NULL && testdatap->data.data != NULL) {
-               memmove(buffer, testdatap->name, testdatap->name_len);
+               build_name_from_str(domain_names[i], &fname);
 
-               isc_buffer_init(&b, buffer, testdatap->name_len);
-               isc_buffer_add(&b, testdatap->name_len);
-               dns_fixedname_init(&fname);
                name = dns_fixedname_name(&fname);
-               result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
-               if (result != ISC_R_SUCCESS) {
-                       testdatap++;
-                       continue;
-               }
-
-               if (name != NULL) {
-                       result = dns_rbt_addname(rbt, name, &testdatap->data);
-                       ATF_CHECK_STREQ(dns_result_totext(result), "success");
-               }
-               testdatap++;
+               n = NULL;
+               result = dns_rbt_findname(rbt, name, 0, foundname,
+                                         (void *) &n);
+               ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+               ATF_CHECK_EQ(*n, i + 1);
        }
+}
 
-       dns_compress_invalidate(&cctx);
+ATF_TC(rbt_create);
+ATF_TC_HEAD(rbt_create, tc) {
+       atf_tc_set_md_var(tc, "descr", "Test the creation of an rbt");
 }
+ATF_TC_BODY(rbt_create, tc) {
+       isc_result_t result;
+       test_context_t *ctx;
+       isc_boolean_t tree_ok;
 
-/*
- * Walk the tree and ensure that all the test nodes are present.
- */
-static void
-check_test_data(dns_rbt_t *rbt) {
-       char buffer[1024];
-       char *arg;
+       UNUSED(tc);
+
+       isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       ctx = test_context_setup();
+
+       check_test_data(ctx->rbt);
+
+       tree_ok = dns__rbt_checkproperties(ctx->rbt);
+       ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+       test_context_teardown(ctx);
+
+       dns_test_end();
+}
+
+ATF_TC(rbt_nodecount);
+ATF_TC_HEAD(rbt_nodecount, tc) {
+       atf_tc_set_md_var(tc, "descr", "Test dns_rbt_nodecount() on a tree");
+}
+ATF_TC_BODY(rbt_nodecount, tc) {
+       isc_result_t result;
+       test_context_t *ctx;
+
+       UNUSED(tc);
+
+       isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       ctx = test_context_setup();
+
+       ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
+
+       test_context_teardown(ctx);
+
+       dns_test_end();
+}
+
+ATF_TC(rbtnode_get_distance);
+ATF_TC_HEAD(rbtnode_get_distance, tc) {
+       atf_tc_set_md_var(tc, "descr",
+                         "Test dns_rbtnode_get_distance() on a tree");
+}
+ATF_TC_BODY(rbtnode_get_distance, tc) {
+       isc_result_t result;
+       test_context_t *ctx;
+       const char *name_str = "a";
        dns_fixedname_t fname;
-       dns_fixedname_t fixed;
        dns_name_t *name;
-       isc_buffer_t b;
-       data_holder_t *data;
-       isc_result_t result;
-       dns_name_t *foundname;
-       rbt_testdata_t *testdatap = testdata;
+       dns_rbtnode_t *node = NULL;
+       dns_rbtnodechain_t chain;
 
-       dns_fixedname_init(&fixed);
-       foundname = dns_fixedname_name(&fixed);
+       UNUSED(tc);
 
-       while (testdatap->name != NULL && testdatap->data.data != NULL) {
-               memmove(buffer, testdatap->name, testdatap->name_len + 1);
-               arg = buffer;
+       isc_mem_debugging = ISC_MEM_DEBUGRECORD;
 
-               isc_buffer_init(&b, arg, testdatap->name_len);
-               isc_buffer_add(&b, testdatap->name_len);
-               dns_fixedname_init(&fname);
-               name = dns_fixedname_name(&fname);
-               result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
-               if (result != ISC_R_SUCCESS) {
-                       testdatap++;
-                       continue;
-               }
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
 
-               data = NULL;
-               result = dns_rbt_findname(rbt, name, 0, foundname,
-                                         (void *) &data);
-               ATF_CHECK_STREQ(dns_result_totext(result), "success");
+       ctx = test_context_setup();
+
+       build_name_from_str(name_str, &fname);
+       name = dns_fixedname_name(&fname);
+
+       dns_rbtnodechain_init(&chain, mctx);
+
+       result = dns_rbt_findnode(ctx->rbt_distances, name, NULL,
+                                 &node, &chain, 0, NULL, NULL);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
 
-               testdatap++;
+       while (node != NULL) {
+               const size_t *distance = (const size_t *) node->data;
+               if (distance != NULL)
+                       ATF_CHECK_EQ(*distance,
+                                    dns__rbtnode_getdistance(node));
+               result = dns_rbtnodechain_next(&chain, NULL, NULL);
+               if (result == ISC_R_NOMORE)
+                     break;
+               dns_rbtnodechain_current(&chain, NULL, NULL, &node);
        }
+
+       ATF_CHECK_EQ(result, ISC_R_NOMORE);
+
+       dns_rbtnodechain_invalidate(&chain);
+
+       test_context_teardown(ctx);
+
+       dns_test_end();
 }
 
-static void
-data_printer(FILE *out, void *datap)
-{
-       data_holder_t *data = (data_holder_t *)datap;
+ATF_TC(rbt_check_distance_random);
+ATF_TC_HEAD(rbt_check_distance_random, tc) {
+       atf_tc_set_md_var(tc, "descr",
+                         "Test tree balance, inserting names in random order");
+}
+ATF_TC_BODY(rbt_check_distance_random, tc) {
+       /* This test checks an important performance-related property of
+        * the red-black tree, which is important for us: the longest
+        * path from a sub-tree's root to a node is no more than
+        * 2log(n). This check verifies that the tree is balanced.
+        */
+       dns_rbt_t *mytree = NULL;
+       const int log_num_nodes = 16;
 
-       fprintf(out, "%d bytes, %s", data->len, data->data);
+       int i;
+       isc_result_t result;
+       isc_boolean_t tree_ok;
+
+       UNUSED(tc);
+
+       isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       /* Names are inserted in random order. */
+
+       /* Make a large 65536 node top-level domain tree, i.e., the
+        * following code inserts names such as:
+        *
+        * savoucnsrkrqzpkqypbygwoiliawpbmz.
+        * wkadamcbbpjtundbxcmuayuycposvngx.
+        * wzbpznemtooxdpjecdxynsfztvnuyfao.
+        * yueojmhyffslpvfmgyfwioxegfhepnqq.
+        */
+       for (i = 0; i < (1 << log_num_nodes); i++) {
+               size_t *n;
+               char namebuf[34];
+
+               n = isc_mem_get(mctx, sizeof(size_t));
+               *n = i + 1;
+
+               while (1) {
+                       int j;
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+
+                       for (j = 0; j < 32; j++) {
+                               isc_uint32_t v;
+                               isc_random_get(&v);
+                               namebuf[j] = 'a' + (v % 26);
+                       }
+                       namebuf[32] = '.';
+                       namebuf[33] = 0;
+
+                       build_name_from_str(namebuf, &fname);
+                       name = dns_fixedname_name(&fname);
+
+                       result = dns_rbt_addname(mytree, name, n);
+                       if (result == ISC_R_SUCCESS)
+                               break;
+               }
+       }
+
+       /* 1 (root . node) + (1 << log_num_nodes) */
+       ATF_CHECK_EQ(1 + (1 << log_num_nodes), dns_rbt_nodecount(mytree));
+
+       /* The distance from each node to its sub-tree root must be less
+        * than 2 * log(n).
+        */
+       ATF_CHECK((2 * log_num_nodes) >= dns__rbt_getheight(mytree));
+
+       /* Also check RB tree properties */
+       tree_ok = dns__rbt_checkproperties(mytree);
+       ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+       dns_rbt_destroy(&mytree);
+
+       dns_test_end();
 }
 
-ATF_TC(rbt);
-ATF_TC_HEAD(rbt, tc) {
-       atf_tc_set_md_var(tc, "descr", "Test the creation of an rbt");
+ATF_TC(rbt_check_distance_ordered);
+ATF_TC_HEAD(rbt_check_distance_ordered, tc) {
+       atf_tc_set_md_var(tc, "descr",
+                         "Test tree balance, inserting names in sorted order");
 }
-ATF_TC_BODY(rbt, tc) {
-       dns_rbt_t *rbt = NULL;
+ATF_TC_BODY(rbt_check_distance_ordered, tc) {
+       /* This test checks an important performance-related property of
+        * the red-black tree, which is important for us: the longest
+        * path from a sub-tree's root to a node is no more than
+        * 2log(n). This check verifies that the tree is balanced.
+        */
+       dns_rbt_t *mytree = NULL;
+       const int log_num_nodes = 16;
+
+       int i;
        isc_result_t result;
+       isc_boolean_t tree_ok;
 
        UNUSED(tc);
 
        isc_mem_debugging = ISC_MEM_DEBUGRECORD;
 
        result = dns_test_begin(NULL, ISC_TRUE);
-       ATF_CHECK_STREQ(dns_result_totext(result), "success");
-       result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
-       ATF_CHECK_STREQ(dns_result_totext(result), "success");
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
 
-       add_test_data(mctx, rbt);
+       result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
 
-       check_test_data(rbt);
+       /* Names are inserted in sorted order. */
 
-       dns_rbt_printall(rbt, data_printer);
+       /* Make a large 65536 node top-level domain tree, i.e., the
+        * following code inserts names such as:
+        *
+        *   name00000000.
+        *   name00000001.
+        *   name00000002.
+        *   name00000003.
+        */
+       for (i = 0; i < (1 << log_num_nodes); i++) {
+               size_t *n;
+               char namebuf[14];
+               dns_fixedname_t fname;
+               dns_name_t *name;
 
-       dns_rbt_destroy(&rbt);
+               n = isc_mem_get(mctx, sizeof(size_t));
+               *n = i + 1;
+
+               snprintf(namebuf, sizeof(namebuf), "name%08x.", i);
+               build_name_from_str(namebuf, &fname);
+               name = dns_fixedname_name(&fname);
+
+               result = dns_rbt_addname(mytree, name, n);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+       }
+
+       /* 1 (root . node) + (1 << log_num_nodes) */
+       ATF_CHECK_EQ(1 + (1 << log_num_nodes), dns_rbt_nodecount(mytree));
+
+       /* The distance from each node to its sub-tree root must be less
+        * than 2 * log(n).
+        */
+       ATF_CHECK((2 * log_num_nodes) >= dns__rbt_getheight(mytree));
+
+       /* Also check RB tree properties */
+       tree_ok = dns__rbt_checkproperties(mytree);
+       ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+       dns_rbt_destroy(&mytree);
 
        dns_test_end();
 }
 
-ATF_TC(serialize);
-ATF_TC_HEAD(serialize, tc) {
-       atf_tc_set_md_var(tc, "descr", "Test writing an rbt to file");
+static isc_result_t
+insert_helper(dns_rbt_t *rbt, const char *namestr, dns_rbtnode_t **node) {
+       dns_fixedname_t fname;
+       dns_name_t *name;
+
+       build_name_from_str(namestr, &fname);
+       name = dns_fixedname_name(&fname);
+
+       return (dns_rbt_addnode(rbt, name, node));
+}
+
+static isc_boolean_t
+compare_labelsequences(dns_rbtnode_t *node, const char *labelstr) {
+       dns_name_t name;
+       isc_result_t result;
+       char *nodestr = NULL;
+       isc_boolean_t is_equal;
+
+       dns_name_init(&name, NULL);
+       dns_rbt_namefromnode(node, &name);
+
+       result = dns_name_tostring(&name, &nodestr, mctx);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       is_equal = strcmp(labelstr, nodestr) == 0 ? ISC_TRUE : ISC_FALSE;
+
+       isc_mem_free(mctx, nodestr);
+
+       return (is_equal);
+}
+
+ATF_TC(rbt_insert);
+ATF_TC_HEAD(rbt_insert, tc) {
+       atf_tc_set_md_var(tc, "descr", "Test insertion into a tree");
 }
-ATF_TC_BODY(serialize, tc) {
-       dns_rbt_t *rbt = NULL;
+ATF_TC_BODY(rbt_insert, tc) {
        isc_result_t result;
-       FILE *rbtfile = NULL;
-       dns_rbt_t *rbt_deserialized = NULL;
-       off_t offset;
-       int fd;
-       off_t filesize = 0;
-       char *base;
+       test_context_t *ctx;
+       dns_rbtnode_t *node;
 
        UNUSED(tc);
 
        isc_mem_debugging = ISC_MEM_DEBUGRECORD;
 
        result = dns_test_begin(NULL, ISC_TRUE);
-       ATF_CHECK_STREQ(dns_result_totext(result), "success");
-       result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
-       ATF_CHECK_STREQ(dns_result_totext(result), "success");
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
 
-       add_test_data(mctx, rbt);
+       ctx = test_context_setup();
 
-       dns_rbt_printall(rbt, data_printer);
+       /* Check node count before beginning. */
+       ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
 
-       /*
-        * Serialize the tree.
+       /* Try to insert a node that already exists. */
+       node = NULL;
+       result = insert_helper(ctx->rbt, "d.e.f", &node);
+       ATF_CHECK_EQ(result, ISC_R_EXISTS);
+
+       /* Node count must not have changed. */
+       ATF_CHECK_EQ(15, dns_rbt_nodecount(ctx->rbt));
+
+       /* Try to insert a node that doesn't exist. */
+       node = NULL;
+       result = insert_helper(ctx->rbt, "0", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK_EQ(compare_labelsequences(node, "0"), ISC_TRUE);
+
+       /* Node count must have increased. */
+       ATF_CHECK_EQ(16, dns_rbt_nodecount(ctx->rbt));
+
+       /* Another. */
+       node = NULL;
+       result = insert_helper(ctx->rbt, "example.com", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_REQUIRE(node != NULL);
+       ATF_CHECK_EQ(node->data, NULL);
+
+       /* Node count must have increased. */
+       ATF_CHECK_EQ(17, dns_rbt_nodecount(ctx->rbt));
+
+       /* Re-adding it should return EXISTS */
+       node = NULL;
+       result = insert_helper(ctx->rbt, "example.com", &node);
+       ATF_CHECK_EQ(result, ISC_R_EXISTS);
+
+       /* Node count must not have changed. */
+       ATF_CHECK_EQ(17, dns_rbt_nodecount(ctx->rbt));
+
+       /* Fission the node d.e.f */
+       node = NULL;
+       result = insert_helper(ctx->rbt, "k.e.f", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK_EQ(compare_labelsequences(node, "k"), ISC_TRUE);
+
+       /* Node count must have incremented twice ("d.e.f" fissioned to
+        * "d" and "e.f", and the newly added "k").
+        */
+       ATF_CHECK_EQ(19, dns_rbt_nodecount(ctx->rbt));
+
+       /* Fission the node "g.h" */
+       node = NULL;
+       result = insert_helper(ctx->rbt, "h", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK_EQ(compare_labelsequences(node, "h"), ISC_TRUE);
+
+       /* Node count must have incremented ("g.h" fissioned to "g" and
+        * "h").
         */
-       printf("serialization begins.\n");
-       rbtfile = fopen("./zone.bin", "w+b");
-       ATF_REQUIRE(rbtfile != NULL);
-       result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
-                                       &offset);
-       ATF_REQUIRE(result == ISC_R_SUCCESS);
-       dns_rbt_destroy(&rbt);
+       ATF_CHECK_EQ(20, dns_rbt_nodecount(ctx->rbt));
+
+       /* Add child domains */
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "m.p.w.y.d.e.f", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK_EQ(compare_labelsequences(node, "m"), ISC_TRUE);
+       ATF_CHECK_EQ(21, dns_rbt_nodecount(ctx->rbt));
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "n.p.w.y.d.e.f", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK_EQ(compare_labelsequences(node, "n"), ISC_TRUE);
+       ATF_CHECK_EQ(22, dns_rbt_nodecount(ctx->rbt));
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "l.a", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK_EQ(compare_labelsequences(node, "l"), ISC_TRUE);
+       ATF_CHECK_EQ(23, dns_rbt_nodecount(ctx->rbt));
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "r.d.e.f", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       node = NULL;
+       result = insert_helper(ctx->rbt, "s.d.e.f", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       ATF_CHECK_EQ(25, dns_rbt_nodecount(ctx->rbt));
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "h.w.y.d.e.f", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       /* Add more nodes one by one to cover left and right rotation
+        * functions.
+        */
+       node = NULL;
+       result = insert_helper(ctx->rbt, "f", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "m", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "nm", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "om", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "k", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "l", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "fe", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "ge", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "i", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
 
+       node = NULL;
+       result = insert_helper(ctx->rbt, "ae", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       node = NULL;
+       result = insert_helper(ctx->rbt, "n", &node);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       test_context_teardown(ctx);
+
+       dns_test_end();
+}
+
+ATF_TC(rbt_remove);
+ATF_TC_HEAD(rbt_remove, tc) {
+       atf_tc_set_md_var(tc, "descr", "Test removal from a tree");
+}
+ATF_TC_BODY(rbt_remove, tc) {
        /*
-        * Deserialize the tree
+        * This testcase checks that after node removal, the
+        * binary-search tree is valid and all nodes that are supposed
+        * to exist are present in the correct order. It mainly tests
+        * DomainTree as a BST, and not particularly as a red-black
+        * tree. This test checks node deletion when upper nodes have
+        * data.
         */
-       printf("deserialization begins.\n");
+       isc_result_t result;
+       size_t j;
+
+       UNUSED(tc);
+
+       isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
 
        /*
-        * Map in the whole file in one go
+        * Delete single nodes and check if the rest of the nodes exist.
         */
-       fd = open("zone.bin", O_RDWR);
-       isc_file_getsizefd(fd, &filesize);
-       base = mmap(NULL, filesize,
-                   PROT_READ|PROT_WRITE,
-                   MAP_FILE|MAP_PRIVATE, fd, 0);
-       ATF_REQUIRE(base != NULL && base != MAP_FAILED);
-       close(fd);
-
-       result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
-                                         delete_data, NULL, fix_data, NULL,
-                                         NULL, &rbt_deserialized);
-
-       /* Test to make sure we have a valid tree */
-       ATF_REQUIRE(result == ISC_R_SUCCESS);
-       if (rbt_deserialized == NULL)
-               atf_tc_fail("deserialized rbt is null!"); /* Abort execution. */
-
-       check_test_data(rbt_deserialized);
-
-       dns_rbt_printall(rbt_deserialized, data_printer);
-
-       dns_rbt_destroy(&rbt_deserialized);
-       munmap(base, filesize);
-       unlink("zone.bin");
+       for (j = 0; j < ordered_names_count; j++) {
+               dns_rbt_t *mytree = NULL;
+               dns_rbtnode_t *node;
+               size_t i;
+               size_t *n;
+               isc_boolean_t tree_ok;
+               dns_rbtnodechain_t chain;
+               size_t start_node;
+
+               /* Create a tree. */
+               result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+               /* Insert test data into the tree. */
+               for (i = 0; i < domain_names_count; i++) {
+                       node = NULL;
+                       result = insert_helper(mytree, domain_names[i], &node);
+                       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+               }
+
+               /* Check that all names exist in order. */
+               for (i = 0; i < ordered_names_count; i++) {
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+
+                       build_name_from_str(ordered_names[i], &fname);
+
+                       name = dns_fixedname_name(&fname);
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL,
+                                                 &node, NULL,
+                                                 DNS_RBTFIND_EMPTYDATA,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+                       /* Add node data */
+                       ATF_REQUIRE(node != NULL);
+                       ATF_REQUIRE_EQ(node->data, NULL);
+
+                       n = isc_mem_get(mctx, sizeof(size_t));
+                       *n = i;
+
+                       node->data = n;
+               }
+
+               /* Now, delete the j'th node from the tree. */
+               {
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+
+                       build_name_from_str(ordered_names[j], &fname);
+
+                       name = dns_fixedname_name(&fname);
+
+                       result = dns_rbt_deletename(mytree, name, ISC_FALSE);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+               }
+
+               /* Check RB tree properties. */
+               tree_ok = dns__rbt_checkproperties(mytree);
+               ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+               dns_rbtnodechain_init(&chain, mctx);
+
+               /* Now, walk through nodes in order. */
+               if (j == 0) {
+                       /*
+                        * Node for ordered_names[0] was already deleted
+                        * above. We start from node 1.
+                        */
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+
+                       build_name_from_str(ordered_names[0], &fname);
+                       name = dns_fixedname_name(&fname);
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL,
+                                                 &node, NULL,
+                                                 0,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_NOTFOUND);
+
+                       build_name_from_str(ordered_names[1], &fname);
+                       name = dns_fixedname_name(&fname);
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL,
+                                                 &node, &chain,
+                                                 0,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+                       start_node = 1;
+               } else {
+                       /* Start from node 0. */
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+
+                       build_name_from_str(ordered_names[0], &fname);
+                       name = dns_fixedname_name(&fname);
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL,
+                                                 &node, &chain,
+                                                 0,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+                       start_node = 0;
+               }
+
+               /*
+                * node and chain have been set by the code above at
+                * this point.
+                */
+               for (i = start_node; i < ordered_names_count; i++) {
+                       dns_fixedname_t fname_j, fname_i;
+                       dns_name_t *name_j, *name_i;
+
+                       build_name_from_str(ordered_names[j], &fname_j);
+                       name_j = dns_fixedname_name(&fname_j);
+                       build_name_from_str(ordered_names[i], &fname_i);
+                       name_i = dns_fixedname_name(&fname_i);
+
+                       if (dns_name_equal(name_i, name_j)) {
+                               /*
+                                * This may be true for the last node if
+                                * we seek ahead in the loop using
+                                * dns_rbtnodechain_next() below.
+                                */
+                               if (node == NULL) {
+                                       break;
+                               }
+
+                               /* All ordered nodes have data
+                                * initially. If any node is empty, it
+                                * means it was removed, but an empty
+                                * node exists because it is a
+                                * super-domain. Just skip it.
+                                */
+                               if (node->data == NULL) {
+                                       result = dns_rbtnodechain_next(&chain,
+                                                                      NULL,
+                                                                      NULL);
+                                       if (result == ISC_R_NOMORE) {
+                                               node = NULL;
+                                       } else {
+                                               dns_rbtnodechain_current(&chain,
+                                                                        NULL,
+                                                                        NULL,
+                                                                        &node);
+                                       }
+                               }
+                               continue;
+                       }
+
+                       ATF_REQUIRE(node != NULL);
+
+                       n = (size_t *) node->data;
+                       if (n != NULL) {
+                               /* printf("n=%zu, i=%zu\n", *n, i); */
+                               ATF_CHECK_EQ(*n, i);
+                       }
+
+                       result = dns_rbtnodechain_next(&chain, NULL, NULL);
+                       if (result == ISC_R_NOMORE) {
+                               node = NULL;
+                       } else {
+                               dns_rbtnodechain_current(&chain, NULL, NULL,
+                                                        &node);
+                       }
+               }
+
+               /* We should have reached the end of the tree. */
+               ATF_REQUIRE_EQ(node, NULL);
+
+               dns_rbt_destroy(&mytree);
+       }
+
        dns_test_end();
 }
 
-ATF_TC(deserialize_corrupt);
-ATF_TC_HEAD(deserialize_corrupt, tc) {
-       atf_tc_set_md_var(tc, "descr", "Test reading a corrupt map file");
+ATF_TC(rbt_remove_empty);
+ATF_TC_HEAD(rbt_remove_empty, tc) {
+       atf_tc_set_md_var(tc, "descr",
+                         "Test removal from a tree when "
+                         "upper nodes are empty");
 }
-ATF_TC_BODY(deserialize_corrupt, tc) {
-       dns_rbt_t *rbt = NULL;
+ATF_TC_BODY(rbt_remove_empty, tc) {
+       /*
+        * This test is similar to the rbt_remove test, but checks node
+        * deletion when upper nodes are empty.
+        */
        isc_result_t result;
-       FILE *rbtfile = NULL;
-       off_t offset;
-       int fd;
-       off_t filesize = 0;
-       char *base, *p, *q;
-       isc_uint32_t r;
-       int i;
+       size_t j;
 
        UNUSED(tc);
 
@@ -400,94 +913,408 @@ ATF_TC_BODY(deserialize_corrupt, tc) {
        result = dns_test_begin(NULL, ISC_TRUE);
        ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
 
-       /* Set up map file */
-       result = dns_rbt_create(mctx, delete_data, NULL, &rbt);
-       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+       /*
+        * Delete single nodes and check if the rest of the nodes exist.
+        */
+       for (j = 0; j < ordered_names_count; j++) {
+               dns_rbt_t *mytree = NULL;
+               dns_rbtnode_t *node;
+               size_t i;
+               isc_boolean_t tree_ok;
+               dns_rbtnodechain_t chain;
+               size_t start_node;
+
+               /* Create a tree. */
+               result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+               /* Insert test data into the tree. */
+               for (i = 0; i < domain_names_count; i++) {
+                       node = NULL;
+                       result = insert_helper(mytree, domain_names[i], &node);
+                       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+               }
+
+               /* Check that all names exist in order. */
+               for (i = 0; i < ordered_names_count; i++) {
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+
+                       build_name_from_str(ordered_names[i], &fname);
+
+                       name = dns_fixedname_name(&fname);
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL,
+                                                 &node, NULL,
+                                                 DNS_RBTFIND_EMPTYDATA,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+                       /* Check that node data is empty */
+                       ATF_REQUIRE(node != NULL);
+                       ATF_REQUIRE_EQ(node->data, NULL);
+               }
+
+               /* Now, delete the j'th node from the tree. */
+               {
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
 
-       add_test_data(mctx, rbt);
-       rbtfile = fopen("./zone.bin", "w+b");
-       ATF_REQUIRE(rbtfile != NULL);
-       result = dns_rbt_serialize_tree(rbtfile, rbt, write_data, NULL,
-                                       &offset);
-       ATF_REQUIRE(result == ISC_R_SUCCESS);
-       dns_rbt_destroy(&rbt);
-
-       /* Read back with random fuzzing */
-       for (i = 0; i < 256; i++) {
-               dns_rbt_t *rbt_deserialized = NULL;
-
-               fd = open("zone.bin", O_RDWR);
-               isc_file_getsizefd(fd, &filesize);
-               base = mmap(NULL, filesize,
-                           PROT_READ|PROT_WRITE,
-                           MAP_FILE|MAP_PRIVATE, fd, 0);
-               ATF_REQUIRE(base != NULL && base != MAP_FAILED);
-               close(fd);
-
-               /* Randomly fuzz a portion of the memory */
-               isc_random_get(&r);
-               p = base + (r % filesize);
-               q = base + filesize;
-               isc_random_get(&r);
-               q -= (r % (q - p));
-               while (p++ < q) {
-                       isc_random_get(&r);
-                       *p = r & 0xff;
+                       build_name_from_str(ordered_names[j], &fname);
+
+                       name = dns_fixedname_name(&fname);
+
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL, &node,
+                                                 NULL, DNS_RBTFIND_EMPTYDATA,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+                       result = dns_rbt_deletenode(mytree, node, ISC_FALSE);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
                }
 
-               result = dns_rbt_deserialize_tree(base, filesize, 0, mctx,
-                                                 delete_data, NULL,
-                                                 fix_data, NULL,
-                                                 NULL, &rbt_deserialized);
-               printf("%d: %s\n", i, isc_result_totext(result));
+               /* Check RB tree properties. */
+               tree_ok = dns__rbt_checkproperties(mytree);
+               ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+
+               dns_rbtnodechain_init(&chain, mctx);
+
+               /* Now, walk through nodes in order. */
+               if (j == 0) {
+                       /*
+                        * Node for ordered_names[0] was already deleted
+                        * above. We start from node 1.
+                        */
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+
+                       build_name_from_str(ordered_names[0], &fname);
+                       name = dns_fixedname_name(&fname);
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL,
+                                                 &node, NULL,
+                                                 DNS_RBTFIND_EMPTYDATA,
+                                                 NULL, NULL);
+                       ATF_CHECK(result != ISC_R_SUCCESS);
+
+                       build_name_from_str(ordered_names[1], &fname);
+                       name = dns_fixedname_name(&fname);
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL,
+                                                 &node, &chain,
+                                                 DNS_RBTFIND_EMPTYDATA,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+                       start_node = 1;
+               } else {
+                       /* Start from node 0. */
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+
+                       build_name_from_str(ordered_names[0], &fname);
+                       name = dns_fixedname_name(&fname);
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, name, NULL,
+                                                 &node, &chain,
+                                                 DNS_RBTFIND_EMPTYDATA,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+                       start_node = 0;
+               }
 
-               /* Test to make sure we have a valid tree */
-               ATF_REQUIRE(result == ISC_R_SUCCESS ||
-                           result == ISC_R_INVALIDFILE);
-               if (result != ISC_R_SUCCESS)
-                       ATF_REQUIRE(rbt_deserialized == NULL);
+               /*
+                * node and chain have been set by the code above at
+                * this point.
+                */
+               for (i = start_node; i < ordered_names_count; i++) {
+                       dns_fixedname_t fntmp1, fntmp2;
+                       dns_name_t *ntmp1, *ntmp2;
+                       dns_fixedname_t fname_j, fname_i;
+                       dns_name_t *name_j, *name_i;
+
+                       build_name_from_str("j.z.d.e.f", &fntmp1);
+                       ntmp1 = dns_fixedname_name(&fntmp1);
+                       build_name_from_str("z.d.e.f", &fntmp2);
+                       ntmp2 = dns_fixedname_name(&fntmp2);
+
+                       build_name_from_str(ordered_names[j], &fname_j);
+                       name_j = dns_fixedname_name(&fname_j);
+                       build_name_from_str(ordered_names[i], &fname_i);
+                       name_i = dns_fixedname_name(&fname_i);
+
+                       if (dns_name_equal(name_j, ntmp1) &&
+                           dns_name_equal(name_j, ntmp2))
+                       {
+                               /*
+                                * The only special case in the
+                                * tree. Here, "z.d.e.f" will not exist
+                                * as it would have been removed during
+                                * removal of "j.z.d.e.f".
+                                */
+                               continue;
+                       }
+
+                       if (dns_name_equal(name_i, name_j)) {
+                               dns_fixedname_t fntmp3, fntmp4, fntmp5, fntmp6;
+                               dns_name_t *ntmp3, *ntmp4, *ntmp5, *ntmp6;
+
+                               /*
+                                * This may be true for the last node if
+                                * we seek ahead in the loop using
+                                * dns_rbtnodechain_next() below.
+                                */
+                               if (node == NULL) {
+                                       break;
+                               }
+
+                               /*
+                                * All ordered nodes are empty
+                                * initially. If an empty removed node
+                                * exists because it is a super-domain,
+                                * just skip it.
+                                */
+                               build_name_from_str("d.e.f", &fntmp3);
+                               ntmp3 = dns_fixedname_name(&fntmp3);
+                               build_name_from_str("w.y.d.e.f", &fntmp4);
+                               ntmp4 = dns_fixedname_name(&fntmp4);
+                               build_name_from_str("z.d.e.f", &fntmp5);
+                               ntmp5 = dns_fixedname_name(&fntmp5);
+                               build_name_from_str("g.h", &fntmp6);
+                               ntmp6 = dns_fixedname_name(&fntmp6);
+
+                               if (dns_name_equal(name_j, ntmp3) ||
+                                   dns_name_equal(name_j, ntmp4) ||
+                                   dns_name_equal(name_j, ntmp5) ||
+                                   dns_name_equal(name_j, ntmp6))
+                               {
+                                       result = dns_rbtnodechain_next(&chain,
+                                                                      NULL,
+                                                                      NULL);
+                                       if (result == ISC_R_NOMORE) {
+                                               node = NULL;
+                                       } else {
+                                               dns_rbtnodechain_current(&chain,
+                                                                        NULL,
+                                                                        NULL,
+                                                                        &node);
+                                       }
+                               }
+                               continue;
+                       }
+
+                       ATF_REQUIRE(node != NULL);
+
+                       result = dns_rbtnodechain_next(&chain, NULL, NULL);
+                       if (result == ISC_R_NOMORE) {
+                               node = NULL;
+                       } else {
+                               dns_rbtnodechain_current(&chain, NULL, NULL,
+                                                        &node);
+                       }
+               }
 
-               if (rbt_deserialized != NULL)
-                       dns_rbt_destroy(&rbt_deserialized);
+               /* We should have reached the end of the tree. */
+               ATF_REQUIRE_EQ(node, NULL);
 
-               munmap(base, filesize);
+               dns_rbt_destroy(&mytree);
        }
 
-       unlink("zone.bin");
        dns_test_end();
 }
 
+static void
+insert_nodes(dns_rbt_t *mytree, char **names,
+            size_t *names_count, isc_uint32_t num_names)
+{
+       isc_uint32_t i;
+
+       for (i = 0; i < num_names; i++) {
+               size_t *n;
+               char namebuf[34];
+
+               n = isc_mem_get(mctx, sizeof(size_t));
+               *n = i; /* Unused value */
+
+               while (1) {
+                       int j;
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+                       isc_result_t result;
+
+                       for (j = 0; j < 32; j++) {
+                               isc_uint32_t v;
+                               isc_random_get(&v);
+                               namebuf[j] = 'a' + (v % 26);
+                       }
+                       namebuf[32] = '.';
+                       namebuf[33] = 0;
+
+                       build_name_from_str(namebuf, &fname);
+                       name = dns_fixedname_name(&fname);
+
+                       result = dns_rbt_addname(mytree, name, n);
+                       if (result == ISC_R_SUCCESS) {
+                               names[*names_count] = isc_mem_strdup(mctx,
+                                                                    namebuf);
+                               *names_count += 1;
+                               break;
+                       }
+               }
+       }
+}
 
-ATF_TC(serialize_align);
-ATF_TC_HEAD(serialize_align, tc) {
+static void
+remove_nodes(dns_rbt_t *mytree, char **names,
+            size_t *names_count, isc_uint32_t num_names)
+{
+       isc_uint32_t i;
+
+       UNUSED(mytree);
+
+       for (i = 0; i < num_names; i++) {
+               isc_uint32_t node;
+               dns_fixedname_t fname;
+               dns_name_t *name;
+               isc_result_t result;
+
+               isc_random_get(&node);
+
+               node %= *names_count;
+
+               build_name_from_str(names[node], &fname);
+               name = dns_fixedname_name(&fname);
+
+               result = dns_rbt_deletename(mytree, name, ISC_FALSE);
+               ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+               isc_mem_free(mctx, names[node]);
+               if (*names_count > 0) {
+                       names[node] = names[*names_count - 1];
+                       names[*names_count - 1] = NULL;
+                       *names_count -= 1;
+               }
+       }
+}
+
+static void
+check_tree(dns_rbt_t *mytree, char **names, size_t names_count) {
+       isc_boolean_t tree_ok;
+
+       UNUSED(names);
+
+       ATF_CHECK_EQ(names_count + 1, dns_rbt_nodecount(mytree));
+
+       /*
+        * The distance from each node to its sub-tree root must be less
+        * than 2 * log_2(1024).
+        */
+       ATF_CHECK((2 * 10) >= dns__rbt_getheight(mytree));
+
+       /* Also check RB tree properties */
+       tree_ok = dns__rbt_checkproperties(mytree);
+       ATF_CHECK_EQ(tree_ok, ISC_TRUE);
+}
+
+ATF_TC(rbt_insert_and_remove);
+ATF_TC_HEAD(rbt_insert_and_remove, tc) {
        atf_tc_set_md_var(tc, "descr",
-                         "Test the dns_rbt_serialize_align() function.");
+                         "Test insert and remove in a loop");
 }
-ATF_TC_BODY(serialize_align, tc) {
+ATF_TC_BODY(rbt_insert_and_remove, tc) {
+       /*
+        * What is the best way to test our red-black tree code? It is
+        * not a good method to test every case handled in the actual
+        * code itself. This is because our approach itself may be
+        * incorrect.
+        *
+        * We test our code at the interface level here by exercising the
+        * tree randomly multiple times, checking that red-black tree
+        * properties are valid, and all the nodes that are supposed to be
+        * in the tree exist and are in order.
+        *
+        * NOTE: These tests are run within a single tree level in the
+        * forest. The number of nodes in the tree level doesn't grow
+        * over 1024.
+        */
+       isc_result_t result;
+       dns_rbt_t *mytree = NULL;
+       /*
+        * We use an array for storing names instead of a set
+        * structure. It's slow, but works and is good enough for tests.
+        */
+       char *names[1024];
+       size_t names_count;
+       int i;
+
        UNUSED(tc);
 
-       ATF_CHECK(dns_rbt_serialize_align(0) == 0);
-       ATF_CHECK(dns_rbt_serialize_align(1) == 8);
-       ATF_CHECK(dns_rbt_serialize_align(2) == 8);
-       ATF_CHECK(dns_rbt_serialize_align(3) == 8);
-       ATF_CHECK(dns_rbt_serialize_align(4) == 8);
-       ATF_CHECK(dns_rbt_serialize_align(5) == 8);
-       ATF_CHECK(dns_rbt_serialize_align(6) == 8);
-       ATF_CHECK(dns_rbt_serialize_align(7) == 8);
-       ATF_CHECK(dns_rbt_serialize_align(8) == 8);
-       ATF_CHECK(dns_rbt_serialize_align(9) == 16);
-       ATF_CHECK(dns_rbt_serialize_align(0xff) == 0x100);
-       ATF_CHECK(dns_rbt_serialize_align(0x301) == 0x308);
+       isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+
+       result = dns_rbt_create(mctx, delete_data, NULL, &mytree);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       memset(names, 0, sizeof(names));
+       names_count = 0;
+
+       /* Repeat the insert/remove test some 4096 times */
+       for (i = 0; i < 4096; i++) {
+               isc_uint32_t num_names;
+               isc_random_get(&num_names);
+
+               if (names_count < 1024) {
+                       num_names %= 1024 - names_count;
+                       num_names++;
+               } else {
+                       num_names = 0;
+               }
+
+               insert_nodes(mytree, names, &names_count, num_names);
+               check_tree(mytree, names, names_count);
+
+               isc_random_get(&num_names);
+               if (names_count > 0) {
+                       num_names %= names_count;
+                       num_names++;
+               } else {
+                       num_names = 0;
+               }
+
+               remove_nodes(mytree, names, &names_count, num_names);
+               check_tree(mytree, names, names_count);
+       }
+
+       /* Remove the rest of the nodes */
+       remove_nodes(mytree, names, &names_count, names_count);
+       check_tree(mytree, names, names_count);
+
+       for (i = 0; i < 1024; i++) {
+               if (names[i] != NULL) {
+                       isc_mem_free(mctx, names[i]);
+               }
+       }
+
+       dns_rbt_destroy(&mytree);
+
+       dns_test_end();
 }
 
 /*
  * Main
  */
 ATF_TP_ADD_TCS(tp) {
-       ATF_TP_ADD_TC(tp, rbt);
-       ATF_TP_ADD_TC(tp, serialize);
-       ATF_TP_ADD_TC(tp, deserialize_corrupt);
-       ATF_TP_ADD_TC(tp, serialize_align);
+       ATF_TP_ADD_TC(tp, rbt_create);
+       ATF_TP_ADD_TC(tp, rbt_nodecount);
+       ATF_TP_ADD_TC(tp, rbtnode_get_distance);
+       ATF_TP_ADD_TC(tp, rbt_check_distance_random);
+       ATF_TP_ADD_TC(tp, rbt_check_distance_ordered);
+       ATF_TP_ADD_TC(tp, rbt_insert);
+       ATF_TP_ADD_TC(tp, rbt_remove);
+       ATF_TP_ADD_TC(tp, rbt_remove_empty);
+       ATF_TP_ADD_TC(tp, rbt_insert_and_remove);
 
        return (atf_no_error());
 }
index 799e4dcabc10dd7bd6a306a39bb546d6dd867ffe..746ed48a9e59baa2c61ec3716bfd98a3e5fbcf68 100644 (file)
@@ -553,7 +553,8 @@ dns_rbt_fullnamefromnode
 dns_rbt_hashsize
 dns_rbt_namefromnode
 dns_rbt_nodecount
-dns_rbt_printall
+dns_rbt_printtext
+dns_rbt_printdot
 dns_rbt_printnodeinfo
 dns_rbt_serialize_align
 dns_rbt_serialize_tree
index a6edc1f225215e46a8c5e321497e202c93fb2bbe..5d67c053b39869ddf83a348d13e2884d1311d76e 100644 (file)
@@ -393,3 +393,17 @@ isc_hash_calc(const unsigned char *key, unsigned int keylen,
 
        return (hash_calc(hash, key, keylen, case_sensitive));
 }
+
+void
+isc__hash_setvec(const isc_uint16_t *vec) {
+       int i;
+       hash_random_t *p;
+
+       if (hash == NULL)
+               return;
+
+       p = hash->rndvector;
+       for (i = 0; i < 256; i++) {
+               p[i] = vec[i];
+       }
+}
index 0bfe936d7f95f7909bb139e2261e478c2851f467..3dec1d9946abb0ea9d5d725e3f0c769a6676ab28 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef ISC_HASH_H
 #define ISC_HASH_H 1
 
+#include <isc/types.h>
+
 /*****
  ***** Module Info
  *****/
@@ -180,6 +182,22 @@ isc_hash_calc(const unsigned char *key, unsigned int keylen,
  */
 /*@}*/
 
+void
+isc__hash_setvec(const isc_uint16_t *vec);
+
+/*!<
+ * \brief Set the contents of the random vector used in hashing.
+ *
+ * WARNING: This function is meant to be used only in testing code. It
+ * must not be used anywhere in normally running code.
+ *
+ * The hash context must have been created beforehand, otherwise this
+ * function is a nop.
+ *
+ * 'vec' is not documented here on purpose. You should know what you are
+ * doing before using this function.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* ISC_HASH_H */