Address sonar complaints about the listener 59/4659/1
authorE. Scott Daniels <daniels@research.att.com>
Wed, 2 Sep 2020 17:09:54 +0000 (13:09 -0400)
committerE. Scott Daniels <daniels@research.att.com>
Wed, 2 Sep 2020 17:09:54 +0000 (13:09 -0400)
This change addresses the bugs flagged by sonar and adds coverage
testing for the mc_listener and other helper applications.

Issue-ID: RIC-632

Signed-off-by: E. Scott Daniels <daniels@research.att.com>
Change-Id: I0db808a4d88e4b3d10903dce71b0f967088c3807

12 files changed:
CMakeLists.txt
sidecars/listener/src/Makefile
sidecars/listener/src/mc_listener.c
sidecars/listener/src/pipe_reader.c
sidecars/listener/src/rdc.c
sidecars/listener/src/rdc_replay.c
sidecars/listener/src/sender.c
sidecars/listener/src/verify.sh
sidecars/listener/src/verify_replay.sh
sidecars/listener/test/README [new file with mode: 0644]
sidecars/listener/test/run_app_tests.ksh [new file with mode: 0755]
sidecars/listener/test/run_unit_test.ksh

index 9470929..aa84f97 100644 (file)
@@ -36,10 +36,17 @@ enable_testing()
 # cmake cannot set env vars, so we have to passed desired vars on the wrapper command
 # and assume the wrapper will do the right thing with them.  CMake also seems unable
 # to reference ../dir1/dir2 (../dir1 works), so we have to use the crazy syntax
-# with the absurdly long variable name.
+# with the absurdly long variable name before the ../.
 #
 add_test(
                NAME drive_listener_tests
                COMMAND  bash run_unit_test.ksh CMBUILD=${CMAKE_CURRENT_BINARY_DIR}
                WORKING_DIRECTORY   ${CMAKE_CURRENT_BINARY_DIR}/../sidecars/listener/test
 )
+
+# the listener application tests for sonar reports
+add_test(
+               NAME drive_mcl_app_tests
+               COMMAND  bash run_app_tests.ksh CMBUILD=${CMAKE_CURRENT_BINARY_DIR}
+               WORKING_DIRECTORY   ${CMAKE_CURRENT_BINARY_DIR}/../sidecars/listener/test
+)
index efb8b18..5094004 100644 (file)
 #
 #---------------------------------------------------------------------------------
 
+# CAUTION:  We expect that the unit test script(s) might build the various tools
+#      with the environment variable "TEST_COV_OPTS" set. This variable should NOT
+#      be set for the normal build.
+
+
 # this make file assuems that both NNG and RMR are installed and that the variables
 # LD_LIBRARY_PATH, LIBRARY_PATH are set correctly.
 
@@ -39,21 +44,21 @@ libmcl.a::  $(lib_obj) $(lib_h)
        ar -v -r libmcl.a $(lib_obj)
 
 mc_listener: mc_listener.c libmcl.a
-       gcc mc_listener.c -o mc_listener -L. -lmcl  -lrmr_si -lm -lpthread
+       gcc mc_listener.c $$TEST_COV_OPTS -o mc_listener -L. -lmcl  -lrmr_si -lm -lpthread
 
 # ---- adjunct tools -----------------------------------------------------------------
 rdc_replay: rdc_replay.c libmcl.a
-       gcc rdc_replay.c -o rdc_replay -L. -lmcl -lrmr_si -lpthread -lm
+       gcc rdc_replay.c $$TEST_COV_OPTS -o rdc_replay -L. -lmcl -lrmr_si -lpthread -lm
 
 rdc_extract: rdc_extract.c libmcl.a
-       gcc rdc_extract.c -o rdc_extract -L. -lmcl -lrmr_si -lpthread -lm
+       gcc rdc_extract.c $$TEST_COV_OPTS -o rdc_extract -L. -lmcl -lrmr_si -lpthread -lm
 
 # ------- container verification programmes -------------------------------------------
 sender : sender.c
-       gcc sender.c -o sender  -lrmr_si -lm -lpthread
+       gcc sender.c $$TEST_COV_OPTS -o sender  -lrmr_si -lm -lpthread
 
 pipe_reader : pipe_reader.c libmcl.a
-       gcc pipe_reader.c -o pipe_reader  -L. -lmcl -lrmr_si -lm -lpthread
+       gcc pipe_reader.c $$TEST_COV_OPTS -o pipe_reader  -L. -lmcl -lrmr_si -lm -lpthread
 
 # ---- housekeeping stuff -------------------------------------------------------------
 # remove only intermediates
@@ -63,4 +68,3 @@ clean:
 # remove anything that can be rebuilt
 nuke: clean
        rm -f *mcl.a $(binaries) $(test_progs)
-
index f9fab09..1eefab4 100644 (file)
@@ -47,6 +47,7 @@
 #include <stdlib.h>
 #include <time.h>
 #include <string.h>
+#include <signal.h>
 
 
 #include "mcl.h"
@@ -71,10 +72,19 @@ static void usage( char* argv0 ) {
        fprintf( stderr, "\n" );
 }
 
+/*
+       We exit on any trapped signal so that we can kill  -15 the proecss
+       and still get gcoverage information to keep sonar happy.
+*/
+static void sigh( int sig ) {
+       fprintf( stderr, "\n[INFO] exiting on signal %d\n", sig );
+       exit( 0 );
+}
+
 //------------------------------------------------------------------------------------------
 int main( int argc,  char** argv ) {
        void*   ctx;                                                    // the mc listener library context
-       char*   dname = "/tmp/mcl/fifos";               // default directory where we open fifos
+       char*   dname = NULL;                                   // default directory where we open fifos
        char*   port = "4560";                                  // default rmr port
        char*   siphon_dir = "/tmp/mci/siphon"; // where siphon files are placed
        int             siphon = 0;                                             // percentage of messages to siphone off
@@ -83,6 +93,11 @@ int main( int argc,  char** argv ) {
        int             error = 0;
        int             long_hdrs = 1;                                  // -e sets and causes extended headers to be written
 
+       signal( SIGINT, sigh );
+       signal( SIGTERM, sigh );
+
+       dname = strdup( "/tmp/mcl/fifos" );                                     // so we can always free
+
        while( pidx < argc && argv[pidx][0] == '-' ) {                  // simple argument parsing (-x  or -x value)
                switch( argv[pidx][1] ) {
                        case 'd':
@@ -127,7 +142,6 @@ int main( int argc,  char** argv ) {
                        case '?':
                                usage( argv[0] );
                                exit( 0 );
-                               break;
 
                        default:
                                bad_arg( argv[pidx] );
@@ -139,6 +153,7 @@ int main( int argc,  char** argv ) {
        }
 
        if( error ) {
+               free( dname );
                usage( argv[0] );
                exit( 1 );
        }
@@ -146,6 +161,7 @@ int main( int argc,  char** argv ) {
        ctx = mcl_mk_context( dname );                  // initialise the library context
        if( ctx == NULL ) {
                fprintf( stderr, "[FAIL] couldn't initialise the mc listener environment\n" );
+               free( dname );
                exit( 1 );
        }
        mcl_set_sigh();                                                                 // set signal handler(s)
@@ -154,5 +170,8 @@ int main( int argc,  char** argv ) {
        mcl_fifo_fanout( ctx, report_freq, long_hdrs );         // listen and fanout messages to fifo; report to stdout every ~2sec
 
        fprintf( stderr, "[INFO] mc listener is finished.\n" );
+
+       free( port );                   // uneeded, but these keep sonar from twisting it's knickers about leaks
+       free( dname );
 }
 
index 4804266..ae0a1da 100644 (file)
@@ -33,6 +33,7 @@
 #include <stdlib.h>
 #include <time.h>
 #include <string.h>
+#include <signal.h>
 
 
 #include "mcl.h"
@@ -51,6 +52,15 @@ static void usage( char* argv0 ) {
        fprintf( stderr, "   -s  stats only mode\n" );
 }
 
+/*
+       We exit on any trapped signal so that we can kill  -15 the proecss
+       and still get gcoverage information to keep sonar happy.
+*/
+static void sigh( int sig ) {
+       fprintf( stderr, "\n[INFO] exiting on signal %d\n", sig );
+       exit( 0 );
+}
+
 //------------------------------------------------------------------------------------------
 int main( int argc,  char** argv ) {
        void*   ctx;                                                    // the mc listener library context
@@ -67,16 +77,19 @@ int main( int argc,  char** argv ) {
        long    count = 0;
        int             blabber = 0;
 
+       signal( SIGINT, sigh );
+       signal( SIGTERM, sigh );
+
        while( pidx < argc && argv[pidx][0] == '-' ) {                  // simple argument parsing (-x  or -x value)
                switch( argv[pidx][1] ) {
                        case 'd':
                                if( pidx+1 < argc ) {
                                        dname = strdup( argv[pidx+1] );
-                                       pidx++; 
+                                       pidx++;
                                } else {
-                                       bad_arg( argv[pidx] ); 
+                                       bad_arg( argv[pidx] );
                                        error = 1;
-                               }       
+                               }
                                break;
 
                        case 'e':
@@ -90,11 +103,11 @@ int main( int argc,  char** argv ) {
                        case 'm':
                                if( pidx+1 < argc ) {
                                        mtype = atoi( argv[pidx+1] );
-                                       pidx++; 
+                                       pidx++;
                                } else {
-                                       bad_arg( argv[pidx] ); 
+                                       bad_arg( argv[pidx] );
                                        error = 1;
-                               }       
+                               }
                                break;
 
                        case 's':
@@ -105,7 +118,6 @@ int main( int argc,  char** argv ) {
                        case '?':
                                usage( argv[0] );
                                exit( 0 );
-                               break;
 
                        default:
                                bad_arg( argv[pidx] );
@@ -120,7 +132,7 @@ int main( int argc,  char** argv ) {
                usage( argv[0] );
                exit( 1 );
        }
-       
+
        ctx = mcl_mk_context( dname );                  // initialise the library context
        if( ctx == NULL ) {
                fprintf( stderr, "[FAIL] couldn't initialise the mc listener library" );
@@ -146,7 +158,5 @@ int main( int argc,  char** argv ) {
                        count++;
                }
        }
-
-       fprintf( stderr, "[INFO] mc listener is finished.\n" );
 }
 
index 6560d2b..049508c 100644 (file)
@@ -30,7 +30,7 @@
                                Where <mtype> is the message type of the message received and
                                <len> is the length of the data that was written to the FIFO.
 
-                       
+               
        Date:           06 Oct 2019
        Author:         E. Scott Daniels
 */
@@ -90,10 +90,10 @@ typedef struct {
        not to discourage them from trying!).
 */
 static int copy_unlink( char* old, char* new, int mode ) {
-       char    buf[8192];      // read buffer
-       char*   tfname;         // temp file name while we have it open
-       char*   wbuf;           // work buffer for disecting the new filename
-       char*   tok;            // token pointer into a buffer
+       char    buf[8192];                      // read buffer
+       char*   tfname = NULL;          // temp file name while we have it open
+       char*   wbuf;                           // work buffer for disecting the new filename
+       char*   tok;                            // token pointer into a buffer
        int     len;
        int     rfd;            // read/write file descriptors
        int     wfd;
@@ -135,6 +135,7 @@ static int copy_unlink( char* old, char* new, int mode ) {
                        if( (len = write( wfd, &buf[start], len )) != remain ) {                // short write
                                if( errno != EINTR && errno != EAGAIN ) {
                                        logit( LOG_ERR, "copy: write failed: %s", strerror( errno ) );
+                                       free( tfname );
                                        close( wfd );
                                        close( rfd );
                                        return -1;
@@ -316,7 +317,7 @@ extern void* rdc_init( char* sdir, char* fdir, char* suffix, char* dsuffix ) {
                } else {
                        ctx->fdir = strdup( fdir );
                }
-       
+
        }
 
        if( suffix ) {
index cd50f66..b960c70 100644 (file)
@@ -6,17 +6,17 @@
                                listener received, and then writes those messages.  The assumption
                                is that a dummy route table is in place that causes one or more message
                                types to be routed to the mc_listener for "replay" into the mc-core
-                               application. 
+                               application.
 
 
                                The RDC file format is expected to be:
                                        @RDC<mtype><len> or as depicted in hex:
 
 
-                                       0000000 40 52 44 43                             << delim == @RDC
+                                       0000000 40 52 44 43                                     << delim == @RDC
                                                                                30 30 31 30 30 35 30 2a  << mtype
-                                                                                                                               30 30 30 30 << msg len
-                                       0000020 30 37 34 00 
+                                                                                                                               30 30 30 30 << msg len
+                                       0000020 30 37 34 00
                                                                                40 4d 43 4c 30 30 30 30 30 34 36 00 << raw message
                                                        :
                                                        :
@@ -65,10 +65,10 @@ int main( int argc, char** argv ) {
        char*   fifo_path = "/tmp/mcl_replay/fifos";    // directory where we create fifos; -d overrieds
        char*   input_file = NULL;                                              // -f to set; stdin is default if nil
        char*   arg;
-       int     i;
+       int             i;
 
-
-       for( i = 1; i < argc; i++ ) {
+       i = 1;
+       while( i < argc ) {
                arg = argv[i];
                if( *arg != '-' ) {
                        break;
@@ -108,17 +108,19 @@ int main( int argc, char** argv ) {
                        default:
                                fprintf( stderr, "unrecognised option: %s\n", arg );
                                err_count++;
-                               break;  
+                               break;
                }
+
+               i++;
        }
-       
+
        if( err_count ) {
                fprintf( stderr, "usage: %s [-d fifo-dir] [-f input-file] [-t msg-type]\n", argv[0] );
                fprintf( stderr, "if -f not given, then standard input is read\n" );
                exit( 1 );
        }
 
-       
+
        if( input_file == NULL ) {
                fd = 0;
        } else {
@@ -144,7 +146,7 @@ int main( int argc, char** argv ) {
                        fprintf( stderr, "abort: @header check, truncated file, or record > read buffer size\n" );
                        exit( 1 );
                }
-       
+
                if( strncmp( nxt, "@RDC", 4 ) == 0 ) {
                        mtype = atoi( nxt+4 );
                        mlen = atoi( nxt+12 );
@@ -161,7 +163,7 @@ int main( int argc, char** argv ) {
                                fprintf( stderr, "abort: truncated file, or record > read buffer size\n" );
                                exit( 1 );
                        }
-                       
+
                        if( desired < 0 || mtype == desired ) {                         // all mtypes, or specific
                                state = mcl_fifo_one( mcl_ctx, nxt, mlen, mtype );
                                if( state ) {
index 704ae15..5f654df 100644 (file)
@@ -27,7 +27,7 @@
                                        [listen_port [delay [stats-freq] [msg-type]]]]
 
                                        Defaults:
-                                               listen_port 43086 
+                                               listen_port 43086
                                                delay (mu-sec) 1000000 (1 sec)
                                                stats-freq 10
                                                msg-type 0
 #include <stdlib.h>
 #include <sys/epoll.h>
 #include <time.h>
+#include <signal.h>
 
 #include <rmr/rmr.h>
 
+/*
+       We exit on any trapped signal so that we can kill  -15 the proecss
+       and still get gcoverage information to keep sonar happy.
+*/
+static void sigh( int sig ) {
+       fprintf( stderr, "\n[<SNDR> exiting on signal %d\n", sig );
+       exit( 0 );
+}
+
 int main( int argc, char** argv ) {
-       void* mrc;                                                      //msg router context
+       void* mrc;                                                              //msg router context
        struct epoll_event events[1];                   // list of events to give to epoll
        struct epoll_event epe;                 // event definition for event to listen to
        int     ep_fd = -1;                                             // epoll's file des (given to epoll_wait)
-       int rcv_fd;                                                     // file des that NNG tickles -- give this to epoll to listen on
+       int rcv_fd;                                                             // file des that NNG tickles -- give this to epoll to listen on
        int nready;                                                             // number of events ready for receive
        rmr_mbuf_t*             sbuf;                                   // send buffer
        rmr_mbuf_t*             rbuf;                                   // received buffer
@@ -61,6 +71,9 @@ int main( int argc, char** argv ) {
        int             mtype = 0;
        int             stats_freq = 100;
 
+       signal( SIGINT, sigh );
+       signal( SIGTERM, sigh );
+
        if( argc > 1 ) {
                listen_port = argv[1];
        }
@@ -102,10 +115,10 @@ int main( int argc, char** argv ) {
                sleep( 1 );
        }
        fprintf( stderr, "<DEMO> rmr is ready\n" );
-       
+
 
        while( 1 ) {                                                                            // send messages until the cows come home
-               snprintf( sbuf->payload, 200, "count=%d received= %d ts=%lld %d stand up and cheer!\n",         // create the payload
+               snprintf( sbuf->payload, 200, "count=%d received= %d ts=%lld %d stand up and cheer!\n",         // create the payload
                        count, rcvd_count, (long long) time( NULL ), rand() );
 
                sbuf->mtype = mtype;                                                    // fill in the message bits
index b747904..d98a16f 100755 (executable)
@@ -26,7 +26,9 @@
 #                      and then will cat a few lines from one of the FIFOs.
 #                      This script is designed to run using the geneated runtime
 #                      image; in other words, it expects to find the binaries
-#                      in /playpen/bin.
+#                      in /playpen/bin if that directory exists. If it does not it
+#                      assumes the current working directory is where the binaries
+#                      exist.
 #
 # Date:                26 August 2019
 # Author:      E. Scott Daniels
@@ -57,34 +59,34 @@ function set_wait_values {
 #
 function run_sender {
        echo "starting sender"
-       RMR_SEED_RT=/tmp/local.rt RMR_RTG_SVC=9989 /playpen/${si}bin/sender 43086 10000 >/tmp/sender.log 2>&1 &
+       RMR_SEED_RT=/tmp/local.rt RMR_RTG_SVC=9989 $bin_dir/sender 43086 10000 >/tmp/sender.log 2>&1 &
        spid=$!
        sleep $sender_wait
 
-       echo "stopping sender"
+       echo "stopping sender $spid"
        kill -15 $spid
 }
 
 function run_listener {
        echo "starting listener"
-       /playpen/${si}bin/mc_listener $ext_hdr -r 1 -d $fifo_dir >/tmp/listen.log 2>&1 &
+       $bin_dir/mc_listener $ext_hdr -r 1 -d $fifo_dir >/tmp/listen.log 2>&1 &
        lpid=$!
 
        sleep $listener_wait
-       echo "stopping listener"
+       echo "stopping listener $lpid"
        kill -15 $lpid
 }
 
 # run a pipe reader for one message type
 function run_pr {
        echo "starting pipe reader $1"
-       /playpen/bin/pipe_reader $ext_hdr -m $1 -d $fifo_dir  >/tmp/pr.$1.log 2>&1 &
-       #/playpen/bin/pipe_reader -m $1 -d $fifo_dir & # >/tmp/pr.$1.log 2>&1 
+       $bin_dir/pipe_reader $ext_hdr -m $1 -d $fifo_dir  >/tmp/pr.$1.log 2>&1 &
+       #$bin_dir/pipe_reader -m $1 -d $fifo_dir & # >/tmp/pr.$1.log 2>&1 
        typeset prpid=$!
        
        sleep $reader_wait
-       echo "stopping pipe reader $1"
-       kill -1 $prpid
+       echo "stopping pipe reader $ppid"
+       kill -15 $prpid
 }
 
 # generate a dummy route table that the sender needs
@@ -122,6 +124,14 @@ do
        shift
 done
 
+
+if [[ -d /playpen/bin ]]       # designed to run in the container, but this allows unit test by jenkins to drive too
+then
+       bin_dir=/playpen/${si}bin
+else
+       bin_dir="."
+fi
+
 set_wait_values
 
 if (( ! raw_capture ))         # -n set, turn off capture
index 3cc0fc9..8ef769e 100755 (executable)
@@ -24,7 +24,7 @@
 # Abstract: Simple script to attempt to verify that the replay utility
 #                      works as expected. This assumes that verify.sh has been run
 #                      and that at least one RDC file is in /tmp/rdc. This script
-#                      will start the replay utility and a few pipe listeners to 
+#                      will start the replay utility and a few pipe listeners to
 #                      parse the data.
 #
 # Date:                19 November 2019
@@ -40,9 +40,10 @@ function set_wait_values {
 function run_replay {
        echo "starting replayer"
        file=$( ls ${stage_dir}/MCLT_*|head -1 )
+       chmod 644 $file
 
        set -x
-       /playpen/bin/rdc_replay -f $file -d $fifo_dir >/tmp/replay.log 2>&1
+       $bin_dir/rdc_replay -f $file -d $fifo_dir >/tmp/replay.log 2>&1
        lpid=$!
        set +x
        echo "replay finished"
@@ -51,10 +52,10 @@ function run_replay {
 # run a pipe reader for one message type
 function run_pr {
        echo "starting pipe reader $1"
-       /playpen/bin/pipe_reader $ext_hdr -m $1 -d $fifo_dir  >/tmp/pr.$1.log 2>&1 &
-       #/playpen/bin/pipe_reader -m $1 -d $fifo_dir & # >/tmp/pr.$1.log 2>&1 
+       $bin_dir/pipe_reader $ext_hdr -m $1 -d $fifo_dir  >/tmp/pr.$1.log 2>&1 &
+       #$bin_dir/pipe_reader -m $1 -d $fifo_dir & # >/tmp/pr.$1.log 2>&1
        typeset prpid=$!
-       
+
        sleep $reader_wait
        echo "stopping pipe reader $1"
        kill -1 $prpid
@@ -66,7 +67,7 @@ ext_hdr=""                                    # run with extended header enabled (-e turns extended off)
 run_listener=0                         # -a turns on to run all
 while [[ $1 == -* ]]
 do
-       case $1 in 
+       case $1 in
                -a)     run_listener=1;;
                *)      echo "$1 is not a recognised option"
                        echo "usage: $0 [-a]"
@@ -78,6 +79,13 @@ do
        shift
 done
 
+if [[ -d /playpen/bin ]]       # designed to run in the container, but this allows unit test by jenkins to drive too
+then
+       bin_dir=/playpen/${si}bin
+else
+       bin_dir="."
+fi
+
 if (( run_listener ))
 then
        echo "running listener to generate test files to replay..."
@@ -151,7 +159,7 @@ do
        fi
 done
 
-if (( ! errors )) 
+if (( ! errors ))
 then
        echo "[OK]    All logs seem good"
 fi
diff --git a/sidecars/listener/test/README b/sidecars/listener/test/README
new file mode 100644 (file)
index 0000000..b8b3b9a
--- /dev/null
@@ -0,0 +1,13 @@
+
+Test things for listener. These are broken out to prevent sonar from
+analysing them as they force things like passing nil pointers which sonar
+finds distasteful and complains (incorrectly) about.
+
+The run_unit_test script will drive the vetting and coverage tests on all
+of the library modules in ../src. Coverage files are moved to the parent
+directory where the jjb jobs expect to find them.
+
+The run_app_test script drives the listener application and tools, as well
+as the test sender, to generate coverage.  There is little vetting done
+here; any problems should be discovered by the verify scripts in ../src
+at the time the code is pushed. This is simply a coverage test for sonar.
diff --git a/sidecars/listener/test/run_app_tests.ksh b/sidecars/listener/test/run_app_tests.ksh
new file mode 100755 (executable)
index 0000000..6866041
--- /dev/null
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+#==================================================================================
+#        Copyright (c) 2018-2020 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:       run_app_test.ksh
+#      Abstract:       This script drives various applications (both mc_listener  related
+#                              and testers) to generate coverage stats for sonar.  There is little
+#                              we can validate with these tests other than the programme doesn't
+#                              crash.  The verify scripts are run at code checkin and will flag
+#                              any real problems, so this is just to keep sonar's knickers unbunched.
+#
+#                              Assumptions:
+#                                      - execution directory is listener/test
+#                                      - source directory is ../src
+#
+#      Date:           2 September 2020
+#      Author:         E. Scott Daniels
+# -------------------------------------------------------------------------
+
+
+# This is a hack! There seems not to be an easy way to have the LF
+# environment add RMR (or other needed packages) for testing. If we don't
+# find RMR in the /usr/local part of the filesystem, we'll force it into
+# /tmp which doesn't require root.  We'll be smart and get the desired
+# rmr version from the repo root juas as we _expected_ the CI environmnt
+# woudl do (but seems not to).
+#
+function ensure_pkgs {
+       if (( no_rmr_load ))
+       then
+               return
+       fi
+
+       if (( force_rmr_load )) || [[ -d /usr/local/include/rmr ]]
+       then
+               echo "[INFO] found RMR installed in /usr/local"
+               return
+       fi
+
+       rv=$( grep "version:" ../../rmr-version.yaml | awk '{ print $NF; exit( 0 ) }' )
+       rr=$( grep "repo:" ../../rmr-version.yaml | awk '{ print $NF; exit( 0 ) }' )
+       if [[ -z $rv ]]
+       then
+               rv="4.2.1"                      # some sane version if not found
+       fi
+       if [[ -z $rr ]]
+       then
+               rr="release"
+       fi
+       echo "[INFO] RMR seems not to be installed in /usr/local; pulling private copy: v=$rv"
+
+       pkg_dir=/tmp/ut_pkg
+       mkdir -p $pkg_dir
+
+       (
+               set -e
+               opts="-nv --content-disposition"
+               url_base="https://packagecloud.io/o-ran-sc/$rr/packages/debian/stretch"
+               cd /tmp
+               wget $opts ${url_base}/rmr_${rv}_amd64.deb/download.deb
+               wget $opts ${url_base}/rmr-dev_${rv}_amd64.deb/download.deb
+
+               for x in *rmr*deb
+               do
+                       dpkg -x $x $pkg_dir
+               done
+       )
+       if (( $? != 0 ))
+       then
+               echo "[FAIL] unable to install one or more RMR packages"
+               exit 1
+       fi
+
+       LD_LIBRARY_PATH=$pkg_dir/usr/local/lib:$LD_LIBRARY_PATH
+       LIBRARY_PATH=$pkg_dir/usr/local/lib:$LIBRARY_PATH
+       export C_INCLUDE_PATH="$pkg_dir/usr/local/include:$C_INCLUDE_PATH"
+}
+
+# ------------------------------------------------------------------------------------------------
+
+# these aren't set by default in some of the CI environments
+#
+export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
+export LIBRARY_PATH=/usr/local/lib:$LIBRARY_PATH
+
+force_rmr_load=0
+no_rmr_load=0
+
+while [[ $1 == -* ]]
+do
+       case $1 in
+               -f)     force_rmr_load=1;;
+               -N) no_rmr_load=1;;                                     # for local testing
+
+               *)      echo "unrecognised option: $1"
+                       exit 1
+                       ;;
+       esac
+
+       shift
+done
+
+ensure_pkgs                                                                    # some CI enviroments may not have RMR; get it
+
+script_dir=${PWD%/*}/src
+cd ../src
+
+# build the binaries with coverage options set
+export TEST_COV_OPTS="-ftest-coverage -fprofile-arcs"          # picked up by make so we get coverage on tools for sonar
+make clean                     # ensure coverage files removed
+make -B                                # ensure coverage data is nuked
+
+# drive with full complement to test good branches, then with bad (missing value) to drive exceptions
+mc_listener -p 4567 -q -r 10 -e -d foo -x  >/dev/null 2>&1             # -x (invalid) prevents execution loop
+for x in d p r \? h                                            # drive with missing values for d, p, r and singletons -h and -?
+do
+       mc_listener -$x >/dev/null 2>&1
+done
+
+pipe_reader -d foo -e -f -m 0 -s  -x >/dev/null 2>&1           # drive for all "good" conditions
+for x in d m \? h
+do
+       pipe_reader  -$x >/dev/null 2>&1                # drive each exception (missing value) or 'help'
+done
+
+rdc_replay -d foo -f bar -t 0  -x >/dev/null 2>&1                      # drive for all "good" conditions
+for x in d f t  \? h
+do
+       rdc_replay  -$x >/dev/null 2>&1         # drive each exception (missing value) or 'help'
+done
+
+$script_dir/verify.sh                                  # verify MUST be first (replay relies on its output)
+$script_dir/verify_replay.sh
+
+# generate and copy coverage files to parent which is where the CI jobs are looking for them
+# we do NOT gen stats for the library functions; the unit test script(s) do that
+#
+for x in mc_listener sender rdc_replay pipe_reader
+do
+       gcov $x.c
+       cp $x.c.gcov ../                                # copy only interesting things (not the lib modules if they exist)
+done
+
+exit
index 6ee435e..93fe2fb 100755 (executable)
@@ -163,7 +163,8 @@ then
        echo "building in src for sonar build wrapper capture"
        (
                cd ../src
-               make
+               export TEST_COV_OPTS="-ftest-coverage -fprofile-arcs"           # picked up by make so we get coverage on tools for sonar
+               make -B
        )
 fi