test(unit): Extend unit tests 53/53/1
authorE. Scott Daniels <daniels@research.att.com>
Tue, 16 Apr 2019 20:47:54 +0000 (20:47 +0000)
committerE. Scott Daniels <daniels@research.att.com>
Tue, 16 Apr 2019 20:47:54 +0000 (20:47 +0000)
Change-Id: I28148ce6ef070ad85cb80fc21f090ff47e856d79
Signed-off-by: E. Scott Daniels <daniels@research.att.com>
18 files changed:
test/Makefile
test/README
test/hdr_static_test.c [new file with mode: 0644]
test/mbuf_api_static_test.c [new file with mode: 0644]
test/mbuf_api_test.c [new file with mode: 0644]
test/ring_static_test.c [new file with mode: 0644]
test/ring_test.c
test/rmr_nng_api_static_test.c [new file with mode: 0644]
test/rmr_nng_test.c [new file with mode: 0644]
test/rt_static_test.c [new file with mode: 0644]
test/sr_nng_static_test.c [new file with mode: 0644]
test/symtab_static_test.c [new file with mode: 0644]
test/test_nng_em.c [new file with mode: 0644]
test/test_support.c
test/tools_static_test.c [new file with mode: 0644]
test/tools_test.c
test/unit_test.ksh
test/wormhole_static_test.c [new file with mode: 0644]

index 20d461e..f23f838 100644 (file)
@@ -3,18 +3,28 @@
 CC = gcc
 coverage_opts = -ftest-coverage -fprofile-arcs
 
 CC = gcc
 coverage_opts = -ftest-coverage -fprofile-arcs
 
-libs = ../build/librmr_nng.a -L ../build/lib -lnng -lpthread -lm
+#libs = ../build/librmr_nng.a -L ../build/lib -lnng -lpthread -lm
+libs =  -L ../build/lib -lnng -lpthread -lm
+
+#sa_tests = sa_tools_test.o
 
 %.o:: %.c
        $(CC) -g $< -c 
 
 %:: %.c
 
 %.o:: %.c
        $(CC) -g $< -c 
 
 %:: %.c
-       $(CC) $(coverage_opts) -fPIC -g $< -o $@  $(libs)
+       $(CC) -I ../src/common/src/ -I ../src/common/include -I ../src/nng/include $(coverage_opts) -fPIC -g $< -o $@  $(libs)
 
 # catch all 
 all:
        echo "run unit_test.ksh to make and run things here"
 
 
 # catch all 
 all:
        echo "run unit_test.ksh to make and run things here"
 
+
+#sa_tools_test.o: sa_tools_test.c
+#      $(CC) -I ../src/common/src/ -I ../src/common/include -I $(coverage_opts) ../src/nng/include  -fPIC -g $< -c 
+
+#.PHONY: sa_tests
+#sa_tests:     $(sa_tests)
+
 # remove intermediates
 clean:
        rm -f *.gcov *.gcda *.dcov *.gcno
 # remove intermediates
 clean:
        rm -f *.gcov *.gcda *.dcov *.gcno
index 85f96ed..5adc3f8 100644 (file)
@@ -1,21 +1,25 @@
 
 Unit test
 
 
 Unit test
 
-The means to unit testing the RMr library is contained in
-this directory.  It is somewhat difficult to accurately generate
-coverage information for parts of the library because the library
-is a fair amount of static functions (to keep them from being
-visible to the user programme).  
+This directory contains the unit test support for the RMr
+library.  The basic test is run with the follwing command:
 
 
-To run the tests:
-       ksh unit_test.sh [specific-test]
+       ksh unit_test.ksh
 
 
-If a specific test (e.g. ring_test.c) is not given on the command line,
-all *_test.c files are sussed out and an attempt to build and run them
-is made by the script. 
+To run a specific test (e.g. ring_test.c) run:
+       ksh unit_test.ksh ring_test.c
 
 
-Output is an interpretation of the resulting gcov output (in more useful
-and/or easier to read format).  For example:
+The script runs the unit test(s) given, and if they pass then
+runs an analysis on the .gcov files generated to generate
+coverage information.  By default, pass/fail of the test is
+based only on the success or failure of the unit tests which
+are testing functionality.  The unit test script can report
+an overall failure if coverage is below the indicated threshold
+when given the strict option (-s).
+
+The analysis of .gcov files generates output shown below which
+is thought to be more straight forward than the typical stuff
+gcov produces:
 
 unit_test.ksh ring_test.c
 ring_test.c --------------------------------------
 
 unit_test.ksh ring_test.c
 ring_test.c --------------------------------------
@@ -26,18 +30,29 @@ ring_test.c --------------------------------------
 [PASS]  91% ../src/common/src/ring_static.c
 
 
 [PASS]  91% ../src/common/src/ring_static.c
 
 
-The output shows, for each function, the coverage (column 2) and an 
+The output shows, for each function, the coverage (column 2) and an
 interpretation (ok or low) wthin an overall pass or fail.
 
 
 interpretation (ok or low) wthin an overall pass or fail.
 
 
-File Names
-The unit test script will find all files named *_test.c and assume that
-they can be compiled and executed using the local Makefile. Files
-which are needed by these programmes (e.g. common functions) are expected
-to reside in this directory as test_*.c and test_*.h files and should 
-be directly included by the test programmes (not built and linked). This 
-allows the unit test script to isngore the functions, and files, when 
-generating coverage reports. 
+Because of the static nature of the RMr library, tests with the
+intent of providing coverage information, as opposed just to providing
+functional verification, are a bit trickier.  To that end, the test
+files in this directory are organised with three file name formats:
+
+       test_*.c  tools for testing, not tests
+
+       *_test.c  main test programmes which can be compiled in
+                         a stand-alone manner (e.g. gcc foo_test.c)
+
+       *_static_test.c  Test functions which are real tests and are
+                         included by one or more stand-alone driver.
+
+The unit_test script will search only for *_test.c and will ignore
+*_static_test.c files when building it's list for testing.
+
+
+Use the command 'unit_test.ksh -?' to see the usage information
+and complete set of options available.
 
 
 Discounting
 
 
 Discounting
@@ -51,13 +66,13 @@ option is given, an augmented coverage listing is saved in .dcov which
 shows the discounted lines with a string of equal signs (====) rather
 than the gcov hash string (###).
 
 shows the discounted lines with a string of equal signs (====) rather
 than the gcov hash string (###).
 
-The discount check is applied only if an entire module has a lower 
-than accepted coverage rate, and can be forced for all modules with 
+The discount check is applied only if an entire module has a lower
+than accepted coverage rate, and can be forced for all modules with
 the -f option.
 
 To illustrate, the following code checks the return from the system
 library strdup() call which is very unlikely to fail under test without
 the -f option.
 
 To illustrate, the following code checks the return from the system
 library strdup() call which is very unlikely to fail under test without
-going to extremes and substituting for the system lib.  Thus, the 
+going to extremes and substituting for the system lib.  Thus, the
 block which checks for a nil pointer has been discounted:
 
      -:  354:
 block which checks for a nil pointer has been discounted:
 
      -:  354:
@@ -70,13 +85,13 @@ block which checks for a nil pointer has been discounted:
 
 Target Coverage
 By default, a target coverage of 80% is used. For some modules this may
 
 Target Coverage
 By default, a target coverage of 80% is used. For some modules this may
-be impossible to achieve, so to prevent always failing these modules 
+be impossible to achieve, so to prevent always failing these modules
 may be listed in the .targets file with their expected minimum coverage.
 Module names need to be qualified (e.g. ../src/common/src/foo.c.
 
 
 -----------------------------------------------------------------------
 A note about ksh (A.K.A Korn shell, or kshell)
 may be listed in the .targets file with their expected minimum coverage.
 Module names need to be qualified (e.g. ../src/common/src/foo.c.
 
 
 -----------------------------------------------------------------------
 A note about ksh (A.K.A Korn shell, or kshell)
-Ksh is preferred for more complex scripts such as the unit test 
+Ksh is preferred for more complex scripts such as the unit test
 script as it does not have some of the limitations that bash
 script as it does not have some of the limitations that bash
-(and other knock-offs) have. 
+(and other knock-offs) have.
diff --git a/test/hdr_static_test.c b/test/hdr_static_test.c
new file mode 100644 (file)
index 0000000..9d7a38c
--- /dev/null
@@ -0,0 +1,112 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mmemonic:       hdr_static_test.c
+       Abstract:       This tests specific properties of the message header
+
+       Author:         E. Scott Daniels
+       Date:           12 April 2019
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <netdb.h>
+
+#include <nng/nng.h>
+#include <nng/protocol/pubsub0/pub.h>
+#include <nng/protocol/pubsub0/sub.h>
+#include <nng/protocol/pipeline0/push.h>
+#include <nng/protocol/pipeline0/pull.h>
+
+#include "../src/common/include/rmr.h"
+#include "../src/common/include/rmr_agnostic.h"
+#include "../src/nng/include/rmr_nng_private.h"
+
+#define EMULATE_NNG
+#include "test_nng_em.c"
+#include "../src/nng/src/sr_nng_static.c"
+
+#include "test_support.c"
+
+/*
+       Dummy for testing here
+*/
+extern void rmr_free_msg( rmr_mbuf_t* mbuf ) {
+}
+
+static int hdr_test( ) {
+       int errors = 0;
+       uta_ctx_t*      ctx;
+       rmr_mbuf_t*     msg;
+       uta_mhdr_t*     hdr;
+       int hlen;
+       int len;
+       int     payload_len = 2049;
+       int trace_len = 37;
+
+       ctx = (uta_ctx_t *) malloc( sizeof( *ctx ) );
+       ctx->trace_data_len = 0;
+       ctx->my_name = strdup( "my-dummy-host-name-and-port:xxxx" );
+
+       msg = alloc_zcmsg( ctx, NULL, payload_len, 0 );                         // header len here should just be len of our struct
+       hdr = (uta_mhdr_t *) msg->header;
+       hlen = RMR_HDR_LEN( hdr );
+
+       fprintf( stderr, "<INFO> struct len= %d  msg len= %d %d\n", (int) sizeof( uta_mhdr_t ), hlen, htonl( hlen ) );
+       errors += fail_not_equal( hlen, (int) sizeof( uta_mhdr_t ), "header len (a) not size of struct when no trace data is present" );
+
+       len = (int) sizeof( uta_mhdr_t ) + payload_len;                                 // expected size of transport buffer allocated
+       errors += fail_not_equal( len, msg->alloc_len, "alloc len (a)  not expected size" );
+
+
+       ctx->trace_data_len = trace_len;                // alloc messages with tracing buffer in place
+       msg = alloc_zcmsg( ctx, NULL, payload_len, 0 );                         // header len here should just be len of our struct
+       hdr = (uta_mhdr_t *) msg->header;
+       hlen = RMR_HDR_LEN( hdr );
+       fprintf( stderr, "<INFO> with trace data: struct+trace len= %d  msg len= %d %d\n", (int) sizeof( uta_mhdr_t )+trace_len, hlen, htonl( hlen ) );
+       errors += fail_not_equal( hlen, (int) sizeof( uta_mhdr_t ) + trace_len, "header len (a) was not header + trace data size (b)" );
+
+       len = RMR_TR_LEN( hdr );
+       errors += fail_not_equal( len, trace_len, "trace len in header (a) not expected value (b)" );
+
+       len = RMR_D1_LEN( hdr );
+       errors += fail_not_equal( len, 0, "d1 len in header (a) not expected value (b)" );
+
+       len = RMR_D2_LEN( hdr );
+       errors += fail_not_equal( len, 0, "d2 len in header (a) not expected value (b)" );
+
+
+       // -------------------------------------------------------------------------------------------
+
+       if( ! errors ) {
+               fprintf( stderr, "<INFO> all msg header tests pass\n" );
+       }
+       return !! errors;
+}
+
+int main() {
+       return hdr_test();
+}
diff --git a/test/mbuf_api_static_test.c b/test/mbuf_api_static_test.c
new file mode 100644 (file)
index 0000000..4c0cd5e
--- /dev/null
@@ -0,0 +1,188 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mmemonic:       mbuf_api_static_test.c
+       Abstract:       Test the message buffer  funcitons. These are meant to be included at compile
+                               time by the test driver.  
+
+       Author:         E. Scott Daniels
+       Date:           3 April 2019
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "../src/common/include/rmr.h"
+#include "../src/common/include/rmr_agnostic.h"
+
+
+int mbuf_api_test( ) {
+       unsigned char* c;
+       int i;
+       int errors = 0;
+       rmr_mbuf_t*     mbuf;
+       unsigned char src_buf[256];
+
+       mbuf = (rmr_mbuf_t *) malloc( sizeof( *mbuf ) );
+       if( mbuf == NULL ) {
+               fprintf( stderr, "[FAIL] tester cannot allocate memory: mbuf\n" );
+               exit( 1 );
+       }
+
+       mbuf->payload = (void *) malloc( sizeof( char ) * 1024 );               // add a dummy payload
+       mbuf->tp_buf = mbuf->payload;
+       mbuf->header = mbuf->payload;
+       mbuf->alloc_len = 1024;
+
+       memset( src_buf, 0, sizeof( src_buf ) );
+       rmr_bytes2payload( mbuf, NULL, strlen( src_buf) );                              // errno should be set on return
+       errors += fail_if( errno == 0, "buf copy to payload with nil src returned good errno" );
+
+       rmr_bytes2payload( NULL, src_buf, strlen( src_buf) );                   // errno should be set on return
+       errors += fail_if( errno == 0, "buf copy to payload with nil mbuf returned good errno" );
+
+       mbuf->state = 1;                                                                                        // force it to something to test that it was set
+       rmr_bytes2payload( mbuf, src_buf, strlen( src_buf) );
+       errors += fail_if( mbuf->state != RMR_OK, "buf copy to payload returned bad state in mbuf" );
+
+       rmr_bytes2payload( mbuf, src_buf, 8192 );                                               // bust the limit
+       errors += fail_if( mbuf->state == RMR_OK, "huge buf copy to payload returned good state in mbuf" );
+       errors += fail_if( errno == 0, "huge buf copy to payload returned good state in errno" );
+       
+
+       snprintf( src_buf, sizeof( src_buf ), "This is some text in the buffer" );
+       rmr_str2payload( mbuf, src_buf );                                                       // this uses bytes2payload, so only one invocation needed
+
+       errno = 0;
+       i = rmr_bytes2meid( NULL, src_buf, RMR_MAX_MEID );
+       errors += fail_if( errno == 0, "(errno) attempt to copy bytes to meid with nil message" );
+       errors += fail_if( i > 0, "(rv) attempt to copy bytes to meid with nil message" );
+
+       errno = 0;
+       i = rmr_bytes2meid( mbuf, NULL, RMR_MAX_MEID );
+       errors += fail_if( errno == 0, "(errno) attempt to copy bytes to meid with nil source buffer" );
+       errors += fail_if( i > 0, "(rv) attempt to copy bytes to meid with nil message" );
+
+       errno = 0;
+       i = rmr_bytes2meid( mbuf, src_buf, RMR_MAX_MEID + 1 );
+       errors += fail_if( errno == 0, "(errno) attempt to copy bytes to meid with large source buffer" );
+       errors += fail_if( i != RMR_MAX_MEID, "(rv) attempt to copy bytes to meid with large source buffer" );
+
+       errno = 0;
+       i = rmr_bytes2meid( mbuf, src_buf, RMR_MAX_MEID  );
+       errors += fail_if( errno != 0, "copy bytes to meid; expected errno to be ok" );
+       errors += fail_if( i != RMR_MAX_MEID, "copy bytes to meid; expected return value to be max meid len" );
+
+
+
+       errno = 0;
+       snprintf( src_buf, sizeof( src_buf ), "meid-fits" );
+       i = rmr_str2meid( NULL, src_buf );
+       errors += fail_if( errno == 0, "(errno) attempt to copy string to meid with nil message" );
+       errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to meid with nil message" );
+
+       errno = 0;
+       i = rmr_str2meid( mbuf, NULL );
+       errors += fail_if( errno == 0, "(errno) attempt to copy string to meid with nil source buffer" );
+       errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to meid with nil message" );
+
+       errno = 0;
+       i = rmr_str2meid( mbuf, src_buf );
+       errors += fail_if( errno != 0, "copy string to meid; expected errno to be ok" );
+       errors += fail_if( i != RMR_OK, "copy string to meid; expected return value to be RMR_OK" );
+
+       errno = 0;
+       snprintf( src_buf, sizeof( src_buf ), "meid-should-be-too-large-to-fit-in-the-meid" );
+       i = rmr_str2meid( mbuf, src_buf );
+       errors += fail_if( errno == 0, "(errno) attempt to copy string to meid with large source buffer" );
+       errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to meid with large source buffer" );
+
+
+       errno = 0;
+       i = rmr_bytes2xact( NULL, src_buf, RMR_MAX_XID );
+       errors += fail_if( errno == 0, "(errno) attempt to copy bytes to xact with nil message" );
+       errors += fail_if( i > 0, "(rv) attempt to copy bytes to xact with nil message" );
+
+       errno = 0;
+       i = rmr_bytes2xact( mbuf, NULL, RMR_MAX_XID );
+       errors += fail_if( errno == 0, "(errno) attempt to copy bytes to xact with nil source buffer" );
+       errors += fail_if( i > 0, "(rv) attempt to copy bytes to xact with nil message" );
+
+       errno = 0;
+       i = rmr_bytes2xact( mbuf, src_buf, RMR_MAX_XID + 1 );
+       errors += fail_if( errno == 0, "(errno) attempt to copy bytes to xact with large source buffer" );
+       errors += fail_if( i != RMR_MAX_XID, "(rv) attempt to copy bytes to xact with large source buffer" );
+
+       errno = 0;
+       i = rmr_bytes2xact( mbuf, src_buf, RMR_MAX_XID  );
+       errors += fail_if( errno != 0, "copy bytes to xact; expected errno to be ok" );
+       errors += fail_if( i != RMR_MAX_XID, "copy bytes to xact; expected return value to be max xact len" );
+
+
+
+       errno = 0;
+       snprintf( src_buf, sizeof( src_buf ), "xact-fits" );
+       i = rmr_str2xact( NULL, src_buf );
+       errors += fail_if( errno == 0, "(errno) attempt to copy string to xact with nil message" );
+       errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to xact with nil message" );
+
+       errno = 0;
+       i = rmr_str2xact( mbuf, NULL );
+       errors += fail_if( errno == 0, "(errno) attempt to copy string to xact with nil source buffer" );
+       errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to xact with nil message" );
+
+       errno = 0;
+       i = rmr_str2xact( mbuf, src_buf );
+       errors += fail_if( errno != 0, "copy string to xact; expected errno to be ok" );
+       errors += fail_if( i != RMR_OK, "copy string to xact; expected return value to be RMR_OK" );
+
+       errno = 0;
+       snprintf( src_buf, sizeof( src_buf ), "xact-should-be-too-large-to-fit-in-the-xact" );
+       i = rmr_str2xact( mbuf, src_buf );
+       errors += fail_if( errno == 0, "(errno) attempt to copy string to xact with large source buffer" );
+       errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to xact with large source buffer" );
+
+       
+       snprintf( src_buf, sizeof( src_buf ), "test-meid" );
+       rmr_str2meid( mbuf, src_buf );
+
+       errno = 0;
+       c = rmr_get_meid( NULL, NULL );
+       errors += fail_if( c != NULL, "get meid with nil message buffer" );
+       errors += fail_if( errno == 0, "(errno bad) get meid with nil msg buffer" ); 
+
+       
+       c = rmr_get_meid( mbuf, NULL );                 // should allocate and return c
+       errors += fail_if( c == NULL, "get meid with nil dest pointer (did not allocate a buffer)" );
+       errors += fail_if( strcmp( c, "test-meid" ) != 0, "did not get expected meid from mbuffer" );
+
+       c = rmr_get_meid( mbuf, c );
+       errors += fail_if( c == NULL, "get meid with a dest pointer returned no pointer" );
+       errors += fail_if( strcmp( c, "test-meid" ) != 0, "did not get expected meid from mbuffer" );
+       
+
+       return errors > 0;                      // overall exit code bad if errors
+}
diff --git a/test/mbuf_api_test.c b/test/mbuf_api_test.c
new file mode 100644 (file)
index 0000000..858efdc
--- /dev/null
@@ -0,0 +1,58 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+
+/*
+       Mnemonic:       mbuf_api_test.c
+       Abstract:       Unit tests for the mbuf common API functions.
+       Author:         E. Scott Daniels
+       Date:           2 April 2019
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <errno.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <ctype.h>
+
+
+#include "../src/common/include/rmr.h"
+#include "../src/common/include/rmr_agnostic.h"
+
+#include "../src/common/src/mbuf_api.c"                        // module under test
+
+#include "test_support.c"                                              // our private library of test tools
+#include "mbuf_api_static_test.c"                              // test functions
+
+int main( ) {
+       int errors = 0;
+
+       errors += mbuf_api_test( );
+
+       if( errors ) {
+               fprintf( stderr, "<FAIL> mbuf_api tests failed\n" );
+       } else {
+               fprintf( stderr, "<OK>   mbuf_api tests pass\n" );
+       }
+}
diff --git a/test/ring_static_test.c b/test/ring_static_test.c
new file mode 100644 (file)
index 0000000..482244c
--- /dev/null
@@ -0,0 +1,164 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mmemonic:       ring_static_test.c
+       Abstract:       Test the ring funcitons. These are meant to be included at compile
+                               time by the test driver.  
+
+       Author:         E. Scott Daniels
+       Date:           3 April 2019
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "../src/common/include/rmr.h"
+#include "../src/common/include/rmr_agnostic.h"
+//#include "../src/common/src/ring_static.c"
+
+
+/*
+       Conduct a series of interleaved tests inserting i-factor 
+       values before beginning to pull values (i-factor must be
+       size - 2 smaller than the ring. 
+       Returns 0 on success, 1 on insert failure and 2 on pull failure.
+*/
+static int ie_test( void* r, int i_factor, long inserts ) {
+       int i;
+       int* dp;
+       int data[29];
+
+       for( i = 0; i < inserts; i++ ) {
+               data[i%29] = i;
+               if( ! uta_ring_insert( r, &data[i%29] ) ) {
+                       fprintf( stderr, "<FAIL> interleaved insert failed on ifactor=%d i=%d\n", i_factor, i );
+                       return 1;
+               }
+               if( i > i_factor-1 ) {
+                       dp = uta_ring_extract( r );
+                       if( *dp != data[(i-i_factor)%29] ) {
+                               fprintf( stderr, "<FAIL> interleaved exctract failed on ifactor=%d i=%d expected=%d got=%d\n", i_factor, i, data[(i-i_factor)%29], *dp );
+                               return 2;
+                       }
+               }
+       }
+       //fprintf( stderr, "<OK>   interleaved insert/extract test passed for insert factor %d\n", i_factor );
+
+       return 0;
+}
+
+static int ring_test( ) {
+       void* r;
+       int i;
+       int j;
+       int     data[20];
+       int*    dp;
+       int size = 18;
+
+       r = uta_mk_ring( 0 );                   // should return nil
+       if( r != NULL ) {
+               fprintf( stderr, "<FAIL> attempt to make a ring with size 0 returned a pointer\n" );
+               return 1;
+       }
+       r = uta_mk_ring( -1 );                  // should also return nil
+       if( r != NULL ) {
+               fprintf( stderr, "<FAIL> attempt to make a ring with size <0 returned a pointer\n" );
+               return 1;
+       }
+
+       r = uta_mk_ring( 18 );
+       if( r == NULL ) {
+               fprintf( stderr, "<FAIL> unable to make ring with 17 entries\n" );
+               return 1;
+       }
+
+       for( i = 0; i < 20; i++ ) {             // test to ensure it reports full when head/tail start at 0
+               data[i] = i;
+               if( ! uta_ring_insert( r, &data[i] ) ) {
+                       break;
+               }
+       }
+
+       if( i > size ) {
+               fprintf( stderr, "<FAIL> didn not report table full: i=%d\n", i );
+               return 1;
+       }
+
+       fprintf( stderr, "<OK>   reported table full at i=%d as expected\n", i );
+
+
+       for( i = 0; i < size + 3; i++ ) {                                                               // ensure they all come back in order, and we don't get 'extras'
+               if( (dp = uta_ring_extract( r )) == NULL ) {
+                       if( i < size-1 ) {
+                               fprintf( stderr, "<FAIL> nil pointer at i=%d\n", i );
+                               return 1;
+                       } else {
+                               break;
+                       }
+               }       
+
+               if( *dp != i ) {
+                       fprintf( stderr, "<FAIL> data at i=% isnt right; expected %d got %d\n", i, i, *dp );
+               }
+       }
+       if( i > size ) {
+               fprintf( stderr, "<FAIL> got too many values on extract: %d\n", i );
+               return 1;
+       }
+       fprintf( stderr, "<OK>   extracted values were sane, got: %d\n", i-1 );
+               
+       uta_ring_free( NULL );                                                  // ensure this doesn't blow up
+       uta_ring_free( r );
+       for( i = 2; i < 15; i++ ) {
+               r = uta_mk_ring( 16 );
+               if( ie_test( r, i, 101 ) != 0 ) {                       // modest number of inserts
+                       fprintf( stderr, "<FAIL> ie test for 101 inserts didn't return 0\n" );
+                       return 1;
+               }
+
+               uta_ring_free( r );
+       }
+       fprintf( stderr, "<OK>   all modest insert/exctract tests pass\n" );
+
+       size = 5;
+       for( j = 0; j < 20; j++ ) {
+               for( i = 2; i < size - 2; i++ ) {
+                       r = uta_mk_ring( size );
+                       if( ie_test( r, i, 66000 ) != 0 ) {                     // should force the 16bit head/tail indexes to roll over
+                               fprintf( stderr, "<FAIL> ie test for 66K inserts didn't return 0\n" );
+                               return 1;
+                       }
+       
+                       uta_ring_free( r );
+               }
+               fprintf( stderr, "<OK>   all large insert/exctract tests pass ring size=%d\n", size );
+
+               size++;
+       }
+
+       fprintf( stderr, "<INFO> all ring tests pass\n" );
+       return 0;
+}
index 331f976..a06668f 100644 (file)
 
 /*
        Mmemonic:       ring_test.c
 
 /*
        Mmemonic:       ring_test.c
-       Abstract:       Test the ring funcitons.
+       Abstract:       This is a stand alone test driver for the ring module. It 
+                               includes the static tests after setting up the environment
+                               then invokes it.
+
        Author:         E. Scott Daniels
        Author:         E. Scott Daniels
-       Date:           31 July 2017
+       Date:           3 April 2019
 */
 
 #include <unistd.h>
 */
 
 #include <unistd.h>
 #include "../src/common/include/rmr_agnostic.h"
 #include "../src/common/src/ring_static.c"
 
 #include "../src/common/include/rmr_agnostic.h"
 #include "../src/common/src/ring_static.c"
 
+#include "test_support.c"                                      // things like fail_if()
+#include "ring_static_test.c"                          // the actual tests
 
 
-/*
-       Conduct a series of interleaved tests inserting i-factor 
-       values before beginning to pull values (i-factor must be
-       size - 2 smaller than the ring. 
-       Returns 0 on success, 1 on insert failure and 2 on pull failure.
-*/
-static int ie_test( void* r, int i_factor, long inserts ) {
-       int i;
-       int* dp;
-       int data[29];
-
-       for( i = 0; i < inserts; i++ ) {
-               data[i%29] = i;
-               if( ! uta_ring_insert( r, &data[i%29] ) ) {
-                       fprintf( stderr, "[FAIL] interleaved insert failed on ifactor=%d i=%d\n", i_factor, i );
-                       return 1;
-               }
-               if( i > i_factor-1 ) {
-                       dp = uta_ring_extract( r );
-                       if( *dp != data[(i-i_factor)%29] ) {
-                               fprintf( stderr, "[FAIL] interleaved exctract failed on ifactor=%d i=%d expected=%d got=%d\n", i_factor, i, data[(i-i_factor)%29], *dp );
-                               return 2;
-                       }
-               }
-       }
-       //fprintf( stderr, "[OK]   interleaved insert/extract test passed for insert factor %d\n", i_factor );
-
-       return 0;
-}
-
-int main( void  ) {
-       void* r;
-       int i;
-       int j;
-       int     data[20];
-       int*    dp;
-       int size = 18;
-
-       r = uta_mk_ring( 0 );                   // should return nil
-       if( r != NULL ) {
-               fprintf( stderr, "[FAIL] attempt to make a ring with size 0 returned a pointer\n" );
-               exit( 1 );
-       }
-       r = uta_mk_ring( -1 );                  // should also return nil
-       if( r != NULL ) {
-               fprintf( stderr, "[FAIL] attempt to make a ring with size <0 returned a pointer\n" );
-               exit( 1 );
-       }
-
-       r = uta_mk_ring( 18 );
-       if( r == NULL ) {
-               fprintf( stderr, "[FAIL] unable to make ring with 17 entries\n" );
-               exit( 1 );
-       }
-
-       for( i = 0; i < 20; i++ ) {             // test to ensure it reports full when head/tail start at 0
-               data[i] = i;
-               if( ! uta_ring_insert( r, &data[i] ) ) {
-                       break;
-               }
-       }
-
-       if( i > size ) {
-               fprintf( stderr, "[FAIL] didn not report table full: i=%d\n", i );
-               exit( 1 );
-       }
-
-       fprintf( stderr, "[OK]   reported table full at i=%d as expected\n", i );
-
-
-       for( i = 0; i < size + 3; i++ ) {                                                               // ensure they all come back in order, and we don't get 'extras'
-               if( (dp = uta_ring_extract( r )) == NULL ) {
-                       if( i < size-1 ) {
-                               fprintf( stderr, "[FAIL] nil pointer at i=%d\n", i );
-                               exit( 1 );
-                       } else {
-                               break;
-                       }
-               }       
-
-               if( *dp != i ) {
-                       fprintf( stderr, "[FAIL] data at i=% isnt right; expected %d got %d\n", i, i, *dp );
-               }
-       }
-       if( i > size ) {
-               fprintf( stderr, "[FAIL] got too many values on extract: %d\n", i );
-               exit( 1 );
-       }
-       fprintf( stderr, "[OK]   extracted values were sane, got: %d\n", i-1 );
-               
-       uta_ring_free( NULL );                                                  // ensure this doesn't blow up
-       uta_ring_free( r );
-       for( i = 2; i < 15; i++ ) {
-               r = uta_mk_ring( 16 );
-               if( ie_test( r, i, 101 ) != 0 ) {                       // modest number of inserts
-                       exit( 1 );
-               }
-
-               uta_ring_free( r );
-       }
-       fprintf( stderr, "[OK]   all modest insert/exctract tests pass\n" );
+int main( ) {
+       int errors = 0;
 
 
-       size = 5;
-       for( j = 0; j < 20; j++ ) {
-               for( i = 2; i < size - 2; i++ ) {
-                       r = uta_mk_ring( size );
-                       if( ie_test( r, i, 66000 ) != 0 ) {                     // should force the 16bit head/tail indexes to roll over
-                               exit( 1 );
-                       }
-       
-                       uta_ring_free( r );
-               }
-               fprintf( stderr, "[OK]   all large insert/exctract tests pass ring size=%d\n", size );
+       errors += ring_test( );
 
 
-               size++;
+       if( errors ) {
+               fprintf( stderr, "<FAIL> ring tests failed\n" );
+       } else {
+               fprintf( stderr, "<OK>   ring tests pass\n" );
        }
 
        }
 
-       return 0;
+       return errors;
 }
 }
diff --git a/test/rmr_nng_api_static_test.c b/test/rmr_nng_api_static_test.c
new file mode 100644 (file)
index 0000000..e9ecb72
--- /dev/null
@@ -0,0 +1,274 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mmemonic:       rmr_api_static_test.c
+       Abstract:       Specific tests related to the API functions in rmr_nng.c/rmr.c.
+                               This should be included by a driver, but only the main RMr
+                               driver and there likely not be a specific stand alone driver
+                               for just this small set of tests because of the depth of the 
+                               library needed to test at this level.
+
+                               The message buffer specific API tests are in a different static
+                               module.  API functions tested here are:
+                                        rmr_close
+                                        rmr_get_rcvfd
+                                        rmr_ready
+                                        rmr_init
+                                        rmr_set_rtimeout
+                                        rmr_set_stimeout
+                                        rmr_rcv_specific
+                                        rmr_torcv_msg
+                                        rmr_rcv_msg
+                                        rmr_call
+                                        rmr_rts_msg
+                                        rmr_send_msg
+                                        rmr_mtosend_msg
+                                        rmr_free_msg
+
+       Author:         E. Scott Daniels
+       Date:           5 April 2019
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "../src/common/include/rmr.h"
+#include "../src/common/include/rmr_agnostic.h"
+//#include "../src/common/src/ring_static.c"
+
+/*
+       Send a 'burst' of messages to drive some send retry failures to increase RMr coverage
+       by handling the retry caee.
+*/
+static void send_n_msgs( void* ctx, int n ) {
+       rmr_mbuf_t*     msg;                    // message buffers
+       int i;
+
+       msg = rmr_alloc_msg( ctx,  1024 );
+       if( ! msg ) {
+               return;
+       }
+
+       for( i = 0; i < n; i++ ) {
+fprintf( stderr, "mass send\n" );
+               msg->len = 100;
+               msg->mtype = 1;
+               msg->state = 999;
+               errno = 999;
+               msg = rmr_send_msg( ctx, msg );
+       }
+}
+
+static int rmr_api_test( ) {
+       int             errors = 0;
+       void*   rmc;                            // route manager context
+       void*   rmc2;                           // second context for non-listener init
+       rmr_mbuf_t*     msg;                    // message buffers
+       rmr_mbuf_t*     msg2;
+       int             v = 0;                                  // some value
+       char    wbuf[128];
+       int             i;
+
+       v = rmr_ready( NULL );
+       errors += fail_if( v != 0, "rmr_ready returned true before initialisation" );
+
+       if( (rmc = rmr_init( "4560", 1024, FL_NOTHREAD )) == NULL ) {
+               fail_if_nil( rmc, "rmr_init returned a nil pointer" );
+               return 1;
+       }
+
+       if( (rmc2 = rmr_init( ":6789", 1024, FL_NOTHREAD )) == NULL ) {         // init without starting a thread
+               errors += fail_if_nil( rmc, "rmr_init returned a nil pointer for non-threaded init" );
+       }
+
+       free_ctx( rmc2 );                       // coverage
+
+       if( (rmc2 = rmr_init( NULL, 1024, FL_NOTHREAD )) == NULL ) {                    // drive default port selector code
+               errors += fail_if_nil( rmc, "rmr_init returned a nil pointer when driving for default port" );
+       }
+
+       v = rmr_ready( rmc );           // unknown return; not checking at the moment
+
+       msg = rmr_alloc_msg( NULL,  1024 );                                                                     // should return nil pointer
+       errors += fail_not_nil( msg, "rmr_alloc_msg didn't return nil when given nil context" );
+
+       msg = rmr_alloc_msg( rmc, 2048 );                               // allocate larger than default size given on init
+       errors += fail_if_nil( msg, "rmr_alloc_msg returned nil msg pointer" );
+
+       v = rmr_payload_size( NULL );
+       errors += fail_if( v >= 0, "rmr_payload_size returned valid size for nil message" );
+       errors += fail_if( errno == 0, "rmr_payload_size did not set errno on failure" );
+
+       v = rmr_payload_size( msg );
+       if( v >= 0 ) {
+               errors += fail_not_equal( v, 2048, "rmr_payload_size returned invalid size (a) instead of expected size (b)" );
+               errors += fail_if( errno != 0, "rmr_payload_size did not clear errno on success" );
+       } else {
+               errors += fail_if( v < 0, "rmr_payload_size returned invalid size for good message" );
+       }
+       
+       v = rmr_get_rcvfd( NULL );
+       errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor when given nil context" );
+       v = rmr_get_rcvfd( rmc );
+       errors += fail_if( v < 0, "rmr_get_rcvfd did not return a valid file descriptor" );
+
+       msg2 = rmr_send_msg( NULL, NULL );                      // drive for coverage
+       errors += fail_not_nil( msg2, "send_msg returned msg pointer when given a nil message and context" );
+
+       // --- sends will fail with a no endpoint error until a dummy route table is set, so we test fail case first.
+       msg->len = 100;
+       msg->mtype = 1;
+       msg->state = 999;
+       errno = 999;
+       msg = rmr_send_msg( rmc, msg );
+       errors += fail_if_nil( msg, "send_msg_ did not return a message on send" );
+       if( msg ) {
+               errors += fail_not_equal( msg->state, RMR_ERR_NOENDPT, "send_msg did not return no endpoints before rtable added" );
+               errors += fail_if( errno == 0, "send_msg did not set errno" );
+       }
+
+       gen_rt( rmc );          // --- after this point there is a dummy route table so send and rts calls should be ok
+
+       msg->len = 100;
+       msg->mtype = 1;
+       msg->state = 999;
+       errno = 999;
+       msg = rmr_send_msg( rmc, msg );
+       errors += fail_if_nil( msg, "send_msg_ did not return a message on send" );
+       if( msg ) {
+               errors += fail_not_equal( msg->state, RMR_OK, "send_msg returned bad status for send that should work" );
+               errors += fail_if( errno != 0, "send_msg set errno for send that should work" );
+               v = rmr_payload_size( msg );
+               errors += fail_if( v != 2048, "send_msg did not allocate new buffer with correct size" );
+       }
+
+       rmr_set_stimeout( NULL, 0 );
+       rmr_set_stimeout( rmc, 20 );
+       rmr_set_stimeout( rmc, -1 );
+       rmr_set_rtimeout( NULL, 0 );
+       rmr_set_rtimeout( rmc, 20 );
+       rmr_set_rtimeout( rmc, -1 );
+
+       msg2 = rmr_rcv_msg( NULL, NULL );
+       errors += fail_if( msg2 != NULL, "rmr_rcv_msg returned msg when given nil context and msg" );
+
+       msg2 = rmr_rcv_msg( rmc, NULL );
+       errors += fail_if( msg2 == NULL, "rmr_rcv_msg returned nil msg when given nil msg" );
+       if( msg2 ) {
+               errors += fail_not_equal( msg2->state, RMR_OK, "receive given nil message did not return msg with good state" );
+       }
+
+       msg = rmr_rcv_msg( rmc, msg );
+       if( msg ) {
+               errors += fail_if( msg->state != RMR_OK, "rmr_rcv_msg did not return an ok state" );
+               errors += fail_not_equal( msg->len, 129, "rmr_rcv_msg returned message with invalid len" );
+       } else {
+               errors += fail_if_nil( msg, "rmr_rcv_msg returned a nil pointer" );
+       }
+
+       rmr_rts_msg( NULL, NULL );                      // drive for coverage
+       rmr_rts_msg( rmc, NULL );
+       errors += fail_if( errno == 0, "rmr_rts_msg did not set errno when given a nil message" );
+
+       msg = rmr_rts_msg( rmc, msg );                  // return the buffer to the sender
+       errors += fail_if_nil( msg, "rmr_rts_msg did not return a message pointer" );
+       errors += fail_if( errno != 0, "rmr_rts_msg did not reset errno" );
+
+
+       snprintf( msg->xaction, 17, "%015d", 16 );              // dummy transaction id (emulation generates, this should arrive after a few calls to recv)
+       msg = rmr_call( rmc, msg );                                             // this call should return a message as we can anticipate a dummy message in
+       errors += fail_if_nil( msg, "rmr_call returned a nil message on call expected to succeed" );
+       if( msg ) {
+               errors += fail_not_equal( msg->state, RMR_OK, "rmr_call did not properly set state on successful return" );
+               errors += fail_if( errno != 0, "rmr_call did not properly set errno on successful return" );
+       }
+
+       snprintf( wbuf, 17, "%015d", 14 );                              // if we call receive we should find this in the first 15 tries
+       for( i = 0; i < 16; i++ ) {
+               msg = rmr_rcv_msg( rmc, msg );
+               if( msg ) {
+                       if( strcmp( wbuf, msg->xaction ) == 0 ) {               // found the queued message
+                               break;
+                       }
+                       fprintf( stderr, "<INFO> msg: %s\n", msg->xaction );
+               } else {
+                       errors += fail_if_nil( msg, "receive returnd nil msg while looking for queued message" );
+               }
+       }
+
+       errors += fail_if( i >= 16, "did not find expected message on queue" );
+               
+       if( ! msg ) {
+               msg = rmr_alloc_msg( rmc, 2048 );                               // something buggered above; get a new one
+       }
+       msg = rmr_call( rmc, msg );                                                     // make a call that we never expect a response on
+       errors += fail_not_nil( msg, "rmr_call returned a non-nil message on call expected not to receive a response" );
+       if( msg ) {
+               errors += fail_not_equal( msg->state, RMR_OK, "rmr_call did not properly set state on queued message receive" );
+               errors += fail_if( errno != 0, "rmr_call did not properly set errno on queued message receivesuccessful" );
+       }
+
+       msg = rmr_call( rmc, msg );                                             // this should "timeout" because the message xaction id won't ever appear again
+       errors += fail_not_nil( msg, "rmr_call returned a non-nil message on call expected to fail" );
+       errors += fail_if( errno == 0, "rmr_call did not set errno on failure" );
+
+       rmr_free_msg( NULL );                   // drive for coverage; nothing to check
+       rmr_free_msg( msg2 );
+
+
+       // ---  test timeout receive; our dummy epoll function will return 1 ready on first call and 0 ready (timeout emulation) on second
+       //              however we must drain the swamp (queue) first, so run until we get a timeout error, or 20 and report error if we get to 20.
+       msg = NULL;
+       for( i = 0; i < 40; i++ ) {
+               msg = rmr_torcv_msg( rmc, msg, 10 );
+               errors += fail_if_nil( msg, "torcv_msg returned nil msg when message expected" );
+               if( msg ) {
+                       if( msg->state == RMR_ERR_TIMEOUT ) {                   // queue drained and we've seen both states from poll if we get a timeout
+                               break;
+                       }
+               }
+       }
+       errors += fail_if( i >= 40, "torcv_msg never returned a timeout" );
+
+       
+       em_send_failures = 1;
+       send_n_msgs( rmc, 30 );                 // send 30 messages with emulation failures
+       em_send_failures = 0;
+
+       
+       rmr_close( NULL );                      // drive for coverage
+       rmr_close( rmc );                       // no return to check; drive for coverage
+
+//extern rmr_mbuf_t* rmr_mtosend_msg( void* vctx, rmr_mbuf_t* msg, int max_to ) {
+//extern rmr_mbuf_t* rmr_torcv_msg( void* vctx, rmr_mbuf_t* old_msg, int ms_to ) {
+
+
+
+       if( ! errors ) {
+               fprintf( stderr, "<INFO> all RMr API tests pass\n" );
+       }
+       return !!errors;
+}
diff --git a/test/rmr_nng_test.c b/test/rmr_nng_test.c
new file mode 100644 (file)
index 0000000..1ebdb94
--- /dev/null
@@ -0,0 +1,130 @@
+// :vi sw=4 ts=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2019 Nokia
+       Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mmemonic:       rmr_nng_test.c
+       Abstract:       This tests the whole rmr nng implementation. This driver
+                               includes all of the module specific unit test static files
+                               (e.g. wormhole_static_test.c) and drives the tests contained
+                               there.   The individual modules allow them to be driven by
+                               a standalone driver, and to be maintained separately. We must
+                               test by inclusion because of the static nature of the internal
+                               functions of the library.
+
+       Author:         E. Scott Daniels
+       Date:           18 January 2018         (IMO HRTL)
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <errno.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <ctype.h>
+
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <sys/epoll.h>
+
+#define DEBUG 1
+
+#include <nng/nng.h>
+#include <nng/protocol/pubsub0/pub.h>
+#include <nng/protocol/pubsub0/sub.h>
+#include <nng/protocol/pipeline0/push.h>
+#include <nng/protocol/pipeline0/pull.h>
+
+#define EMULATE_NNG
+#include "test_nng_em.c"                                                       // nng/nn emulation (before including things under test)
+
+
+#include "../src/common/include/rmr.h"                                 // things the users see
+#include "../src/common/include/rmr_symtab.h"
+#include "../src/common/include/rmr_agnostic.h"                        // transport agnostic header
+#include "../src/nng/include/rmr_nng_private.h"                        // transport specific
+
+#include "../src/common/src/symtab.c"
+#include "../src/nng/src/rmr_nng.c"
+
+static void gen_rt( uta_ctx_t* ctx );          // defined in sr_nng_static_test, but used by a few others (eliminate order requirement below)
+
+                                                                                       // specific test tools in this directory
+#include "test_support.c"                                      // things like fail_if()
+                                                                                       // and finally....
+#include "tools_static_test.c"                         // local test functions pulled directly because of static nature of things
+#include "symtab_static_test.c"
+#include "ring_static_test.c"
+#include "rt_static_test.c"
+#include "sr_nng_static_test.c"
+#include "wormhole_static_test.c"
+#include "rmr_nng_api_static_test.c"
+
+
+/*
+       Drive each of the separate tests and report.
+*/
+int main() {
+       int errors = 0;
+
+       fprintf( stderr, "<INFO> starting tool tests\n" );
+       errors += tools_test();
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting ring tests (%d)\n", errors );
+       errors += ring_test();
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting symtab tests\n" );
+       errors += symtab_test( );
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting rtable tests\n" );
+       errors += rt_test();                            // route table tests
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting RMr API tests\n" );
+       errors += rmr_api_test();
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting wormhole tests\n" );
+       errors += worm_test();                          // test wormhole funcitons
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting send/receive tests\n" );
+       errors += sr_nng_test();                                // test the send/receive static functions
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       if( errors == 0 ) {
+               fprintf( stderr, "<PASS> all tests were OK\n" );
+       } else {
+               fprintf( stderr, "<FAIL> %d modules reported errors\n", errors );
+       }
+
+       return !!errors;
+}
diff --git a/test/rt_static_test.c b/test/rt_static_test.c
new file mode 100644 (file)
index 0000000..f1e6981
--- /dev/null
@@ -0,0 +1,231 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mmemonic:       rt_static_test.c
+       Abstract:       Test the route table funcitons. These are meant to be included at compile
+                               time by the test driver.  
+
+       Author:         E. Scott Daniels
+       Date:           3 April 2019
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "../src/common/include/rmr.h"
+#include "../src/common/include/rmr_agnostic.h"
+
+typedef struct entry_info {
+       int group;
+       char* ep_name;
+} ei_t;
+
+
+/*
+       This is the main route table test. It sets up a very specific table
+       for testing (not via the generic setup function for other test
+       situations).
+*/
+static int rt_test( ) {
+       uta_ctx_t* ctx;                 // context needed to test load static rt
+       route_table_t* rt;              // route table
+       route_table_t* crt;             // cloned route table
+       rtable_ent_t*   rte;    // entry in the table
+       endpoint_t*     ep;                     // endpoint added
+       int more = 0;                   // more flag from round robin
+       int errors = 0;                 // number errors found
+       int     i;
+       int k;
+       int mtype;
+       int value;
+       int alt_value;
+       ei_t    entries[50];    // end point information
+       int             gcounts[5];             // number of groups in this set
+       int             ecounts[5];             // number of elements per group
+       int             mtypes[5];              // msg type for each group set
+       char*   tok;
+       char*   nxt_tok;
+       int             enu = 0;
+       int             state;
+       char    *buf;
+       char*   seed_fname;             // seed file
+       nng_socket nn_sock;             // this is a struct in nng, so difficult to validate
+
+       setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 );                     // allow for verbose code in rtc to be driven
+       i = open( ".rmr_verbose", O_CREAT, 0664 );
+       if( i >= 0 ) {
+               close( i );
+       }
+
+       gcounts[0] = 1;                 // build entry info -- this is hackish, but saves writing another parser
+       ecounts[0] = 2;
+       mtypes[0] = 0;
+       entries[enu].group = 0; entries[enu].ep_name = "yahoo.com:4561"; enu++;         // use a dns resolvable name to test that
+       entries[enu].group = 0; entries[enu].ep_name = "localhost:4562"; enu++; // rest can default to some dummy ip
+
+       gcounts[1] = 2;
+       ecounts[1] = 3;
+       mtypes[1] = 1;
+       entries[enu].group = 0; entries[enu].ep_name = "localhost:4561"; enu++;
+       entries[enu].group = 0; entries[enu].ep_name = "localhost:4568"; enu++;
+       entries[enu].group = 0; entries[enu].ep_name = "localhost:4569"; enu++;
+
+       gcounts[2] = 0;         // 0 groups means use same rte, this is the next gropup
+       ecounts[2] = 2;
+       mtypes[2] = 1;
+       entries[enu].group = 1; entries[enu].ep_name = "localhost:4561"; enu++;
+       entries[enu].group = 1; entries[enu].ep_name = "localhost:4562"; enu++;
+
+       gcounts[3] = 1;         // 0 groups means use same rte, this is the next gropup
+       ecounts[3] = 2;
+       mtypes[3] = 2;
+       entries[enu].group = 0; entries[enu].ep_name = "localhost:4563"; enu++;
+       entries[enu].group = 0; entries[enu].ep_name = "localhost:4564"; enu++;
+
+       gcounts[4] = 1;         // 0 groups means use same rte, this is the next gropup
+       ecounts[4] = 1;
+       mtypes[4] = 3;
+       entries[enu].group = 0; entries[enu].ep_name = "localhost:4565"; enu++;
+
+
+
+       rt = uta_rt_init( );                                                                            // get us a route table
+       if( (errors += fail_if_nil( rt, "pointer to route table" )) ) {
+               fprintf( stderr, "<FAIL> abort: cannot continue without a route table\n" );
+               exit( 1 );
+       }
+
+       enu = 0;
+       rte = NULL;
+       for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) {                                // add entries defined above
+               if( gcounts[i] ) {
+                       rte = uta_add_rte( rt, mtypes[i], gcounts[i] );                                 // get/create entry for message type
+                       if( (errors += fail_if_nil( rte, "route table entry" )) ) {
+                               fprintf( stderr, "<FAIL> abort: cannot continue without a route table entry\n" );
+                               exit( 1 );
+                       }
+               } else {
+                       if( rte == NULL ) {
+                               fprintf( stderr, "<SNAFU> internal testing error -- rte was nil for gcount == 0\n" );
+                               exit( 1 );
+                       }
+               }
+
+               for( k = 0; k < ecounts[i]; k++ ) {
+                       ep = uta_add_ep( rt, rte, entries[enu].ep_name, entries[enu].group );
+                       errors += fail_if_nil( ep, "endpoint" );
+                       enu++;
+               }
+       }
+
+       crt = uta_rt_clone( rt );
+       errors += fail_if_nil( crt, "cloned route table" );
+
+       ep = uta_get_ep( rt, "localhost:4561" );
+       errors += fail_if_nil( ep, "end point (fetch by name)" );
+       ep = uta_get_ep( rt, "bad_name:4560" );
+       errors += fail_not_nil( ep, "end point (fetch by name with bad name)" );
+
+       state = uta_epsock_byname( rt, "localhost:4561", &nn_sock );            // this should be found
+       errors += fail_if_equal( state, 0, "socket (by name)" );
+       //alt_value = uta_epsock_byname( rt, "localhost:4562" );                        // we might do a memcmp on the two structs, but for now nothing
+       //errors += fail_if_equal( value, alt_value, "app1/app2 sockets" );
+
+       alt_value = -1;
+       for( i = 0; i < 10; i++ ) {                                                                             // round robin return value should be different each time
+               value = uta_epsock_rr( rt, 1, 0, &more, &nn_sock );                     // msg type 1, group 1
+               errors += fail_if_equal( value, alt_value, "round robiin sockets with multiple end points" );
+               errors += fail_if_false( more, "more for mtype==1" );
+               alt_value = value;
+       }
+
+       more = -1;
+       for( i = 0; i < 10; i++ ) {                                                     // this mtype has only one endpoint, so rr should be same each time
+               value = uta_epsock_rr( rt, 3, 0, NULL, &nn_sock );              // also test ability to deal properly with nil more pointer
+               if( i ) {
+                       errors += fail_not_equal( value, alt_value, "round robin sockets with one endpoint" );
+                       errors += fail_not_equal( more, -1, "more value changed in single group instance" );
+               }
+               alt_value = value;
+       }
+
+       value = uta_epsock_rr( rt, 9, 0, &more, &nn_sock );                     // non-existant message type; should return false (0)
+       errors += fail_not_equal( value, 0, "socket for bad mtype was valid" );
+
+       uta_rt_clone( NULL );                                                           // verify null parms don't crash things
+       uta_rt_drop( NULL );
+       uta_epsock_rr( NULL, 1, 0, &more, &nn_sock );           // drive null case for coverage
+       uta_add_rte( NULL, 99, 1 );
+
+       fprintf( stderr, "[INFO] test: adding end points with nil data; warnings expected\n" );
+       uta_add_ep( NULL, NULL, "foo", 1 );
+       uta_add_ep( rt, NULL, "foo", 1 );
+
+       buf = uta_fib( ".gitignore" );
+       errors += fail_if_nil( buf, "buffer from read file into buffer" );
+       if( buf ) {
+               free( buf );
+       }
+       buf = uta_fib( "no-file" );
+       errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" );
+       if( buf ) {
+               free( buf );
+       }
+
+       uta_rt_drop( rt );
+
+       uta_rt_drop( crt );
+
+       if( (ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) )) != NULL ) {
+               memset( ctx, 0, sizeof( *ctx ) );
+
+               if( (seed_fname = getenv( "RMR_SEED_RT" )) != NULL ) {
+                       if( ! (fail_if_nil( rt, "pointer to rt for load test" )) ) {
+                               errors++;
+                               read_static_rt( ctx, 0 );
+                               unsetenv( "RMR_SEED_RT" );                      // unset to test the does not exist condition
+                               read_static_rt( ctx, 0 );
+                       } else {
+                               fprintf( stderr, "<FAIL> cannot gen rt for load test\n" );
+                       }
+               } else {
+                       read_static_rt( ctx, 0 );               // not defined, just drive for that one case
+               }
+       }
+
+       uta_fib( "no-suhch-file" );                     // drive some error checking for coverage
+
+/*
+       if( ctx ) {
+               if( ctx->rtg_addr ) {
+                       free( ctx->rtg_addr );
+               }
+               free( ctx );
+       }
+*/
+
+       return !!errors;                        // 1 or 0 regardless of count
+}
diff --git a/test/sr_nng_static_test.c b/test/sr_nng_static_test.c
new file mode 100644 (file)
index 0000000..69090a1
--- /dev/null
@@ -0,0 +1,188 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mmemonic:       sr_nng_static_test.c
+       Abstract:       Test the send/receive funcitons. These are meant to be included at compile
+                               time by the test driver.  
+
+       Author:         E. Scott Daniels
+       Date:           3 April 2019
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "../src/common/include/rmr.h"
+#include "../src/common/include/rmr_agnostic.h"
+
+/*
+       Generate a simple route table (for all but direct route table testing).
+*/
+static void gen_rt( uta_ctx_t* ctx ) {
+       int             fd;
+       char*   rt_stuff;               // strings for the route table
+
+       rt_stuff = 
+               "newrt|start\n"                                                         // false start to drive detection 
+               "xxx|badentry to drive default case"
+               "newrt|start\n"
+        "rte|0|localhost:4560,localhost:4562\n"
+        "rte|1|localhost:4562;localhost:4561,localhost:4569\n"
+        "rte|2|localhost:4562\n"
+           "rte|4|localhost:4561\n"
+               "rte|5|localhost:4563\n"
+        "rte|6|localhost:4562\n"
+               "newrt|end\n";
+
+       fd = open( "utesting.rt", O_WRONLY | O_CREAT, 0600 );
+       if( fd < 0 ) {
+               fprintf( stderr, "<BUGGERED> unable to open file for testing route table gen\n" );
+               return;
+       }
+
+       setenv( "RMR_SEED_RT", "utesting.rt", 1 );
+       write( fd, rt_stuff, strlen( rt_stuff ) );
+       close( fd );
+       read_static_rt( ctx, 0 );
+       unlink( "utesting.rt" );
+}
+
+
+/*
+       Drive the send and receive functions.  We also drive as much of the route
+       table collector as is possible without a real rtg process running somewhere.
+
+       Send and receive functions are indirectly exercised from the rmr_nng_static_test
+       module as it tests the user facing send/receive/call/rts functions. These tests
+       should exercise specific cases for the internal functions as they will not 
+       specifically be driven elsewhere.
+*/
+static int sr_nng_test() {
+       uta_ctx_t* ctx;                         // context needed to test load static rt
+       uta_ctx_t*      real_ctx;       // real one to force odd situations for error testing
+       int errors = 0;                 // number errors found
+       rmr_mbuf_t*     mbuf;           // mbuf to send/receive
+       rmr_mbuf_t*     mb2;            // error capturing msg buf
+       int             whid = -1;
+       int             last_whid;
+       int     state;
+       nng_socket nn_dummy_sock;                                       // dummy needed to drive send
+       int             size;
+       int             i;
+
+       //ctx = rmr_init( "tcp:4360", 2048, 0 );                                // do NOT call init -- that starts the rtc thread which isn't good here
+       ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) );              // alloc the context manually
+       memset( ctx, 0, sizeof( uta_ctx_t ) );
+
+       ctx->mring = NULL;              //uta_mk_ring( 128 );
+       ctx->max_plen = RMR_MAX_RCV_BYTES + sizeof( uta_mhdr_t );
+       ctx->max_mlen = ctx->max_plen + sizeof( uta_mhdr_t );
+       ctx->my_name = strdup( "dummy-test" );
+       uta_lookup_rtg( ctx );
+
+       gen_rt( ctx );                                                          // forces a static load with some known info since we don't start the rtc()
+
+       state = rmr_ready( NULL );
+       errors += fail_if_true( state, "reported ready when given a nil context" );
+       state = rmr_ready( ctx );
+       errors += fail_if_false( state, "reported not ready when it should be" );
+
+       mbuf = rcv_msg( ctx, NULL );
+       errors += fail_if_nil( mbuf, "no mbuf returned on receive test" );
+
+       mbuf->len = 10;
+       mbuf->mtype = 1;
+
+       mb2 = clone_msg( mbuf );
+       errors += fail_if_nil( mb2, "clone message returned nil pointer" );
+       errors += fail_not_equal( mbuf->flags, mb2->flags, "clone did not duplicate flags" );
+       errors += fail_not_equal( mbuf->alloc_len, mb2->alloc_len, "clone did not dup alloc-len" );
+       errors += fail_not_equal( mbuf->state, mb2->state, "clone did not dup state" );
+       rmr_free_msg( mb2 );    
+
+       mbuf = rmr_send_msg( NULL, mbuf );
+       errors += fail_if_nil( mbuf, "send with nil context but buffere didn't return buffer" );
+       if( mbuf ) {
+               errors += fail_not_equal( mbuf->state, RMR_ERR_BADARG, "send with buffer but nil context didn't return right state" );
+       } else {
+               mbuf = rmr_rcv_msg( ctx, NULL );
+       }
+
+       size = 2048 - sizeof( uta_mhdr_t );             // emulated nng receive allocates 2K payloads
+       state = rmr_payload_size( mbuf );
+       errors += fail_not_equal( state, size, "payload size didn't return expected value" );   // receive should always give 4k buffer
+
+       rmr_free_msg( mbuf );
+
+
+       state = xlate_nng_state( NNG_EAGAIN, 99 );
+       errors += fail_if( state == 99, "xlate_nng_state returned default for nng_eagain" );
+       errors += fail_if( errno != EAGAIN, "xlate_nng_state did not set errno to eagain for nng_eagain" );
+
+       state = xlate_nng_state( NNG_ETIMEDOUT, 99 );
+       errors += fail_if( state == 99, "xlate_nng_state returned default for nng_timeout" );
+       errors += fail_if( errno != EAGAIN, "xlate_nng_state did not set errno to eagain for nng_timeout" );
+
+       state = xlate_nng_state( NNG_ENOTSUP, 99 );
+       errors += fail_if( state != 99, "xlate_nng_state did not return  default for nng_notsup" );
+
+       state = xlate_nng_state( NNG_ENOTSUP, 99 );
+       errors += fail_if( state != 99, "xlate_nng_state did not return  default for nng_notsup" );
+       errors += fail_if( errno == 0, "xlate_nng_state did not set errno (1)" );
+
+       state = xlate_nng_state( NNG_EINVAL, 99 );
+       errors += fail_if( state != 99, "xlate_nng_state did not return  default for nng_inval" );
+       errors += fail_if( errno == 0, "xlate_nng_state did not set errno (2)" );
+
+       state = xlate_nng_state( NNG_ENOMEM, 99 );
+       errors += fail_if( state != 99, "xlate_nng_state did not return  default for nng_nomem" );
+       errors += fail_if( errno == 0, "xlate_nng_state did not set errno (3)" );
+
+       state = xlate_nng_state( NNG_ESTATE, 99 );
+       errors += fail_if( state != 99, "xlate_nng_state did not return  default for nng_state" );
+       errors += fail_if( errno == 0, "xlate_nng_state did not set errno (4)" );
+
+       state = xlate_nng_state( NNG_ECLOSED, 99 );
+       errors += fail_if( state != 99, "xlate_nng_state did not return  default for nng_closed" );
+       errors += fail_if( errno == 0, "xlate_nng_state did not set errno (5)" );
+
+       state = xlate_nng_state( 999, 99 );
+       errors += fail_if( state != 99, "xlate_nng_state did not return  default for unknown error" );
+       errors += fail_if( errno == 0, "xlate_nng_state did not set errno (6)" );
+
+       // ---- drive rtc in a 'static' (not pthreaded) mode -----
+       setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 );                     // allow for verbose code in rtc to be driven
+       i = open( ".rmr_verbose", O_CREAT, 0664 );
+       if( i >= 0 ) {
+               write( i, "0\n", 2 );
+               close( i );
+       }
+       ctx->shutdown = 1;                      // should force rtc to quit on first pass
+       rtc( ctx );
+       
+
+       return !!errors;
+}
diff --git a/test/symtab_static_test.c b/test/symtab_static_test.c
new file mode 100644 (file)
index 0000000..181cd13
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+
+/*
+       Mnemonic:       symtab_static_test.c
+       Abstract:       This is the static function that should be included by 
+                               any test that wants to test the symbol table. It must
+                               be included in the compile, and not built to object.
+
+       Date:           1 April 2019
+       Author:         E. Scott Daniels
+*/
+
+#include "../src/common/include/rmr_symtab.h"
+//  -- parent must include if needed #include "../src/common/src/symtab.c"
+
+#include "test_support.c"
+
+#ifndef GOOD 
+#define GOOD 0
+#define BAD 1
+#endif
+
+int symtab_state = GOOD;                                                       // overall pass/fail state 0==fail
+int symtab_counter = 0;                                                                // global counter for for-each tests
+
+static void st_fetch( void* st, char* key, int class, int expected ) {
+       char* val;
+
+       val = rmr_sym_get( st, key, class );
+       if( val ) {
+               fprintf( stderr, "<%s> get returns key=%s val=%s\n",  !expected ? "FAIL" : "OK", key, val );
+               if( !expected ) {
+                       symtab_state = BAD;
+               }
+               
+       } else {
+               fprintf( stderr, "<%s> string key st_fetch return nil\n", expected ? "FAIL" : "OK" );
+               if( expected ) {
+                       symtab_state = BAD;
+               }
+       }
+}
+
+static void st_nfetch( void* st, int key, int expected ) {
+       char* val;
+
+       val = rmr_sym_pull( st, key );
+       if( val ) {
+               fprintf( stderr, "<%s> get returns key=%d val=%s\n", !expected ? "FAIL" : "OK", key, val );
+               if( !expected )  {
+                       symtab_state = BAD;
+               }
+       } else {
+               fprintf( stderr, "<%s> get return nil for key=%d\n", expected ? "FAIL" : "OK", key );
+               if( expected )  {
+                       symtab_state = BAD;
+               }
+       }
+}
+
+
+/*
+       Driven by foreach class -- just incr the counter.
+*/
+static void each_counter( void* a, void* b, const char* c, void* d, void* e ) {
+       symtab_counter++;
+}
+
+static int symtab_test( ) {
+    void*   st;
+    char*   foo = "foo";
+    char*   bar = "bar";
+       char*   goo = "goo";                            // name not in symtab
+       int             i;
+       int             class = 1;
+       int             s;
+       void*   p;
+       int             errors = 0;
+
+    st = rmr_sym_alloc( 10 );                                          // alloc with small value to force adjustment inside
+       errors += fail_if_nil( st, "symtab pointer" );
+
+    s = rmr_sym_put( st, foo, class, bar );                    // add entry with string key; returns 1 if it was inserted
+       errors += fail_if_false( s, "insert foo existed" );
+
+    s = rmr_sym_put( st, foo, class+1, bar );          // add to table with a different class
+       errors += fail_if_false( s, "insert foo existed" );
+
+    s = rmr_sym_put( st, foo, class, bar );                    // inserted above, should return not inserted (0)
+       errors += fail_if_true( s, "insert foo existed" );
+
+       st_fetch( st, foo, class, 1 );
+       st_fetch( st, goo, class, 0 );                                  // st_fetch non existant
+    rmr_sym_stats( st, 4 );                                                    // early stats at verbose level 4 so chatter is minimised
+       rmr_sym_dump( st );
+
+       for( i = 2000; i < 3000; i++ ) {                        // bunch of dummy things to force chains in the table
+               rmr_sym_map( st, i, foo );                                      // add entry with unsigned integer key
+       }
+    rmr_sym_stats( st, 0 );                                                    // just the small facts to verify the 1000 we stuffed in
+       rmr_sym_ndel( st, 2001 );                                               // force a numeric key delete
+       rmr_sym_ndel( st, 12001 );                                              // delete numeric key not there
+
+       s = rmr_sym_map( st, 1234, foo );                                       // add known entries with unsigned integer key
+       errors += fail_if_false( s, "numeric add of key 1234 should not have existed" );
+       s = rmr_sym_map( st, 2345, bar );
+       errors += fail_if_true( s, "numeric add of key 2345 should have existed" );
+
+       symtab_counter = 0;
+       rmr_sym_foreach_class( st, 0, each_counter, NULL );
+       errors += fail_if_false( symtab_counter, "expected counter after foreach to be non-zero" );
+
+       st_nfetch( st, 1234, 1 );
+       st_nfetch( st, 2345, 1 );
+
+    rmr_sym_del( st, foo, 0 );         // drive for coverage
+    rmr_sym_stats( st, 0 );
+
+       rmr_sym_free( NULL );                   // ensure it doesn't barf when given a nil pointer
+       rmr_sym_free( st );
+
+    return !!( errors + symtab_state );
+}
+
diff --git a/test/test_nng_em.c b/test/test_nng_em.c
new file mode 100644 (file)
index 0000000..76fe72b
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+==================================================================================
+       Copyright (c) 2019 Nokia 
+       Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mnemonic:       test_nng_em.c
+       Abstract:       A nano/NNG message emulator for testing without needing to 
+                               actually have nanomsg, nng, or external processes.
+                               We also emulate the epoll_wait() function for controlled
+                               poll related testing.
+
+                               This module must be directly included to be used.
+       Date:           11 February 2019
+       Author:         E. Scott Daniels
+*/
+
+// ---------------------- emulated nng functions ---------------------------
+
+
+#ifndef _em_nn
+#define _em_nn
+
+static int em_send_failures = 0;       // test programme can set this to emulate eagain send failures
+
+// ----------- epoll emulation ---------------------------------------------
+
+// CAUTION: sys/epoll.h must be included before this define and function will properly compile. 
+#define epoll_wait em_wait
+/*
+       Every other call returns 1 ready; alternate calls return 0 ready. 
+       Mostly for testing the timeout receive call. First call should return 
+       something ready and the second should return nothing ready so we can
+       drive both cases. 
+*/
+static int em_wait( int fd, void* events, int n, int to ) {
+       static int ready = 0;
+
+       ready = !ready;
+       return ready;
+}
+
+
+
+//--------------------------------------------------------------------------
+#ifdef EMULATE_NNG
+struct nn_msghdr {
+       int boo;
+};
+
+static int return_value = 0;
+
+/*
+       Test app can call this to have all emulated functions return failure instead
+       of success.
+*/
+static void en_set_retur( int rv ) {
+       return_value = rv;
+}
+
+
+
+static int em_nng_foo() {
+       fprintf( stderr, "emulated functions in play" );
+}                              
+
+
+/*
+       Simulated v1 message for receive to return. This needs to match the RMr header
+       so that we can fill in length, type and xaction id things.
+#define MSG_VER 1
+struct em_msg {
+       int32_t mtype;                                          // message type  ("long" network integer)
+       int32_t plen;                                           // payload length
+       int32_t rmr_ver;                                        // our internal message version number
+       unsigned char xid[32];                          // space for user transaction id or somesuch
+       unsigned char sid[32];                          // sender ID for return to sender needs
+       unsigned char src[16];                          // name of the sender (source)
+       unsigned char meid[32];                         // managed element id.
+       struct timespec ts;                                     // timestamp ???
+};
+*/
+
+/*
+       v2 message; should be able to use it for everything that is set up here as 
+       we don't add a payload even if setting a v1 type.
+*/
+#define ALT_MSG_VER 1  // alternate every so often
+#define MSG_VER 2              // default version to insert
+struct em_msg {
+       int32_t mtype;                                          // message type  ("long" network integer)
+       int32_t plen;                                           // payload length
+       int32_t rmr_ver;                                        // our internal message version number
+       unsigned char xid[32];                          // space for user transaction id or somesuch
+       unsigned char sid[32];                          // sender ID for return to sender needs
+       unsigned char src[64];                          // name of the sender (source)
+       unsigned char meid[32];                         // managed element id.
+       struct timespec ts;                                     // timestamp ???
+
+                                        // V2 extension
+    int32_t flags;                      // HFL_* constants
+    int32_t len0;                       // length of the RMr header data
+    int32_t len1;                       // length of the tracing data
+    int32_t len2;                       // length of data 1 (d1)
+    int32_t len3;                       // length of data 2 (d2)
+
+};
+
+/*
+       Receive message must allocate a new buffer and return the pointer into *m.
+       Every 9 messages or so we'll simulate an old version message
+*/
+static int em_nng_recvmsg( nng_socket s, nng_msg ** m, int i ) {
+       void* b;
+       struct em_msg* msg;
+       static int count = 0;                   // we'll simulate a message going in by dropping an rmr-ish msg with transaction id only
+       int trace_size = 0;
+
+       //sleep( 1 );
+
+       b = (void *) malloc( 2048 );
+       if( m != NULL ) {
+               memset( b, 0, 2048 );
+               *m = (nng_msg *) b;
+               msg = (struct em_msg *) b;
+               if( count % 10  == 9 ) {
+                       //msg->rmr_ver = htonl( MSG_VER );
+                       msg->rmr_ver = ALT_MSG_VER;             // emulate the bug in RMr v1
+               } else {
+                       msg->rmr_ver = htonl( MSG_VER );
+               }
+               msg->mtype = htonl( 1 );
+               msg->plen = htonl( 129 );
+               msg->len0 = htonl( sizeof( struct em_msg ) );
+               msg->len1 = htonl( trace_size );
+               snprintf( msg->xid, 32, "%015d", count++ );             // simple transaction id so we can test receive specific and ring stuff
+               snprintf( msg->src, 16, "localhost:4562" );             // set src id (unrealistic) so that rts() can be tested
+       }
+
+       //fprintf( stderr, ">>> simulated received message: %s\n", msg->xid );
+       return return_value;
+}
+
+static void* em_msg_body( nng_msg* msg ) {
+       return (void *) msg;                                                            // we don't manage a real msg, so body is just the buffer we allocated
+}
+
+static size_t em_msg_len( const nng_msg* msg ) {
+       if( msg ) {
+               return  2048;
+       }
+
+       return 0;
+}
+
+
+static int em_nng_pull_open(nng_socket * s ) {
+       return return_value;
+}
+static int em_nng_pull0_open(nng_socket * s ) {
+       return return_value;
+}
+static int em_nng_listen(nng_socket s, const char * c, nng_listener * l, int i ) {
+       return return_value;
+}
+static int em_nng_close(nng_socket s ) {
+       return return_value;
+}
+static int em_nng_push0_open(nng_socket * s ) {
+       return return_value;
+}
+static int em_nng_dial(nng_socket s, const char * c, nng_dialer * d, int i ) {
+       //fprintf( stderr, "<info> === simulated dialing: %s\n", c );
+       return return_value;
+}
+static int em_nng_setopt(nng_socket s, const char * c, const void * p, size_t t ) {
+       return return_value;
+}
+static int em_nng_sub_open(nng_socket * s ) {
+       return return_value;
+}
+static int em_nng_sub0_open(nng_socket * s ) {
+       return return_value;
+}
+static int em_nng_recv(nng_socket s, void * v, size_t * t, int i ) {
+       return return_value;
+}
+static int em_nng_send( nng_socket s, void* m, int l, int f ) {
+       return return_value;
+}
+
+/*
+       Emulate sending a message. If the global em_send_failures is set,
+       then every so often we fail with an EAGAIN to drive that part 
+       of the code in RMr.
+*/
+static int em_sendmsg( nng_socket s, nng_msg* m, int i ) {
+       static int count = 0;
+
+       if( em_send_failures && (count++ % 15 == 14) ) {
+               //fprintf( stderr, ">>>> failing send\n\n" );
+               return NNG_EAGAIN;
+       }
+
+       return return_value;
+}
+
+static void* em_nng_alloc( size_t len ) {
+       return malloc( len );
+}
+
+static int em_nng_msg_alloc( nng_msg** mp, size_t l ) {
+       void*   p;
+
+       if( !mp || return_value != 0  ) {
+               return -1;
+       }
+
+       p = (void *) malloc( sizeof( char ) * l );
+       *mp = (nng_msg *) p;
+
+       return return_value;
+}
+
+/*
+       We just free the buffer here as it was a simple malloc.
+*/
+static void em_nng_free( void* p, size_t l ) {
+       if( p ) {
+               //fprintf( stderr, ">>>>> not freed: %p\n", p );
+               free( p );
+       }
+}
+static void em_nng_msg_free( void* p ) {
+       if( p ) {
+               //fprintf( stderr, ">>>>> not freed: %p\n", p );
+               free( p );
+       }
+}
+
+static int em_dialer_create( void* d, nng_socket s, char* stuff ) {
+       //fprintf( stderr, ">>>> emulated dialer create\n\n" );
+       return 0;
+}
+
+static int em_dialer_start( nng_dialer d, int i ) {
+       //fprintf( stderr, ">>>> emulated dialer start\n\n" );
+       return return_value;
+}
+
+
+static int em_dialer_setopt_ms( nng_dialer dialer, void* option, int ms ) {
+       return return_value;
+}
+
+static int em_nng_getopt_int( nng_socket s, void* con, int* target ) {
+       if( target ) {
+               *target = 0;
+       }
+       return return_value;
+}
+
+
+
+// nng redefines some of these to point directly to various 'versions' of the function (ugg, function versions, really?)
+#undef nng_recvmsg 
+#undef nng_free 
+#undef nng_pull_open 
+#undef nng_pull0_open 
+#undef nng_listen 
+#undef nng_close 
+#undef nng_getopt_int 
+#undef nng_push0_open 
+#undef nng_dial 
+#undef nng_setopt 
+#undef nng_sub_open 
+#undef nng_sub0_open 
+#undef nng_recv 
+#undef nng_alloc 
+
+#define nng_msg_alloc em_nng_msg_alloc
+#define nng_recvmsg em_nng_recvmsg
+#define nng_free em_nng_free
+#define nng_free em_nng_free
+#define nng_msg_free em_nng_msg_free
+#define nng_pull_open em_nng_pull_open
+#define nng_pull0_open em_nng_pull0_open
+#define nng_listen em_nng_listen
+#define nng_close em_nng_close
+#define nng_getopt_int em_nng_getopt_int
+#define nng_push0_open em_nng_push0_open
+#define nng_dial em_nng_dial
+#define nng_setopt em_nng_setopt
+#define nng_sub_open em_nng_sub_open
+#define nng_sub0_open em_nng_sub0_open
+#define nng_recv em_nng_recv
+#define nng_send em_nng_send
+#define nng_sendmsg em_sendmsg
+#define nng_alloc em_nng_alloc
+#define nng_free em_nng_free
+#define nng_dialer_setopt_ms em_dialer_setopt_ms
+#define nng_dialer_start em_dialer_start
+#define nng_dialer_create em_dialer_create
+#define nng_msg_body em_msg_body
+#define nng_msg_len em_msg_len
+
+
+#else
+
+
+// ----------------------- emulated nano functions --------------------------
+struct em_nn_msghdr {
+       int dummy;
+};
+
+static int em_nn_socket (int domain, int protocol ) {
+       static int s = 1;
+
+       return ++s;
+}
+
+static int em_nn_close (int s ) {
+       return 1;
+}
+
+static int em_nn_setsockopt (int s, int level, int option, const void *optval, size_t optvallen ) {
+       return 1;
+}
+
+static int em_nn_getsockopt (int s, int level, int option, void *optval, size_t *optvallen ) {
+       return 1;
+}
+
+static int em_nn_bind (int s, const char *addr ) {
+fprintf( stderr, ">>> ===== emulated bind called ====\n" );
+       return 1;
+}
+
+static int em_nn_connect (int s, const char *addr ) {
+       return 1;
+}
+
+static int em_nn_shutdown (int s, int how ) {
+       return 1;
+}
+
+static int em_nn_send (int s, const void *buf, size_t len, int flags ) {
+       return 1;
+}
+
+static int em_nn_recv (int s, void *buf, size_t len, int flags ) {
+       return 1;
+}
+
+static int em_sendmsg (int s, const struct em_nn_msghdr *msghdr, int flags ) {
+       return 1;
+}
+
+static int em_nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags ) {
+       return 1;
+}
+
+// nanomsg 
+#define nn_socket  em_nn_socket
+#define nn_close  em_nn_close
+#define nn_setsockopt  em_nn_setsockopt
+#define nn_getsockopt  em_nn_getsockopt
+#define nn_bind  em_nn_bind
+#define nn_connect  em_nn_connect
+#define nn_shutdown  em_nn_shutdown
+#define nn_send  em_nn_send
+#define nn_recv  em_nn_recv
+#define nn_sendmsg  em_nn_sendmsg
+#define nn_recvmsg  em_nn_recvmsg
+
+#endif 
+
+
+#endif
index d9a5f47..4ed976c 100644 (file)
        Date:           6 January 2019
 */
 
        Date:           6 January 2019
 */
 
+#ifndef _test_support_c
+#define _test_support_c
+
 #include <signal.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <signal.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 #ifndef BAD
 #define BAD 1                  // these are exit codes unless user overrides
 
 #ifndef BAD
 #define BAD 1                  // these are exit codes unless user overrides
@@ -81,21 +86,21 @@ static void set_signals( void ) {
 
 static int fail_if_nil( void* p, char* what ) {
        if( !p ) {
 
 static int fail_if_nil( void* p, char* what ) {
        if( !p ) {
-               fprintf( stderr, "[FAIL] pointer to '%s' was nil\n", what );
+               fprintf( stderr, "<FAIL> %s: pointer was nil\n", what );
        }
        return p ? GOOD : BAD;
 }
 
 static int fail_not_nil( void* p, char* what ) {
        if( p ) {
        }
        return p ? GOOD : BAD;
 }
 
 static int fail_not_nil( void* p, char* what ) {
        if( p ) {
-               fprintf( stderr, "[FAIL] pointer to '%s' was not nil\n", what );
+               fprintf( stderr, "<FAIL> %s: pointer was not nil\n", what );
        }
        return !p ? GOOD : BAD;
 }
 
 static int fail_if_false( int bv, char* what ) {
        if( !bv ) {
        }
        return !p ? GOOD : BAD;
 }
 
 static int fail_if_false( int bv, char* what ) {
        if( !bv ) {
-               fprintf( stderr, "[FAIL] boolean was false (%d) %s\n", bv, what );
+               fprintf( stderr, "<FAIL> %s: expected true, boolean test was false (%d)\n", what, bv );
        }
 
        return bv ? GOOD : BAD;
        }
 
        return bv ? GOOD : BAD;
@@ -103,7 +108,7 @@ static int fail_if_false( int bv, char* what ) {
 
 static int fail_if_true( int bv, char* what ) {
        if( bv ) {
 
 static int fail_if_true( int bv, char* what ) {
        if( bv ) {
-               fprintf( stderr, "[FAIL] boolean was true (%d) %s\n", bv, what );
+               fprintf( stderr, "<FAIL> %s: expected false, boolean test was true (%d)\n", what, bv );
        }
        return bv ? BAD : GOOD;
 }
        }
        return bv ? BAD : GOOD;
 }
@@ -114,21 +119,23 @@ static int fail_if_true( int bv, char* what ) {
 static int fail_if( int bv, char* what ) {
 
        if( bv ) {
 static int fail_if( int bv, char* what ) {
 
        if( bv ) {
-               fprintf( stderr, "[FAIL] boolean was true (%d) %s\n", bv, what );
+               fprintf( stderr, "<FAIL> %s: expected false, boolean test was true (%d)\n", what, bv );
        }
        return bv ? BAD : GOOD;
 }
 
 static int fail_not_equal( int a, int b, char* what ) {
        if( a != b ) {
        }
        return bv ? BAD : GOOD;
 }
 
 static int fail_not_equal( int a, int b, char* what ) {
        if( a != b ) {
-               fprintf( stderr, "[FAIL] %s values were not equal a=%d b=%d\n", what, a, b );
+               fprintf( stderr, "<FAIL> %s: values were not equal a=%d b=%d\n", what, a, b );
        }
        return a == b ? GOOD : BAD;                     // user may override good/bad so do NOT return a==b directly!
 }
 
 static int fail_if_equal( int a, int b, char* what ) {
        if( a == b ) {
        }
        return a == b ? GOOD : BAD;                     // user may override good/bad so do NOT return a==b directly!
 }
 
 static int fail_if_equal( int a, int b, char* what ) {
        if( a == b ) {
-               fprintf( stderr, "[FAIL] %s values were equal a=%d b=%d\n", what, a, b );
+               fprintf( stderr, "<FAIL> %s values were equal a=%d b=%d\n", what, a, b );
        }
        return a != b ? GOOD : BAD;                     // user may override good/bad so do NOT return a==b directly!
 }
        }
        return a != b ? GOOD : BAD;                     // user may override good/bad so do NOT return a==b directly!
 }
+
+#endif
diff --git a/test/tools_static_test.c b/test/tools_static_test.c
new file mode 100644 (file)
index 0000000..66ddeb6
--- /dev/null
@@ -0,0 +1,154 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+
+/*
+       Mnemonic:       tools_static_test.c
+       Abstract:       Unit tests for the RMr tools module. This file is a static include
+                               that is pulle in at compile time by the test driver.  The driver is
+                               expected to include necessary rmr*.h and test_support files before
+                               including this file.  In addition, a context struct, or dummy, must
+                               be provided based on the type of testing being done.
+
+       Author:         E. Scott Daniels
+       Date:           3 April 2019
+*/
+
+
+static int tools_test( ) {
+       int i;
+       int j;
+       int errors = 0;
+       char* tokens[127];
+       char* buf = "2,Fred,Wilma,Barney,Betty,Dino,Pebbles,Bambam,Mr. Slate,Gazoo";
+       char*   dbuf;                           // duplicated buf since C marks a const string is unumtable
+       char*   hname;
+       uta_ctx_t ctx;                          // context for uta_lookup test
+       void*   if_list;
+
+       
+       // ------------------ tokenise tests -----------------------------------------------------------
+       dbuf = strdup( buf );
+       i = uta_tokenise( dbuf, tokens, 127, ',' );
+       errors += fail_not_equal( i, 10, "unexpected number of tokens returned (comma sep)" );
+       for( j = 0; j < i; j++ ) {
+               //fprintf( stderr, ">>>> [%d] (%s)\n", j, tokens[j] );
+               errors += fail_if_nil( tokens[j], "token from buffer" );
+       }
+       errors += fail_not_equal( strcmp( tokens[4], "Betty" ), 0, "4th token wasn't 'Betty'" );
+
+       free( dbuf );
+       dbuf = strdup( buf );
+       i = uta_tokenise( dbuf, tokens, 127, '|' );
+       errors += fail_not_equal( i, 1, "unexpected number of tokens returned (bar sep)" );
+       free( dbuf );
+
+       // ------------ has str tests -----------------------------------------------------------------
+       j = uta_has_str( buf, "Mr. Slate", ',', 1 );                    // should fail (-1) because user should use strcmp in this situation
+       errors += fail_if_true( j >= 0, "test to ensure has str rejects small max" );
+
+       j = uta_has_str( buf, "Mr. Slate", ',', 27 );
+       errors += fail_if_true( j < 0, "has string did not find Mr. Slate" );
+
+       j = uta_has_str( buf, "Mrs. Slate", ',', 27 );
+       errors += fail_if_true( j >= 0, "has string not found Mrs. Slate" );
+       
+       // ------------ host name 2 ip tests ---------------------------------------------------------
+       hname = uta_h2ip( "192.168.1.2" );
+       errors += fail_not_equal( strcmp( hname, "192.168.1.2" ), 0, "h2ip did not return IP address when given address" );
+       errors += fail_if_nil( hname, "h2ip did not return a pointer" );
+
+       hname = uta_h2ip( "yahoo.com" );
+       errors += fail_if_nil( hname, "h2ip did not return a pointer" );
+
+       hname = uta_h2ip( "yahoo.com:1234" );                                                   // should ignore the port
+       errors += fail_if_nil( hname, "h2ip did not return a pointer" );
+
+       // ------------ rtg lookup test -------------------------------------------------------------
+       ctx.rtg_port = 0;
+       ctx.rtg_addr = NULL;
+       
+       i = uta_lookup_rtg( NULL );                                             // ensure it handles a nil context
+       errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to (nil context)" );
+
+       setenv( "RMR_RTG_SVC", "localhost:1234", 1);
+       i = uta_lookup_rtg( &ctx );
+       errors += fail_if_false( i, "rtg lookup returned that it did not find something when expected to" );
+       errors += fail_if_nil( ctx.rtg_addr, "rtg lookup did not return a pointer (with port)" );
+       errors += fail_not_equal( ctx.rtg_port, 1234, "rtg lookup did not capture the port" );
+
+       setenv( "RMR_RTG_SVC", "localhost", 1);                 // test ability to generate default port
+       uta_lookup_rtg( &ctx );
+       errors += fail_if_nil( ctx.rtg_addr, "rtg lookup did not return a pointer (no port)" );
+       errors += fail_not_equal( ctx.rtg_port, 5656, "rtg lookup did not return default port" );
+
+       unsetenv( "RMR_RTG_SVC" );                                              // this should fail as the default name (rtg) will be unknown during testing
+       i = uta_lookup_rtg( &ctx );
+       errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to" );
+
+/*
+//==== moved out of generic tools ==========
+       // -------------- test link2 stuff ----------------------------------------------------------
+       i = uta_link2( "bad" );                                 // should fail
+       errors += fail_if_true( i >= 0, "uta_link2 didn't fail when given bad address" );
+
+       i = uta_link2( "nohost:-1234" );
+       errors += fail_if_true( i >= 0, "uta_link2 did not failed when given a bad (negative) port " );
+
+       i = uta_link2( "nohost:1234" );                                 // nn should go off and set things up, but it will never successd, but uta_ call should
+       errors += fail_if_true( i < 0, "uta_link2 failed when not expected to" );
+*/
+
+       // ------------ my ip stuff -----------------------------------------------------------------
+
+       if_list = mk_ip_list( "1235" );
+       errors += fail_if_nil( if_list, "mk_ip_list returned nil pointer" );
+
+       i = has_myip( NULL, NULL, ',', 128 );           // should be false if pointers are nil
+       errors += fail_if_true( i, "has_myip returned true when given nil buffer" );
+
+       i = has_myip( "buffer contents not valid", NULL, ',', 128 );            // should be false if pointers are nil
+       errors += fail_if_true( i, "has_myip returned true when given nil list" );
+
+       i = has_myip( "buffer contents not valid", NULL, ',', 1 );                      // should be false if max < 2
+       errors += fail_if_true( i, "has_myip returned true when given small max value" );
+
+       i = has_myip( "buffer.contents.not.valid", if_list, ',', 128 );         // should be false as there is nothing valid in the list
+       errors += fail_if_true( i, "has_myip returned true when given a buffer with no valid info" );
+
+
+       setenv( "RMR_BIND_IF", "192.168.4.30", 1 );                     // drive the case where we have a hard set interface; and set known interface in list
+       if_list = mk_ip_list( "1235" );
+       errors += fail_if_nil( if_list, "mk_ip_list with env set returned nil pointer" );
+
+       i = has_myip( "192.168.1.2:1235,192.168.4.30:1235,192.168.2.19:4567", if_list, ',', 128 );              // should find our ip in middle
+       errors += fail_if_false( i, "has_myip did not find IP in middle of list" );
+
+       i = has_myip( "192.168.4.30:1235,192.168.2.19:4567,192.168.2.19:2222", if_list, ',', 128 );             // should find our ip at head
+       errors += fail_if_false( i, "has_myip did not find IP at head of list" );
+
+       i = has_myip( "192.168.23.45:4444,192.168.1.2:1235,192.168.4.30:1235", if_list, ',', 128 );             // should find our ip at end
+       errors += fail_if_false( i, "has_myip did not find IP at tail of list" );
+
+       i = has_myip( "192.168.4.30:1235", if_list, ',', 128 );                                                                                 // should find our ip when only in list
+       errors += fail_if_false( i, "has_myip did not find IP when only one in list" );
+
+       return !!errors;                        // 1 or 0 regardless of count
+}
index 786d2e6..a29c0e2 100644 (file)
 #include <pthread.h>
 #include <ctype.h>
 
 #include <pthread.h>
 #include <ctype.h>
 
-/*
-#include <nanomsg/nn.h>
-#include <nanomsg/tcp.h>
-#include <nanomsg/pair.h>
-#include <nanomsg/pipeline.h>
-#include <nanomsg/pubsub.h>
-*/
-
 #include "../src/common/include/rmr.h"
 #include "../src/common/include/rmr_agnostic.h"
 #include "test_support.c"              // our private library of test tools
 #include "../src/common/include/rmr.h"
 #include "../src/common/include/rmr_agnostic.h"
 #include "test_support.c"              // our private library of test tools
@@ -148,18 +140,6 @@ int main( ) {
        i = uta_lookup_rtg( &ctx );
        errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to" );
 
        i = uta_lookup_rtg( &ctx );
        errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to" );
 
-/*
-//==== moved out of generic tools ==========
-       // -------------- test link2 stuff ----------------------------------------------------------
-       i = uta_link2( "bad" );                                 // should fail
-       errors += fail_if_true( i >= 0, "uta_link2 didn't fail when given bad address" );
-
-       i = uta_link2( "nohost:-1234" );
-       errors += fail_if_true( i >= 0, "uta_link2 did not failed when given a bad (negative) port " );
-
-       i = uta_link2( "nohost:1234" );                                 // nn should go off and set things up, but it will never successd, but uta_ call should
-       errors += fail_if_true( i < 0, "uta_link2 failed when not expected to" );
-*/
 
        // ------------ my ip stuff -----------------------------------------------------------------
 
 
        // ------------ my ip stuff -----------------------------------------------------------------
 
index fb5fa2a..745cf07 100755 (executable)
 #                              but only the result of the discount test is taken into 
 #                              consideration with regard to overall success.
 #
 #                              but only the result of the discount test is taken into 
 #                              consideration with regard to overall success.
 #
+#                              Overall Pass/Fail
+#                              By default the overall state is based only on the success
+#                              or failure of the unit tests and NOT on the perceived 
+#                              state of coverage.  If the -s (strict) option is given, then
+#                              overall state will be failure if code coverage expectations
+#                              are not met.
+#
 #      Date:           16 January 2018
 #      Author:         E. Scott Daniels
 # -------------------------------------------------------------------------
 
 function usage {
 #      Date:           16 January 2018
 #      Author:         E. Scott Daniels
 # -------------------------------------------------------------------------
 
 function usage {
-       echo "usage: $0 [-G|-M|-C custom-command-string] [-c cov-target]  [-f] [-v]  [files]"
+       echo "usage: $0 [-G|-M|-C custom-command-string] [-c cov-target]  [-f] [-F] [-v]  [files]"
        echo "  if -C is used to provide a custom build command then it must "
        echo "  contain a %s which will be replaced with the unit test file name."
        echo '  e.g.:  -C "mk -a %s"'
        echo "  -c allows user to set the target coverage for a module to pass; default is 80"
        echo "  -f forces a discount check (normally done only if coverage < target)"
        echo "  if -C is used to provide a custom build command then it must "
        echo "  contain a %s which will be replaced with the unit test file name."
        echo '  e.g.:  -C "mk -a %s"'
        echo "  -c allows user to set the target coverage for a module to pass; default is 80"
        echo "  -f forces a discount check (normally done only if coverage < target)"
+       echo "  -F show only failures at the function level"
+       echo "  -s strict mode; code coverage must also pass to result in a good exit code"
        echo "  -v will write additional information to the tty and save the disccounted file if discount run or -f given"
 }
 
        echo "  -v will write additional information to the tty and save the disccounted file if discount run or -f given"
 }
 
@@ -82,7 +91,7 @@ function add_ignored_func {
                }
        ' | while read f
        do
                }
        ' | while read f
        do
-               iflist+="$f "   
+               iflist="${iflist}$f "   
        done
 }
 
        done
 }
 
@@ -90,7 +99,7 @@ function add_ignored_func {
 #      Parse the .gcov file and discount any unexecuted lines which are in if() 
 #      blocks that are testing the result of alloc/malloc calls, or testing for
 #      nil pointers.  The feeling is that these might not be possible to drive
 #      Parse the .gcov file and discount any unexecuted lines which are in if() 
 #      blocks that are testing the result of alloc/malloc calls, or testing for
 #      nil pointers.  The feeling is that these might not be possible to drive
-#      and shoudn't contribute to coverage deficencies.
+#      and shoudn't contribute to coverage deficiencies.
 #
 #      In verbose mode, the .gcov file is written to stdout and any unexecuted
 #      line which is discounted is marked with ===== replacing the ##### marking
 #
 #      In verbose mode, the .gcov file is written to stdout and any unexecuted
 #      line which is discounted is marked with ===== replacing the ##### marking
@@ -114,9 +123,11 @@ function discount_an_checks {
        fi
 
        awk -v module_cov_target=$mct \
        fi
 
        awk -v module_cov_target=$mct \
+               -v cfail=${cfail:-WARN} \
+               -v show_all=$show_all \
                -v full_name="${1}"  \
                -v module="${f%.*}"  \
                -v full_name="${1}"  \
                -v module="${f%.*}"  \
-               -v chatty=$verbose \
+               -v chatty=1 \
        '
        function spit_line( ) {
                if( chatty ) {
        '
        function spit_line( ) {
                if( chatty ) {
@@ -201,13 +212,15 @@ function discount_an_checks {
                net = unexec - discount
                orig_cov = ((nexec-unexec)/nexec)*100           # original coverage
                adj_cov = ((nexec-net)/nexec)*100                       # coverage after discount
                net = unexec - discount
                orig_cov = ((nexec-unexec)/nexec)*100           # original coverage
                adj_cov = ((nexec-net)/nexec)*100                       # coverage after discount
-               pass_fail = adj_cov < module_cov_target ? "FAIL" : "PASS"
+               pass_fail = adj_cov < module_cov_target ? cfail : "PASS"
                rc = adj_cov < module_cov_target ? 1 : 0
                rc = adj_cov < module_cov_target ? 1 : 0
-               if( chatty ) {
-                       printf( "[%s] %s executable=%d unexecuted=%d discounted=%d net_unex=%d  cov=%d% ==> %d%%%  target=%d%%\n", 
-                               pass_fail, full_name ? full_name : module, nexec, unexec, discount, net, orig_cov, adj_cov, module_cov_target )
-               } else {
-                       printf( "[%s] %d%% (%d%%) %s\n", pass_fail, adj_cov, orig_cov, full_name ? full_name : module )
+               if( pass_fail == cfail || show_all ) {
+                       if( chatty ) {
+                               printf( "[%s] %s executable=%d unexecuted=%d discounted=%d net_unex=%d  cov=%d% ==> %d%%%  target=%d%%\n", 
+                                       pass_fail, full_name ? full_name : module, nexec, unexec, discount, net, orig_cov, adj_cov, module_cov_target )
+                       } else {
+                               printf( "[%s] %d%% (%d%%) %s\n", pass_fail, adj_cov, orig_cov, full_name ? full_name : module )
+                       }
                }
 
                exit( rc )
                }
 
                exit( rc )
@@ -232,12 +245,27 @@ function get_mct {
 
 # ------------------------------------------------------------------------
 
 
 # ------------------------------------------------------------------------
 
+# we assume that the project has been built in the ../[.]build directory
+if [[ -d ../build/lib ]]
+then
+       export LD_LIBRARY_PATH=../build/lib
+else
+       if [[ -d ../.build/lib ]]
+       then
+               export LD_LIBRARY_PATH=../.build/lib
+       else
+               echo "[WARN] cannot find ../[.]build/lib; things might not work"
+               echo ""
+       fi
+fi
+
 export C_INCLUDE_PATH="../src/common/include"
 
 module_cov_target=80
 builder="make -B %s"           # default to plain ole make
 verbose=0
 export C_INCLUDE_PATH="../src/common/include"
 
 module_cov_target=80
 builder="make -B %s"           # default to plain ole make
 verbose=0
-trigger_discount_str="FAIL"
+show_all=1                                     # show all things -F sets to show failures only
+strict=0                                       # -s (strict) will set; when off, coverage state ignored in final pass/fail
 
 while [[ $1 == "-"* ]]
 do
 
 while [[ $1 == "-"* ]]
 do
@@ -248,9 +276,12 @@ do
 
                -c)     module_cov_target=$2; shift;;
                -f)     force_discounting=1; 
 
                -c)     module_cov_target=$2; shift;;
                -f)     force_discounting=1; 
-                       trigger_discount_str="FAIL|PASS"                # check all outcomes for each module
+                       trigger_discount_str="WARN|FAIL|PASS"           # check all outcomes for each module
                        ;;
 
                        ;;
 
+               -F)     show_all=0;;
+
+               -s)     strict=1;;                                      # coverage counts toward pass/fail state
                -v)     (( verbose++ ));;
 
                -h)     usage; exit 0;;
                -v)     (( verbose++ ));;
 
                -h)     usage; exit 0;;
@@ -266,17 +297,35 @@ do
        shift
 done
 
        shift
 done
 
+
+if (( strict ))                # if in strict mode, coverage shortcomings are failures
+then
+       cfail="FAIL"
+else
+       cfail="WARN"
+fi
+if [[ -z $trigger_discount_str ]]
+then
+       trigger_discount_str="$cfail"
+fi
+
+
 if [[ -z $1 ]]
 then
        flist=""
        for tfile in *_test.c
        do
 if [[ -z $1 ]]
 then
        flist=""
        for tfile in *_test.c
        do
-               flist+="$tfile "
+               if [[ $tfile != *"static_test.c" ]]
+               then
+                       flist="${flist}$tfile "
+               fi
        done
 else
        flist="$@"
 fi
 
        done
 else
        flist="$@"
 fi
 
+
+ut_errors=0                    # unit test errors (not coverage errors)
 errors=0
 for tfile in $flist
 do
 errors=0
 for tfile in $flist
 do
@@ -293,12 +342,18 @@ do
        iflist="main sig_clean_exit "           # ignore external functions from our tools
        add_ignored_func $tfile                         # ignore all static functions in our test driver
        add_ignored_func test_support.c         # ignore all static functions in our test tools
        iflist="main sig_clean_exit "           # ignore external functions from our tools
        add_ignored_func $tfile                         # ignore all static functions in our test driver
        add_ignored_func test_support.c         # ignore all static functions in our test tools
+       add_ignored_func test_nng_em.c          # the nng/nano emulated things
+       for f in *_static_test.c                        # all static modules here
+       do
+               add_ignored_func $f
+       done
        
        if ! ${tfile%.c} >/tmp/PID$$.log 2>&1
        then
                echo "[FAIL] unit test failed for: $tfile"
                cat /tmp/PID$$.log
        
        if ! ${tfile%.c} >/tmp/PID$$.log 2>&1
        then
                echo "[FAIL] unit test failed for: $tfile"
                cat /tmp/PID$$.log
-               continue
+               (( ut_errors++ ))                               # cause failure even if not in strict mode
+               continue                                                # skip coverage tests for this
        fi
 
        (
        fi
 
        (
@@ -306,11 +361,14 @@ do
                sed '/^#/ d; /^$/ d; s/^/TARGET: /' ./.targets
                gcov -f ${tfile%.c} | sed "s/'//g" 
        ) | awk \
                sed '/^#/ d; /^$/ d; s/^/TARGET: /' ./.targets
                gcov -f ${tfile%.c} | sed "s/'//g" 
        ) | awk \
+               -v cfail=$cfail \
+               -v show_all=$show_all \
                -v ignore_list="$iflist" \
                -v module_cov_target=$module_cov_target \
                -v chatty=$verbose \
                '
                BEGIN {
                -v ignore_list="$iflist" \
                -v module_cov_target=$module_cov_target \
                -v chatty=$verbose \
                '
                BEGIN {
+                       announce_target = 1;
                        nignore = split( ignore_list, ignore, " " )
                        for( i = 1; i <= nignore; i++ ) {
                                imap[ignore[i]] = 1
                        nignore = split( ignore_list, ignore, " " )
                        for( i = 1; i <= nignore; i++ ) {
                                imap[ignore[i]] = 1
@@ -359,30 +417,48 @@ do
                        pct = a[2]+0
 
                        if( file ) {
                        pct = a[2]+0
 
                        if( file ) {
+                               if( announce_target ) {                         # announce default once at start
+                                       announce_target = 0;
+                                       printf( "\n[INFO] default target coverage for modules is %d%%\n", module_cov_target )
+                               }
+
                                if( target[fname] ) {
                                        mct = target[fname] 
                                if( target[fname] ) {
                                        mct = target[fname] 
+                                       announce_target = 1;
                                } else {
                                        mct = module_cov_target
                                }
                                } else {
                                        mct = module_cov_target
                                }
-                               if( chatty ) {
+
+                               if( announce_target ) {                                 # annoucne for module if different from default
                                        printf( "[INFO] target coverage for %s is %d%%\n", fname, mct )
                                }
                                        printf( "[INFO] target coverage for %s is %d%%\n", fname, mct )
                                }
+
                                if( pct < mct ) {
                                if( pct < mct ) {
-                                       printf( "[FAIL] %3d%% %s\n\n", pct, fname )     # CAUTION: write only 3 things  here
+                                       printf( "[%s] %3d%% %s\n", cfail, pct, fname )  # CAUTION: write only 3 things  here
                                        exit_code = 1
                                } else {
                                        exit_code = 1
                                } else {
-                                       printf( "[PASS] %3d%% %s\n\n", pct, fname )
+                                       printf( "[PASS] %3d%% %s\n", pct, fname )
                                }
                                }
+
+                               announce_target = 0;
                        } else {
                        } else {
-                               if( pct < 80 ) {
+                               if( pct < 70 ) {
                                        printf( "[LOW]  %3d%% %s\n", pct, fname )
                                } else {
                                        printf( "[LOW]  %3d%% %s\n", pct, fname )
                                } else {
-                                       printf( "[OK]   %3d%% %s\n", pct, fname )
+                                       if( pct < 80 ) {
+                                               printf( "[MARG] %3d%% %s\n", pct, fname )
+                                       } else {
+                                               if( show_all ) {
+                                                       printf( "[OK]   %3d%% %s\n", pct, fname )
+                                               }
+                                       }
                                }
                        }
                                }
                        }
+
                }
 
                END {
                }
 
                END {
+                       printf( "\n" );
                        exit( exit_code )
                }
        ' >/tmp/PID$$.log                                       # capture output to run discount on failures
                        exit( exit_code )
                }
        ' >/tmp/PID$$.log                                       # capture output to run discount on failures
@@ -390,27 +466,52 @@ do
        cat /tmp/PID$$.log
        if (( rc  || force_discounting ))       # didn't pass, or forcing, see if discounting helps
        then
        cat /tmp/PID$$.log
        if (( rc  || force_discounting ))       # didn't pass, or forcing, see if discounting helps
        then
+               show_all=1
+               if (( ! verbose ))
+               then
+                       echo "[INFO] checking to see if discounting improves coverage for failures listed above"
+               fi
+
                egrep "$trigger_discount_str"  /tmp/PID$$.log | while read state junk  name
                do
                egrep "$trigger_discount_str"  /tmp/PID$$.log | while read state junk  name
                do
-                       echo "[INFO] checking to see if discounting improves coverage for $name"
                        if ! discount_an_checks $name.gcov >/tmp/PID$$.disc
                        then
                                (( errors++ ))
                        fi
                        if ! discount_an_checks $name.gcov >/tmp/PID$$.disc
                        then
                                (( errors++ ))
                        fi
+
                        tail -1 /tmp/PID$$.disc
                        tail -1 /tmp/PID$$.disc
-                       if (( verbose ))                        # updated file was generated, keep here
+
+                       if (( verbose > 1 ))                    # updated file was generated, keep here
                        then
                                echo "[INFO] discounted coverage info in: ${tfile##*/}.dcov"
                        then
                                echo "[INFO] discounted coverage info in: ${tfile##*/}.dcov"
-                               mv /tmp/PID$$.disc ${tfile##*/}.dcov
                        fi
                        fi
+
+                       mv /tmp/PID$$.disc ${name##*/}.dcov
                done
        fi
 done
 
                done
        fi
 done
 
+state=0                                                # final state
 rm -f /tmp/PID$$.*
 rm -f /tmp/PID$$.*
-if (( errors ))
+if (( strict ))                                # fail if some coverage failed too
 then
 then
-       exit 1
+       if (( errors + ut_errors ))
+       then
+               state=1
+       fi
+else                                           # not strict; fail only if unit tests themselves failed
+       if (( ut_errors ))
+       then
+               state=1
+       fi
+fi
+
+echo""
+if (( state ))
+then
+       echo "[FAIL] overall unit testing fails: coverage errors=$errors   unit test errors=$ut_errors"
+else
+       echo "[PASS] overall unit testing passes"
 fi
 fi
-exit 0
+exit $state
 
 
diff --git a/test/wormhole_static_test.c b/test/wormhole_static_test.c
new file mode 100644 (file)
index 0000000..7a4246f
--- /dev/null
@@ -0,0 +1,151 @@
+// : vi ts=4 sw=4 noet :
+/*
+==================================================================================
+        Copyright (c) 2019 Nokia 
+        Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+   Licensed 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.
+==================================================================================
+*/
+
+/*
+       Mmemonic:       wormhole_static.c
+       Abstract:       Specific tests for wormhole. This module is included directly by
+                               the test driver at compile time.
+
+       Author:         E. Scott Daniels
+       Date:           3 April 2019
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "../src/common/include/rmr.h"
+#include "../src/common/include/rmr_agnostic.h"
+
+
+/*
+       Note that the last tests in this function destroy the context and message so
+       any tests added MUST be ahead of those tests. 
+*/
+static int worm_test( ) {
+       uta_ctx_t* ctx;                 // context needed to test load static rt
+       char    wbuf[1024];
+       int errors = 0;                 // number errors found
+       int     i;
+
+       rmr_mbuf_t*     mbuf;           // mbuf to send to peer
+       int             whid = -1;
+       int             last_whid;
+
+       ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) );
+       if( ctx == NULL ) {
+               fail_if_nil( ctx, "could not allocate dummy context" );
+               return 1;
+       }
+       memset( ctx, 0, sizeof( *ctx ) );
+       ctx->my_name = strdup( "tester" );
+
+       gen_rt( ctx );
+
+       whid = rmr_wh_open( NULL, NULL );
+       errors += fail_not_equal( whid, -1, "call to wh_open with invalid values did not return bad whid" );
+
+
+       whid = rmr_wh_open( ctx, NULL );
+       errors += fail_not_equal( whid, -1, "call to wh_open with invalid target did not return bad whid" );
+
+       whid = rmr_wh_open( ctx, "" );
+       errors += fail_not_equal( whid, -1, "call to wh_open with empty target did not return bad whid" );
+
+       whid = rmr_wh_open( ctx, "localhost:89219" );
+       errors += fail_if_equal( whid, -1, "call to wh_open with valid target failed" );
+
+       rmr_wh_close( ctx, 4 );                                 // test for coverage only; [5] should have nil pointer
+       rmr_wh_close( ctx, 50 );                                        // test for coverage only; more than allocated reference
+
+       last_whid = whid;
+       whid = rmr_wh_open( ctx, "localhost:89219" );
+       errors += fail_not_equal( whid, last_whid, "call to wh_open with duplicate target did not return the same whid" );
+
+       for( i = 0; i < 20; i++ ) {                                                                                             // test ability to extend the table
+               snprintf( wbuf, sizeof( wbuf ), "localhost:864%02d", i );                       // new address for each so whid is different
+               whid = rmr_wh_open( ctx, wbuf );
+               snprintf( wbuf, sizeof( wbuf ), "call to wh_open failed for iteration = %d", i );
+               errors += fail_if_equal( whid, -1, wbuf );
+               if( i ) {
+                       snprintf( wbuf, sizeof( wbuf ), "call to wh_open for iteration = %d returned same whid: %d", i, whid );
+                       errors += fail_if_equal( whid, last_whid, wbuf );
+               }
+
+               last_whid = whid;
+       }
+
+       rmr_wh_close( ctx, 3 );         // close one, then open a new one to verify that hole is found
+       whid = rmr_wh_open( ctx, "localhost:21961" );
+       errors += fail_not_equal( whid, 3, "attempt to fill in a hole didn't return expected" );
+
+       rmr_wh_send_msg( NULL, 0, NULL );                       // tests for coverage
+       rmr_wh_send_msg( ctx, 0, NULL );
+
+       mbuf = rmr_alloc_msg( ctx, 2048 );                      // get an muf to pass round
+       errors += fail_if_nil( mbuf, "unable to allocate mbuf for send tests (giving up on send tests)" );
+       while( mbuf ) {
+               if( !(mbuf = rmr_wh_send_msg( ctx, 50, mbuf )) ) {              // test for coverage
+                       errors += fail_if_nil( mbuf, "send didn't return an mbuf (skip rest of send tests)" );
+                       break;
+               }
+
+               mbuf = rmr_wh_send_msg( ctx, 4, mbuf );
+               errors += fail_not_equal( mbuf->state, RMR_OK, "valid wormhole send failed" );
+               errors += fail_not_equal( errno, 0, "errno after valid wormhole send was not 0" );
+
+               rmr_wh_close( ctx, 4 );
+               mbuf = rmr_wh_send_msg( ctx, 4, mbuf );
+               rmr_wh_send_msg( ctx, 4, mbuf );
+               errors += fail_not_equal( mbuf->state, RMR_ERR_WHID, "send on closed wormhole didn't set correct state in msg" );
+
+               break;
+       }
+
+
+       // WARNING:  these tests destroy the context, so they MUST be last
+       if( mbuf ) {                    // only if we got an mbuf
+               errno = 0;
+               mbuf->header = NULL;
+               mbuf = rmr_wh_send_msg( ctx, 5, mbuf );         // coverage test on mbuf header check
+               errors += fail_not_equal( errno, EBADMSG, "wh_send didn't set errno after bad mbuf send" );
+               errors += fail_not_equal( mbuf->state, RMR_ERR_NOHDR, "send with bad header did now set msg state correctly" );
+
+               errno = 0;
+               wh_nuke( ctx );
+               ctx->wormholes = NULL;
+               mbuf = rmr_wh_send_msg( ctx, 4, mbuf );         // coverage test on mbuf header check
+               errors += fail_not_equal( errno, EINVAL, "wh_send didn't set errno after send without wormole reference" );
+               errors += fail_not_equal( mbuf->state, RMR_ERR_NOWHOPEN, "wh_send didn't set msg state after send without wormole reference" );
+
+               rmr_free_msg( mbuf );
+       }
+
+       if( ctx ) {
+               free( ctx->my_name );
+               free( ctx );
+       }
+
+       return !!errors;                        // 1 or 0 regardless of count
+}