# 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
+)
#
#---------------------------------------------------------------------------------
+# 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.
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
# remove anything that can be rebuilt
nuke: clean
rm -f *mcl.a $(binaries) $(test_progs)
-
#include <stdlib.h>
#include <time.h>
#include <string.h>
+#include <signal.h>
#include "mcl.h"
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
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':
case '?':
usage( argv[0] );
exit( 0 );
- break;
default:
bad_arg( argv[pidx] );
}
if( error ) {
+ free( dname );
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 environment\n" );
+ free( dname );
exit( 1 );
}
mcl_set_sigh(); // set signal handler(s)
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 );
}
#include <stdlib.h>
#include <time.h>
#include <string.h>
+#include <signal.h>
#include "mcl.h"
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
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':
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':
case '?':
usage( argv[0] );
exit( 0 );
- break;
default:
bad_arg( argv[pidx] );
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" );
count++;
}
}
-
- fprintf( stderr, "[INFO] mc listener is finished.\n" );
}
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
*/
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;
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;
} else {
ctx->fdir = strdup( fdir );
}
-
+
}
if( suffix ) {
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
:
:
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;
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 {
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 );
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 ) {
[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
int mtype = 0;
int stats_freq = 100;
+ signal( SIGINT, sigh );
+ signal( SIGTERM, sigh );
+
if( argc > 1 ) {
listen_port = argv[1];
}
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
# 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
#
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
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
# 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
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"
# 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
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]"
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..."
fi
done
-if (( ! errors ))
+if (( ! errors ))
then
echo "[OK] All logs seem good"
fi
--- /dev/null
+
+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.
--- /dev/null
+#!/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
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