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
 
-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
-       $(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"
 
+
+#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
index 85f96ed..5adc3f8 100644 (file)
@@ -1,21 +1,25 @@
 
 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 --------------------------------------
@@ -26,18 +30,29 @@ ring_test.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.
 
 
-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
@@ -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 (###).
 
-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
-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:
@@ -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
-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)
-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
-(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
-       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
-       Date:           31 July 2017
+       Date:           3 April 2019
 */
 
 #include <unistd.h>
 #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
 */
 
+#ifndef _test_support_c
+#define _test_support_c
+
 #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
@@ -81,21 +86,21 @@ static void set_signals( void ) {
 
 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 ) {
-               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 ) {
-               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;
@@ -103,7 +108,7 @@ static int fail_if_false( int bv, char* what ) {
 
 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;
 }
@@ -114,21 +119,23 @@ static int fail_if_true( int bv, char* what ) {
 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 ) {
-               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 ) {
-               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!
 }
+
+#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 <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
@@ -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" );
 
-/*
-//==== 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 -----------------------------------------------------------------
 
index fb5fa2a..745cf07 100755 (executable)
 #                              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 {
-       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 "  -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"
 }
 
@@ -82,7 +91,7 @@ function add_ignored_func {
                }
        ' | while read f
        do
-               iflist+="$f "   
+               iflist="${iflist}$f "   
        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
-#      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
@@ -114,9 +123,11 @@ function discount_an_checks {
        fi
 
        awk -v module_cov_target=$mct \
+               -v cfail=${cfail:-WARN} \
+               -v show_all=$show_all \
                -v full_name="${1}"  \
                -v module="${f%.*}"  \
-               -v chatty=$verbose \
+               -v chatty=1 \
        '
        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
-               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
-               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 )
@@ -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
-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
@@ -248,9 +276,12 @@ do
 
                -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;;
@@ -266,17 +297,35 @@ do
        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
-               flist+="$tfile "
+               if [[ $tfile != *"static_test.c" ]]
+               then
+                       flist="${flist}$tfile "
+               fi
        done
 else
        flist="$@"
 fi
 
+
+ut_errors=0                    # unit test errors (not coverage errors)
 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
+       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
-               continue
+               (( ut_errors++ ))                               # cause failure even if not in strict mode
+               continue                                                # skip coverage tests for this
        fi
 
        (
@@ -306,11 +361,14 @@ do
                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 {
+                       announce_target = 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 ) {
+                               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] 
+                                       announce_target = 1;
                                } 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 )
                                }
+
                                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 {
-                                       printf( "[PASS] %3d%% %s\n\n", pct, fname )
+                                       printf( "[PASS] %3d%% %s\n", pct, fname )
                                }
+
+                               announce_target = 0;
                        } else {
-                               if( pct < 80 ) {
+                               if( pct < 70 ) {
                                        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 {
+                       printf( "\n" );
                        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
+               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
-                       echo "[INFO] checking to see if discounting improves coverage for $name"
                        if ! discount_an_checks $name.gcov >/tmp/PID$$.disc
                        then
                                (( errors++ ))
                        fi
+
                        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"
-                               mv /tmp/PID$$.disc ${tfile##*/}.dcov
                        fi
+
+                       mv /tmp/PID$$.disc ${name##*/}.dcov
                done
        fi
 done
 
+state=0                                                # final state
 rm -f /tmp/PID$$.*
-if (( errors ))
+if (( strict ))                                # fail if some coverage failed too
 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
-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
+}