test(unit): Add nanomsg unit tests 03/103/3
authorE. Scott Daniels <daniels@research.att.com>
Thu, 2 May 2019 17:09:35 +0000 (17:09 +0000)
committerE. Scott Daniels <daniels@research.att.com>
Thu, 2 May 2019 17:29:05 +0000 (17:29 +0000)
This adds unit tests which drive the nanomsg portion of the
library. The unit test script also now merges coverage
reports so that common portions of the library do not need
to be tested by both nano and nng tests in order to show
accurate coverage information.

Change-Id: Id852d46272972f481d4f37d8a60681228b4bc0ff
Signed-off-by: E. Scott Daniels <daniels@research.att.com>
14 files changed:
test/.gitignore
test/Makefile
test/README
test/cov2xml.ksh [new file with mode: 0755]
test/hdr_static_test.c
test/mbuf_api_static_test.c
test/mbuf_api_test.c
test/rmr_nano_test.c [new file with mode: 0644]
test/rmr_nng_api_static_test.c
test/rt_nano_static_test.c [new file with mode: 0644]
test/rt_static_test.c
test/sr_nano_static_test.c [new file with mode: 0644]
test/test_nng_em.c
test/unit_test.ksh

index 6770301..62ea44b 100644 (file)
@@ -2,3 +2,5 @@
 *.dcov
 *.gcno
 *.gcda
+*.xml
+*test
index f23f838..d6c2c64 100644 (file)
@@ -1,3 +1,22 @@
+# :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.
+#==================================================================================
+#
 
 
 CC = gcc
@@ -9,26 +28,20 @@ libs =  -L ../build/lib -lnng -lpthread -lm
 #sa_tests = sa_tools_test.o
 
 %.o:: %.c
-       $(CC) -g $< -c 
+       $(CC) -g $< -c
 
 %:: %.c
-       $(CC) -I ../src/common/src/ -I ../src/common/include -I ../src/nng/include $(coverage_opts) -fPIC -g $< -o $@  $(libs)
+       $(CC) -I ../src/common/src/ -I ../src/common/include -I ../src/nng/include -I ../src/nanomsg/include  $(coverage_opts) -fPIC -g $< -o $@  $(libs)
 
-# catch all 
+# catch all
 all:
        echo "run unit_test.ksh to make and run things here"
 
 
-#sa_tools_test.o: sa_tools_test.c
-#      $(CC) -I ../src/common/src/ -I ../src/common/include -I $(coverage_opts) ../src/nng/include  -fPIC -g $< -c 
-
-#.PHONY: sa_tests
-#sa_tests:     $(sa_tests)
-
 # remove intermediates
 clean:
        rm -f *.gcov *.gcda *.dcov *.gcno
 
-# remove anything that can be builts
+# remove anything that can be built
 nuke: clean
-       rm -f ring_test symtab_test 
+       rm -f ring_test symtab_test
index 5adc3f8..eb243de 100644 (file)
@@ -55,6 +55,16 @@ Use the command 'unit_test.ksh -?' to see the usage information
 and complete set of options available.
 
 
+Merging .gcov files
+As some unit test programmes may not be able/designed to cover all 
+of a module completely, the script will merge each .gcov prooduced
+with the prior, matching, file after each pass.  The result is a
+final .gcov file that shows the coverage of the module over all 
+tests.  This allows a true coverage value to be determined while 
+permitting more simple tests to be used without the requirement of
+each test to cover everything.
+
+
 Discounting
 The unit test script makes a discount pass on low coverage files in
 attempt to discount the coverage rate by ignoring what are considered
@@ -82,6 +92,10 @@ block which checks for a nil pointer has been discounted:
  =====:  358:        return 0;
      -:  359:    }
 
+A final discount pass is made on all merged .gcov files after all unit 
+tests are finished. This final pass should indicate the true discounted
+coverage
+
 
 Target Coverage
 By default, a target coverage of 80% is used. For some modules this may
@@ -90,6 +104,15 @@ 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.
 
 
+Sonar XML Files
+If the -x option is given on the unit test script, then two XML files 
+will be generated for Sonar.  These are gcov.xml and dcov.xml and are 
+the coverage information as reflected in the merged .gcov files and in
+the .dcov files produced during the final pass.   The XML files should
+be uploaded to Sonar only after a complete unit test run is made, otherwise
+the coverage information may be lacking (reflecting only the last test
+executed and not a full complement of tests).
+
 -----------------------------------------------------------------------
 A note about ksh (A.K.A Korn shell, or kshell)
 Ksh is preferred for more complex scripts such as the unit test
diff --git a/test/cov2xml.ksh b/test/cov2xml.ksh
new file mode 100755 (executable)
index 0000000..1b57c53
--- /dev/null
@@ -0,0 +1,100 @@
+#!/usr/bin/env ksh
+
+#==================================================================================
+#        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:       cov2xml.ksh
+#      Abstract:       Process the coverage file(s) which are read from standard in
+#                              to generate a single set of XML output that Sonar can ingest.
+#                              Basic usage is:
+#                                      cat *.gcov | cov2xml.ksh >file.xml
+#
+#                              The XML generated is based on the Sonar doc:
+#                                      https://docs.sonarqube.org/latest/analysis/generic-test/
+#
+#      Date:           30 April 2019
+#      Author:         E. Scott Daniels
+# -------------------------------------------------------------------------
+
+awk '
+       function start() {
+               printf(  "<coverage version=\"1\">\n" )         # sonar is VERY picky about this
+       }
+
+       function end() {
+               printf( "</coverage>\n" )
+       }
+
+       function start_file( name ) {
+         printf( "<file path=\"%s\">\n", name )
+       }
+
+       function end_file( ) {
+               printf( "</file>\n" )
+       }
+
+       function good_line( num ) {
+               if( num > 0 ) {
+                       printf( "<lineToCover lineNumber=\"%d\" covered=\"true\"/>\n", num )
+               }
+       }
+
+       function bad_line( num ) {
+               if( num > 0 ) {
+                       printf( "<lineToCover lineNumber=\"%d\" covered=\"false\"/>\n", num )
+               }
+       }
+
+       BEGIN {
+               start()
+       }
+
+       /Source:/ {
+               gsub( "../src", "src", $0 )             # sonar doesnt view test dir as root, so strip
+               n = split( $0, a, ":" )
+               if( fip ) {
+                       end_file()
+               }
+               fip = 1
+               start_file( a[n] )
+               next;
+       }
+
+       /-:/ { next }                                   # not executable
+
+       /#####:/ {                                              # never executed
+               bad_line( $2 + 0 )
+               next
+       }
+
+       {
+               good_line( $2 + 0 )
+       }
+
+       END {
+               if( fip ) {
+                       end_file();
+               }
+
+               end()
+       }
+' $1
+
+exit $/
+
index 1e50390..82a01a8 100644 (file)
@@ -1,7 +1,7 @@
 // : vi ts=4 sw=4 noet :
 /*
 ==================================================================================
-           Copyright (c) 2019 Nokia 
+           Copyright (c) 2019 Nokia
            Copyright (c) 2018-2019 AT&T Intellectual Property.
 
    Licensed under the Apache License, Version 2.0 (the "License");
index 93ab45c..b2f1f7d 100644 (file)
@@ -262,7 +262,7 @@ int mbuf_api_test( ) {
        errors += fail_not_nil( buf, "rmr_get_src returned a pointer when given a nil dest buffer" );
 
        buf = rmr_get_src( mbuf, src_buf );
-       errors += fail_not_equal( buf, src_buf, "rmr_get_src didn't return expexted buffer pointer" );
+       errors += fail_not_equal( (int) buf, (int) src_buf, "rmr_get_src didn't return expexted buffer pointer" );
 
 
        return errors > 0;                      // overall exit code bad if errors
index b1a1ebd..573415e 100644 (file)
@@ -84,7 +84,7 @@ extern rmr_mbuf_t* rmr_realloc_msg( rmr_mbuf_t* msg, int new_tr_size ) {
        new_msg->header = new_msg->tp_buf;
        new_msg->alloc_len = 2048;
        new_msg->len = msg->len;
-       
+
        return new_msg;
 }
 
diff --git a/test/rmr_nano_test.c b/test/rmr_nano_test.c
new file mode 100644 (file)
index 0000000..50c4266
--- /dev/null
@@ -0,0 +1,126 @@
+// :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_nano_test.c
+       Abstract:       This tests the whole rmr nanomsg 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.
+
+                               This test only needs to drive the soruce in nanomsg/src. The
+                               common library functions are driven by the nng tests.
+
+       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 <nanomsg/nn.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>
+
+#undef 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/nanomsg/include/rmr_private.h"                        // transport specific
+
+#include "../src/common/src/symtab.c"
+#include "../src/nanomsg/src/rmr.c"
+#include "../src/common/src/mbuf_api.c"
+#include "../src/nanomsg/src/rtable_static.c"
+
+static void gen_rt( uta_ctx_t* ctx );          // defined in sr_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 "wormhole_static_test.c"
+#include "mbuf_api_static_test.c"
+
+#include "sr_nano_static_test.c"
+#include "rmr_nng_api_static_test.c"           // this can be used for both nng and nano
+#include "rt_nano_static_test.c"                                       // this can be used for both
+
+
+/*
+       Drive each of the separate tests and report.
+*/
+int main() {
+       int errors = 0;
+
+       fprintf( stderr, "<INFO> starting RMr Nanomsg based API tests\n" );
+       errors += rmr_api_test();
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting mbuf related tests\n" );
+       errors += mbuf_api_test( );
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting send/receive tests\n" );
+       errors += sr_nano_test();                               // test the send/receive static functions
+       fprintf( stderr, "<INFO> error count: %d\n", errors );
+
+       fprintf( stderr, "<INFO> starting rt_static tests\n" );
+       errors += rt_test();                                    // route table things specific to nano
+       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;
+}
index a120906..e0dc4d8 100644 (file)
        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
+                               driver and there will 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.
 
+                               This can be used for both the nng and nanomsg outward facing
+                               RMr API functions.
+
                                The message buffer specific API tests are in a different static
                                module.  API functions tested here are:
                                         rmr_close
@@ -57,7 +60,6 @@
 
 #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
@@ -94,54 +96,62 @@ static int rmr_api_test( ) {
        int             state;
 
        v = rmr_ready( NULL );
-       errors += fail_if( v != 0, "rmr_ready returned true before initialisation" );
+       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" );
+               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" );
+               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" );
+               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" );
+       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" );
+       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" );
+       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" );
+               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" );
+               errors += fail_if( v < 0, "rmr_payload_size returned invalid size for good message "  );
        }
 
+#ifdef EMULATE_NNG
+       // this is only supported in nng, so behavour is different depending on the underlying library being tested
+       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 "  );
+#else
        v = rmr_get_rcvfd( NULL );
-       errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor when given nil context" );
+       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" );
+       errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor (not supported in nanomsg)"  );
+#endif
 
        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" );
+       errors += fail_not_nil( msg2, "send_msg returned msg pointer when given a nil message and context "  );
 
        msg->state = 0;
        msg = rmr_send_msg( NULL, msg );
-       errors += fail_if( msg->state == 0, "rmr_send_msg did not set msg state when msg given with nil context" );
+       errors += fail_if( msg->state == 0, "rmr_send_msg did not set msg state when msg given with nil 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;
@@ -149,10 +159,10 @@ static int rmr_api_test( ) {
        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" );
+       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" );
+               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
@@ -162,12 +172,12 @@ static int rmr_api_test( ) {
        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" );
+       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" );
+               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" );
+               errors += fail_if( v != 2048, "send_msg did not allocate new buffer with correct size "  );
        }
 
        rmr_set_stimeout( NULL, 0 );
@@ -178,48 +188,52 @@ static int rmr_api_test( ) {
        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" );
+       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" );
+       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" );
+               if( msg2->state != RMR_ERR_EMPTY ) {
+                       errors += fail_not_equal( msg2->state, RMR_OK, "receive given nil message did not return msg with good state (not empty) "  );
+               }
+               //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" );
+               errors += fail_not_equal( msg->state, RMR_OK, "rmr_rcv_msg did not return an ok state "  );
+               errors += fail_not_equal( msg->len, 220, "rmr_rcv_msg returned message with invalid len "  );
        } else {
-               errors += fail_if_nil( msg, "rmr_rcv_msg returned a nil pointer" );
+               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" );
+       errors += fail_if( errno == 0, "rmr_rts_msg did not set errno when given a nil message "  );
 
        msg->state = 0;
        msg = rmr_rts_msg( NULL, msg );                 // should set state in msg
-       errors += fail_if_equal( msg->state, 0, "rmr_rts_msg did not set state when given valid message but no context" );
-       
+       errors += fail_if_equal( msg->state, 0, "rmr_rts_msg did not set state when given valid message but no context "  );
+
 
        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" );
+       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->state = 0;
        msg = rmr_call( NULL, msg );
-       errors += fail_if( msg->state == 0, "rmr_call did not set message state when given message with nil context" );
+       errors += fail_if( msg->state == 0, "rmr_call did not set message state when given message with nil context "  );
 
        msg->mtype = 0;
        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" );
+       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_not_equal( errno, 0, "rmr_call did not properly set errno (a) on successful return" );
+               errors += fail_not_equal( msg->state, RMR_OK, "rmr_call did not properly set state on successful return "  );
+               errors += fail_not_equal( errno, 0, "rmr_call did not properly set errno (a) on successful return "  );
        }
 
        snprintf( wbuf, 17, "%015d", 14 );                              // if we call receive we should find this in the first 15 tries
@@ -231,65 +245,65 @@ static int rmr_api_test( ) {
                        }
                        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_nil( msg, "receive returnd nil msg while looking for queued message "  );
                }
        }
 
-       errors += fail_if( i >= 16, "did not find expected message on queue" );
+       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_if_nil( msg, "rmr_call returned a non-nil message on call expected not to receive a response" );
+       errors += fail_if_nil( msg, "rmr_call returned a non-nil message on call expected not to receive a response "  );
        if( msg ) {
-               errors += fail_if_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" );
+               errors += fail_if_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_if_nil( msg, "rmr_call returned a nil message on call expected to fail (but return a pointer)" );
-       errors += fail_if( errno == 0, "rmr_call did not set errno on failure" );
+       errors += fail_if_nil( msg, "rmr_call returned a 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 );
 
 
        msg2 = rmr_torcv_msg( NULL, NULL, 10 );
-       errors += fail_not_nil( msg2, "rmr_torcv_msg returned a pointer when given nil information" );
+       errors += fail_not_nil( msg2, "rmr_torcv_msg returned a pointer when given nil information "  );
        msg2 = rmr_torcv_msg( rmc, NULL, 10 );
-       errors += fail_if_nil( msg2, "rmr_torcv_msg did not return a message pointer when given a nil old msg" );
+       errors += fail_if_nil( msg2, "rmr_torcv_msg did not return a message pointer when given a nil old msg "  );
 
        // ---  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" );
+               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
+                       if( msg->state == RMR_ERR_TIMEOUT || msg->state == RMR_ERR_EMPTY ) {            // 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" );
+       errors += fail_if( i >= 40, "torcv_msg never returned a timeout "  );
 
 
        // ---- trace things that are not a part of the mbuf_api functions and thus must be tested here
        state = rmr_init_trace( NULL, 37 );                                             // coverage test nil context
-       errors += fail_not_equal( state, 0, "attempt to initialise trace with nil context returned non-zero state (a)" );
-       errors += fail_if_equal( errno, 0, "attempt to initialise trace with nil context did not set errno as expected" );
+       errors += fail_not_equal( state, 0, "attempt to initialise trace with nil context returned non-zero state (a) "  );
+       errors += fail_if_equal( errno, 0, "attempt to initialise trace with nil context did not set errno as expected "  );
 
        state = rmr_init_trace( rmc, 37 );
-       errors += fail_if_equal( state, 0, "attempt to set trace len in context was not successful" );
-       errors += fail_not_equal( errno, 0, "attempt to set trace len in context did not clear errno" );
+       errors += fail_if_equal( state, 0, "attempt to set trace len in context was not successful "  );
+       errors += fail_not_equal( errno, 0, "attempt to set trace len in context did not clear errno "  );
 
        msg = rmr_tralloc_msg( rmc, 1024, 17, "1904308620110417" );
-       errors += fail_if_nil( msg, "attempt to allocate message with trace data returned nil message" );
+       errors += fail_if_nil( msg, "attempt to allocate message with trace data returned nil message "  );
        state = rmr_get_trace( msg, wbuf, 17 );
-       errors += fail_not_equal( state, 17, "len of trace data (a) returned after msg allocation was not expected size (b)" );
+       errors += fail_not_equal( state, 17, "len of trace data (a) returned after msg allocation was not expected size (b) "  );
        state = strcmp( wbuf, "1904308620110417" );
-       errors += fail_not_equal( state, 0, "trace data returned after tralloc was not correct" );
+       errors += fail_not_equal( state, 0, "trace data returned after tralloc was not correct "  );
 
        em_send_failures = 1;
        send_n_msgs( rmc, 30 );                 // send 30 messages with emulation failures
diff --git a/test/rt_nano_static_test.c b/test/rt_nano_static_test.c
new file mode 100644 (file)
index 0000000..90cad7e
--- /dev/null
@@ -0,0 +1,260 @@
+// : 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;
+
+/*
+       Create a dummy endpoint for some direct function calls.
+*/
+static endpoint_t* mk_ep( char* name ) {
+       endpoint_t* ep;
+
+       ep = (endpoint_t *) malloc( sizeof( struct endpoint ) );
+       ep->name = strdup( name );
+       ep->proto = strdup( "tcp" );
+       ep->addr = strdup( "address" );
+       ep->nn_sock = -1;
+       ep->open = 0;
+
+       return ep;
+}
+
+
+/*
+       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
+       int                     nn_sock;
+
+       setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 );                     // allow for verbose code in rtc to be driven
+       i = open( ".ut_rmr_verbose", O_RDWR | O_CREAT, 0644 );
+       if( i >= 0 ) {
+               write( i, "2\n", 2 );
+               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" );              // this should be found
+       errors += fail_if_true( state < 0, "socket (by name) returned socket less than 0 when expected >= 0 socket" );
+
+       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 );                                       // nano returns the socket which should be different than the last call
+               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 );                // 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, 289486, 0, &more  );                 // non-existant message type; should return false (0)
+       errors += fail_if_true( value >= 0, "socket for bad hash key was valid" );
+
+       uta_rt_clone( NULL );                                                           // verify null parms don't crash things
+       uta_rt_drop( NULL );
+       uta_epsock_rr( NULL, 1, 0, &more );                                     // drive null case for coverage
+       uta_add_rte( NULL, 99, 1 );
+
+       fprintf( stderr, "<INFO> adding end points with nil data; warnings from RMr code are 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" )) ) {
+                               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
+
+       rt = uta_rt_init( );                                                                            // get us a route table
+       state = uta_link2( NULL );
+       errors += fail_not_equal( state, -1, "link2 did not return (a==) -1 when given nil pointers" );
+
+       ep = mk_ep( "foo" );
+       state = rt_link2_ep( NULL );
+       errors += fail_not_equal( state, 0, "link2_ep did not return (a) bad  when given nil pointers" );
+
+       state = rt_link2_ep( ep );
+       errors += fail_not_equal( state, 1, "link2_ep did not return (a) bad  when given an ep to use to open" );
+
+       ep->open = 1;
+       state = rt_link2_ep( ep );
+       errors += fail_not_equal( state, 1, "link2_ep did not return (a) bad when given an ep which was set open" );
+
+       state = uta_epsock_rr( rt, 122, 0, NULL );
+       errors += fail_not_equal( state, -1, "uta_epsock_rr returned bad state when given nil socket pointer" );
+
+       state = uta_epsock_rr( rt, 0, -1, NULL );
+       errors += fail_not_equal( state, -1, "uta_epsock_rr returned bad state (a) when given negative group number" );
+
+       state = rt_link2_ep( NULL );
+       errors += fail_if_equal( state, -1, "call to link2_ep with nil ep returned true when false expected" );
+
+
+       return !!errors;                        // 1 or 0 regardless of count
+}
index 710c75e..ce0eb25 100644 (file)
@@ -1,7 +1,7 @@
 // : vi ts=4 sw=4 noet :
 /*
 ==================================================================================
-           Copyright (c) 2019 Nokia 
+           Copyright (c) 2019 Nokia
            Copyright (c) 2018-2019 AT&T Intellectual Property.
 
    Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,7 +21,7 @@
 /*
        Mmemonic:       rt_static_test.c
        Abstract:       Test the route table funcitons. These are meant to be included at compile
-                               time by the test driver.  
+                               time by the test driver.
 
        Author:         E. Scott Daniels
        Date:           3 April 2019
@@ -230,7 +230,7 @@ static int rt_test( ) {
 
        state = uta_link2( "worm", NULL, NULL );
        errors += fail_if_true( state, "link2 did not return false when given nil pointers" );
-    
+
        state = uta_epsock_rr( rt, 122, 0, NULL, NULL );
        errors += fail_if_true( state, "uta_epsock_rr returned bad state when given nil socket pointer" );
 
diff --git a/test/sr_nano_static_test.c b/test/sr_nano_static_test.c
new file mode 100644 (file)
index 0000000..8e939da
--- /dev/null
@@ -0,0 +1,185 @@
+// : 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_nano_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|end\n"                                                           // end of table check before start of table found
+               "# comment to drive full comment test\n"
+               "\n"                                                                            // handle blank lines
+               "   \n"                                                                         // handle blank lines
+           "mse|4|10|localhost:4561\n"                                 // entry before start message
+           "rte|4|localhost:4561\n"                                    // entry before start message
+               "newrt|start\n"                                                         // false start to drive detection
+               "xxx|badentry to drive default case"
+               "newrt|start\n"
+           "rte|0|localhost:4560,localhost:4562\n"                                     // these are legitimate entries for our testing
+           "rte|1|localhost:4562;localhost:4561,localhost:4569\n"
+           "rte|2|localhost:4562| 10\n"                                                                // new subid at end
+           "mse|4|10|localhost:4561\n"                                                                 // new msg/subid specifier rec
+           "mse|4|localhost:4561\n"                                                                    // new mse entry with less than needed fields
+               "   rte|   5   |localhost:4563    #garbage comment\n"           // tests white space cleanup
+           "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_nano_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_nano_test() {
+       int errors = 0;                 // number errors found
+
+       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
+       rmr_mbuf_t*     mbuf;           // mbuf to send/receive
+       rmr_mbuf_t*     mb2;            // error capturing msg buf
+       int             whid = -1;
+       int             last_whid;
+       int     state;
+       int nn_dummy_sock;                                      // dummy needed to drive send
+       int             size;
+       int             i;
+       void*   p;
+
+       //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()
+       gen_rt( ctx );                                                          // force a second load to test cloning
+
+       p = rt_ensure_ep( NULL, "foo" );                                // drive for coverage
+       errors += fail_not_nil( p,  "rt_ensure_ep did not return nil when given nil route table" );
+
+       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 = 4096;
+       state = rmr_payload_size( mbuf );
+       errors += fail_not_equal( state, size, "payload size (b) didn't return expected value (a)" );   // receive should always give 4k buffer
+
+       rmr_free_msg( mbuf );
+
+
+       // ---- direct message read into payload (no rmr header) -------------------------
+       mbuf = rcv_payload( ctx, NULL );
+       errors += fail_if_nil( mbuf, "rcv_payload did not return a message buffer when given a nil messge" );
+       if( mbuf ) {
+               errors += fail_if_true( mbuf->len <= 0, "rcv_payload did not return a buffer with payload length set when given a nil messge" );
+               errors += fail_not_equal( mbuf->state, 0, "rcv_payload did not return a buffer with good state when given a nil messge" );
+       }
+
+       mbuf = rcv_payload( ctx, NULL );
+       errors += fail_if_nil( mbuf, "rcv_payload did not return a message buffer" );
+       if( mbuf ) {
+               errors += fail_if_true( mbuf->len <= 0, "rcv_payload did not return a buffer with payload length set" );
+               errors += fail_not_equal( mbuf->state, 0, "rcv_payload did not return a buffer with good state" );
+       }
+
+       // ---- drive rtc in a 'static' (not pthreaded) mode to get some coverage; no 'results' to be verified -----
+       setenv( ENV_RTG_RAW, "1", 1 );                                                          // rtc should expect raw messages (mostly coverage here)
+       setenv( ENV_VERBOSE_FILE, ".ut_rmr_verbose", 1 );                       // allow for verbose code in rtc to be driven
+       i = open( ".ut_rmr_verbose", O_RDWR | O_CREAT, 0654 );
+       if( i >= 0 ) {
+               write( i, "2\n", 2 );
+               close( i );
+       }
+       ctx->shutdown = 1;                      // should force rtc to quit on first pass
+       rtc( NULL );                            // coverage test with nil pointer
+       rtc( ctx );
+
+
+       // --- drive the route table things which are nanomsg specific ------
+
+       return !!errors;
+}
index 685a43c..f1976b9 100644 (file)
@@ -36,6 +36,7 @@
 #define _em_nn
 
 static int em_send_failures = 0;       // test programme can set this to emulate eagain send failures
+static int em_timeout = -1;                    // set by set socket option
 
 // ----------- epoll emulation ---------------------------------------------
 
@@ -56,29 +57,6 @@ static int em_wait( int fd, void* events, int n, int to ) {
 
 
 
-//--------------------------------------------------------------------------
-#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.
@@ -120,6 +98,30 @@ struct em_msg {
 
 };
 
+static int return_value = 0;
+
+//--------------------------------------------------------------------------
+#ifdef EMULATE_NNG
+struct nn_msghdr {
+       int boo;
+};
+
+
+/*
+       Test app can call this to have all emulated functions return failure instead
+       of success.
+*/
+static void en_set_return( int rv ) {
+       return_value = rv;
+}
+
+
+
+static int em_nng_foo() {
+       fprintf( stderr, "emulated functions in play" );
+}
+
+
 /*
        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
@@ -144,7 +146,7 @@ static int em_nng_recvmsg( nng_socket s, nng_msg ** m, int i ) {
                        msg->rmr_ver = htonl( MSG_VER );
                }
                msg->mtype = htonl( 1 );
-               msg->plen = htonl( 129 );
+               msg->plen = htonl( 220 );
                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
@@ -197,6 +199,7 @@ 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 ) {
@@ -337,16 +340,16 @@ 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_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" );
+       //      fprintf( stderr, ">>> ===== emulated bind called ====\n" );
        return 1;
 }
 
@@ -362,8 +365,43 @@ 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_nn_recv (int s, void *m, size_t len, int flags ) {
+       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;
+       static int counter = 0;                         // if timeout value is set; we return timeout (eagain) every 3 calls
+
+       if( em_timeout > 0 ) {
+               counter++;
+               if( counter % 3 == 0 ) {
+                       return EAGAIN;
+               }
+       }
+
+       b = (void *) malloc( 2048 );
+       if( m != NULL ) {                                               // blindly we assume this is 2k or bigger
+               memset( m, 0, 2048 );
+               msg = (struct em_msg *) m;
+               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( 220 );
+               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, "<EM>   returning message len=%d\n\n", ntohl( msg->plen ) );
+       } else {
+               fprintf( stderr, "<EM>   message was nil\n\n" );
+       }
+
+       //fprintf( stderr, ">>> simulated received message: %s len=%d\n", msg->xid, msg->plen );
+       return 2048;
 }
 
 static int em_sendmsg (int s, const struct em_nn_msghdr *msghdr, int flags ) {
@@ -374,10 +412,24 @@ static int em_nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags ) {
        return 1;
 }
 
+static void em_nn_freemsg( void* ptr ) {
+       return;
+}
+
+/*
+       Hacky implementation of set sock opt. We assume value is a pointer to int and ignore size.
+*/
+static int em_setsockopt( int sock, int foo, int action, int* value, int size ) {
+       if( action ==  NN_RCVTIMEO ) {
+               em_timeout = *value;
+       }
+}
+
+
 // nanomsg
 #define nn_socket  em_nn_socket
 #define nn_close  em_nn_close
-#define nn_setsockopt  em_nn_setsockopt
+//#define nn_setsockopt  em_nn_setsockopt
 #define nn_getsockopt  em_nn_getsockopt
 #define nn_bind  em_nn_bind
 #define nn_connect  em_nn_connect
@@ -386,6 +438,8 @@ static int em_nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags ) {
 #define nn_recv  em_nn_recv
 #define nn_sendmsg  em_nn_sendmsg
 #define nn_recvmsg  em_nn_recvmsg
+#define nn_setsockopt  em_setsockopt
+#define nn_freemsg  em_nn_freemsg
 
 #endif
 
index 74bc048..929976c 100755 (executable)
@@ -2,7 +2,7 @@
 # this will fail if run with bash!
 
 #==================================================================================
-#        Copyright (c) 2019 Nokia 
+#        Copyright (c) 2019 Nokia
 #        Copyright (c) 2018-2019 AT&T Intellectual Property.
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
 
 #
 #      Mnemonic:       unit_test.ksh
-#      Abstract:       Execute unit test(s) in the directory and produce a more 
+#      Abstract:       Execute unit test(s) in the directory and produce a more
 #                              meaningful summary than gcov gives by default (exclude
 #                              coverage  on the unit test functions).
 #
-#                              Test files must be named *_test.c, or must explicitly be 
+#                              Test files must be named *_test.c, or must explicitly be
 #                              supplied on the command line. Functions in the test
 #                              files will not be reported on provided that they have
 #                              their prototype (all on the SAME line) as:
 #                                      static type name() {
 #
-#                              Functions with coverage less than 80% will be reported as 
+#                              Functions with coverage less than 80% will be reported as
 #                              [LOW] in the output.  A file is considered to pass if the
 #                              overall execution percentage for the file is >= 80% regardless
 #                              of the number of functions that reported low.
 #
 #                              Test programmes are built prior to execution. Plan-9 mk is
 #                              the preferred builder, but as it's not widly adopted (sigh)
-#                              make is assumed and -M will shift to Plan-9. Use -C xxx to 
+#                              make is assumed and -M will shift to Plan-9. Use -C xxx to
 #                              invoke a customised builder.
 #
 #                              For a module which does not pass, we will attempt to boost
 #                              the coverage by discounting the unexecuted lines which are
-#                              inside of if() statements that are checking return from 
+#                              inside of if() statements that are checking return from
 #                              (m)alloc() calls or are checking for nil pointers as these
 #                              cases are likely impossible to drive. When discount testing
 #                              is done both the failure message from the original analysis
-#                              and a pass/fail message from the discount test are listed, 
-#                              but only the result of the discount test is taken into 
+#                              and a pass/fail message from the discount test are listed,
+#                              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 
+#                              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.
@@ -63,7 +63,7 @@
 # -------------------------------------------------------------------------
 
 function usage {
-       echo "usage: $0 [-G|-M|-C custom-command-string] [-c cov-target]  [-f] [-F] [-v]  [files]"
+       echo "usage: $0 [-G|-M|-C custom-command-string] [-c cov-target]  [-f] [-F] [-v] [-x]  [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"'
@@ -72,9 +72,10 @@ function usage {
        echo "  -F show only failures at the function level"
        echo "  -s strict mode; code coverage must also pass to result in a good exit code"
        echo "  -v will write additional information to the tty and save the disccounted file if discount run or -f given"
+       echo "  -x generates the coverage XML files for Sonar (implies -f)"
 }
 
-# read through the given file and add any functions that are static to the 
+# read through the given file and add any functions that are static to the
 # ignored list.  Only test and test tools files should be parsed.
 #
 function add_ignored_func {
@@ -85,18 +86,64 @@ function add_ignored_func {
 
        typeset f=""
        grep "^static.*(.*).*{" $1 | awk '              # get list of test functions to ignore
-               { 
+               {
                        gsub( "[(].*", "" )
                        print $3
                }
        ' | while read f
        do
-               iflist="${iflist}$f "   
+               iflist="${iflist}$f "
        done
 }
 
+
+# Merge two coverage files to preserve the total lines covered by different
+# test programmes.
 #
-#      Parse the .gcov file and discount any unexecuted lines which are in if() 
+function merge_cov {
+       if [[ -z $1 || -z $2 ]]
+       then
+               return
+       fi
+
+       if [[ ! -e $1 || ! -e $2 ]]
+       then
+               return
+       fi
+
+       (
+               cat $1
+               echo "==merge=="
+               cat $2
+       ) | awk '
+               /^==merge==/ {
+                       merge = 1
+                       next
+               }
+
+               merge && /#####:/ {
+                       line = $2+0
+                       if( executed[line] ) {
+                               $1 = sprintf( "%9d:", executed[line] )
+                       }
+               }
+
+               merge {
+                       print
+                       next
+               }
+
+               {
+                       line = $2+0
+                       if( $1+0 > 0 ) {
+                               executed[line] = $1+0
+                       }
+               }
+       '
+}
+
+#
+#      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 deficiencies.
@@ -115,7 +162,7 @@ function discount_an_checks {
        then
                if [[ -f ${1##*/} ]]
                then
-                       f=${1##*/} 
+                       f=${1##*/}
                else
                        echo "cant find: $f"
                        return
@@ -127,7 +174,8 @@ function discount_an_checks {
                -v show_all=$show_all \
                -v full_name="${1}"  \
                -v module="${f%.*}"  \
-               -v chatty=1 \
+               -v chatty=$chatty \
+               -v replace_flags=$replace_flags \
        '
        function spit_line( ) {
                if( chatty ) {
@@ -138,7 +186,7 @@ function discount_an_checks {
        /-:/ {                          # skip unexecutable lines
                spit_line()
                seq++                                   # allow blank lines in a sequence group
-               next 
+               next
        }
 
        {
@@ -158,8 +206,9 @@ function discount_an_checks {
                if( prev_if && prev_malloc ) {
                        if( prev_malloc ) {
                                #printf( "allow discount: %s\n", $0 )
-                               if( chatty ) {
-                                       gsub( "#####", "=====", $0 )
+                               if( replace_flags ) {
+                                       gsub( "#####", "    1", $0 )
+                                       //gsub( "#####", "=====", $0 )
                                }
                                discount++;
                        }
@@ -204,7 +253,7 @@ function discount_an_checks {
                next
        }
 
-       { 
+       {
                spit_line()
        }
 
@@ -216,7 +265,7 @@ function discount_an_checks {
                rc = adj_cov < module_cov_target ? 1 : 0
                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", 
+                               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 )
@@ -228,7 +277,7 @@ function discount_an_checks {
        ' $f
 }
 
-# Given a file name ($1) see if it is in the ./.targets file. If it is 
+# Given a file name ($1) see if it is in the ./.targets file. If it is
 # return the coverage listed, else return (echo)  the default $module_cov_target
 #
 function get_mct {
@@ -242,8 +291,18 @@ function get_mct {
        echo ${tv:-$v}
 }
 
+# Remove unneeded coverage files, then generate the xml files that can be given
+# to sonar.  gcov.xml is based on the "raw" coverage and dcov.xml is based on
+# the discounted coverage.
+#
+function mk_xml {
+       rm -fr *_test.c.gcov test_*.c.gcov *_test.c.dcov test_*.c.dcov          # we don't report on the unit test code, so ditch
+       cat *.gcov | cov2xml.ksh >gcov.xml
+       cat *.dcov | cov2xml.ksh >dcov.xml
+}
 
-# ------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------------------------------------------
 
 # we assume that the project has been built in the ../[.]build directory
 if [[ -d ../build/lib ]]
@@ -253,6 +312,8 @@ else
        if [[ -d ../.build/lib ]]
        then
                export LD_LIBRARY_PATH=../.build/lib
+               export C_INCLUDE_PATH=../.build/include
+
        else
                echo "[WARN] cannot find ../[.]build/lib; things might not work"
                echo ""
@@ -268,16 +329,18 @@ 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
 show_output=0                          # show output from each test execution (-S)
 quiet=0
+gen_xml=0
+replace_flags=1                                # replace ##### in gcov for discounted lines
 
 while [[ $1 == "-"* ]]
 do
-       case $1 in 
+       case $1 in
                -C)     builder="$2"; shift;;           # custom build command
                -G)     builder="gmake %s";;
                -M)     builder="mk -a %s";;            # use plan-9 mk (better, but sadly not widly used)
 
                -c)     module_cov_target=$2; shift;;
-               -f)     force_discounting=1; 
+               -f)     force_discounting=1;
                        trigger_discount_str="WARN|FAIL|PASS"           # check all outcomes for each module
                        ;;
 
@@ -287,6 +350,11 @@ do
                -S)     show_output=1;;                         # test output shown even on success
                -v)     (( verbose++ ));;
                -q)     quiet=1;;                                       # less chatty when spilling error log files
+               -x)     gen_xml=1
+                       force_discounting=1
+                       trigger_discount_str="WARN|FAIL|PASS"           # check all outcomes for each module
+                       rm *cov.xml
+                       ;;
 
                -h)     usage; exit 0;;
                --help) usage; exit 0;;
@@ -329,10 +397,20 @@ else
 fi
 
 
+rm *.gcov                      # ditch the previous coverage files
 ut_errors=0                    # unit test errors (not coverage errors)
 errors=0
+
 for tfile in $flist
 do
+       for x in *.gcov
+       do
+               if [[ -e $x ]]
+               then
+                       cp $x $x-
+               fi
+       done
+
        echo "$tfile --------------------------------------"
        bcmd=$( printf "$builder" "${tfile%.c}" )
        if ! $bcmd >/tmp/PID$$.log 2>&1
@@ -351,11 +429,11 @@ do
        do
                add_ignored_func $f
        done
-       
+
        if ! ./${tfile%.c} >/tmp/PID$$.log 2>&1
        then
                echo "[FAIL] unit test failed for: $tfile"
-               if (( quiet )) 
+               if (( quiet ))
                then
                        grep "^<" /tmp/PID$$.log        # in quiet mode just dump <...> messages which are assumed from the test programme not appl
                else
@@ -375,7 +453,7 @@ do
        (
                touch ./.targets
                sed '/^#/ d; /^$/ d; s/^/TARGET: /' ./.targets
-               gcov -f ${tfile%.c} | sed "s/'//g" 
+               gcov -f ${tfile%.c} | sed "s/'//g"
        ) | awk \
                -v cfail=$cfail \
                -v show_all=$show_all \
@@ -439,7 +517,7 @@ do
                                }
 
                                if( target[fname] ) {
-                                       mct = target[fname] 
+                                       mct = target[fname]
                                        announce_target = 1;
                                } else {
                                        mct = module_cov_target
@@ -480,9 +558,9 @@ do
        ' >/tmp/PID$$.log                                       # capture output to run discount on failures
        rc=$?
        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"
@@ -495,7 +573,7 @@ do
                                (( errors++ ))
                        fi
 
-                       tail -1 /tmp/PID$$.disc
+                       tail -1 /tmp/PID$$.disc | grep '\['
 
                        if (( verbose > 1 ))                    # updated file was generated, keep here
                        then
@@ -505,6 +583,29 @@ do
                        mv /tmp/PID$$.disc ${name##*/}.dcov
                done
        fi
+
+       for x in *.gcov                                                 # merge any previous coverage file with this one
+       do
+               if [[ -e $x && -e $x- ]]
+               then
+                       merge_cov $x $x- >/tmp/PID$$.mc
+                       cp /tmp/PID$$.mc $x
+                       rm $x-
+               fi
+       done
+done
+
+echo ""
+echo "[INFO] final discount checks on merged gcov files"
+show_all=1
+for xx in *.gcov
+do
+       if [[ $xx != *"test"* ]]
+       then
+               of=${xx%.gcov}.dcov
+               discount_an_checks $xx  >$of
+               tail -1 $of |  grep '\['
+       fi
 done
 
 state=0                                                # final state
@@ -528,6 +629,10 @@ then
        echo "[FAIL] overall unit testing fails: coverage errors=$errors   unit test errors=$ut_errors"
 else
        echo "[PASS] overall unit testing passes"
+       if (( gen_xml ))
+       then
+               mk_xml
+       fi
 fi
 exit $state