test(unit): Extend unit tests
Change-Id: I28148ce6ef070ad85cb80fc21f090ff47e856d79
Signed-off-by: E. Scott Daniels <daniels@research.att.com>
diff --git a/test/Makefile b/test/Makefile
index 20d461e..f23f838 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -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
diff --git a/test/README b/test/README
index 85f96ed..5adc3f8 100644
--- a/test/README
+++ b/test/README
@@ -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 @@
[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 @@
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 @@
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
index 0000000..9d7a38c
--- /dev/null
+++ b/test/hdr_static_test.c
@@ -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
index 0000000..4c0cd5e
--- /dev/null
+++ b/test/mbuf_api_static_test.c
@@ -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
index 0000000..858efdc
--- /dev/null
+++ b/test/mbuf_api_test.c
@@ -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
index 0000000..482244c
--- /dev/null
+++ b/test/ring_static_test.c
@@ -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;
+}
diff --git a/test/ring_test.c b/test/ring_test.c
index 331f976..a06668f 100644
--- a/test/ring_test.c
+++ b/test/ring_test.c
@@ -20,9 +20,12 @@
/*
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>
@@ -37,123 +40,19 @@
#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];
+int main( ) {
+ int errors = 0;
- 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 );
+ errors += ring_test( );
- 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 );
+ if( errors ) {
+ fprintf( stderr, "<FAIL> ring tests failed\n" );
+ } else {
+ fprintf( stderr, "<OK> ring tests pass\n" );
}
- 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" );
-
- 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 );
-
- size++;
- }
-
- 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
index 0000000..e9ecb72
--- /dev/null
+++ b/test/rmr_nng_api_static_test.c
@@ -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
index 0000000..1ebdb94
--- /dev/null
+++ b/test/rmr_nng_test.c
@@ -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
index 0000000..f1e6981
--- /dev/null
+++ b/test/rt_static_test.c
@@ -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
index 0000000..69090a1
--- /dev/null
+++ b/test/sr_nng_static_test.c
@@ -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
index 0000000..181cd13
--- /dev/null
+++ b/test/symtab_static_test.c
@@ -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
index 0000000..76fe72b
--- /dev/null
+++ b/test/test_nng_em.c
@@ -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
diff --git a/test/test_support.c b/test/test_support.c
index d9a5f47..4ed976c 100644
--- a/test/test_support.c
+++ b/test/test_support.c
@@ -27,10 +27,15 @@
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 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_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( 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
index 0000000..66ddeb6
--- /dev/null
+++ b/test/tools_static_test.c
@@ -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
+}
diff --git a/test/tools_test.c b/test/tools_test.c
index 786d2e6..a29c0e2 100644
--- a/test/tools_test.c
+++ b/test/tools_test.c
@@ -36,14 +36,6 @@
#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 @@
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 -----------------------------------------------------------------
diff --git a/test/unit_test.ksh b/test/unit_test.ksh
index fb5fa2a..745cf07 100755
--- a/test/unit_test.ksh
+++ b/test/unit_test.ksh
@@ -51,17 +51,26 @@
# 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 @@
}
' | while read f
do
- iflist+="$f "
+ iflist="${iflist}$f "
done
}
@@ -90,7 +99,7 @@
# 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 @@
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 @@
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 @@
# ------------------------------------------------------------------------
+# 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 @@
-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 @@
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 @@
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 @@
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 @@
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 @@
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
-exit 0
+
+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 $state
diff --git a/test/wormhole_static_test.c b/test/wormhole_static_test.c
new file mode 100644
index 0000000..7a4246f
--- /dev/null
+++ b/test/wormhole_static_test.c
@@ -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
+}