You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kg...@apache.org on 2017/08/09 17:52:21 UTC

[5/8] qpid-dispatch git commit: DISPATCH-731: add parse tree unit tests

DISPATCH-731: add parse tree unit tests


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/f6dab30f
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/f6dab30f
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/f6dab30f

Branch: refs/heads/master
Commit: f6dab30fbeba609b227c1ed8eab5911428f10257
Parents: 778a9dd
Author: Kenneth Giusti <kg...@apache.org>
Authored: Tue Aug 1 15:07:05 2017 -0400
Committer: Kenneth Giusti <kg...@apache.org>
Committed: Wed Aug 2 10:28:13 2017 -0400

----------------------------------------------------------------------
 src/parse_tree.c         |  67 +++++-
 src/parse_tree.h         |   7 +-
 tests/CMakeLists.txt     |   1 +
 tests/parse_tree_tests.c | 470 ++++++++++++++++++++++++++++++++++++++++++
 tests/run_unit_tests.c   |   3 +
 5 files changed, 542 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/src/parse_tree.c
----------------------------------------------------------------------
diff --git a/src/parse_tree.c b/src/parse_tree.c
index bd93ed9..3fe877a 100644
--- a/src/parse_tree.c
+++ b/src/parse_tree.c
@@ -181,6 +181,7 @@ struct qd_parse_node {
     struct qd_parse_node  *star_child;
     struct qd_parse_node  *hash_child;
     void *payload;      // data returned on match against this node
+    qd_log_source_t *log_source;
 };
 ALLOC_DECLARE(qd_parse_node_t);
 ALLOC_DEFINE(qd_parse_node_t);
@@ -195,6 +196,7 @@ static qd_parse_node_t *new_parse_node(const token_t *t)
         n->payload = NULL;
         n->pattern = NULL;
         n->star_child = n->hash_child = NULL;
+        n->log_source = qd_log_source("DEFAULT");
 
         if (t) {
             const size_t tlen = TOKEN_LEN(*t);
@@ -399,6 +401,12 @@ static bool parse_node_find(qd_parse_node_t *, token_iterator_t *,
 static bool parse_node_find_children(qd_parse_node_t *node, token_iterator_t *value,
                                      qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "find_children token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     if (!token_iterator_done(value)) {
 
         // check exact match first (precedence)
@@ -437,9 +445,16 @@ static bool parse_node_find_children(qd_parse_node_t *node, token_iterator_t *va
 static bool parse_node_find_token(qd_parse_node_t *node, token_iterator_t *value,
                                   qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "find_token token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     if (token_iterator_done(value) && node->pattern) {
         // exact match current node
-        return callback(handle, node->pattern, node->payload);
+        if (!callback(handle, node->pattern, node->payload))
+            return false;
     }
 
     // no payload or more tokens.  Continue to lower sub-trees. Even if no more
@@ -453,6 +468,12 @@ static bool parse_node_find_token(qd_parse_node_t *node, token_iterator_t *value
 static bool parse_node_find_star(qd_parse_node_t *node, token_iterator_t *value,
                                  qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "find_star token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     // must match exactly one token:
     if (token_iterator_done(value))
         return true;  // no match here, but continue searching
@@ -476,6 +497,12 @@ static bool parse_node_find_star(qd_parse_node_t *node, token_iterator_t *value,
 static bool parse_node_find_hash(qd_parse_node_t *node, token_iterator_t *value,
                                  qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "find_hash token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     // consume each token and look for a match on the
     // remaining key.
     while (!token_iterator_done(value)) {
@@ -496,6 +523,12 @@ static bool parse_node_find_hash(qd_parse_node_t *node, token_iterator_t *value,
 static bool parse_node_find(qd_parse_node_t *node, token_iterator_t *value,
                             qd_parse_tree_visit_t *callback, void *handle)
 {
+    qd_log(node->log_source, QD_LOG_DEBUG,
+           "node_find token=%s pattern=%s input=%s",
+           node->token ? node->token : "NULL",
+           node->pattern ? node->pattern : "NULL",
+           value->token);
+
     if (node->is_star)
         return parse_node_find_star(node, value, callback, handle);
     else if (node->is_hash)
@@ -578,7 +611,7 @@ void *qd_parse_tree_add_pattern(qd_parse_node_t *node,
     char *str = (char *)qd_iterator_copy(dup);
 
     normalize_pattern(str);
-    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+    qd_log(node->log_source, QD_LOG_DEBUG,
            "Parse tree add address pattern '%s'", str);
 
     token_iterator_init(&key, str);
@@ -601,7 +634,7 @@ bool qd_parse_tree_get_pattern(qd_parse_node_t *node,
     char *str = (char *)qd_iterator_copy(dup);
 
     normalize_pattern((char *)str);
-    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+    qd_log(node->log_source, QD_LOG_DEBUG,
            "Parse tree get address pattern '%s'", str);
 
     token_iterator_init(&key, str);
@@ -624,7 +657,7 @@ void *qd_parse_tree_remove_pattern(qd_parse_node_t *node,
     char *str = (char *)qd_iterator_copy(dup);
 
     normalize_pattern(str);
-    qd_log(qd_log_source("DEFAULT"), QD_LOG_DEBUG,
+    qd_log(node->log_source, QD_LOG_DEBUG,
            "Parse tree remove address pattern '%s'", str);
 
     token_iterator_init(&key, str);
@@ -634,6 +667,32 @@ void *qd_parse_tree_remove_pattern(qd_parse_node_t *node,
 }
 
 
+bool qd_parse_tree_walk(qd_parse_node_t *node, qd_parse_tree_visit_t *callback, void *handle)
+{
+    if (node->pattern) {  // terminal node for pattern
+        if (!callback(handle, node->pattern, node->payload))
+            return false;
+    }
+
+    qd_parse_node_t *child = DEQ_HEAD(node->children);
+    while (child) {
+        if (!qd_parse_tree_walk(child, callback, handle))
+            return false;
+        child = DEQ_NEXT(child);
+    }
+
+    if (node->star_child)
+        if (!qd_parse_tree_walk(node->star_child, callback, handle))
+            return false;
+
+    if (node->hash_child)
+        if (!qd_parse_tree_walk(node->hash_child, callback, handle))
+            return false;
+
+    return true;
+}
+
+
 #if 0
 #include <stdio.h>
 void qd_parse_tree_dump(qd_parse_node_t *node, int depth)

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/src/parse_tree.h
----------------------------------------------------------------------
diff --git a/src/parse_tree.h b/src/parse_tree.h
index d616e48..2e53269 100644
--- a/src/parse_tree.h
+++ b/src/parse_tree.h
@@ -71,15 +71,18 @@ bool qd_parse_tree_retrieve_match(qd_parse_node_t *node,
                                   void **payload);
 
 // parse tree traversal
-// visit each matching pattern that matches value in the order based on the
-// above precedence rules
 
 // return false to stop tree transversal
 typedef bool qd_parse_tree_visit_t(void *handle,
                                    const char *pattern,
                                    void *payload);
 
+// visit each matching pattern that matches value in the order based on the
+// above precedence rules
 void qd_parse_tree_search(qd_parse_node_t *node, const qd_iterator_t *value,
                           qd_parse_tree_visit_t *callback, void *handle);
 
+// visit each terminal node on the tree, returns last value returned by callback
+bool qd_parse_tree_walk(qd_parse_node_t *node, qd_parse_tree_visit_t *callback, void *handle);
+
 #endif /* parse_tree.h */

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index bc62232..8a5cbd1 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -32,6 +32,7 @@ set(unit_test_SOURCES
     tool_test.c
     failoverlist_test.c
     timer_test.c
+    parse_tree_tests
     )
 if (USE_MEMORY_POOL)
   list(APPEND unit_test_SOURCES alloc_test.c)

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/tests/parse_tree_tests.c
----------------------------------------------------------------------
diff --git a/tests/parse_tree_tests.c b/tests/parse_tree_tests.c
new file mode 100644
index 0000000..347f6df
--- /dev/null
+++ b/tests/parse_tree_tests.c
@@ -0,0 +1,470 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "test_case.h"
+#include <stdio.h>
+#include <string.h>
+#include "parse_tree.h"
+
+
+static char *test_add_remove(void *context)
+{
+    qd_iterator_t *piter = qd_iterator_string("I.am.Sam", ITER_VIEW_ALL);
+    qd_iterator_t *piter2 = qd_iterator_string("Sam.I.Am", ITER_VIEW_ALL);
+    qd_parse_node_t *node = qd_parse_tree_new();
+    void *payload;
+
+    if (qd_parse_tree_remove_pattern(node, piter))
+        return "Failed to remove a non-existing pattern";
+
+    if (qd_parse_tree_get_pattern(node, piter, &payload))
+        return "Got a non-existing pattern";
+
+    if (qd_parse_tree_add_pattern(node, piter, "Hi Sam"))
+        return "Add returned existing value";
+
+    if (qd_parse_tree_add_pattern(node, piter2, "Bye Sam"))
+        return "Add returned existing value";
+
+    if (!qd_parse_tree_get_pattern(node, piter, &payload))
+        return "Could not get pattern";
+
+    if (!payload || strcmp("Hi Sam", (char *)payload))
+        return "Got bad pattern";
+
+    if (!qd_parse_tree_get_pattern(node, piter2, &payload))
+        return "Could not get pattern";
+
+    if (!payload || strcmp("Bye Sam", (char *)payload))
+        return "Got bad pattern";
+
+    if (!qd_parse_tree_remove_pattern(node, piter))
+        return "Failed to remove an existing pattern";
+
+    if (!qd_parse_tree_remove_pattern(node, piter2))
+        return "Failed to remove an existing pattern";
+
+    qd_parse_tree_free(node);
+    qd_iterator_free(piter);
+    qd_iterator_free(piter2);
+    return NULL;
+}
+
+// for pattern match callback
+typedef struct {
+    int count;
+    const char **patterns;
+    void **payloads;
+} visit_handle_t;
+
+
+// callback to visit all matching patterns in tree
+static bool visit_all(void *handle,
+                      const char *pattern,
+                      void *payload)
+{
+    visit_handle_t *h = (visit_handle_t *)handle;
+    h->patterns[h->count] = pattern;
+    h->payloads[h->count] = payload;
+    h->count++;
+    return true;
+}
+
+
+// callback to return first (best) match
+static bool find_best(void *handle,
+                      const char *pattern,
+                      void *payload)
+{
+    visit_handle_t *h = (visit_handle_t *)handle;
+    h->patterns[0] = pattern;
+    h->payloads[0] = payload;
+    h->count = 1;
+    return false;
+}
+
+
+// check if input patterns are correctly "normalized" (see parse_tree.c)
+static char *check_normalize(const char *input,
+                             const char *expected)
+{
+    const char *patterns[1];
+    void *payloads[1];
+    visit_handle_t vh = {0, patterns, payloads};
+    qd_parse_node_t *node = qd_parse_tree_new();
+    qd_iterator_t *iter = qd_iterator_string(input, ITER_VIEW_ALL);
+    void *payload;
+
+    if (qd_parse_tree_add_pattern(node, iter, (void *)input) != NULL)
+        return "Unexpected duplicate pattern";
+    if (!qd_parse_tree_get_pattern(node, iter, &payload))
+        return "Could not find added pattern";
+    if (!payload || strcmp((const char *)payload, input))
+        return "Failed to find pattern";
+
+    qd_parse_tree_walk(node, visit_all, &vh);
+    if (vh.count != 1)
+        return "Did not find expected pattern";
+    if (strcmp(vh.payloads[0], input))
+        return "Unexpected payload!";
+    if (strcmp(vh.patterns[0], expected)) {
+        fprintf(stderr, "%s %s\n", vh.patterns[0], expected);
+        return "Incorrect normalization";
+    }
+
+    payload = qd_parse_tree_remove_pattern(node, iter);
+    if (!payload || strcmp((const char *)payload, input))
+        return "Failed to remove pattern";
+
+    qd_parse_tree_free(node);
+    qd_iterator_free(iter);
+    return NULL;
+}
+
+
+static char *test_normalization(void *context)
+{
+    char *rc = NULL;
+    char *patterns[][2] = {
+        // normalized  raw
+        {"",          ""},
+        {"a.b.c",     "a.b.c"},
+        {"a.*.c",     "a.*.c"},
+        {"#",         "#"},
+        {"#",         "#.#.#.#"},
+        {"*.*.*.#",   "#.*.#.*.#.#.*"},
+        {"a.*.*.*.#", "a.*.#.*.#.*.#"},
+        {"a.*.*.*.#", "a.*.#.*.#.*"},
+        {"*.*.*.#",   "*.#.#.*.*.#"},
+        {NULL, NULL}
+    };
+
+    for (int i = 0; !rc && patterns[i][0]; i++)
+        rc = check_normalize(patterns[i][1], patterns[i][0]);
+
+    return rc;
+}
+
+
+typedef struct {
+    const char *address;
+    bool match;
+} match_test_t;
+
+static char *match_test(const char *pattern,
+                        const match_test_t *tests)
+{
+    char *rc = NULL;
+    qd_iterator_t *piter = qd_iterator_string(pattern, ITER_VIEW_ALL);
+    qd_parse_node_t *node = qd_parse_tree_new();
+    void *payload = (void *)"found";
+
+    if (qd_parse_tree_add_pattern(node, piter, payload))
+        return "Unexpected payload when adding pattern";
+
+    for (int i = 0; tests[i].address && !rc; i++) {
+        qd_iterator_t *iter = qd_iterator_string(tests[i].address, ITER_VIEW_ALL);
+        bool match = (int)qd_parse_tree_retrieve_match(node, iter, &payload);
+        if (match != tests[i].match) {
+            printf("match address '%s' to pattern '%s': expected %d got %d\n",
+                   tests[i].address, pattern, (int)tests[i].match, (int)match);
+            return "Match test failed";
+        }
+        qd_iterator_free(iter);
+    }
+
+    qd_parse_tree_free(node);
+    qd_iterator_free(piter);
+    return NULL;
+}
+
+
+// check various pattern matches
+static char *test_matches(void *context)
+{
+    match_test_t test1[] = {
+        { "ab.cd.e",   true},
+        { "abx.cd.e",  false},
+        { "ab.cd",     false},
+        { "ab.cd.ef.", false},
+        { "ab.cd.E",   false},
+        { "x.ab.cd.e", false},
+        {NULL, false}
+    };
+
+    char *rc = match_test("ab.cd.e", test1);
+    if (rc) return rc;
+
+    match_test_t test2[] = {
+        {"", true},
+        {NULL, false},
+    };
+    rc = match_test("", test2);
+    if (rc) return rc;
+
+    match_test_t test3[] = {
+        {".", true},
+        {NULL, false},
+    };
+    rc = match_test(".", test3);
+    if (rc) return rc;
+
+    match_test_t test4[] = {
+        {"a.xx.b", true},
+        {"a.b", false},
+        {NULL, false}
+    };
+    rc = match_test("a.*.b", test4);
+    if (rc) return rc;
+
+    match_test_t test5[] = {
+        {"y.x", true},
+        {"x",   false},
+        {"x.y", false},
+        {NULL,  false}
+    };
+    rc = match_test("*.x", test5);
+    if (rc) return rc;
+
+    match_test_t test6[] = {
+        {"x.x.y", true},
+        {"x.x",   false},
+        {"y.x.x", false},
+        {"q.x.y", false},
+        {NULL, false}
+    };
+    rc = match_test("x.x.*", test6);
+    if (rc) return rc;
+
+
+    match_test_t test7[] = {
+        {"a.b", true},
+        {"a.x.b", true},
+        {"a..x.y.zz.b", true},
+        {"a.b.z", false},
+        {"z.a.b", false},
+        {"q.x.b", false},
+        {NULL, false}
+    };
+    rc = match_test("a.#.b", test7);
+    if (rc) return rc;
+
+    match_test_t test8[] = {
+        {"a", true},
+        {"a.b", true},
+        {"a.b.c", true},
+        {"b.a", false},
+        {NULL, false}
+    };
+    rc = match_test("a.#", test8);
+    if (rc) return rc;
+
+    match_test_t test9[] = {
+        {"a", true},
+        {"x.y.a", true},
+        {"a.b", false},
+        {NULL, false}
+    };
+    rc = match_test("#.a", test9);
+    if (rc) return rc;
+
+    match_test_t test10[] = {
+        {"a.b.c", true},
+        {"a.x.b.y.c", true},
+        {"a.x.x.b.y.y.c", true},
+        {"a.b", false},
+        {NULL, false}
+    };
+    rc = match_test("a.#.b.#.c", test10);
+    if (rc) return rc;
+
+    match_test_t test11[] = {
+        {"a.x.y", true},
+        {"a.x.p.qq.y", true},
+        {"a.a.x.y", false},
+        {"aa.x.b.c", false},
+        {"x.p.qq.y", false},
+        {"a.x.p.qq.y.b", false},
+        {NULL, false}
+    };
+    rc = match_test("*.x.#.y", test11);
+    if (rc) return rc;
+
+    match_test_t test12[] = {
+        {"a.b.x", true},
+        {"a.x.x.x.b.x", true},
+        {"a.b.b.b.b.y", true},
+        {"a.b.b.b.b", true},
+        {"a.b", false},
+        {NULL, false}
+    };
+    rc = match_test("a.#.b.*", test12);
+    if (rc) return rc;
+
+    match_test_t test13[] = {
+        {"x/y/z", true},
+        {"x.y.z/a.b.c", true},
+        {"x.y/z", true},
+        {"x.y", false},
+        {"x/y", false},
+        {"x", false},
+        {NULL, false}
+    };
+    rc = match_test("*.*.*.#", test13);
+    if (rc) return rc;
+
+    match_test_t test14[] = {
+        {"x", false},
+        {"x.y", true},
+        {"x.y.z", true},
+        {"x.y.z.y.z.y.z", true},
+        {NULL, false}
+    };
+    rc = match_test("*/#/*", test14);
+
+    return rc;
+}
+
+// For debug (see parse_tree.c)
+// void qd_parse_tree_dump(qd_parse_node_t *node, int depth);
+
+// search a full parse tree for multiple and best matches
+static char *test_multiple_matches(void *context)
+{
+#define PCOUNT 17
+    const char *patterns[PCOUNT] =
+      { "alpha",
+        "bravo",
+        "alpha.bravo",
+        "bravo.charlie",
+        "alpha.bravo.charlie.delta",
+        "bravo.charlie.delta.echo",
+        "alpha.*",
+        "alpha.#",
+        "alpha.*.#",
+        "#.bravo",
+        "*.bravo",
+        "*.#.bravo",
+        "alpha.*.bravo",
+        "alpha.#.bravo",
+        "alpha.*.#.bravo",
+        "*.bravo.*",
+        "#.bravo.#",
+      };
+    const char *_patterns[PCOUNT] = {NULL};
+    void *_payloads[PCOUNT] = {NULL};
+    visit_handle_t vh = {0, _patterns, _payloads};
+    qd_parse_node_t *node = qd_parse_tree_new();
+
+    // build the tree
+    for (int i = 0; i < PCOUNT; i++) {
+        qd_iterator_t *pattern = qd_iterator_string(patterns[i], ITER_VIEW_ALL);
+        if (qd_parse_tree_add_pattern(node, pattern, (void *)patterns[i])) {
+            printf("Failed to add pattern %s to parse tree\n", patterns[i]);
+            return "failed adding pattern to tree";
+        }
+        qd_iterator_free(pattern);
+    }
+
+    {
+        // read all patterns and verify all are present
+        qd_parse_tree_walk(node, visit_all, &vh);
+        if (vh.count != PCOUNT)
+            return "Not all patterns in tree";
+        for (int i = 0; i < PCOUNT; i++) {
+            bool found = false;
+            for (int j = 0; j < PCOUNT; j++) {
+                if (strcmp(patterns[i], vh.patterns[j]) == 0)
+                    found = true;
+            }
+            if (!found)
+                return "All patterns not visited";
+        }
+    }
+
+    // matches are listed in order of best->least best match
+    struct {
+        const char *address;
+        const int   count;
+        const char *matches[PCOUNT];
+    } tests[] = {
+        {"alpha",       2, {"alpha", "alpha.#"}},
+        {"alpha.zulu",  3, { "alpha.*", "alpha.*.#", "alpha.#"}},
+        {"alpha.bravo", 9, {"alpha.bravo", "alpha.*", "alpha.*.#", "alpha.#.bravo", "alpha.#", "*.bravo", "*.#.bravo", "#.bravo", "#.bravo.#"}},
+        {"bravo",       3, {"bravo", "#.bravo",  "#.bravo.#"}},
+        {"xray.bravo",  4, {"*.bravo", "*.#.bravo", "#.bravo", "#.bravo.#"}},
+        {"alpha.bravo.charlie",         4, {"alpha.*.#", "alpha.#", "*.bravo.*", "#.bravo.#"}},
+        {"xray.yankee.zulu.bravo",      3, {"*.#.bravo", "#.bravo", "#.bravo.#"}},
+        {"alpha.bravo.charlie.delta",   4, {"alpha.bravo.charlie.delta", "alpha.*.#","alpha.#", "#.bravo.#"}},
+        {"alpha.charlie.charlie.bravo", 7, {"alpha.*.#.bravo", "alpha.*.#", "alpha.#.bravo", "alpha.#", "*.#.bravo", "#.bravo", "#.bravo.#"}},
+        {"xray.yankeee.zulu.bravo.alpha.bravo.charlie", 2, {"#.bravo.#", "#.bravo.#"}},
+        {"I.match.nothing", 0, {NULL}},
+        {NULL, 0, {NULL}}
+    };
+
+    // verify all matching patterns are hit and in the correct order
+    for (int k = 0; tests[k].address; k++) {
+        qd_iterator_t *find_me = qd_iterator_string(tests[k].address, ITER_VIEW_ALL);
+        vh.count = 0;
+        qd_parse_tree_search(node, find_me, visit_all, (void *)&vh);
+        // printf("Matches for %s:\n", tests[k].address);
+        // for (int i = 0; i < vh.count; i++)
+        //    printf("%s, ", vh.patterns[i]);
+        // printf("count = %d\n", vh.count);
+        if (vh.count != tests[k].count)
+            return "Unexpected match count";
+        for (int i = 0; i < tests[k].count; i++) {
+            if (strcmp(vh.patterns[i], tests[k].matches[i]))
+                return "Unexpected pattern match";
+        }
+        qd_iterator_free(find_me);
+    }
+
+    // verify 'best' match is found
+    for (int k = 0; tests[k].address; k++) {
+        qd_iterator_t *find_me = qd_iterator_string(tests[k].address, ITER_VIEW_ALL);
+        vh.count = 0;
+        qd_parse_tree_search(node, find_me, find_best, (void *)&vh);
+        // printf("best match for %s: %s\n", tests[k].address, vh.patterns[0]);
+        if (tests[k].count == 0) {
+            if (vh.count != 0) {
+                return "Did not expect to find a best match!";
+            }
+        } else if (vh.count == 0 || strcmp(vh.patterns[0], tests[k].matches[0]))
+                return "Unexpected best pattern match";
+        qd_iterator_free(find_me);
+    }
+
+    qd_parse_tree_free(node);
+
+    return NULL;
+}
+
+
+int parse_tree_tests(void)
+{
+    int result = 0;
+    char *test_group = "parse_tree_tests";
+
+    TEST_CASE(test_add_remove, 0);
+    TEST_CASE(test_normalization, 0);
+    TEST_CASE(test_matches, 0);
+    TEST_CASE(test_multiple_matches, 0);
+    return result;
+}

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/f6dab30f/tests/run_unit_tests.c
----------------------------------------------------------------------
diff --git a/tests/run_unit_tests.c b/tests/run_unit_tests.c
index c8c4ef3..4c2b469 100644
--- a/tests/run_unit_tests.c
+++ b/tests/run_unit_tests.c
@@ -29,6 +29,7 @@ int alloc_tests(void);
 int compose_tests(void);
 int policy_tests(void);
 int failoverlist_tests(void);
+int parse_tree_tests(void);
 
 int main(int argc, char** argv)
 {
@@ -60,6 +61,8 @@ int main(int argc, char** argv)
 #endif
     result += policy_tests();
     result += failoverlist_tests();
+    result += parse_tree_tests();
+
     qd_dispatch_free(qd);       // dispatch_free last.
 
     return result;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org