This is an automated email from the ASF dual-hosted git repository. kgiusti pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/qpid-dispatch.git
The following commit(s) were added to refs/heads/master by this push: new f3dda13 DISPATCH-1636: extract the peer router's version from the Open frame f3dda13 is described below commit f3dda1304d77f6f715f1aeb1d432cb8cccb743f9 Author: Kenneth Giusti <kgiu...@apache.org> AuthorDate: Mon Apr 27 09:26:11 2020 -0400 DISPATCH-1636: extract the peer router's version from the Open frame This closes #730 --- include/qpid/dispatch/router_core.h | 28 ++- src/router_core/connections.c | 7 +- src/router_core/router_core_private.h | 1 + src/router_node.c | 380 ++++++++++++++++++---------------- tests/CMakeLists.txt | 1 + tests/run_unit_tests.c | 2 + tests/system_tests_edge_router.py | 46 ++++ tests/version_test.c | 146 +++++++++++++ 8 files changed, 434 insertions(+), 177 deletions(-) diff --git a/include/qpid/dispatch/router_core.h b/include/qpid/dispatch/router_core.h index 7339714..ce45950 100644 --- a/include/qpid/dispatch/router_core.h +++ b/include/qpid/dispatch/router_core.h @@ -851,6 +851,30 @@ void qdr_query_free(qdr_query_t *query); typedef void (*qdr_manage_response_t) (void *context, const qd_amqp_error_t *status, bool more); void qdr_manage_handler(qdr_core_t *core, qdr_manage_response_t response_handler); +typedef struct { + uint16_t major; + uint16_t minor; + uint16_t patch; + uint16_t flags; +#define QDR_ROUTER_VERSION_SNAPSHOT 0x0100 +#define QDR_ROUTER_VERSION_RC 0x0200 // lower byte == RC # +#define QDR_ROUTER_VERSION_RC_MASK 0x00FF +} qdr_router_version_t; + +// version >= (Major, Minor, Patch) +#define QDR_ROUTER_VERSION_AT_LEAST(V, MAJOR, MINOR, PATCH) \ + ((V).major > (MAJOR) || ((V).major == (MAJOR) \ + && ((V).minor > (MINOR) || ((V).minor == (MINOR) \ + && (V).patch >= (PATCH)) \ + ) \ + ) \ + ) + +// version < (Major, Minor, Patch) +#define QDR_ROUTER_VERSION_LESS_THAN(V, MAJOR, MINOR, PATCH) \ + (!QDR_ROUTER_VERSION_AT_LEAST(V, MAJOR, MINOR, PATCH)) + + qdr_connection_info_t *qdr_connection_info(bool is_encrypted, bool is_authenticated, bool opened, @@ -863,7 +887,9 @@ qdr_connection_info_t *qdr_connection_info(bool is_encrypted, const char *container, pn_data_t *connection_properties, int ssl_ssf, - bool ssl); + bool ssl, + // set if remote is a qdrouter + const qdr_router_version_t *version); typedef struct { diff --git a/src/router_core/connections.c b/src/router_core/connections.c index e2b0eb1..4c0e73b 100644 --- a/src/router_core/connections.c +++ b/src/router_core/connections.c @@ -161,7 +161,8 @@ qdr_connection_info_t *qdr_connection_info(bool is_encrypted, const char *container, pn_data_t *connection_properties, int ssl_ssf, - bool ssl) + bool ssl, + const qdr_router_version_t *version) { qdr_connection_info_t *connection_info = new_qdr_connection_info_t(); ZERO(connection_info); @@ -190,6 +191,10 @@ qdr_connection_info_t *qdr_connection_info(bool is_encrypted, connection_info->ssl_ssf = ssl_ssf; connection_info->ssl = ssl; + if (version) { // only if peer is a router + connection_info->version = *version; + } + return connection_info; } diff --git a/src/router_core/router_core_private.h b/src/router_core/router_core_private.h index 5f358d0..e3757c4 100644 --- a/src/router_core/router_core_private.h +++ b/src/router_core/router_core_private.h @@ -636,6 +636,7 @@ struct qdr_connection_info_t { pn_data_t *connection_properties; bool ssl; int ssl_ssf; //ssl strength factor + qdr_router_version_t version; // if role is router or edge }; ALLOC_DECLARE(qdr_connection_info_t); diff --git a/src/router_node.c b/src/router_node.c index 664b5b1..b2dd1d5 100644 --- a/src/router_node.c +++ b/src/router_node.c @@ -44,6 +44,7 @@ static char *direct_prefix; static char *node_id; static void deferred_AMQP_rx_handler(void *context, bool discard); +static bool parse_failover_property_list(qd_router_t *router, qd_connection_t *conn, pn_data_t *props); //============================================================================== // Functions to handle the linkage between proton deliveries and qdr deliveries @@ -997,11 +998,12 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool { qdr_connection_role_t role = 0; int cost = 1; - int remote_cost = 1; int link_capacity = 1; const char *name = 0; bool multi_tenant = false; const char *vhost = 0; + qdr_router_version_t rversion = {0}; + bool rversion_found = false; uint64_t connection_id = qd_connection_connection_id(conn); pn_connection_t *pn_conn = qd_connection_pn(conn); pn_transport_t *tport = 0; @@ -1042,192 +1044,73 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool qd_router_connection_get_config(conn, &role, &cost, &name, &multi_tenant, &conn->strip_annotations_in, &conn->strip_annotations_out, &link_capacity); + // if connection properties are present parse out any important data + // pn_data_t *props = pn_conn ? pn_connection_remote_properties(pn_conn) : 0; - - if (role == QDR_ROLE_INTER_ROUTER || role == QDR_ROLE_EDGE_CONNECTION) { - // - // Check the remote properties for an inter-router cost value. - // - if (props) { - pn_data_rewind(props); - pn_data_next(props); - if (props && pn_data_type(props) == PN_MAP) { - pn_data_enter(props); - while (pn_data_next(props)) { - if (pn_data_type(props) == PN_SYMBOL) { - pn_bytes_t sym = pn_data_get_symbol(props); - if (sym.size == strlen(QD_CONNECTION_PROPERTY_COST_KEY) && - strcmp(sym.start, QD_CONNECTION_PROPERTY_COST_KEY) == 0) { - pn_data_next(props); - if (pn_data_type(props) == PN_INT) - remote_cost = pn_data_get_int(props); - break; - } - } - } - } - } - - // - // Use the larger of the local and remote costs for this connection - // - if (remote_cost > cost) - cost = remote_cost; - } - - bool found_failover = false; - if (props) { + const bool is_router = (role == QDR_ROLE_INTER_ROUTER || role == QDR_ROLE_EDGE_CONNECTION); pn_data_rewind(props); - pn_data_next(props); - if (props && pn_data_type(props) == PN_MAP) { + if (pn_data_next(props) && pn_data_type(props) == PN_MAP) { + const size_t num_items = pn_data_get_map(props); + int props_found = 0; // once all props found exit loop pn_data_enter(props); - - // - // We are attempting to find a connection property called failover-server-list which is a list of failover host names and ports.. - // failover-server-list looks something like this - // :"failover-server-list"=[{:"network-host"="some-host", :port="35000"}, {:"network-host"="localhost", :port="25000"}] - // There are three cases here - - // 1. The failover-server-list is present but the content of the list is empty in which case we scrub the failover list except we keep the original connector information and current connection information. - // 2. If the failover list contains one or more maps that contain failover connection information, that information will be appended to the list which already contains the original connection information - // and the current connection information. Any other failover information left over from the previous connection is deleted - // 3. If the failover-server-list is not present at all in the connection properties, the failover list we maintain in untoched. - // - while (pn_data_next(props)) { - if (pn_data_type(props) == PN_SYMBOL) { - pn_bytes_t sym = pn_data_get_symbol(props); - if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_LIST_KEY) && - strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_LIST_KEY) == 0) { - found_failover = true; + for (int i = 0; i < num_items / 2 && props_found < 3; ++i) { + if (!pn_data_next(props)) break; + if (pn_data_type(props) != PN_SYMBOL) break; // invalid properties map + pn_bytes_t key = pn_data_get_symbol(props); + + if (key.size == strlen(QD_CONNECTION_PROPERTY_COST_KEY) && + strncmp(key.start, QD_CONNECTION_PROPERTY_COST_KEY, key.size) == 0) { + props_found += 1; + if (!pn_data_next(props)) break; + if (is_router) { + if (pn_data_type(props) == PN_INT) { + const int remote_cost = (int) pn_data_get_int(props); + if (remote_cost > cost) + cost = remote_cost; + } } - } - else if (pn_data_type(props) == PN_LIST && found_failover) { - size_t list_num_items = pn_data_get_list(props); - - if (list_num_items > 0) { - - save_original_and_current_conn_info(conn); - - pn_data_enter(props); // enter list - - for (int i=0; i < list_num_items; i++) { - pn_data_next(props);// this is the first element of the list, a map. - if (props && pn_data_type(props) == PN_MAP) { - - size_t map_num_items = pn_data_get_map(props); - pn_data_enter(props); - - qd_failover_item_t *item = NEW(qd_failover_item_t); - ZERO(item); - - // We have found a map with the connection information. Step thru the map contents and create qd_failover_item_t - - for (int j=0; j < map_num_items/2; j++) { - - pn_data_next(props); - if (pn_data_type(props) == PN_SYMBOL) { - pn_bytes_t sym = pn_data_get_symbol(props); - if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_NETHOST_KEY) && - strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_NETHOST_KEY) == 0) { - pn_data_next(props); - if (pn_data_type(props) == PN_STRING) { - item->host = strdup(pn_data_get_string(props).start); - } - } - else if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_PORT_KEY) && - strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_PORT_KEY) == 0) { - pn_data_next(props); - item->port = qdpn_data_as_string(props); - - } - else if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_SCHEME_KEY) && - strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_SCHEME_KEY) == 0) { - pn_data_next(props); - if (pn_data_type(props) == PN_STRING) { - item->scheme = strdup(pn_data_get_string(props).start); - } - - } - else if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_HOSTNAME_KEY) && - strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_HOSTNAME_KEY) == 0) { - pn_data_next(props); - if (pn_data_type(props) == PN_STRING) { - item->hostname = strdup(pn_data_get_string(props).start); - } - } - } - } - - int host_length = strlen(item->host); - // - // We will not even bother inserting the item if there is no host available. - // - if (host_length != 0) { - if (item->scheme == 0) - item->scheme = strdup("amqp"); - if (item->port == 0) - item->port = strdup("5672"); - - int hplen = strlen(item->host) + strlen(item->port) + 2; - item->host_port = malloc(hplen); - snprintf(item->host_port, hplen, "%s:%s", item->host, item->port); - - // - // Iterate through failover list items and sets insert_tail to true - // when list has just original connector's host and port or when new - // reported host and port is not yet part of the current list. - // - bool insert_tail = false; - if ( DEQ_SIZE(conn->connector->conn_info_list) == 1 ) { - insert_tail = true; - } else { - qd_failover_item_t *conn_item = DEQ_HEAD(conn->connector->conn_info_list); - insert_tail = true; - while ( conn_item ) { - if ( !strcmp(conn_item->host_port, item->host_port) ) { - insert_tail = false; - break; - } - conn_item = DEQ_NEXT(conn_item); - } - } - - // Only inserts if not yet part of failover list - if ( insert_tail ) { - DEQ_INSERT_TAIL(conn->connector->conn_info_list, item); - qd_log(router->log_source, QD_LOG_DEBUG, "Added %s as backup host", item->host_port); - } - else { - free(item->scheme); - free(item->host); - free(item->port); - free(item->hostname); - free(item->host_port); - free(item); - } - - } - else { - free(item->scheme); - free(item->host); - free(item->port); - free(item->hostname); - free(item->host_port); - free(item); - } + + } else if (key.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_LIST_KEY) && + strncmp(key.start, QD_CONNECTION_PROPERTY_FAILOVER_LIST_KEY, key.size) == 0) { + props_found += 1; + if (!pn_data_next(props)) break; + parse_failover_property_list(router, conn, props); + + } else if (key.size == strlen(QD_CONNECTION_PROPERTY_VERSION_KEY) + && strncmp(key.start, QD_CONNECTION_PROPERTY_VERSION_KEY, key.size) == 0) { + props_found += 1; + if (!pn_data_next(props)) break; + if (is_router) { + pn_bytes_t vdata = pn_data_get_string(props); + if (vdata.size < 64) { // > strlen("u16.u16.u16-SNAPSHOT") + char vstr[64]; + memcpy(vstr, vdata.start, vdata.size); + vstr[vdata.size] = 0; + int rc = sscanf(vstr, "%"SCNu16".%"SCNu16".%"SCNu16, + &rversion.major, + &rversion.minor, + &rversion.patch); + if (strstr(vstr, "SNAPSHOT")) { + rversion.flags = QDR_ROUTER_VERSION_SNAPSHOT; + } + rversion_found = rc == 3; + if (rversion_found) { + qd_log(router->log_source, QD_LOG_DEBUG, "[C%"PRIu64"] Peer router version: %u.%u.%u%s", + connection_id, rversion.major, rversion.minor, rversion.patch, (rversion.flags) ? "-SNAPSHOT" : ""); } - pn_data_exit(props); } - } // list_num_items > 0 - else { - save_original_and_current_conn_info(conn); - } + + } else { + // skip this key + if (!pn_data_next(props)) break; } } } } + if (multi_tenant) vhost = pn_connection_remote_hostname(pn_conn); @@ -1262,7 +1145,8 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool container, props, ssl_ssf, - is_ssl); + is_ssl, + (rversion_found) ? &rversion : 0); qdr_connection_opened(router->router_core, inbound, role, cost, connection_id, name, pn_connection_remote_container(pn_conn), @@ -1296,6 +1180,152 @@ static void AMQP_opened_handler(qd_router_t *router, qd_connection_t *conn, bool authenticated ? mech : "no", (char*) user, container, props_str); } + +// We are attempting to find a connection property called failover-server-list which is a list of failover host names and ports.. +// failover-server-list looks something like this +// :"failover-server-list"=[{:"network-host"="some-host", :port="35000"}, {:"network-host"="localhost", :port="25000"}] +// There are three cases here - +// 1. The failover-server-list is present but the content of the list is empty in which case we scrub the failover list except we keep the original connector information and current connection information. +// 2. If the failover list contains one or more maps that contain failover connection information, that information will be appended to the list which already contains the original connection information +// and the current connection information. Any other failover information left over from the previous connection is deleted +// 3. If the failover-server-list is not present at all in the connection properties, the failover list we maintain in untoched. +// +// props should be pointing at the value that corresponds to the QD_CONNECTION_PROPERTY_FAILOVER_LIST_KEY +// returns true if failover list properly parsed. +// +static bool parse_failover_property_list(qd_router_t *router, qd_connection_t *conn, pn_data_t *props) +{ + bool found_failover = false; + + if (pn_data_type(props) != PN_LIST) + return false; + + size_t list_num_items = pn_data_get_list(props); + + if (list_num_items > 0) { + + save_original_and_current_conn_info(conn); + + pn_data_enter(props); // enter list + + for (int i=0; i < list_num_items; i++) { + pn_data_next(props);// this is the first element of the list, a map. + if (props && pn_data_type(props) == PN_MAP) { + + size_t map_num_items = pn_data_get_map(props); + pn_data_enter(props); + + qd_failover_item_t *item = NEW(qd_failover_item_t); + ZERO(item); + + // We have found a map with the connection information. Step thru the map contents and create qd_failover_item_t + + for (int j=0; j < map_num_items/2; j++) { + + pn_data_next(props); + if (pn_data_type(props) == PN_SYMBOL) { + pn_bytes_t sym = pn_data_get_symbol(props); + if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_NETHOST_KEY) && + strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_NETHOST_KEY) == 0) { + pn_data_next(props); + if (pn_data_type(props) == PN_STRING) { + item->host = strdup(pn_data_get_string(props).start); + } + } + else if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_PORT_KEY) && + strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_PORT_KEY) == 0) { + pn_data_next(props); + item->port = qdpn_data_as_string(props); + + } + else if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_SCHEME_KEY) && + strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_SCHEME_KEY) == 0) { + pn_data_next(props); + if (pn_data_type(props) == PN_STRING) { + item->scheme = strdup(pn_data_get_string(props).start); + } + + } + else if (sym.size == strlen(QD_CONNECTION_PROPERTY_FAILOVER_HOSTNAME_KEY) && + strcmp(sym.start, QD_CONNECTION_PROPERTY_FAILOVER_HOSTNAME_KEY) == 0) { + pn_data_next(props); + if (pn_data_type(props) == PN_STRING) { + item->hostname = strdup(pn_data_get_string(props).start); + } + } + } + } + + int host_length = strlen(item->host); + // + // We will not even bother inserting the item if there is no host available. + // + if (host_length != 0) { + if (item->scheme == 0) + item->scheme = strdup("amqp"); + if (item->port == 0) + item->port = strdup("5672"); + + int hplen = strlen(item->host) + strlen(item->port) + 2; + item->host_port = malloc(hplen); + snprintf(item->host_port, hplen, "%s:%s", item->host, item->port); + + // + // Iterate through failover list items and sets insert_tail to true + // when list has just original connector's host and port or when new + // reported host and port is not yet part of the current list. + // + bool insert_tail = false; + if ( DEQ_SIZE(conn->connector->conn_info_list) == 1 ) { + insert_tail = true; + } else { + qd_failover_item_t *conn_item = DEQ_HEAD(conn->connector->conn_info_list); + insert_tail = true; + while ( conn_item ) { + if ( !strcmp(conn_item->host_port, item->host_port) ) { + insert_tail = false; + break; + } + conn_item = DEQ_NEXT(conn_item); + } + } + + // Only inserts if not yet part of failover list + if ( insert_tail ) { + DEQ_INSERT_TAIL(conn->connector->conn_info_list, item); + qd_log(router->log_source, QD_LOG_DEBUG, "Added %s as backup host", item->host_port); + found_failover = true; + } + else { + free(item->scheme); + free(item->host); + free(item->port); + free(item->hostname); + free(item->host_port); + free(item); + } + + } + else { + free(item->scheme); + free(item->host); + free(item->port); + free(item->hostname); + free(item->host_port); + free(item); + } + } + pn_data_exit(props); + } + } // list_num_items > 0 + else { + save_original_and_current_conn_info(conn); + } + + return found_failover; +} + + static int AMQP_inbound_opened_handler(void *type_context, qd_connection_t *conn, void *context) { qd_router_t *router = (qd_router_t*) type_context; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 041ee26..9fdd1aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -36,6 +36,7 @@ set(unit_test_SOURCES parse_tree_tests.c proton_utils_tests.c alloc_test.c + version_test.c ) add_executable(unit_tests ${unit_test_SOURCES}) diff --git a/tests/run_unit_tests.c b/tests/run_unit_tests.c index a944b43..08c75e3 100644 --- a/tests/run_unit_tests.c +++ b/tests/run_unit_tests.c @@ -31,6 +31,7 @@ int policy_tests(void); int failoverlist_tests(void); int parse_tree_tests(void); int proton_utils_tests(void); +int version_tests(void); int main(int argc, char** argv) { @@ -63,6 +64,7 @@ int main(int argc, char** argv) result += parse_tree_tests(); result += proton_utils_tests(); result += core_timer_tests(); + result += version_tests(); qd_dispatch_free(qd); // dispatch_free last. diff --git a/tests/system_tests_edge_router.py b/tests/system_tests_edge_router.py index dd256a0..d546a2f 100644 --- a/tests/system_tests_edge_router.py +++ b/tests/system_tests_edge_router.py @@ -22,6 +22,7 @@ from __future__ import division from __future__ import absolute_import from __future__ import print_function +from os import path from time import sleep from threading import Event from threading import Timer @@ -1640,6 +1641,51 @@ class LinkRouteProxyTest(TestCase): tr.queue.get(timeout=TIMEOUT) tr.stop() + def test_000_check_peer_version_info(self): + """ + Not a link proxy test - ensure router correctly parses peer version + numbers advertised in the incoming @open frame + """ + lines = None + with open(path.join(self.INT_A.outdir, "INT.A.log")) as inta_log: + lines = [l for l in inta_log.read().split("\n") + if "] Connection Opened: " in l + or "] Peer router version: " in l] + + self.assertTrue(lines is not None) + + for peer in ['INT.B', 'EA1']: + + conn_id = None + open_version = None + parsed_version = None + + # extract the version string from the incoming @open + for l in lines: + ls = l.split() + if "container_id=%s" % peer in ls: + conn_id = ls[5] + for f in ls: + index = f.find(':version=') + if index >= 0: + open_version = f[index + len(':version='):].strip("\",") + break; + break; + + self.assertTrue(conn_id is not None) + self.assertTrue(open_version is not None) + + # find the debug log message where the router logs the parsed peers + # version on the given connection + for l in lines: + ls = l.split() + if ls[5] == conn_id and "version:" in ls: + parsed_version = ls[9] + break; + + self.assertTrue(parsed_version is not None) + self.assertEqual(open_version, parsed_version) + def test_01_immedate_detach_reattach(self): if self.skip [ 'test_01' ] : self.skipTest ( "Test skipped during development." ) diff --git a/tests/version_test.c b/tests/version_test.c new file mode 100644 index 0000000..494c411 --- /dev/null +++ b/tests/version_test.c @@ -0,0 +1,146 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <stdint.h> +#include "test_case.h" +#include <qpid/dispatch/router_core.h> + +typedef struct { + qdr_router_version_t version; + uint16_t major; + uint16_t minor; + uint16_t patch; + bool expected; +} test_data_t; + +static const test_data_t data[] = { + { + .version = {.major = 10, + .minor = 11, + .patch = 12}, + .major = 10, + .minor = 11, + .patch = 12, + .expected = true, + }, + { + .version = {.major = 10, + .minor = 11, + .patch = 12}, + .major = 10, + .minor = 11, + .patch = 13, + .expected = false, + }, + { + .version = {.major = 10, + .minor = 11, + .patch = 12}, + .major = 10, + .minor = 12, + .patch = 0, + .expected = false, + }, + { + .version = {.major = 10, + .minor = 11, + .patch = 12}, + .major = 11, + .minor = 0, + .patch = 0, + .expected = false, + }, + { + .version = {.major = 10, + .minor = 11, + .patch = 12}, + .major = 10, + .minor = 11, + .patch = 11, + .expected = true, + }, + { + .version = {.major = 10, + .minor = 11, + .patch = 12}, + .major = 10, + .minor = 10, + .patch = 13, + .expected = true, + }, + { + .version = {.major = 10, + .minor = 11, + .patch = 12}, + .major = 9, + .minor = 12, + .patch = 13, + .expected = true, + }, + { + .version = {.major = 10, + .minor = 11, + .patch = 12}, + .major = 0, + .minor = 0, + .patch = 0, + .expected = true, + }, +}; +const int data_count = (sizeof(data)/sizeof(data[0])); + +static char buffer[100]; + +static char *test_version_compare(void *context) +{ + + const test_data_t *p = data; + for (int i = 0; i < data_count; ++i) { + + if (QDR_ROUTER_VERSION_AT_LEAST(p->version, p->major, p->minor, p->patch) != p->expected) { + snprintf(buffer, sizeof(buffer), "At least failed: %u.%u.%u / %u.%u.%u e=%s\n", + p->version.major, p->version.minor, p->version.patch, + p->major, p->minor, p->patch, p->expected ? "true" : "false"); + return buffer; + } + if (QDR_ROUTER_VERSION_LESS_THAN(p->version, p->major, p->minor, p->patch) == p->expected) { + snprintf(buffer, sizeof(buffer), "Less than failed: %u.%u.%u / %u.%u.%u e=%s\n", + p->version.major, p->version.minor, p->version.patch, + p->major, p->minor, p->patch, p->expected ? "true" : "false"); + return buffer; + } + ++p; + } + + return 0; +} + + +int version_tests() +{ + int result = 0; + char *test_group = "version_tests"; + + TEST_CASE(test_version_compare, 0); + + return result; +} + --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org