Bridge remapping refactoring and tests.

This commit is contained in:
Roger A. Light 2019-10-03 16:46:15 +01:00
parent 8463c33720
commit fad184c9c2
9 changed files with 380 additions and 165 deletions

1
.gitignore vendored
View File

@ -68,6 +68,7 @@ test/ssl/signingCA/
test/lib/c/*.test
test/lib/cpp/*.test
test/unit/bridge_topic_test
test/unit/coverage.info
test/unit/mosq_test
test/unit/persist_read_test

View File

@ -11,6 +11,7 @@ endif
OBJS= mosquitto.o \
alias_mosq.o \
bridge.o \
bridge_topic.o \
conf.o \
conf_includedir.o \
context.o \
@ -83,6 +84,9 @@ alias_mosq.o : ../lib/alias_mosq.c ../lib/alias_mosq.h
bridge.o : bridge.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@
bridge_topic.o : bridge_topic.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@
conf.o : conf.c mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@

View File

@ -760,63 +760,4 @@ void bridge_check(struct mosquitto_db *db, struct pollfd *pollfds, int *pollfd_i
}
}
int bridge__remap_topic(struct mosquitto *context, char **topic)
{
struct mosquitto__bridge_topic *cur_topic;
char *topic_temp;
int i;
int len;
int rc;
bool match;
if(context->bridge && context->bridge->topics && context->bridge->topic_remapping){
for(i=0; i<context->bridge->topic_count; i++){
cur_topic = &context->bridge->topics[i];
if((cur_topic->direction == bd_both || cur_topic->direction == bd_in)
&& (cur_topic->remote_prefix || cur_topic->local_prefix)){
/* Topic mapping required on this topic if the message matches */
rc = mosquitto_topic_matches_sub(cur_topic->remote_topic, *topic, &match);
if(rc){
mosquitto__free(*topic);
return rc;
}
if(match){
if(cur_topic->remote_prefix){
/* This prefix needs removing. */
if(!strncmp(cur_topic->remote_prefix, *topic, strlen(cur_topic->remote_prefix))){
topic_temp = mosquitto__strdup((*topic)+strlen(cur_topic->remote_prefix));
if(!topic_temp){
mosquitto__free(*topic);
return MOSQ_ERR_NOMEM;
}
mosquitto__free(*topic);
*topic = topic_temp;
}
}
if(cur_topic->local_prefix){
/* This prefix needs adding. */
len = strlen(*topic) + strlen(cur_topic->local_prefix)+1;
topic_temp = mosquitto__malloc(len+1);
if(!topic_temp){
mosquitto__free(*topic);
return MOSQ_ERR_NOMEM;
}
snprintf(topic_temp, len, "%s%s", cur_topic->local_prefix, *topic);
topic_temp[len] = '\0';
mosquitto__free(*topic);
*topic = topic_temp;
}
break;
}
}
}
}
return MOSQ_ERR_SUCCESS;
}
#endif

214
src/bridge_topic.c Normal file
View File

@ -0,0 +1,214 @@
/*
Copyright (c) 2009-2019 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include "mosquitto.h"
#include "mosquitto_broker_internal.h"
#include "memory_mosq.h"
#ifdef WITH_BRIDGE
/* topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix] */
int bridge__add_topic(struct mosquitto__bridge *bridge, const char *topic, enum mosquitto__bridge_direction direction, int qos, const char *local_prefix, const char *remote_prefix)
{
struct mosquitto__bridge_topic *topics;
struct mosquitto__bridge_topic *cur_topic;
int len;
if(bridge == NULL) return MOSQ_ERR_INVAL;
if(direction != bd_out && direction != bd_in && direction != bd_both){
return MOSQ_ERR_INVAL;
}
if(qos < 0 || qos > 2){
return MOSQ_ERR_INVAL;
}
if(local_prefix && mosquitto_pub_topic_check(local_prefix)){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic local prefix '%s'.", local_prefix);
return MOSQ_ERR_INVAL;
}
if(remote_prefix && mosquitto_pub_topic_check(remote_prefix)){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic remote prefix '%s'.", remote_prefix);
return MOSQ_ERR_INVAL;
}
if((topic == NULL || !strcmp(topic, "\"\"")) &&
(local_prefix == NULL || remote_prefix == NULL)){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge remapping.");
return MOSQ_ERR_INVAL;
}
bridge->topic_count++;
topics = mosquitto__realloc(bridge->topics,
sizeof(struct mosquitto__bridge_topic)*bridge->topic_count);
if(topics == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
bridge->topics = topics;
cur_topic = &bridge->topics[bridge->topic_count-1];
cur_topic->direction = direction;
cur_topic->qos = qos;
cur_topic->local_prefix = NULL;
cur_topic->remote_prefix = NULL;
if(topic == NULL || !strcmp(topic, "\"\"")){
cur_topic->topic = NULL;
}else{
cur_topic->topic = mosquitto__strdup(topic);
if(cur_topic->topic == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
if(local_prefix || remote_prefix){
bridge->topic_remapping = true;
if(local_prefix){
cur_topic->local_prefix = mosquitto__strdup(local_prefix);
if(cur_topic->local_prefix == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
if(remote_prefix){
cur_topic->remote_prefix = mosquitto__strdup(remote_prefix);
if(cur_topic->remote_prefix == NULL){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
}
if(cur_topic->local_prefix){
if(cur_topic->topic){
len = strlen(cur_topic->topic) + strlen(cur_topic->local_prefix)+1;
cur_topic->local_topic = mosquitto__malloc(len+1);
if(!cur_topic->local_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
snprintf(cur_topic->local_topic, len+1, "%s%s", cur_topic->local_prefix, cur_topic->topic);
cur_topic->local_topic[len] = '\0';
}else{
cur_topic->local_topic = mosquitto__strdup(cur_topic->local_prefix);
if(!cur_topic->local_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
}else{
cur_topic->local_topic = mosquitto__strdup(cur_topic->topic);
if(!cur_topic->local_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
if(cur_topic->remote_prefix){
if(cur_topic->topic){
len = strlen(cur_topic->topic) + strlen(cur_topic->remote_prefix)+1;
cur_topic->remote_topic = mosquitto__malloc(len+1);
if(!cur_topic->remote_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
snprintf(cur_topic->remote_topic, len, "%s%s", cur_topic->remote_prefix, cur_topic->topic);
cur_topic->remote_topic[len] = '\0';
}else{
cur_topic->remote_topic = mosquitto__strdup(cur_topic->remote_prefix);
if(!cur_topic->remote_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
}else{
cur_topic->remote_topic = mosquitto__strdup(cur_topic->topic);
if(!cur_topic->remote_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
return MOSQ_ERR_SUCCESS;
}
int bridge__remap_topic_in(struct mosquitto *context, char **topic)
{
struct mosquitto__bridge_topic *cur_topic;
char *topic_temp;
int i;
int len;
int rc;
bool match;
if(context->bridge && context->bridge->topics && context->bridge->topic_remapping){
for(i=0; i<context->bridge->topic_count; i++){
cur_topic = &context->bridge->topics[i];
if((cur_topic->direction == bd_both || cur_topic->direction == bd_in)
&& (cur_topic->remote_prefix || cur_topic->local_prefix)){
/* Topic mapping required on this topic if the message matches */
rc = mosquitto_topic_matches_sub(cur_topic->remote_topic, *topic, &match);
if(rc){
mosquitto__free(*topic);
return rc;
}
if(match){
if(cur_topic->remote_prefix){
/* This prefix needs removing. */
if(!strncmp(cur_topic->remote_prefix, *topic, strlen(cur_topic->remote_prefix))){
topic_temp = mosquitto__strdup((*topic)+strlen(cur_topic->remote_prefix));
if(!topic_temp){
mosquitto__free(*topic);
return MOSQ_ERR_NOMEM;
}
mosquitto__free(*topic);
*topic = topic_temp;
}
}
if(cur_topic->local_prefix){
/* This prefix needs adding. */
len = strlen(*topic) + strlen(cur_topic->local_prefix)+1;
topic_temp = mosquitto__malloc(len+1);
if(!topic_temp){
mosquitto__free(*topic);
return MOSQ_ERR_NOMEM;
}
snprintf(topic_temp, len, "%s%s", cur_topic->local_prefix, *topic);
topic_temp[len] = '\0';
mosquitto__free(*topic);
*topic = topic_temp;
}
break;
}
}
}
}
return MOSQ_ERR_SUCCESS;
}
#endif

View File

@ -785,8 +785,6 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct
#ifdef WITH_BRIDGE
char *tmp_char;
struct mosquitto__bridge *cur_bridge = NULL;
struct mosquitto__bridge_topic *cur_topic;
int len;
#endif
struct mosquitto__auth_plugin_config *cur_auth_plugin_config = NULL;
@ -1973,29 +1971,14 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration.");
return MOSQ_ERR_INVAL;
}
char *topic = NULL;
enum mosquitto__bridge_direction direction = bd_out;
int qos = 0;
char *local_prefix = NULL, *remote_prefix = NULL;
token = strtok_r(NULL, " ", &saveptr);
if(token){
cur_bridge->topic_count++;
cur_bridge->topics = mosquitto__realloc(cur_bridge->topics,
sizeof(struct mosquitto__bridge_topic)*cur_bridge->topic_count);
if(!cur_bridge->topics){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
cur_topic = &cur_bridge->topics[cur_bridge->topic_count-1];
if(!strcmp(token, "\"\"")){
cur_topic->topic = NULL;
}else{
cur_topic->topic = mosquitto__strdup(token);
if(!cur_topic->topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
cur_topic->direction = bd_out;
cur_topic->qos = 0;
cur_topic->local_prefix = NULL;
cur_topic->remote_prefix = NULL;
topic = token;
}else{
log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty topic value in configuration.");
return MOSQ_ERR_INVAL;
@ -2003,11 +1986,11 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct
token = strtok_r(NULL, " ", &saveptr);
if(token){
if(!strcasecmp(token, "out")){
cur_topic->direction = bd_out;
direction = bd_out;
}else if(!strcasecmp(token, "in")){
cur_topic->direction = bd_in;
direction = bd_in;
}else if(!strcasecmp(token, "both")){
cur_topic->direction = bd_both;
direction = bd_both;
}else{
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic direction '%s'.", token);
return MOSQ_ERR_INVAL;
@ -2017,106 +2000,37 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct
if (token[0] == '#'){
strtok_r(NULL, "", &saveptr);
}
cur_topic->qos = atoi(token);
if(cur_topic->qos < 0 || cur_topic->qos > 2){
qos = atoi(token);
if(qos < 0 || qos > 2){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge QoS level '%s'.", token);
return MOSQ_ERR_INVAL;
}
token = strtok_r(NULL, " ", &saveptr);
if(token){
cur_bridge->topic_remapping = true;
if(!strcmp(token, "\"\"") || token[0] == '#'){
cur_topic->local_prefix = NULL;
local_prefix = NULL;
if (token[0] == '#'){
strtok_r(NULL, "", &saveptr);
}
}else{
if(mosquitto_pub_topic_check(token) != MOSQ_ERR_SUCCESS){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic local prefix '%s'.", token);
return MOSQ_ERR_INVAL;
}
cur_topic->local_prefix = mosquitto__strdup(token);
if(!cur_topic->local_prefix){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
local_prefix = token;
}
token = strtok_r(NULL, " ", &saveptr);
if(token){
if(!strcmp(token, "\"\"") || token[0] == '#'){
cur_topic->remote_prefix = NULL;
remote_prefix = NULL;
}else{
if(mosquitto_pub_topic_check(token) != MOSQ_ERR_SUCCESS){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic remote prefix '%s'.", token);
remote_prefix = mosquitto__strdup(token);
}
}
}
}
}
if(bridge__add_topic(cur_bridge, topic, direction, qos, local_prefix, remote_prefix)){
return MOSQ_ERR_INVAL;
}
cur_topic->remote_prefix = mosquitto__strdup(token);
if(!cur_topic->remote_prefix){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
}
}
}
}
if(cur_topic->topic == NULL &&
(cur_topic->local_prefix == NULL || cur_topic->remote_prefix == NULL)){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge remapping.");
return MOSQ_ERR_INVAL;
}
if(cur_topic->local_prefix){
if(cur_topic->topic){
len = strlen(cur_topic->topic) + strlen(cur_topic->local_prefix)+1;
cur_topic->local_topic = mosquitto__malloc(len+1);
if(!cur_topic->local_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
snprintf(cur_topic->local_topic, len+1, "%s%s", cur_topic->local_prefix, cur_topic->topic);
cur_topic->local_topic[len] = '\0';
}else{
cur_topic->local_topic = mosquitto__strdup(cur_topic->local_prefix);
if(!cur_topic->local_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
}else{
cur_topic->local_topic = mosquitto__strdup(cur_topic->topic);
if(!cur_topic->local_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
if(cur_topic->remote_prefix){
if(cur_topic->topic){
len = strlen(cur_topic->topic) + strlen(cur_topic->remote_prefix)+1;
cur_topic->remote_topic = mosquitto__malloc(len+1);
if(!cur_topic->remote_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
snprintf(cur_topic->remote_topic, len, "%s%s", cur_topic->remote_prefix, cur_topic->topic);
cur_topic->remote_topic[len] = '\0';
}else{
cur_topic->remote_topic = mosquitto__strdup(cur_topic->remote_prefix);
if(!cur_topic->remote_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
}else{
cur_topic->remote_topic = mosquitto__strdup(cur_topic->topic);
if(!cur_topic->remote_topic){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
#else
log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available.");
#endif

View File

@ -184,7 +184,7 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context)
}
#ifdef WITH_BRIDGE
rc = bridge__remap_topic(context, &topic);
rc = bridge__remap_topic_in(context, &topic);
if(rc) return rc;
#endif

View File

@ -685,7 +685,8 @@ void bridge_check(struct mosquitto_db *db);
void bridge_check(struct mosquitto_db *db, struct pollfd *pollfds, int *pollfd_index);
#endif
int bridge__register_local_connections(struct mosquitto_db *db);
int bridge__remap_topic(struct mosquitto *context, char **topic);
int bridge__add_topic(struct mosquitto__bridge *bridge, const char *topic, enum mosquitto__bridge_direction direction, int qos, const char *local_prefix, const char *remote_prefix);
int bridge__remap_topic_in(struct mosquitto *context, char **topic);
#endif
/* ============================================================

View File

@ -24,6 +24,15 @@ LIB_OBJS = memory_mosq.o \
util_topic.o \
utf8_mosq.o
BRIDGE_TOPIC_TEST_OBJS = \
bridge_topic_test.o \
stubs.o \
BRIDGE_TOPIC_OBJS = \
bridge_topic.o \
memory_mosq.o \
util_topic.o \
PERSIST_READ_TEST_OBJS = \
persist_read_test.o \
persist_read_stubs.o
@ -63,6 +72,9 @@ check : test
mosq_test : ${TEST_OBJS} ${LIB_OBJS}
$(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD)
bridge_topic_test : ${BRIDGE_TOPIC_TEST_OBJS} ${BRIDGE_TOPIC_OBJS}
$(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD)
persist_read_test : ${PERSIST_READ_TEST_OBJS} ${PERSIST_READ_OBJS}
$(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD)
@ -70,6 +82,9 @@ persist_write_test : ${PERSIST_WRITE_TEST_OBJS} ${PERSIST_WRITE_OBJS}
$(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD)
bridge_topic.o : ../../src/bridge_topic.c
$(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_BRIDGE -c -o $@ $^
database.o : ../../src/database.c
$(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^
@ -112,14 +127,15 @@ utf8_mosq.o : ../../lib/utf8_mosq.c
test-lib : mosq_test
./mosq_test
test-broker : persist_read_test persist_write_test
test-broker : bridge_topic_test persist_read_test persist_write_test
./bridge_topic_test
./persist_read_test
./persist_write_test
test : test-broker test-lib
clean :
-rm -rf mosq_test persist_read_test persist_write_test
-rm -rf mosq_test bridge_topic_test persist_read_test persist_write_test
-rm -rf *.o *.gcda *.gcno coverage.info out/
coverage :

View File

@ -0,0 +1,124 @@
#include "config.h"
#include <stdio.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#define WITH_BRIDGE
#define WITH_BROKER
#include "mosquitto_broker_internal.h"
#include "property_mosq.h"
#include "packet_mosq.h"
static void map_valid_helper(const char *topic, const char *local_prefix, const char *remote_prefix, const char *incoming, const char *expected)
{
struct mosquitto mosq;
struct mosquitto__bridge bridge;
char *map_topic;
int rc;
memset(&mosq, 0, sizeof(struct mosquitto));
memset(&bridge, 0, sizeof(struct mosquitto__bridge));
mosq.bridge = &bridge;
rc = bridge__add_topic(&bridge, topic, bd_in, 0, local_prefix, remote_prefix);
CU_ASSERT_EQUAL(rc, 0);
map_topic = strdup(incoming);
rc = bridge__remap_topic_in(&mosq, &map_topic);
CU_ASSERT_EQUAL(rc, 0);
CU_ASSERT_PTR_NOT_NULL(map_topic);
if(topic){
CU_ASSERT_STRING_EQUAL(map_topic, expected);
free(map_topic);
}
}
static void map_invalid_helper(const char *topic, const char *local_prefix, const char *remote_prefix)
{
struct mosquitto mosq;
struct mosquitto__bridge bridge;
int rc;
memset(&mosq, 0, sizeof(struct mosquitto));
memset(&bridge, 0, sizeof(struct mosquitto__bridge));
mosq.bridge = &bridge;
rc = bridge__add_topic(&bridge, topic, bd_in, 0, local_prefix, remote_prefix);
CU_ASSERT_NOT_EQUAL(rc, 0);
}
static void TEST_remap_valid(void)
{
/* Examples from man page */
map_valid_helper("pattern", "L/", "R/", "R/pattern", "L/pattern");
map_valid_helper("pattern", "L/", NULL, "pattern", "L/pattern");
map_valid_helper("pattern", NULL, "R/", "R/pattern", "pattern");
map_valid_helper("pattern", NULL, NULL, "pattern", "pattern");
map_valid_helper(NULL, "local", "remote", "local", "remote");
}
static void TEST_remap_invalid(void)
{
/* Examples from man page */
map_invalid_helper(NULL, "L/", NULL);
map_invalid_helper(NULL, NULL, "R/");
map_invalid_helper(NULL, NULL, NULL);
}
/* ========================================================================
* TEST SUITE SETUP
* ======================================================================== */
int init_bridge_tests(void)
{
CU_pSuite test_suite = NULL;
test_suite = CU_add_suite("Bridge remap", NULL, NULL);
if(!test_suite){
printf("Error adding CUnit Bridge remap test suite.\n");
return 1;
}
if(0
|| !CU_add_test(test_suite, "Remap valid", TEST_remap_valid)
|| !CU_add_test(test_suite, "Remap invalid", TEST_remap_invalid)
){
printf("Error adding Bridge remap CUnit tests.\n");
return 1;
}
return 0;
}
int main(int argc, char *argv[])
{
unsigned int fails;
if(CU_initialize_registry() != CUE_SUCCESS){
printf("Error initializing CUnit registry.\n");
return 1;
}
if(0
|| init_bridge_tests()
){
CU_cleanup_registry();
return 1;
}
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests();
fails = CU_get_number_of_failures();
CU_cleanup_registry();
return (int)fails;
}