blob: d09a1ffa069a963c70221f8afc5d7b8686e8ec55 [file] [log] [blame]
/*
* 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 "parse_tree.h"
#include "test_case.h"
#include <stdio.h>
#include <string.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_tree_t *node = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
void *payload;
if (qd_parse_tree_remove_pattern(node, piter)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Failed to remove a non-existing pattern";
}
if (qd_parse_tree_get_pattern(node, piter, &payload)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Got a non-existing pattern";
}
if (qd_parse_tree_add_pattern(node, piter, "Hi Sam")) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Add returned existing value";
}
if (qd_parse_tree_add_pattern(node, piter2, "Bye Sam")) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Add returned existing value";
}
if (!qd_parse_tree_get_pattern(node, piter, &payload)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Could not get pattern";
}
if (!payload || strcmp("Hi Sam", (char *)payload)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Got bad pattern";
}
if (!qd_parse_tree_get_pattern(node, piter2, &payload)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Could not get pattern";
}
if (!payload || strcmp("Bye Sam", (char *)payload)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Got bad pattern";
}
if (!qd_parse_tree_remove_pattern(node, piter)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Failed to remove an existing pattern";
}
if (!qd_parse_tree_remove_pattern(node, piter2)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return "Failed to remove an existing pattern";
}
qd_parse_tree_free(node);
qd_iterator_free(piter);
qd_iterator_free(piter2);
return NULL;
}
static char *test_add_and_match_str(void *context)
{
const char *str1 = "I.am.Sam";
const char *str2 = "Sam.I.Am";
qd_parse_tree_t *node = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
void *payload;
if (qd_parse_tree_add_pattern_str(node, str1, "Hi Sam")) {
qd_parse_tree_free(node);
return "Add returned existing value (1)";
}
if (qd_parse_tree_add_pattern_str(node, str2, "Bye Sam")) {
qd_parse_tree_free(node);
return "Add returned existing value (2)";
}
if (!qd_parse_tree_retrieve_match_str(node, str1, &payload)) {
qd_parse_tree_free(node);
return "Failed to get expected match (1)";
}
if (!qd_parse_tree_retrieve_match_str(node, str2, &payload)) {
qd_parse_tree_free(node);
return "Failed to get expected match (2)";
}
if (qd_parse_tree_retrieve_match_str(node, "I", &payload) ||
qd_parse_tree_retrieve_match_str(node, "I.am", &payload)) {
qd_parse_tree_free(node);
return "Should not match part of a pattern";
}
if (qd_parse_tree_retrieve_match_str(node, "notSoFast", &payload)) {
qd_parse_tree_free(node);
return "Match pattern should not match but did match";
}
if (!qd_parse_tree_remove_pattern_str(node, str1)) {
qd_parse_tree_free(node);
return "Failed to remove an existing pattern str";
}
if (qd_parse_tree_retrieve_match_str(node, str1, &payload)) {
qd_parse_tree_free(node);
return "Removed pattern should not match but did match";
}
qd_parse_tree_free(node);
return NULL;
}
static char *test_duplicate_error_str(void *context)
{
// While A and B are different strings they are semantically the same
// pattern and should trigger duplicate detection in the parse tree
const char *A = "#";
const char *B = "#.#.#.#";
qd_parse_tree_t *node = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
void *payload;
qd_error_t rc;
rc = qd_parse_tree_add_pattern_str(node, A, (void *)A);
if (rc) {
qd_parse_tree_free(node);
return (char *)qd_error_name(rc);
}
// matches on A or B both return A
if (!qd_parse_tree_retrieve_match_str(node, A, &payload)) {
qd_parse_tree_free(node);
return "Could not get pattern";
}
if (!payload || strcmp(A, (char *)payload)) {
qd_parse_tree_free(node);
return "Got bad pattern";
}
if (!qd_parse_tree_retrieve_match_str(node, B, &payload)) {
qd_parse_tree_free(node);
return "Could not get pattern";
}
if (!payload || strcmp(A, (char *)payload)) {
qd_parse_tree_free(node);
return "Got bad pattern";
}
// attempt to add B pattern, expect duplication error
rc = qd_parse_tree_add_pattern_str(node, B, (void *)B);
if (!rc) {
qd_parse_tree_free(node);
return "Duplicate pattern NOT detected";
}
// matches on A or B both return B
if (!qd_parse_tree_retrieve_match_str(node, A, &payload)) {
qd_parse_tree_free(node);
return "Could not get pattern";
}
if (!payload || strcmp(A, (char *)payload)) {
qd_parse_tree_free(node);
return "Got bad pattern";
}
if (!qd_parse_tree_retrieve_match_str(node, B, &payload)) {
qd_parse_tree_free(node);
return "Could not get pattern";
}
if (!payload || strcmp(A, (char *)payload)) {
qd_parse_tree_free(node);
return "Got bad pattern";
}
// now replace A with B correctly
payload = qd_parse_tree_remove_pattern_str(node, A);
if (!payload || strcmp(A, (char *)payload)) {
qd_parse_tree_free(node);
return "remove pattern failed";
}
rc = qd_parse_tree_add_pattern_str(node, B, (void *)B);
if (rc) {
qd_parse_tree_free(node);
return "Replace add failed";
}
// matches on A or B both return B
if (!qd_parse_tree_retrieve_match_str(node, A, &payload)) {
qd_parse_tree_free(node);
return "Could not get pattern";
}
if (!payload || strcmp(B, (char *)payload)) {
qd_parse_tree_free(node);
return "Got bad pattern";
}
if (!qd_parse_tree_retrieve_match_str(node, B, &payload)) {
qd_parse_tree_free(node);
return "Could not get pattern";
}
if (!payload || strcmp(B, (char *)payload)) {
qd_parse_tree_free(node);
return "Got bad pattern";
}
qd_parse_tree_free(node);
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_tree_t *node = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
qd_iterator_t *iter = qd_iterator_string(input, ITER_VIEW_ALL);
void *payload;
if (qd_parse_tree_add_pattern(node, iter, (void *)input)) {
qd_parse_tree_free(node);
qd_iterator_free(iter);
return "Unexpected duplicate pattern";
}
if (!qd_parse_tree_get_pattern(node, iter, &payload)) {
qd_parse_tree_free(node);
qd_iterator_free(iter);
return "Could not find added pattern";
}
if (!payload || strcmp((const char *)payload, input)) {
qd_parse_tree_free(node);
qd_iterator_free(iter);
return "Failed to find pattern";
}
qd_parse_tree_walk(node, visit_all, &vh);
if (vh.count != 1) {
qd_parse_tree_free(node);
qd_iterator_free(iter);
return "Did not find expected pattern";
}
if (strcmp(vh.payloads[0], input)) {
qd_parse_tree_free(node);
qd_iterator_free(iter);
return "Unexpected payload!";
}
if (strcmp(vh.patterns[0], expected)) {
fprintf(stderr, "%s %s\n", vh.patterns[0], expected);
qd_parse_tree_free(node);
qd_iterator_free(iter);
return "Incorrect normalization";
}
payload = qd_parse_tree_remove_pattern(node, iter);
if (!payload || strcmp((const char *)payload, input)) {
qd_parse_tree_free(node);
qd_iterator_free(iter);
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(qd_parse_tree_type_t type,
const char *pattern,
const match_test_t *tests)
{
char *rc = NULL;
qd_iterator_t *piter = qd_iterator_string(pattern, ITER_VIEW_ALL);
qd_parse_tree_t *node = qd_parse_tree_new(type);
void *payload = (void *)"found";
if (qd_parse_tree_add_pattern(node, piter, payload)) {
qd_parse_tree_free(node);
qd_iterator_free(piter);
return "Unexpected error 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);
qd_iterator_free(iter);
qd_parse_tree_free(node);
qd_iterator_free(piter);
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(QD_PARSE_TREE_ADDRESS, "ab.cd.e", test1);
if (rc) return rc;
match_test_t test4[] = {
{"a.xx.b", true},
{"a.b", false},
{NULL, false}
};
rc = match_test(QD_PARSE_TREE_ADDRESS, "a.*.b", test4);
if (rc) return rc;
match_test_t test5[] = {
{"y.x", true},
{"x", false},
{"x.y", false},
{NULL, false}
};
rc = match_test(QD_PARSE_TREE_ADDRESS, "*.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(QD_PARSE_TREE_ADDRESS, "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(QD_PARSE_TREE_ADDRESS, "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(QD_PARSE_TREE_ADDRESS, "a.#", test8);
if (rc) return rc;
match_test_t test9[] = {
{"a", true},
{"x.y.a", true},
{"a.b", false},
{NULL, false}
};
rc = match_test(QD_PARSE_TREE_ADDRESS, "#.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(QD_PARSE_TREE_ADDRESS, "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(QD_PARSE_TREE_ADDRESS, "*.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(QD_PARSE_TREE_ADDRESS, "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(QD_PARSE_TREE_ADDRESS, "*.*.*.#", 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(QD_PARSE_TREE_ADDRESS, "*/#/*", test14);
match_test_t test15[] = {
{"/policy", true},
{"/good/policy", true},
{"/really/really/good/policy", true},
{"help/police", false},
{"bad/polic", false},
{"/bad/p", false},
{NULL, false}
};
rc = match_test(QD_PARSE_TREE_ADDRESS, "/#/policy", test15);
match_test_t test16[] = {
{"some/policy", false},
{"some.policy", true},
{"policy", false},
{"policy.ycilop", false},
{"/hi/there/.policy", true},
{"hi.there.policy", true},
{"hi.there/policy", false},
{NULL, false}
};
rc = match_test(QD_PARSE_TREE_AMQP_0_10, ".#.*.policy", test16);
match_test_t test17[] = {
{"some/policy", false},
{"some.policy", false},
{"policy", false},
{".#./policy", false},
{"hi/.#./policy", true},
{"/hi/.there./policy", false},
{NULL, false}
};
rc = match_test(QD_PARSE_TREE_MQTT, "+/.#./policy", test17);
match_test_t test18[] = {
{"test/?/*", true},
{"test/?/*/one/two/three", true},
{"test/something.or.other/*", true},
{"test/something.or.other/xxx", false},
{"test/something.or.other", false},
{"test", false},
{NULL, false}
};
rc = match_test(QD_PARSE_TREE_MQTT, "test/+/*/#", test18);
return rc;
}
// For debug (see parse_tree.c)
// void qd_parse_tree_dump(qd_parse_node_t *node, int depth);
#define PCOUNT 17
typedef struct {
const char *address;
int count;
const char *matches[PCOUNT];
} multi_match_t;
static char *multiple_matches(qd_parse_tree_type_t type,
const char *patterns[],
multi_match_t *tests)
{
const char *_patterns[PCOUNT] = {NULL};
void *_payloads[PCOUNT] = {NULL};
visit_handle_t vh = {0, _patterns, _payloads};
qd_parse_tree_t *node = qd_parse_tree_new(type);
// 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]);
qd_iterator_free(pattern);
qd_parse_tree_free(node);
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) {
qd_parse_tree_free(node);
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) {
qd_parse_tree_free(node);
return "All patterns not visited";
}
}
}
// 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) {
qd_iterator_free(find_me);
qd_parse_tree_free(node);
return "Unexpected match count";
}
for (int i = 0; i < tests[k].count; i++) {
if (strcmp(vh.patterns[i], tests[k].matches[i])) {
qd_iterator_free(find_me);
qd_parse_tree_free(node);
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) {
qd_iterator_free(find_me);
qd_parse_tree_free(node);
return "Did not expect to find a best match!";
}
} else if (vh.count == 0 || strcmp(vh.patterns[0], tests[k].matches[0])) {
qd_iterator_free(find_me);
qd_parse_tree_free(node);
return "Unexpected best pattern match";
}
qd_iterator_free(find_me);
}
qd_parse_tree_free(node);
return NULL;
}
// search a full parse tree for multiple and best matches
static char *test_multiple_matches(void *context)
{
const char *patterns_amqp_0_10[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.#",
};
// matches are listed in order of best->least best match
multi_match_t tests_amqp_0_10[] = {
{"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}}
};
char *rc = multiple_matches(QD_PARSE_TREE_AMQP_0_10,
patterns_amqp_0_10,
tests_amqp_0_10);
if (rc) return rc;
const char *patterns_mqtt[PCOUNT] =
{ "alpha",
"bravo",
"alpha/bravo",
"bravo/charlie",
"alpha/bravo/charlie/delta",
"alpha/+",
"alpha/#",
"alpha/+/#",
"bravo/charlie/echo",
"bravo/charlie/+",
"+/bravo",
"bravo/#",
"alpha/+/bravo",
"alpha/+/bravo/#",
"+/+/+/#",
"+/bravo/+",
"xray/+/#"
};
// matches are listed in order of best->least best match
multi_match_t tests_mqtt[] = {
{"alpha", 2, {"alpha", "alpha/#"}},
{"alpha/zulu", 3, { "alpha/+", "alpha/+/#", "alpha/#"}},
{"alpha/bravo", 5, {"alpha/bravo", "alpha/+", "alpha/+/#", "alpha/#", "+/bravo"}},
{"bravo", 2, {"bravo", "bravo/#"}},
{"xray/bravo", 2, {"xray/+/#", "+/bravo"}},
{"alpha/bravo/charlie", 4, {"alpha/+/#", "alpha/#", "+/bravo/+", "+/+/+/#"}},
{"xray/yankee/zulu/bravo", 2, {"xray/+/#", "+/+/+/#"}},
{"alpha/bravo/charlie/delta", 4, {"alpha/bravo/charlie/delta", "alpha/+/#", "alpha/#", "+/+/+/#"}},
{"alpha/charlie/charlie/bravo", 3, {"alpha/+/#", "alpha/#", "+/+/+/#"}},
{"xray/bravo/alpha", 3, {"xray/+/#", "+/bravo/+", "+/+/+/#"}},
{"bravo/charlie/ech", 3, {"bravo/charlie/+", "bravo/#", "+/+/+/#"}},
{"I/match.nothing", 0, {NULL}},
{NULL, 0, {NULL}}
};
rc = multiple_matches(QD_PARSE_TREE_MQTT, patterns_mqtt, tests_mqtt);
return rc;
}
static char *test_validation(void *context)
{
qd_iterator_t *iter = qd_iterator_string("sam.*.am.#", ITER_VIEW_ALL);
qd_iterator_t *iter_good = qd_iterator_string("sam/+/a.#.m/#", ITER_VIEW_ALL);
qd_iterator_t *iter_bad = qd_iterator_string("", ITER_VIEW_ALL); // no tokens
qd_iterator_t *iter_bad_slash = qd_iterator_string("/", ITER_VIEW_ALL); // just separators
qd_iterator_t *iter_bad_dot = qd_iterator_string(".", ITER_VIEW_ALL); // just separators
qd_iterator_t *iter_bad_mqtt = qd_iterator_string("sam/#/am/+", ITER_VIEW_ALL); // glob must be last
qd_iterator_t *iter_const = qd_iterator_string("sam/I/am", ITER_VIEW_ALL);
qd_parse_tree_t *mqtt_tree = qd_parse_tree_new(QD_PARSE_TREE_MQTT);
qd_parse_tree_t *addr_tree = qd_parse_tree_new(QD_PARSE_TREE_ADDRESS);
qd_parse_tree_t *amqp_tree = qd_parse_tree_new(QD_PARSE_TREE_AMQP_0_10);
char *error = 0;
if (!qd_parse_tree_validate_pattern(addr_tree, iter) ||
!qd_parse_tree_validate_pattern(amqp_tree, iter)) {
error = "expected valid pattern";
goto cleanup;
}
if (qd_parse_tree_validate_pattern(addr_tree, iter_bad) ||
qd_parse_tree_validate_pattern(mqtt_tree, iter_bad) ||
qd_parse_tree_validate_pattern(amqp_tree, iter_bad)) {
error = "expected null pattern to be invalid";
goto cleanup;
}
if (qd_parse_tree_validate_pattern(addr_tree, iter_bad_dot) ||
qd_parse_tree_validate_pattern(amqp_tree, iter_bad_dot)) {
error = "expected separator dot pattern to be invalid";
goto cleanup;
}
if (qd_parse_tree_validate_pattern(addr_tree, iter_bad_slash) ||
qd_parse_tree_validate_pattern(mqtt_tree, iter_bad_slash)) {
error = "expected separator slash pattern to be invalid";
goto cleanup;
}
if (!qd_parse_tree_validate_pattern(mqtt_tree, iter_good)) {
error = "expected to pass mqtt validation";
goto cleanup;
}
if (qd_parse_tree_validate_pattern(mqtt_tree, iter_bad_mqtt)) {
error = "expected to fail mqtt validation";
goto cleanup;
}
if (!qd_parse_tree_validate_pattern(mqtt_tree, iter_const)) {
error = "expected to pass mqtt constant string validation";
// fallthrough
}
cleanup:
qd_iterator_free(iter);
qd_iterator_free(iter_good);
qd_iterator_free(iter_bad);
qd_iterator_free(iter_bad_slash);
qd_iterator_free(iter_bad_dot);
qd_iterator_free(iter_bad_mqtt);
qd_iterator_free(iter_const);
qd_parse_tree_free(mqtt_tree);
qd_parse_tree_free(addr_tree);
qd_parse_tree_free(amqp_tree);
return error ? error : 0;
}
int parse_tree_tests(void)
{
int result = 0;
char *test_group = "parse_tree_tests";
TEST_CASE(test_add_remove, 0);
TEST_CASE(test_add_and_match_str, 0);
TEST_CASE(test_duplicate_error_str, 0);
TEST_CASE(test_normalization, 0);
TEST_CASE(test_matches, 0);
TEST_CASE(test_multiple_matches, 0);
TEST_CASE(test_validation, 0);
return result;
}