*.o
*.a
-
+*.gcov
+*.dcov
+*.gcno
+*.gcda
RUN mkdir /playpen/bin /playpen/src
ARG SRC=.
-COPY ${SRC}/Makefile ${SRC}/*.h ${SRC}/*.c /playpen/src/
+COPY ${SRC}/*.ksh ${SRC}/Makefile ${SRC}/*.h ${SRC}/*.c /playpen/src/
+COPY ${SRC}/verify_replay.sh ${SRC}/verify.sh /playpen/src/
+# Build all binaries; verify scripts expect them to be in bin, so we must copy too
+#
ENV LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib
ENV C_INCLUDE_PATH=/usr/local/include
-RUN cd /playpen/src/; make -B mc_listener sender pipe_reader rdc_replay rdc_extract
+RUN cd /playpen/src/; make -B all; ls -al mc_listener; cp mc_listener sender pipe_reader rdc_replay rdc_extract /playpen/bin/
+
+# Run unit tests. If they don't pass the build fails here. Tests can be run from src, but expect binaries in bin
+# so that they can be run in the final image as well.
+#
+ENV PATH /playpen/bin:/playpen/src:$PATH
+RUN cd /playpen/src/; ./run_unit_test.ksh
+RUN /playpen/src/verify.sh; /playpen/src/verify_replay.sh
+
# ----- final, smaller image ----------------------------------
FROM ubuntu:18.04
RUN mkdir -p /playpen/bin
COPY --from=buildenv /usr/local/lib/* /usr/local/lib/
COPY --from=buildenv /playpen/src/mc_listener /playpen/src/sender /playpen/src/pipe_reader /playpen/src/rdc_replay /playpen/src/rdc_extract /playpen/bin/
-COPY ${SRC}/verify_replay.sh ${SRC}/verify.sh ${SRC}run_replay.sh ${SRC}/help /playpen/bin/
+COPY ${SRC}/verify_replay.sh ${SRC}/verify.sh ${SRC}/run_replay.sh ${SRC}/help /playpen/bin/
ENV PATH=/playpen/bin:$PATH
ENV LD_LIBRARY_PATH=/usr/local/lib64:/usr/local/lib
# LD_LIBRARY_PATH, LIBRARY_PATH are set correctly.
binaries = mc_listener
+adjuncts = rdc_replay rdc_extract
+testers = sender pipe_reader
+
test_progs = sender unit_test pipe_reader
lib_obj = mcl.o rdc.o
lib_h = mcl.h
coverage_opts = -ftest-coverage -fprofile-arcs
-# make with no parms should just build 'production' binaries
-all: $(binaries) mc_listener
+# make with no parms should build all production and adjunct/verification binaries
+all: $(binaries) $(adjuncts) $(testers)
libmcl.a:: $(lib_obj) $(lib_h)
ar -v -r libmcl.a $(lib_obj)
gcc pipe_reader.c -o pipe_reader -L. -lmcl -lrmr_nng -lnng -lm -lpthread
unit_test: unit_test.c mcl.c
- gcc $(coverage_opts) unit_test.c -o unit_test -lrmr_nng -lnng -lm -lpthread
+ gcc -g $(coverage_opts) unit_test.c -o unit_test -lrmr_nng -lnng -lm -lpthread
# ---- adjunct tools -----------------------------------------------------------------
rdc_replay: rdc_replay.c libmcl.a
- gcc $(coverage_opts) rdc_replay.c -o rdc_replay -L. -lmcl -lrmr_nng -lnng -lpthread -lm
+ gcc rdc_replay.c -o rdc_replay -L. -lmcl -lrmr_nng -lnng -lpthread -lm
rdc_extract: rdc_extract.c libmcl.a
- gcc $(coverage_opts) rdc_extract.c -o rdc_extract -L. -lmcl -lrmr_nng -lnng -lpthread -lm
+ gcc rdc_extract.c -o rdc_extract -L. -lmcl -lrmr_nng -lnng -lpthread -lm
# ---- housekeeping stuff -------------------------------------------------------------
# remove only intermediates
--- /dev/null
+#!/usr/bin/env bash
+
+#==================================================================================
+# 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: discount_chk.ksh
+# Abstract: This checks the gcov output from unit tests to determine
+# what number of lines of code are highly untestable. Primararily
+# these are lines in blocks which are executed when systems
+# calles (e.g. malloc) fail.
+#
+# Date: 10 December 2019
+# Author: E. Scott Daniels
+# -------------------------------------------------------------------------
+
+
+#
+# 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.
+#
+# In verbose mode, the .gcov file is written to stdout and any unexecuted
+# line which is discounted is marked with ===== replacing the ##### marking
+# that gcov wrote.
+#
+# The return value is 0 for pass; non-zero for fail.
+function discount_gcov {
+ typeset f="$1"
+
+ #mct=$( get_mct ${1%.gcov} ) # see if a special coverage target is defined for this
+ mct=80
+
+ if [[ ! -f $1 ]]
+ then
+ if [[ -f ${1##*/} ]]
+ then
+ f=${1##*/}
+ else
+ echo "cant find: $f"
+ return
+ fi
+ fi
+
+ awk -v module_cov_target=$mct \
+ -v cfail=${cfail:-WARN} \
+ -v show_all=$show_all \
+ -v full_name="${1}" \
+ -v module="${f%.*}" \
+ -v chatty=$chatty \
+ -v replace_flags=$replace_flags \
+ '
+ function spit_line( ) {
+ if( chatty ) {
+ printf( "%s\n", $0 )
+ }
+ }
+
+ /-:/ { # skip unexecutable lines
+ spit_line()
+ seq++ # allow blank lines in a sequence group
+ next
+ }
+
+ {
+ nexec++ # number of executable lines
+ }
+
+ /#####:/ {
+ unexec++;
+ if( $2+0 != seq+1 ) {
+ prev_malloc = 0
+ prev_if = 0
+ seq = 0
+ spit_line()
+ next
+ }
+
+ if( prev_if && prev_malloc ) {
+ if( prev_malloc ) {
+ #printf( "allow discount: %s\n", $0 )
+ if( replace_flags ) {
+ if( replace_flags == 1 ) {
+ gsub( "#####", " 1", $0 )
+ } else {
+ gsub( "#####", "=====", $0 )
+ }
+ }
+ discount++;
+ }
+ }
+
+ seq++;;
+ spit_line()
+ next;
+ }
+
+ /if[(].*errno ==.*{/ || # if( errno == ... ) assume error check
+
+ /if[(].*alloc.*{/ { # if( (x = malloc( ... )) != NULL ) or if( (p = sym_alloc(...)) != NULL )
+ prev_malloc = 1
+ prev_if = 1
+ spit_line()
+ seq = $2+0
+ next
+ }
+
+ /if[(].* == NULL/ { # a nil check likely not easily forced if it wasnt driven
+ prev_malloc = 1
+ prev_if = 1
+ spit_line()
+ seq = $2+0
+ next
+ }
+
+ /if[(]/ {
+ if( seq+1 == $2+0 && prev_malloc ) { # malloc on previous line
+ prev_if = 1
+ } else {
+ prev_malloc = 0
+ prev_if = 0
+ }
+ spit_line()
+ next
+ }
+
+ /alloc[(]/ {
+ seq = $2+0
+ prev_malloc = 1
+ spit_line()
+ next
+ }
+
+ {
+ spit_line()
+ }
+
+ END {
+ net = unexec - discount
+ orig_cov = ((nexec-unexec)/nexec)*100 # original coverage
+ adj_cov = ((nexec-net)/nexec)*100 # coverage after discount
+ pass_fail = adj_cov < module_cov_target ? cfail : "PASS"
+ 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",
+ pass_fail, full_name ? full_name : module, nexec, unexec, discount, net, orig_cov, adj_cov, module_cov_target )
+ } else {
+ printf( "[%s] %d%% (%d%%) %s\n", pass_fail, adj_cov, orig_cov, full_name ? full_name : module )
+ }
+ }
+
+ exit( rc )
+ }
+ ' $f
+}
+
+# ----------------------------------------------------------------
+
+ignore_list="main"
+module_cov_target=80
+chatty=0
+show_all=1
+replace_flags=1
+cfail="FAIL"
+
+while [[ $1 == -* ]]
+do
+ case $1 in
+ -a) show_all-1;; # only show things not passing
+ -c) module_cov_target=$2;; # coverage target for module
+ -r) replace_flag=$2; shift;; # replace with ==== if 2; no repalce if 0
+ -V) chatty=1;; # show "dcov" output
+ esac
+
+ shift
+done
+
+for ckf in "$@"
+do
+ discount_gcov ${ckf%.gcov}.gcov
+done
#include "mcl.h"
+#ifndef FOREVER
+#define FOREVER 1
+#endif
+
#define READER 0
#define WRITER 1
/*
Set up for raw data capture. We look for directory overriedes from
environment variables, and then invoke the rdc_init() to actually
- set things upd.
+ set things up.
*/
static void* setup_rdc() {
void* ctx;
*/
static int open_fifo( mcl_ctx_t* ctx, int mtype, int io_dir ) {
char wbuf[1024];
- int fd; // real file des
+ int fd; // real file des
int jfd = -1; // junk file des
int state;
allowing for direct update of counts after the write.
*/
static int suss_fifo( mcl_ctx_t* ctx, int mtype, int io_dir, fifo_t** fref ) {
- fifo_t* fifo;
+ fifo_t* fifo = NULL;
void* hash;
- if( io_dir == READER ) { // with an integer key, we nned two hash tables
+ if( ctx == NULL ) {
+ if( fref != NULL ) {
+ *fref = NULL;
+ }
+ return -1;
+ }
+
+ if( io_dir == READER ) { // with an integer key, we need two hash tables
hash = ctx->rd_hash;
} else {
hash = ctx->wr_hash;
if( (fifo = (fifo_t *) rmr_sym_pull( hash, mtype )) == NULL ) {
fifo = (fifo_t *) malloc( sizeof( *fifo ) );
- if( fifo == NULL ) {
- return -1;
+ if( fifo != NULL ) {
+ memset( fifo, 0, sizeof( *fifo ) );
+ fifo->key = mtype;
+ fifo->fd = open_fifo( ctx, mtype, io_dir );
+ if( fifo->fd >= 0 ) { // save only on good open
+ rmr_sym_map( hash, mtype, fifo );
+ } else {
+ free( fifo );
+ fifo = NULL;
+ }
}
-
- memset( fifo, 0, sizeof( *fifo ) );
- fifo->key = mtype;
- fifo->fd = open_fifo( ctx, mtype, io_dir );
- rmr_sym_map( hash, mtype, fifo );
}
if( fref != NULL ) {
*fref = fifo;
}
- return fifo->fd;
+
+ return fifo == NULL ? -1 : fifo->fd;
}
/*
rdc_ctx = setup_rdc( ); // pull rdc directories from enviornment and initialise
- while( 1 ) {
+ do {
mbuf = mcl_get_msg( ctx, mbuf, report ); // wait up to report sec for msg (0 == block until message)
if( mbuf != NULL && mbuf->state == RMR_OK && mbuf->len > 0 ) {
fflush( stdout );
}
}
- }
+ } while( FOREVER ); // forever allows for escape during unit testing
}
There seems to be an issue with some collectors and thus it is required
to initially name the file with a leading dot (.) until the file is closed
- and ready to be read by external processes (marking it write only seems
+ and ready to be read by external processes (marking it write-only seems
not to discourage them from trying!).
*/
static int copy_unlink( char* old, char* new, int mode ) {
errno = 0;
if( (rfd = open( old, O_RDONLY )) < 0 ) {
- logit( LOG_ERR, "copy: open src for copy failed: %s: %s\n", old, strerror( errno ) );
+ logit( LOG_ERR, "copy: open src for copy failed: %s: %s", old, strerror( errno ) );
return -1;
}
//logit( LOG_INFO, "copy: creating file in tmp filename: %s", tfname );
if( (wfd = open( tfname, O_WRONLY | O_CREAT | O_TRUNC, 0200 )) < 0 ) {
- logit( LOG_ERR, "copy: open tmp file for copy failed: %s: %s\n", tfname, strerror( errno ) );
+ logit( LOG_ERR, "copy: open tmp file for copy failed: %s: %s", tfname, strerror( errno ) );
return -1;
}
ctx = (rdc_ctx_t *) vctx;
- if( ctx != NULL && freq > 10 ) {
+ if( ctx != NULL && freq >= 10 ) {
ctx->frequency = freq;
+ logit( LOG_INFO, "(rdc) file roll frequency set to %d seconds", ctx->frequency );
+ } else {
+ logit( LOG_ERR, "(rdc) file roll frequency was not set; ctx was nill or frequency was less than 10s" );
}
-
- logit( LOG_INFO, "(rdc) file roll frequency set to %d seconds", ctx->frequency );
}
/*
ctx = rdc_init( "/tmp/rdc/stage", "/tmp/rdc/final", ".rdc", NULL ); // does not create done files
//ctx = rdc_init( "/tmp/rdc/stage", "/tmp/rdc/final", ".rdc", ".done" ); // will create a "done" file
if( ctx == NULL ) {
- logit( LOG_CRIT, "<TEST> abort! rdc_init returned a nil pointer\n" );
+ logit( LOG_CRIT, "<TEST> abort! rdc_init returned a nil pointer" );
exit( 1 );
}
--- /dev/null
+#!/usr/bin/env bash
+
+#==================================================================================
+# 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: run_unit_test.ksh
+# Abstract: This drives unit testing setting up working directories
+# and such.
+#
+# Date: 10 December 2019
+# Author: E. Scott Daniels
+# -------------------------------------------------------------------------
+
+function setup_dirs {
+ mkdir -p /tmp/fifos
+ mkdir -p /tmp/mc_listener_test/final
+ mkdir -p /tmp/mc_listener_test/stage
+
+ mv_src=/tmp/mc_listener_test/mv_src # source that will be renamed rather than copied
+ mv_dest=/tmp/mc_listener_test/mv_dest
+ ps -elf >$mv_src
+
+ copy_src=/tmp/mc_listener_test/copy_src
+ copy_dest=/tmp/mc_listener_test/copy_dest
+ ps -elf >$copy_src
+
+ src_md5=$( cat $copy_src | md5sum ) # use cat so that filename doesn't factor in to output
+ rm -f $copy_dest
+}
+
+function purge_dirs {
+ rm -fr /tmp/mc_listener_test
+}
+
+
+if ! make -B unit_test # ensure that it's fresh
+then
+ echo "[FAIL] cannot make unit_test"
+ exit 1
+fi
+
+if [[ $1 == "purge" ]]
+then
+ purge_dirs
+ exit 0
+fi
+
+setup_dirs
+
+if [[ $1 == "set"* ]]
+then
+ exit
+fi
+
+if ! unit_test >/tmp/PID$$.utlog 2>&1
+then
+ cat /tmp/PID$$.utlog
+ rm -f /tmp/PID$$.*
+ purge_dirs
+ exit 1
+fi
+
+echo "[PASS] base unit tests all pass"
+echo "[INFO] file/directory verification begins...."
+
+# validate files that should have been created/copied
+
+rc=0
+
+ls -al /tmp/mc_listener_test/* >/tmp/PID$$.fdlog 2>&1
+
+if [[ -e $copy_src ]]
+then
+ echo "[FAIL] copy source test should have been unlinked but was there!"
+ rc=1
+else
+ dest_md5=$( cat $copy_dest | md5sum ) # use cat so that filename doesn't factor in to output
+ if [[ $dest_md5 != $src_md5 ]]
+ then
+ echo "[FAIL] md5 of copy test file didn't match soruce"
+cat $dest_md5
+echo "$dest_md5 $src_md5"
+ rc=1
+ fi
+fi
+purge_dirs
+
+if (( rc > 0 ))
+then
+ cat /tmp/PID$$.fdlog
+fi
+
+
+show_coverage.ksh unit_test.c # compute coverage and generate .gcov files
+echo "Coverage with discounting (raw values in parens)"
+discount_chk.ksh $(ls *gcov|egrep -v "^test_|unit_test.c")
+
+
+if (( rc > 0 ))
+then
+ echo "[FAIL] overall test fails"
+else
+ echo "[PASS] overall test passes"
+fi
+
+rm -f /tmp/PID$$.*
+exit $rc
--- /dev/null
+#!/usr/bin/env bash
+
+#==================================================================================
+# 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: show_coverage.ksh
+# Abstract: This script parses the coverage file associated with the .c file
+# listed on the command line and displays a more readable summary.
+#
+# Date: 10 December 2019
+# Author: E. Scott Daniels
+# -------------------------------------------------------------------------
+
+show_all=0
+ignore_list="main"
+module_cov_target=80
+chatty=0
+show_all=0
+cfail="DCHK"
+
+gcov -f $1 | sed "s/'//g" | awk \
+ -v cfail=$cfail \
+ -v show_all=$show_all \
+ -v ignore_list="$iflist" \
+ -v module_cov_target=$module_cov_target \
+ -v chatty=$verbose \
+ '
+ BEGIN {
+ announce_target = 1;
+ nignore = split( ignore_list, ignore, " " )
+ for( i = 1; i <= nignore; i++ ) {
+ imap[ignore[i]] = 1
+ }
+
+ exit_code = 0 # assume good
+ }
+
+ /^TARGET:/ {
+ if( NF > 1 ) {
+ target[$2] = $NF
+ }
+ next;
+ }
+
+ /File.*_test/ || /File.*test_/ { # dont report on test files
+ skip = 1
+ file = 1
+ fname = $2
+ next
+ }
+
+ /File/ {
+ skip = 0
+ file = 1
+ fname = $2
+ next
+ }
+
+ /Function/ {
+ fname = $2
+ file = 0
+ if( imap[fname] ) {
+ fname = "skipped: " fname # should never see and make it smell if we do
+ skip = 1
+ } else {
+ skip = 0
+ }
+ next
+ }
+
+ skip { next }
+
+ /Lines executed/ {
+ split( $0, a, ":" )
+ pct = a[2]+0
+
+ if( file ) {
+ if( announce_target ) { # announce default once at start
+ announce_target = 0;
+ printf( "\n[INFO] default target coverage for modules is %d%%\n", module_cov_target )
+ }
+
+ if( target[fname] ) {
+ mct = target[fname]
+ announce_target = 1;
+ } else {
+ mct = module_cov_target
+ }
+
+ if( announce_target ) { # annoucne for module if different from default
+ printf( "[INFO] target coverage for %s is %d%%\n", fname, mct )
+ }
+
+ if( pct < mct ) {
+ printf( "[%s] %3d%% %s\n", cfail, pct, fname ) # CAUTION: write only 3 things here
+ exit_code = 1
+ } else {
+ printf( "[PASS] %3d%% %s\n", pct, fname )
+ }
+
+ announce_target = 0;
+ } else {
+ if( pct < 70 ) {
+ printf( "[LOW] %3d%% %s\n", pct, fname )
+ } else {
+ if( pct < 80 ) {
+ printf( "[MARG] %3d%% %s\n", pct, fname )
+ } else {
+ if( show_all ) {
+ printf( "[OK] %3d%% %s\n", pct, fname )
+ }
+ }
+ }
+ }
+
+ }
+
+ END {
+ printf( "\n" );
+ exit( exit_code )
+ }
+ '
--- /dev/null
+// vim: ts=4 sw=4 noet:
+/*
+--------------------------------------------------------------------------------
+ Copyright (c) 2018-2019 AT&T Intellectual Property.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+--------------------------------------------------------------------------------
+*/
+
+/*
+ Mnemonic: test_rmr_em.c.
+ Abstract: Emulates some RMR functions (message receipt mostly)
+ so that we can unit test.
+
+ This file should be included by the test programme BEFORE
+ any module which uses RMR functions is included
+
+ Date: 10 December 2019
+ Author: E. Scott Daniels
+*/
+
+#include <rmr/rmr.h>
+
+#define rmr_torcv_msg RMR_torcv_msg
+
+extern rmr_mbuf_t* RMR_torcv_msg( void* ctx, rmr_mbuf_t* old_msg, int ms_to ) {
+ static int timeout = 0;
+
+ if( old_msg == NULL ) {
+ old_msg = rmr_alloc_msg( ctx, 256 );
+ if( old_msg == NULL ) {
+ return NULL;
+ }
+ }
+
+ timeout = ! timeout; // every other message results in a timeout
+ if( timeout ) {
+ old_msg->state = RMR_ERR_TIMEOUT;
+ return old_msg;
+ }
+
+
+ snprintf( old_msg->payload, 100, "DUMMY MESSAGE" );
+ old_msg->mtype = 100;
+ old_msg->len = strlen( old_msg->payload );
+ old_msg->sub_id = -1;
+ old_msg->state = 0;
+
+ return old_msg;
+}
Author: E. Scott Daniels
*/
-// this/these are what we are testing; include them directly
+#define FOREVER 0 // allows forever loops in mcl code to escape after one loop
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "test_rmr_em.c" // emulated rmr functions (for receives)
+
+// this/these are what we are testing; include them directly (must be after forever def)
#include "mcl.c"
+#include "rdc.c"
+
+/*
+ Set up env things for the rdc setup call.
+*/
+static void set_env() {
+ setenv( "MCL_RDC_ENABLE", "1", 1 ); // cause 'raw' capture to be setup
+ setenv( "MCL_RDC_STAGE", "/tmp/mc_listener_test/stage", 1 ); // unit test script should create and will purge after
+ setenv( "MCL_RDC_FINAL", "/tmp/mc_listener_test/final", 1 );
+ setenv( "MCL_RDC_SUFFIX", ".xxx", 1 );
+ setenv( "MCL_RDC_DONE", ".done", 1 );
+ setenv( "MCL_RDC_FREQ", "10", 1 );
+}
/*
Parms: [fifo-dir-name]
*/
int main( int argc, char** argv ) {
void* ctx;
- int errors;
+ void* bad_ctx; // context with a bad directory path for coverage/error testing
+ int errors = 0;
char* dname = "/tmp/fifos";
char* port = "4560";
int fd;
int fd2;
+ int rfd; // a reader file des so we can read what we write
+ fifo_t* fref = NULL; // fifo type reference; we just verify that suss sets it
+ char wbuf[2048];
+ char payload[1024];
+ char* bp;
+ void* buf;
+ int state;
+ char timestamp[1024]; // read will put a timestamp here
if( argc > 1 ) {
dname = argv[1];
}
- ctx = mcl_mk_context( dname );
+ set_env(); // set env that setup_rdc() looks for
+
+ ctx = mcl_mk_context( dname ); // allocate the context
if( ctx == NULL ) {
- fprintf( stderr, "[FAIL] couldn't make context" );
+ fprintf( stderr, "[FAIL] couldn't make context\n" );
exit( 1 );
}
- mcl_set_sigh(); // prevent colobber from broken pipe
+ mcl_set_sigh(); // prevent colobber from broken pipe
- fd = suss_fifo( ctx, 101, 1 ); // should open the file for writing and return the fdes
+ open_fifo( ctx, 100, WRITER ); // open dummy to prevent blocking reader
+ rfd = open_fifo( ctx, 100, READER ); // open a reader to check fanout output
+ if( rfd < 0 ) {
+ fprintf( stderr, "[FAIL] unable to open a pipe reader for type == 100\n" );
+ errors++;
+ }
+
+ fd = suss_fifo( ctx, 100, 1, &fref ); // should open the file for writing and return the fdes
if( fd < 0 ) {
fprintf( stderr, "[FAIL] suss_fifo did not return a valid fd\n" );
errors++;
}
- fd2= suss_fifo( ctx, 101, 0 ); // should open the file file for reading and return a different fd
+ if( fref == NULL ) {
+ fprintf( stderr, "[FAIL] suss_fifo did not set the fifo reference pointer\n" );
+ errors++;
+ } else {
+ chalk_ok( fref );
+ chalk_error( fref );
+ }
+
+ fd2= suss_fifo( ctx, 100, 0, NULL ); // should open the file file for reading and return a different fd
if( fd < 0 ) {
fprintf( stderr, "[FAIL] suss_fifo did not return a valid fd\n" );
errors++;
errors++;
}
- mcl_start_listening( ctx, port, 0 ); // start the listener, no waiting for rt since we don't send
+ mcl_start_listening( ctx, port, 0 ); // start the listener
+
+ // under test, the forever keeps fanout from blocking; drive for each of two cases:
+ mcl_fifo_fanout( ctx, 5, 1 ); // first rmr receive call will simulate a timeout
+ mcl_fifo_fanout( ctx, 5, 1 ); // second receive call simualtes a message arriving
+ mcl_fifo_fanout( ctx, 5, 1 ); // another round so there are two to read
+ mcl_fifo_fanout( ctx, 5, 1 );
+
+ *timestamp = 0;
+ state = mcl_fifo_read1( ctx, 100, payload, sizeof( payload ), TRUE );
+ if( state < 1 ) {
+ fprintf( stderr, "[FAIL] fifo_read return positive value when expected to\n" );
+ errors++;
+ }
+ state = mcl_fifo_tsread1( ctx, 100, payload, sizeof( payload ), TRUE, timestamp );
+ if( state < 1 ) {
+ fprintf( stderr, "[FAIL] fifo_read with timestamp return positive value when expected to\n" );
+ errors++;
+ }
+
+ state = fifo_read1( NULL, 100, payload, sizeof( payload ), 1, timestamp ); // coverage error check
+ if( state != 0 ) {
+ fprintf( stderr, "[FAIL] fifo_read didn't return 0 when given a nil context to\n" );
+ errors++;
+ }
+
+ mcl_fifo_fanout( ctx, 5, 0 ); // test with writing short header
+ mcl_fifo_fanout( ctx, 5, 0 );
+
+ // ------ some error/coverage testing ---------------------------
+ logit( LOG_CRIT, "critical message" );
+ logit( LOG_ERR, "error message" );
+ logit( LOG_WARN, "warning message" );
+ logit( LOG_STAT, "stats message" );
+
+ bad_ctx = mcl_mk_context( "/nosuchdirectoryinthesystem" ); // create a context where fifo opens should fail
+ if( bad_ctx == NULL ) {
+ fprintf( stderr, "[FAIL] couldn't make 'bad' context" );
+ exit( 1 );
+ }
+
+ fref = NULL;
+ fd = suss_fifo( bad_ctx, 100, 1, &fref ); // should fail to open the file for writing beacuse directory is bad
+ if( fd >= 0 ) {
+ fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a context with a bad directory path\n" );
+ errors++;
+ }
+ if( fref != NULL ) {
+ fprintf( stderr, "[FAIL] suss_fifo returned an fref pointer when given a bad context\n" );
+ errors++;
+ }
+
+ fd = suss_fifo( NULL, 100, 1, &fref ); // coverage nil pointer check
+ if( fd >= 0 ) {
+ fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a nil context a bad directory path\n" );
+ errors++;
+ }
+
+ fd = suss_fifo( ctx, -1, 1, &fref ); // mad message type check
+ if( fd >= 0 ) {
+ fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a bad message type\n" );
+ errors++;
+ }
+
+ // -- buffer testing ------------------------------------------------------
+ bp = build_hdr( 1024, wbuf, 0 );
+ bp = build_hdr( 1024, NULL, 0 );
+ if( bp == NULL ) {
+ fprintf( stderr, "[FAIL] build_hdr didn't return a buffer pointer when given a nil buffer\n" );
+ errors++;
+ }
+ free( bp );
+
+ bp = build_hdr( 1024, wbuf, sizeof( wbuf ) );
+ if( bp == NULL ) {
+ fprintf( stderr, "[FAIL] build_hdr didn't return a buffer pointer\n" );
+ errors++;
+ }
+
+
+ // ----- msg receive testing ----------------------------------------------------
+ buf = mcl_get_msg( NULL, NULL, 1 ); // drive nil pointer checks
+ if( buf != NULL ) {
+ errors++;
+ fprintf( stderr, "[FAIL], get_msg call with nil context returned a buffer pointer\n" );
+ }
+
+ buf = mcl_get_msg( ctx, NULL, 1 ); // drive to force coverage; nothing is sent, so we can't validate buffer
+
+
+ mcl_fifo_one( NULL, NULL, 1, 1 );
+ mcl_fifo_one( ctx, NULL, 1, 1 );
+ mcl_fifo_one( ctx, wbuf, 0, 1 );
+ mcl_fifo_one( ctx, wbuf, 10, 100 );
+
+
+ // --- some rdc testing as best as we can without message generators --------
+ rdc_init( NULL, NULL, ".foo", ".bar" ); // coverage testing
+
+ ctx = setup_rdc(); // coverage test to ensure that it generates a context
+ if( ctx == NULL ) {
+ fprintf( stderr, "[FAIL] setup_rdc did not return a context pointer\n" );
+ errors++;
+ }
+
+ rdc_set_freq( NULL, 0 ); // error/nil test
+ rdc_set_freq( ctx, 0 ); // error/nil test
+ rdc_set_freq( ctx, 10 ); // roll after 10seconds to test that
+
+ build_hdr( 1024, wbuf, sizeof( wbuf ) );
+ bp = NULL;
+ bp = rdc_init_buf( 100, wbuf, 10, bp ); // set up for write
+ rdc_write( ctx, bp, payload, sizeof( payload ) ); // write the raw data
+
+ fprintf( stderr, "[INFO] pausing to test rdc file rolling\n" );
+ sleep( 15 );
+ build_hdr( 1024, wbuf, sizeof( wbuf ) );
+ bp = NULL;
+ bp = rdc_init_buf( 100, wbuf, 10, bp );
+ rdc_write( ctx, bp, payload, sizeof( payload ) );
+
+
+ // CAUTION: filenames need to match those expected in the run script as it creates src, and will validate, destination files
+ state = copy_unlink( "/tmp/mc_listener_test/no-such-copy_src", "/tmp/mc_listener_test/copy_dest", 0664 ); // first couple drive for error and coverage
+ if( state >= 0 ) {
+ fprintf( stderr, "[FAIL] copy-unlink of bad file didn't return bad state\n" );
+ errors++;
+ }
+ state = copy_unlink( "/tmp/mc_listener_test/copy_src", "/tmp/mc_listener_test-nodir/copy_dest", 0664 );
+ if( state >= 0 ) {
+ fprintf( stderr, "[FAIL] copy-unlink of bad target didn't return bad state\n" );
+ errors++;
+ }
+ state = copy_unlink( "/tmp/mc_listener_test/copy_src", "/tmp/mc_listener_test/copy_dest", 0664 ); // drive for coverage; setup script can check contents
+ if( state < 0 ) {
+ fprintf( stderr, "[FAIL] copy-unlink expected success but failed\n" );
+ errors++;
+ }
+ state = mvocp( "/tmp/mc_listener_test/bad-src-mv_src", "/tmp/mc_listener_test/mv_dest" );
+ if( state >= 0 ) {
+ fprintf( stderr, "[FAIL] mv or copy expected failure didn't set bad state\n" );
+ errors++;
+ }
+ state = mvocp( "/tmp/mc_listener_test/mv_src", "/tmp/mc_listener_test/mv_dest" );
+ if( state < 0 ) {
+ fprintf( stderr, "[FAIL] mv or copy expected to succeed didn't set good state\n" );
+ errors++;
+ }
+
+ // ---- finally, check error count, write nice cheerful message and exit ----
if( ! errors ) {
- fprintf( stderr, "[PASS] all tests look peachy\n" );
+ fprintf( stderr, "[PASS] unit_test: everything looks peachy\n" );
+ } else {
+ fprintf( stderr, "[FAIL] unit_test: there were %d errors\n", errors );
}
return errors != 0;