Remove NNG libraries from packages

The default build process will no longer include the NNG
libraries (librmr_nng.*) by default.  It is still possible
to enable them with a CMake build flag (-DBUILD_NNG=1).

The NNG specific unit tests have been disabled.

Some SI95 specific unit tests have been enhanced in an effort
to work toward full coverage of the SI95 code.

This change is in a major version bump as the package contents
change. However, there is NOT an API change; all existing
applications will be able to use the new version without any
modification (other than possibly removing references to the
NNG based libraries).

Issue-ID: RIC-337

Signed-off-by: E. Scott Daniels <daniels@research.att.com>
Change-Id: Ic9854b1ffc1e82c765692a11724d8086d24cceed
diff --git a/CHANGES_CORE.txt b/CHANGES_CORE.txt
index 89d0595..481452c 100644
--- a/CHANGES_CORE.txt
+++ b/CHANGES_CORE.txt
@@ -5,6 +5,17 @@
 # API and build change  and fix summaries. Doc correctsions
 # and/or changes are not mentioned here; see the commit messages.
 
+2020 April 21; version 4.0.0
+	The NNG based libraries are no longer included in the RMR packages.
+	This is considered a breaking change as NNG will not be supported by
+	default.  It is still possible to build with RMR-NNG libraries, but
+	that is the exception.  The API between 3.8.2 and 4.0.0 is the SAME.
+	Upgrading to 4.0.0 only means that the underlying transport mechanism
+	is limited only to SI95.
+
+	The rmr_rcv_specific() function has been deprecated as it was necessary
+	only for NNG and Nanomsg support.  Its use should be discontinued.
+
 2020 April 20; version 3.8.2
 	Fix bug which was preventing an instance receiving dynamic route
 	table updates. (RIC-336)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3109513..a8aa1ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,6 +22,7 @@
 #	-DDEBUG=n			Enable debugging level n
 #	-DDEV_PKG=1			Development package configuration
 #	-DBUILD_DOC=1		Man pages generated
+#	-DBUILD_NNG=1		Enable building of NNG and the RMR NNG based libraries
 #	-DIGNORE_LIBDIR=1	Installation of rmr libries is into /usr/local/lib and ignores
 #						value in CMAKE_INSTALL_LIBDIR.
 #						system preferred (typically /usr/local/lib64).
@@ -38,9 +39,9 @@
 project( rmr LANGUAGES C )
 cmake_minimum_required( VERSION 3.5 )
 
-set( major_version "3" )		# should be automatically populated from git tag later, but until CI process sets a tag we use this
-set( minor_version "8" )
-set( patch_level "2" )
+set( major_version "4" )		# should be automatically populated from git tag later, but until CI process sets a tag we use this
+set( minor_version "0" )
+set( patch_level "0" )
 
 set( install_root "${CMAKE_INSTALL_PREFIX}" )
 set( install_inc "include/rmr" )
@@ -50,11 +51,10 @@
 	set( install_man "/usr/share/man" )			# this needs to be fixed so it's not hard coded
 endif()
 
-# Must use GNUInstallDirs to install libraries into correct
-# locations on all platforms.
+# Must use GNUInstallDirs to install libraries into correct locations on all platforms.
 include( GNUInstallDirs )
 
-# nng installs using LIBDIR as established by the gnu include; it varies from system
+# We install using LIBDIR as established by the gnu include; it varies from system
 # to system, and we don't trust that it is always set, so we default to lib if it is missing.
 #
 if( NOT CMAKE_INSTALL_LIBDIR )
@@ -165,56 +165,60 @@
 endif()
 
 # ---------------- setup nano/nng things ---------------------------------------
-if( NOT SKIP_EXTERNALS )
-	set( need_ext 1 )				# we force dependences on these for right build order
-    execute_process( COMMAND  git submodule update --init -- ext/nng
-            WORKING_DIRECTORY  ${CMAKE_CURRENT_SOURCE_DIR}
-    )
-
-    if( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/ext/nng/CMakeLists.txt )
-        message( FATAL_ERROR "cannot find nng in our git source as a submodule: Giving up" )	# this will abort which seems wrong, but tdam.
-    endif()
-
-	include( ExternalProject )
-	ExternalProject_Add(
-		ext_nng
-		SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/nng"
-		CMAKE_ARGS "-DBUILD_SHARED_LIBS=1"
-		CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}"
-		BUILD_COMMAND "make"
-		UPDATE_COMMAND ""
-		TEST_COMMAND ""
-		STEP_TARGETS build
-	)
-
-	# it seems impossible to install everything that lands in {bin}/lib, so we need to
-	# hard code (shudder) some things. Even worse, we have to make exceptions for
-	# builds on apple (osx) since their naming convention wandered off the path.
-	set( nng_major 1 )
-	set( nng_minor 1.0 )
-	set( so ${CMAKE_SHARED_LIBRARY_SUFFIX} )	# cmake variables are impossibly long :(
-	if( NOT APPLE ) 				# probably breaks in windows, but idc
-		set( nng_so_suffix ${so} )
-		set( nng_so_suffix_m ${so}.${nng_major} )
-		set( nng_so_suffix_mm ${so}.${nng_major}.${nng_minor} )
-	else()
-		# of course apple puts versions before the suffix :(
-		set( nng_so_suffix ${so} )									# so has a lead dot, so NOT needed
-		set( nng_so_suffix_m ".${nng_major}${so}" )					# these need leading dots
-		set( nng_so_suffix_mm ".${nng_major}.${nng_minor}${so}" )
-	endif()
-
-	message( "+++ building with nng: ${nng_major}.${nng_minor}" )
+if( NOT BUILD_NNG )
+	set( PACK_EXTERNALS 0 )
 else()
-	if( PACK_EXTERNALS )
-		# This makes some stand-alone unit testing possible for bindings and transport layer testing;
-		# it is not meant for production packages.
-		#
-		unset( SKIP_EXTERNALS  CACHE )	# must remove so as not to trap user into a never ending failure
-		unset( PACK_EXTERNALS  CACHE )
-		message( FATAL_ERROR "ERROR: PACK_EXTERNALS can be set only if SKIP_EXTERNALS is unset (=0, or not supplied on command line)" )
+	if( NOT SKIP_EXTERNALS )
+		set( need_ext 1 )				# we force dependences on these for right build order
+		execute_process( COMMAND  git submodule update --init -- ext/nng
+				WORKING_DIRECTORY  ${CMAKE_CURRENT_SOURCE_DIR}
+		)
+
+		if( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/ext/nng/CMakeLists.txt )
+			message( FATAL_ERROR "cannot find nng in our git source as a submodule: Giving up" )	# this will abort which seems wrong, but tdam.
+		endif()
+
+		include( ExternalProject )
+		ExternalProject_Add(
+			ext_nng
+			SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/nng"
+			CMAKE_ARGS "-DBUILD_SHARED_LIBS=1"
+			CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}"
+			BUILD_COMMAND "make"
+			UPDATE_COMMAND ""
+			TEST_COMMAND ""
+			STEP_TARGETS build
+		)
+
+		# it seems impossible to install everything that lands in {bin}/lib, so we need to
+		# hard code (shudder) some things. Even worse, we have to make exceptions for
+		# builds on apple (osx) since their naming convention wandered off the path.
+		set( nng_major 1 )
+		set( nng_minor 1.0 )
+		set( so ${CMAKE_SHARED_LIBRARY_SUFFIX} )	# cmake variables are impossibly long :(
+		if( NOT APPLE )								# probably breaks in windows, but idc
+			set( nng_so_suffix ${so} )
+			set( nng_so_suffix_m ${so}.${nng_major} )
+			set( nng_so_suffix_mm ${so}.${nng_major}.${nng_minor} )
+		else()
+			# of course apple puts versions before the suffix :(
+			set( nng_so_suffix ${so} )									# so has a lead dot, so NOT needed
+			set( nng_so_suffix_m ".${nng_major}${so}" )					# these need leading dots
+			set( nng_so_suffix_mm ".${nng_major}.${nng_minor}${so}" )
+		endif()
+
+		message( "+++ building with nng: ${nng_major}.${nng_minor}" )
+	else()
+		if( PACK_EXTERNALS )
+			# This makes some stand-alone unit testing possible for bindings and transport layer testing;
+			# it is not meant for production packages.
+			#
+			unset( SKIP_EXTERNALS  CACHE )	# must remove so as not to trap user into a never ending failure
+			unset( PACK_EXTERNALS  CACHE )
+			message( FATAL_ERROR "ERROR: PACK_EXTERNALS can be set only if SKIP_EXTERNALS is unset (=0, or not supplied on command line)" )
+		endif()
+		set( need_ext 0 )
 	endif()
-	set( need_ext 0 )
 endif()
 
 
@@ -243,30 +247,34 @@
 
 # Include modules
 add_subdirectory( src/rmr/common )
-add_subdirectory( src/rmr/nng )
+if( BUILD_NNG )
+	add_subdirectory( src/rmr/nng )
+endif()
 add_subdirectory( src/rmr/si )
 add_subdirectory( doc )				# this will auto skip if {X}fm is not available
 
 
 # shared and static libraries are built from the same object files.
-# librmr_nng is a combination of common and nng specific rmr functions.
+# librmr_* is a combination of common and * specific rmr functions.
 #
 
-add_library( rmr_nng_shared SHARED "$<TARGET_OBJECTS:nng_objects>;$<TARGET_OBJECTS:common_objects>" )
-set_target_properties( rmr_nng_shared
-	PROPERTIES
-	OUTPUT_NAME "rmr_nng"
-	SOVERSION ${major_version}
-	VERSION ${major_version}.${minor_version}.${patch_level} )
-
-# we only build/export the static archive (.a) if generating a dev package
-if( DEV_PKG )
-	add_library( rmr_nng_static STATIC "$<TARGET_OBJECTS:nng_objects>;$<TARGET_OBJECTS:common_objects>" )
-	set_target_properties( rmr_nng_static
+if( BUILD_NNG )
+	add_library( rmr_nng_shared SHARED "$<TARGET_OBJECTS:nng_objects>;$<TARGET_OBJECTS:common_objects>" )
+	set_target_properties( rmr_nng_shared
 		PROPERTIES
 		OUTPUT_NAME "rmr_nng"
 		SOVERSION ${major_version}
 		VERSION ${major_version}.${minor_version}.${patch_level} )
+
+	# we only build/export the static archive (.a) if generating a dev package
+	if( DEV_PKG )
+		add_library( rmr_nng_static STATIC "$<TARGET_OBJECTS:nng_objects>;$<TARGET_OBJECTS:common_objects>" )
+		set_target_properties( rmr_nng_static
+			PROPERTIES
+			OUTPUT_NAME "rmr_nng"
+			SOVERSION ${major_version}
+			VERSION ${major_version}.${minor_version}.${patch_level} )
+	endif()
 endif()
 
 add_library( rmr_si_shared SHARED "$<TARGET_OBJECTS:rmr_si_objects>;$<TARGET_OBJECTS:common_objects>" )
@@ -276,7 +284,7 @@
 	SOVERSION ${major_version}
 	VERSION ${major_version}.${minor_version}.${patch_level} )
 
-# even if not generating a development package we still need to generate the .a so that health check 
+# even if not generating a development package we still need to generate the .a so that health check
 # can link against it to avoid RPM install issues.
 #
 add_library( rmr_si_static STATIC "$<TARGET_OBJECTS:rmr_si_objects>;$<TARGET_OBJECTS:common_objects>" )
@@ -286,12 +294,14 @@
 	SOVERSION ${major_version}
 	VERSION ${major_version}.${minor_version}.${patch_level} )
 
-# if externals need to be built, then we must force them to be built first by depending on them
-if( need_ext )
-	if( DEV_PKG )
-		add_dependencies( rmr_nng_shared;rmr_nng_static ext_nng )
-	else()
-		add_dependencies( rmr_nng_shared ext_nng )
+if( BUILD_NNG )
+	# if externals need to be built, then we must force them to be built first by depending on them
+	if( need_ext )
+		if( DEV_PKG )
+			add_dependencies( rmr_nng_shared;rmr_nng_static ext_nng )
+		else()
+			add_dependencies( rmr_nng_shared ext_nng )
+		endif()
 	endif()
 endif()
 
@@ -319,32 +329,43 @@
 # ------------- packaging -----------------------------------------------------
 
 #
-if( APPLE  )
-	message( "### apple hack: forcing hard coded library paths for nng/nano dynamic libraries" )
-	target_link_libraries( rmr_nng_shared ${CMAKE_CURRENT_BINARY_DIR}/lib/libnng${nng_so_suffix} )
+if( BUILD_NNG )
+	if( APPLE  )
+		message( "### apple hack: forcing hard coded library paths for nng/nano dynamic libraries" )
+		target_link_libraries( rmr_nng_shared ${CMAKE_CURRENT_BINARY_DIR}/lib/libnng${nng_so_suffix} )
+	endif()
 endif()
 
 # Define what should be installed, and where they should go. For dev package we install
 # only the RMr headers, man pages and archive (.a) files.  The run-time package gets just
 # the library (.so) files and nothing more.
 #
-if( DEV_PKG )
-	set( target_list "rmr_nng_static;rmr_si_static" )
+if( BUILD_NNG )
+	if( DEV_PKG )
+		set( target_list "rmr_nng_static;rmr_si_static" )
+	else()
+		set( target_list "rmr_nng_shared;rmr_si_shared" )
+	endif()
 else()
-	set( target_list "rmr_nng_shared;rmr_si_shared" )
+	if( DEV_PKG )
+		set( target_list "rmr_si_static" )
+	else()
+		set( target_list "rmr_si_shared" )
+	endif()
 endif()
 
 install( TARGETS ${target_list} EXPORT LibraryConfig
-   	LIBRARY  DESTINATION ${install_lib}
-   	ARCHIVE  DESTINATION ${install_lib}
-   	PUBLIC_HEADER DESTINATION ${install_inc}
+	LIBRARY  DESTINATION ${install_lib}
+	ARCHIVE  DESTINATION ${install_lib}
+	PUBLIC_HEADER DESTINATION ${install_inc}
 )
 
 
 unset( DEV_PKG  CACHE )			# prevent from being a hidden setting if user redoes things
 
 # install any nano/nng libraries in to the deb as well, but ONLY if asked for on the 'cmake ..' command
-# (sure would be nice if FILEs allowed for globbing; sadly it does not.)
+# (sure would be nice if FILEs allowed for globbing; sadly it does not.) Disabled by default if BUILD_NNG
+# is turned off.
 #
 if( PACK_EXTERNALS )
 	message( "+++ including nano and nng libraries in the deb" )
diff --git a/src/rmr/common/include/RIC_message_types.h b/src/rmr/common/include/RIC_message_types.h
index 03b212b..d28fe42 100644
--- a/src/rmr/common/include/RIC_message_types.h
+++ b/src/rmr/common/include/RIC_message_types.h
@@ -20,19 +20,19 @@
 
 
 
-/* 
+/*
 	Header  file defining  message types for various RMR messages
 */
 
 #define RIC_UNDEFINED				-1
 
-/* 
+/*
 ---------------------------------------------------------
 	RMR Reserved types
 		All message types 0 - 99 are reserved for RMM.
 ---------------------------------------------------------
 */
-		
+
 #define RMRRM_TABLE_DATA			20		// table data from route manger
 #define	RMRRM_REQ_TABLE				21		// request for table update to route mangager
 #define RMRRM_TABLE_STATE			22		// state of table to route mgr
@@ -147,19 +147,23 @@
 #define DC_ADM_GET_POLICY			20002
 #define DC_ADM_GET_POLICY_ACK		20003
 
-#define A1_POLICY_REQ      			20010
-#define A1_POLICY_RESP    			20011
+#define A1_POLICY_REQ				20010
+#define A1_POLICY_RESP				20011
 #define A1_POLICY_QUERY				20012
 
 
 // --- application specific message numbers 30000 - 39999
-    
+
 #define TS_UE_LIST					30000		// traffic steering
 #define TS_QOE_PRED_REQ				30001
 #define TS_QUE_PREDICTION			30002
 
 #define MC_REPORT					30010		// Measurement campaign xApp reports
 
+#define	DCAPTERM_RTPM_RMR_MSGTYPE	33001
+#define	DCAPTERM_GEO_RMR_MSGTYPE	33002
+
+
 
 // ---- these are old (release 0) definitions and should not be used ------
 
diff --git a/src/rmr/common/src/rtc_static.c b/src/rmr/common/src/rtc_static.c
index 5fdf064..03f9db0 100644
--- a/src/rmr/common/src/rtc_static.c
+++ b/src/rmr/common/src/rtc_static.c
@@ -84,6 +84,9 @@
 
 		read_static_rt( ctx, vlevel );						// seed the route table if one provided
 
+		if( ctx->shutdown != 0 ) {							// allow for graceful termination and unit testing
+			return NULL;
+		}
 		sleep( 60 );
 	}
 }
@@ -103,27 +106,102 @@
 }
 
 /*
+	Rtc_parse_msg parses a single message from the route manager. We allow multiple, newline terminated,
+	records in each message; it is required that the last record in the message be complete (we do not
+	reconstruct records split over multiple messages).  For each record, we call the record parser
+	to parse and add the information to the table being built.
+
+	This function was broken from the main rtc() function in order to be able to unit test it. Without
+	this as a standalone funciton, it was impossible to simulate a message arriving on the RTC's private
+	context.
+
+	To reduce malloc/free cycles, we allocate a static work buffer and expand it when needed; in other
+	words, this is not thread safe but it shouldn't need to be.
+*/
+static void rtc_parse_msg( uta_ctx_t *ctx, uta_ctx_t* pvt_cx, rmr_mbuf_t* msg, int vlevel,  int* flags ) {
+	static	unsigned char* pbuf = NULL;
+	static	int pbuf_size = 0;
+
+	unsigned char* payload;
+	unsigned char* curr;
+	unsigned char* nextr;
+	int mlen;
+
+	payload = msg->payload;
+	mlen = msg->len;					// usable bytes in the payload
+
+	if( DEBUG > 1 || (vlevel > 0) ) rmr_vlog( RMR_VL_DEBUG, "rmr_rtc: received rt message type=%d len=%d\n", msg->mtype, (int) mlen );
+	switch( msg->mtype ) {
+		case RMRRM_TABLE_DATA:
+			if( (*flags & RTCFL_HAVE_UPDATE) == 0 ) {
+				*flags |= RTCFL_HAVE_UPDATE;
+				rmr_vlog( RMR_VL_INFO, "message flow from route manager starts\n" );
+			}
+
+			if( pbuf_size <= mlen ) {
+				if( pbuf ) {
+					free( pbuf );
+				}
+				if( mlen < 512 ) {
+					pbuf_size = 1024;
+				} else {
+					pbuf_size = mlen * 2;
+				}
+				pbuf = (char *) malloc( sizeof( char ) * pbuf_size );
+			}
+			memcpy( pbuf, payload, mlen );
+			pbuf[mlen] = 0;										// don't depend on sender making this a legit string
+			if( vlevel > 1 ) {
+				rmr_vlog_force( RMR_VL_DEBUG, "rmr_rtc: rt message: (%s)\n", pbuf );
+			}
+
+			curr = pbuf;
+			while( curr ) {										// loop over each record in the buffer
+				nextr = strchr( (char *) curr, '\n' );			// allow multiple newline records, find end of current and mark
+
+				if( nextr ) {
+					*(nextr++) = 0;
+				}
+
+				if( vlevel > 1 ) {
+					rmr_vlog_force( RMR_VL_DEBUG, "rmr_rtc: processing (%s)\n", curr );
+				}
+				parse_rt_rec( ctx, pvt_cx, curr, vlevel, msg );		// parse record and add to in progress table; ack using rts to msg
+
+				curr = nextr;
+			}
+
+			msg->len = 0;				// force back into the listen loop
+			break;
+
+		default:
+			rmr_vlog( RMR_VL_WARN, "rmr_rtc: invalid message type=%d len=%d\n", msg->mtype, (int) msg->len );
+			break;
+	}
+}
+
+/*
 	Route Table Collector
 	A side thread which either attempts to connect and request a table
 	from the Route Manager, or opens a port and listens for Route Manager
 	to push table updates.
 
-	It may do other things along the way (latency measurements, alarms, 
+	It may do other things along the way (latency measurements, alarms,
 	respond to RMR pings, etc.).
 
 	The behaviour with respect to listening for Route Manager updates vs
 	the initiation of the connection and sending a request depends on the
 	value of the ENV_RTG_ADDR (RMR_RTG_SVC)  environment variable. If
 	host:port, or IP:port, is given, then we assume that we make the connection
-	and send a request for the table (request mode).  If the variable is just 
-	a port, then we assume Route Manager will connect and push updates (original 
+	and send a request for the table (request mode).  If the variable is just
+	a port, then we assume Route Manager will connect and push updates (original
 	method).
 
 	If the variable is not defined, the default behaviour, in order to be
-	backwards compatable, depends on the presence of the ENV_CTL_PORT 
+	backwards compatable, depends on the presence of the ENV_CTL_PORT
 	(RMR_CTL_PORT) variable (new with the support for requesting a table).
 
-	
+
 	ENV_CTL_PORT    ENV_RTG_ADDR	Behaviour
 	unset			unset			Open default CTL port (DEF_CTL_PORT) and
 									wait for Rt Mgr to push tables
@@ -134,15 +212,15 @@
 									used is the value set by ENV_CTL_PORT.
 
 	unset			set				As described above. The default control
-									port (DEF_CTL_PORT) is used. 
+									port (DEF_CTL_PORT) is used.
 
-	When we are running in request mode, then we will send the RMR message 
-	RMRRM_REFRESH to this address (wormhole) as a request for the route manager 
+	When we are running in request mode, then we will send the RMR message
+	RMRRM_REFRESH to this address (wormhole) as a request for the route manager
 	to send a new table. We will attempt to connect and send requests until
 	we have a table. Calls to rmr_ready() will report FALSE until a table is
 	loaded _unless_ a seed table was given.
 
-	Route table information is expected to arrive on RMR messages with type 
+	Route table information is expected to arrive on RMR messages with type
 	RMRRM_TABLE_DATA.  There is NOT a specific message type for each possible
 	table record, so the payload is as it appears in the seed file or as
 	delivered in old versions.  It may take several RMRRM_TABLE_DATA messages
@@ -163,22 +241,16 @@
 	uta_ctx_t*	ctx;					// context user has -- where we pin the route table
 	uta_ctx_t*	pvt_cx;					// private context for session with rtg
 	rmr_mbuf_t*	msg = NULL;				// message from rtg
-	char*	payload;					// payload in the message
-	size_t	mlen;
 	char*	my_port;					// the port number that we will listen on (4561 has been the default for this)
 	char*	rtg_addr;					// host:port address of route table generator (route manager)
 	char*	daddr;						// duplicated rtg address string to parse/trash
 	size_t	buf_size;					// nng needs var pointer not just size?
-	char*	nextr;						// pointer at next record in the message
-	char*	curr;						// current record
-	int 	i;
+	int		i;
 	long	blabber = 0;				// time of last blabber so we don't flood if rtg goes bad
 	int		cstate = -1;				// connection state to rtg
 	int		state;						// processing state of some nng function
 	char*	tokens[128];
 	char	wbuf[128];
-	char*	pbuf = NULL;
-	int		pbuf_size = 0;				// number allocated in pbuf
 	int		ntoks;
 	int		vfd = -1;					// verbose file des if we have one
 	int		vlevel = 0;					// how chatty we should be 0== no nattering allowed
@@ -201,14 +273,14 @@
 		vlevel = refresh_vlevel( vfd );
 	}
 
-	ctx->flags |= CFL_NO_RTACK; 			// don't ack when reading from a file
+	ctx->flags |= CFL_NO_RTACK;				// don't ack when reading from a file
 	read_static_rt( ctx, vlevel );			// seed the route table if one provided
 	ctx->flags &= ~CFL_NO_RTACK;
 
 
 	my_port = getenv( ENV_CTL_PORT );				// default port to listen on (likely 4561)
 	if( my_port == NULL || ! *my_port ) {			// if undefined, then go with default
-		my_port = DEF_CTL_PORT;	
+		my_port = DEF_CTL_PORT;
 		daddr = DEF_CTL_PORT;						// backwards compat; if ctl port not hard defined, default is to listen
 	} else {
 		daddr = DEF_RTG_WK_ADDR;					// if ctl port is defined, then default changes to connecting to well known RM addr
@@ -235,7 +307,7 @@
 				flags |= RTCFL_HAVE_UPDATE;					// and signal not to try to request an update
 				my_port = tokens[1];
 			} else {
-				// rtg_addr points at rt mgr address and my port set from env or default stands as is	
+				// rtg_addr points at rt mgr address and my port set from env or default stands as is
 			}
 			break;
 	}
@@ -278,62 +350,16 @@
 					rt_epcounts( ctx->rtable, ctx->my_name );
 				}
 			}
+
+			if( ctx->shutdown != 0 ) {
+				break;							// mostly for unit test, but allows a forced stop
+			}
 		}
 
 		vlevel = refresh_vlevel( vfd );			// ensure it's fresh when we get a message
 
 		if( msg != NULL && msg->len > 0 ) {
-			payload = msg->payload;
-			mlen = msg->len;					// usable bytes in the payload
-
-			if( DEBUG > 1 || (vlevel > 0) ) rmr_vlog( RMR_VL_DEBUG, "rmr_rtc: received rt message type=%d len=%d\n", msg->mtype, (int) mlen );
-			switch( msg->mtype ) {
-				case RMRRM_TABLE_DATA:
-					if( (flags & RTCFL_HAVE_UPDATE) == 0 ) {
-						flags |= RTCFL_HAVE_UPDATE;
-						rmr_vlog( RMR_VL_INFO, "message flow from route manager starts\n" );
-					}
-
-					if( pbuf_size <= mlen ) {
-						if( pbuf ) {
-							free( pbuf );
-						}
-						if( mlen < 512 ) {
-							pbuf_size = 512;
-						} else {
-							pbuf_size = mlen * 2;
-						}
-						pbuf = (char *) malloc( sizeof( char ) * pbuf_size );
-					}
-					memcpy( pbuf, payload, mlen );
-					pbuf[mlen] = 0;										// don't depend on sender making this a legit string
-					if( vlevel > 1 ) {
-						rmr_vlog_force( RMR_VL_DEBUG, "rmr_rtc: rt message: (%s)\n", pbuf );
-					}
-
-					curr = pbuf;
-					while( curr ) {								// loop over each record in the buffer
-						nextr = strchr( curr, '\n' );			// allow multiple newline records, find end of current and mark
-
-						if( nextr ) {
-							*(nextr++) = 0;
-						}
-
-						if( vlevel > 1 ) {
-							rmr_vlog_force( RMR_VL_DEBUG, "rmr_rtc: processing (%s)\n", curr );
-						}
-						parse_rt_rec( ctx, pvt_cx, curr, vlevel, msg );		// parse record and add to in progress table; ack using rts to msg
-
-						curr = nextr;
-					}
-
-					msg->len = 0;				// force back into the listen loop
-					break;
-
-				default:
-					rmr_vlog( RMR_VL_WARN, "rmr_rtc: invalid message type=%d len=%d\n", msg->mtype, (int) msg->len );
-					break;
-			}
+			rtc_parse_msg( ctx, pvt_cx, msg, vlevel, &flags );
 		}
 
 		if( ctx->shutdown ) {		// mostly for testing, but allows user app to close us down if rmr_*() function sets this
@@ -346,7 +372,7 @@
 }
 
 #ifndef SI95_BUILD
-// this is nng specific inas much as we allow raw (non-RMR) messages 
+// this is nng specific inas much as we allow raw (non-RMR) messages
 
 /*
 	NOTE:	This is the original rtc code when we supported "raw" nano/nng messages
@@ -404,7 +430,7 @@
 	size_t	buf_size;					// nng needs var pointer not just size?
 	char*	nextr;						// pointer at next record in the message
 	char*	curr;						// current record
-	int 	i;
+	int		i;
 	long	blabber = 0;				// time of last blabber so we don't flood if rtg goes bad
 	int		cstate = -1;				// connection state to rtg
 	int		state;						// processing state of some nng function
@@ -433,7 +459,7 @@
 	if( (eptr = getenv( ENV_VERBOSE_FILE )) != NULL ) {
 		vfd = open( eptr, O_RDONLY );
 		vlevel = refresh_vlevel( vfd );
-	}                
+	}
 
 	read_static_rt( ctx, vlevel );						// seed the route table if one provided
 
@@ -474,7 +500,7 @@
 			sleep( count_delay );
 			rt_epcounts( ctx->rtable, ctx->my_name );
 		}
-		
+
 		free( fport );					// parinoid free and return
 		return NULL;
 	}
diff --git a/src/rmr/si/src/mt_call_si_static.c b/src/rmr/si/src/mt_call_si_static.c
index 2d422bb..fbbffaf 100644
--- a/src/rmr/si/src/mt_call_si_static.c
+++ b/src/rmr/si/src/mt_call_si_static.c
@@ -234,7 +234,7 @@
 			remain = 0;
 		} else {
 			need = river->msg_size - river->ipt;						// bytes from transport we need to have complete message
-			if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "data callback enough in the buffer size=%d need=%d remain=%d\n", river->msg_size, need, remain );
+			if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "data callback enough in the buffer size=%d need=%d remain=%d flgs=%02x\n", river->msg_size, need, remain, river->flags );
 			if( (river->flags & RF_DROP) == 0  ) {
 				memcpy( &river->accum[river->ipt], buf+bidx, need );				// grab just what is needed (might be more)
 				buf2mbuf( ctx, river->accum, river->nbytes, fd );					// build an RMR mbuf and queue
@@ -242,7 +242,7 @@
 				river->accum = (char *) malloc( sizeof( char ) *  river->nbytes );	// fresh accumulator
 			} else {
 				if( !(river->flags & RF_NOTIFIED) ) {
-					rmr_vlog( RMR_VL_WARN, "message larger than max (%d) have arrived on fd %d\n", river->nbytes, fd );
+					rmr_vlog( RMR_VL_WARN, "message larger than allocated buffer (%d) arrived on fd %d\n", river->nbytes, fd );
 					river->flags |= RF_NOTIFIED;
 				}
 			}
diff --git a/src/rmr/si/src/rmr_si.c b/src/rmr/si/src/rmr_si.c
index d1b9f26..712275e 100644
--- a/src/rmr/si/src/rmr_si.c
+++ b/src/rmr/si/src/rmr_si.c
@@ -419,6 +419,10 @@
 }
 
 /*
+	DEPRECATED -- this function is not needed in the SI world, and when NNG goes away this will
+		too.  This function likely will not behave as expected in SI, and we are pretty sure it
+		isn't being used as there was an abort triggering reference to rmr_rcv() until now.
+
 	This blocks until the message with the 'expect' ID is received. Messages which are received
 	before the expected message are queued onto the message ring.  The function will return
 	a nil message and set errno to ETIMEDOUT if allow2queue messages are received before the
@@ -455,22 +459,25 @@
 	if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, " rcv_specific waiting for id=%s\n",  expect );
 
 	while( queued < allow2queue ) {
-		msg = rcv_msg( ctx, msg );					// hard wait for next
-		if( msg->state == RMR_OK ) {
-			if( memcmp( msg->xaction, expect, exp_len ) == 0 ) {			// got it -- return it
-				if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, " rcv-specific matched (%s); %d messages were queued\n", msg->xaction, queued );
-				return msg;
-			}
+		msg = rmr_rcv_msg( ctx, msg );					// hard wait for next
+		if( msg != NULL ) {
+			if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, " rcv_specific checking message; queued=%d allowed=%d state=%d\n",  queued, allow2queue, msg->state );
+			if( msg->state == RMR_OK ) {
+				if( memcmp( msg->xaction, expect, exp_len ) == 0 ) {			// got it -- return it
+					if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, " rcv_specific matched (%s); %d messages were queued\n", msg->xaction, queued );
+					return msg;
+				}
 
-			if( ! uta_ring_insert( ctx->mring, msg ) ) {					// just queue, error if ring is full
-				if( DEBUG > 1 ) rmr_vlog( RMR_VL_DEBUG, " rcv_specific ring is full\n" );
-				errno = ENOBUFS;
-				return NULL;
-			}
+				if( ! uta_ring_insert( ctx->mring, msg ) ) {					// just queue, error if ring is full
+					if( DEBUG > 1 ) rmr_vlog( RMR_VL_DEBUG, " rcv_specific ring is full\n" );
+					errno = ENOBUFS;
+					return NULL;
+				}
 
-			if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, " rcv_specific queued message type=%d\n", msg->mtype );
-			queued++;
-			msg = NULL;
+				if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, " rcv_specific queued message type=%d\n", msg->mtype );
+				queued++;
+				msg = NULL;
+			}
 		}
 	}
 
@@ -1079,8 +1086,10 @@
 
 	// must vet call_id here, all others vetted by workhorse mt_call() function
 	if( call_id > MAX_CALL_ID || call_id < 2 ) {		// 0 and 1 are reserved; user app cannot supply them
-		mbuf->state = RMR_ERR_BADARG;
-		mbuf->tp_state = EINVAL;
+		if( mbuf != NULL ) {
+			mbuf->state = RMR_ERR_BADARG;
+			mbuf->tp_state = EINVAL;
+		}
 		return mbuf;
 	}
 
diff --git a/test/Makefile b/test/Makefile
index e44b2bb..22dff3a 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -22,10 +22,8 @@
 CC = gcc
 coverage_opts = -ftest-coverage -fprofile-arcs
 
-libs = -lnng -lpthread -lm
+libs = -lpthread -lm
 ipaths = -I ../src/rmr/common/src/ -I ../src/rmr/common/include  \
-	-I ../src/rmr/nng/include/ -I ../src/rmr/nng/src/  \
-	-I ../src/rmr/nanomsg/include/ -I ../src/rmr/nanomsg/src/  \
 	-I ../src/rmr/si/include -I ../src/rmr/si/src -I ../src/rmr/si/si95
 
 #sa_tests = sa_tools_test.o
diff --git a/test/rmr_si_api_static_test.c b/test/rmr_si_api_static_test.c
index 528e9cc..b7f97bf 100644
--- a/test/rmr_si_api_static_test.c
+++ b/test/rmr_si_api_static_test.c
@@ -96,8 +96,8 @@
 	v = strcmp( ((uta_ctx_t *) rmc2)->my_name, "somehost:6789" );
 	errors += fail_not_equal( v, 0, "source name not set from environment variable (see previous info)" );
 	free_ctx( rmc2 );			// coverage
-	
-	unsetenv( "RMR_SRC_ID" );												// context should NOT have our artificial name 
+
+	unsetenv( "RMR_SRC_ID" );												// context should NOT have our artificial name
 	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 "  );
 	}
@@ -112,7 +112,7 @@
 	msg = rmr_alloc_msg( NULL,  1024 );									// should return nil pointer
 	errors += fail_not_nil( msg, "rmr_alloc_msg didn't return nil when given nil context "  );
 
-	
+
 	msg = rmr_alloc_msg( rmc, 2048 );				// allocate larger than default size given on init
 	errors += fail_if_nil( msg, "rmr_alloc_msg returned nil msg pointer "  );
 	if( msg ) {
@@ -210,9 +210,8 @@
 		max_tries--;
 	}
 
-
 	// ----- the queue load and disc cb tests should be last! -----------------------------
-	for( i = 0; i < 4000; i++ ) {			// test ring drop 
+	for( i = 0; i < 4000; i++ ) {			// test ring drop
 		if( msg == NULL ) {
 			msg = rmr_alloc_msg( rmc, 2048 );				// get a buffer with a transport header
 		}
@@ -257,6 +256,13 @@
 	rmr_close( rmc );			// no return to check; drive for coverage
 
 
+	// --------------- nil pointer exception checks
+	rmr_rcv_specific( NULL, NULL, "foo", 0 );
+	rmr_mt_rcv( NULL, NULL, 0 );
+	mt_call( NULL, NULL, 0, 1, NULL );
+	rmr_mt_call( NULL, NULL, 0, 1 );
+	rmr_set_low_latency( NULL );
+	rmr_set_fack( NULL );
 
 	// --------------- phew, done ------------------------------------------------------------------------------
 
diff --git a/test/rmr_si_rcv_static_test.c b/test/rmr_si_rcv_static_test.c
index ad543a0..57313f8 100644
--- a/test/rmr_si_rcv_static_test.c
+++ b/test/rmr_si_rcv_static_test.c
@@ -150,6 +150,7 @@
 	}
 
 	rmr_rts_msg( NULL, NULL );			// drive for coverage
+	errors += fail_if( errno == 0, "rmr_rts_msg did not set errno when given a nil context "  );
 	rmr_rts_msg( rmc, NULL );
 	errors += fail_if( errno == 0, "rmr_rts_msg did not set errno when given a nil message "  );
 
@@ -225,6 +226,42 @@
 	rmr_close( rmc );			// no return to check; drive for coverage
 
 
+	// ------- receive specific is deprecated, but we still test to keep sonar happy ---------------
+
+	rmr_rcv_specific( NULL, NULL, "12345", 0 );		// drive for error handling coverage
+	rmr_rcv_specific( NULL, msg, "12345", 2 );
+
+	strncpy( wbuf, "dummy message", sizeof( wbuf ) );
+	msg = mk_populated_msg( 1024, 0, 0, -1, strlen( wbuf ) + 1 );
+	strncpy( msg->payload, wbuf, msg->len );
+	msg = rmr_send_msg( rmc, msg );						// give specific something to chew on
+
+	strncpy( msg->payload, wbuf, msg->len );
+	msg->mtype = 0;
+	rmr_str2xact( msg, "12345" );						// should allow rcv to find it
+	msg = rmr_send_msg( rmc, msg );
+
+	msg = rmr_rcv_specific( rmc, NULL, "12345", 2 );
+	if( msg ) {
+		errors += fail_if( msg->state != 0, "rmr_rcv_specific failed to find the expected message" );
+	} else {
+		errors++;
+		fprintf( stderr, "<FAIL> rcv specific expected to return a message and did not\n" );
+	}
+
+	strncpy( wbuf, "dummy message", sizeof( wbuf ) );
+	msg = mk_populated_msg( 1024, 0, 0, -1, strlen( wbuf ) + 1 );
+	strncpy( msg->payload, wbuf, msg->len );
+	msg = rmr_send_msg( rmc, msg );						// give specific something to chew on
+
+	fprintf( stderr, "<INFO> starting rmr_rcv_specific test for no expected message\n" );
+	strncpy( msg->payload, wbuf, msg->len );
+	msg->mtype = 0;
+	rmr_str2xact( msg, "72345" );						// rcv should not find it
+	msg = rmr_send_msg( rmc, msg );
+	msg = rmr_rcv_specific( rmc, msg, "12345", 2 );
+	fail_if_nil( msg, "rmr_rcv_specific expected to retun nil message did  not" );
+
 	// --------------- phew, done ------------------------------------------------------------------------------
 
 	if( ! errors ) {
diff --git a/test/rt_static_test.c b/test/rt_static_test.c
index d87a743..dbe8d78 100644
--- a/test/rt_static_test.c
+++ b/test/rt_static_test.c
@@ -98,6 +98,7 @@
 */
 static int rt_test( ) {
 	uta_ctx_t* ctx;			// context needed to test load static rt
+	uta_ctx_t* pctx;		// "private" context for route manager communication tests
 	route_table_t* rt;		// route table
 	route_table_t* crt;		// cloned route table
 	rtable_ent_t*	rte;	// route table entries from table
@@ -521,6 +522,36 @@
 		errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil socket pointer" );
 	#endif
 
+	// ------------  debugging and such; coverage only calls ----------------------------------------------------------
+		ep_stats( ctx->rtable, NULL, "name", NULL, NULL );			// ensure no crash when given nil pointer
+		rt_epcounts( ctx->rtable, "testing" );
+		rt_epcounts( NULL, "testing" );
+
+		buf = ensure_nlterm( strdup( "Stand up and cheer!" ) );					// force addition of newline
+		if( buf ) {
+			errors += fail_not_equal( strcmp( buf, "Stand up and cheer!\n" ), 0, "ensure nlterm didn't add newline" );
+			free( buf );
+			buf = NULL;
+		}
+
+
+	// ------------- route manager request/response funcitons -------------------------------------------------------
+		{
+			rmr_mbuf_t*	smsg;
+
+			smsg = rmr_alloc_msg( ctx, 1024 );
+			send_rt_ack( ctx, smsg, "123456", 0, "no reason" );
+
+			pctx = mk_dummy_ctx();
+			ctx->rtg_whid = -1;
+			state = send_update_req( pctx, ctx );
+			errors += fail_not_equal( state, 0, "send_update_req did not return 0" );
+
+			ctx->rtg_whid = rmr_wh_open( ctx, "localhost:19289" );
+			state = send_update_req( pctx, ctx );
+			errors += fail_if_equal( state, 0, "send_update_req to an open whid did not return 0" );
+		}
+
 
 	// ------------- si only; fd to ep conversion functions ---------------------------------------------------------
 	#ifndef NNG_UNDER_TEST
diff --git a/test/si95_test.c b/test/si95_test.c
index c77f389..fd6dd56 100644
--- a/test/si95_test.c
+++ b/test/si95_test.c
@@ -21,7 +21,7 @@
 /*
 	Mmemonic:	si95_test.c
 	Abstract:	This is the main driver to test the si95 core functions
-				(within rmr/src/si/src/si95). 
+				(within rmr/src/si/src/si95).
 
 	Author:		E. Scott Daniels
 	Date:		6 March 2018
@@ -58,7 +58,7 @@
 #define DEBUG 1
 
 											// specific test tools in this directory
-#undef NNG_UNDER_TEST 						// NNG is NOT under test so undefine if set
+#undef NNG_UNDER_TEST						// NNG is NOT under test so undefine if set
 #define NO_EMULATION 1						// no emulation of transport functions
 #define NO_PRIVATE_HEADERS 1				// no rmr_si or rmr_nng headers
 #define NO_DUMMY_RMR 1						// no msg things
@@ -127,7 +127,7 @@
 		((struct tp_blk *)ptr)->squeue = iptr;
 		SItrash(  TP_BLK, ptr );
 	}
-	
+
 	ptr = SInew( GI_BLK );
 	errors += fail_if_nil( ptr, "memory: sinew returned nil when given giblk request" );
 	SItrash(  GI_BLK, ptr );		// GI block cannot be trashed, ensure this (valgind will complain about a leak)
@@ -155,7 +155,7 @@
 
 static int cleanup() {
 	int errors = 0;
-	
+
 	if( ! si_ctx ) {
 		return 0;
 	}
@@ -163,8 +163,9 @@
 	SItp_stats( si_ctx );		// drive for coverage only
 	SItp_stats( NULL );
 
-	SIconnect( si_ctx, "localhost:43086" ); 	// ensure context has a tp block to free on shutdown
-	SIshutdown( si_ctx );
+	SIconnect( si_ctx, "localhost:43086" );	// ensure context has a tp block to free on shutdown
+	SIshutdown( NULL );
+	SIabort( si_ctx );
 
 
 	fprintf( stderr, "<INFO> cleanup  module finished with %d errors\n", errors );
@@ -194,9 +195,8 @@
 	l = SIaddress( buf1, (void **)  &dest, AC_TOADDR6 );
 	errors += fail_if_true( l > 0, "to addr6 with bad addr convdersion returned valid len" );
 
-	snprintf( buf1, sizeof( buf1 ), "[ff02::5]:4002" );		// v6 might not be supported so failure is OK here
+	snprintf( buf1, sizeof( buf1 ), "[ff02::5]:4002" );		// v6 might not be supported so failure is OK here; driving for coverage
 	l=SIaddress( buf1, (void **) &dest, AC_TOADDR6 );
-	errors += fail_if_true( l < 1, "to addr convdersion failed" );
 
 	snprintf( buf1, sizeof( buf1 ), "localhost:43086" );
 	l = SIaddress( buf1, (void **) &dest, AC_TOADDR );
@@ -222,11 +222,11 @@
 	state = SIconnect( si_ctx, "localhost:4567" );		// driver regular connect
 	errors += fail_if_true( state < 0, "connect to low port failed" );
 
-	state = SIconnect( si_ctx, "localhost:43086" ); 	// drive save connect with good return code
+	state = SIconnect( si_ctx, "localhost:43086" );		// drive save connect with good return code
 	errors += fail_if_true( state < 0, "connect to high port failed" );
 
 	tpem_set_addr_dup_state( 1 );				// force get sockket name emulation to return a duplicate address
-	state = SIconnect( si_ctx, "localhost:43086" ); 	// drive save connect with good return code
+	state = SIconnect( si_ctx, "localhost:43086" );		// drive save connect with good return code
 	errors += fail_if_true( state >= 0, "forced dup connect did not return error" );
 
 	tpem_set_addr_dup_state( 0 );				// force get sockket name emulation to return a duplicate address
diff --git a/test/sr_si_static_test.c b/test/sr_si_static_test.c
index 5768f3e..9efba71 100644
--- a/test/sr_si_static_test.c
+++ b/test/sr_si_static_test.c
@@ -41,7 +41,7 @@
 #include "rmr_agnostic.h"
 
 // ----------- local test support ----------------------------------------------------------
-#define CLONE 	1			// convenience constants for payload realloc tests
+#define CLONE	1			// convenience constants for payload realloc tests
 #define NO_CLONE 0
 #define COPY 1
 #define NO_COPY 0
@@ -59,21 +59,24 @@
 	have been included by the test module(s) which include this.
 */
 static int sr_si_test() {
-	uta_ctx_t* ctx;			// context needed to test load static rt
+	uta_ctx_t* ctx;			// two context structs needed to test route table collector
+	uta_ctx_t* pctx;
 	uta_ctx_t*	real_ctx;	// real one to force odd situations for error testing
 	int errors = 0;			// number errors found
 	rmr_mbuf_t*	mbuf;		// mbuf to send/receive
 	rmr_mbuf_t*	mb2;		// second mbuf when needed
 	int		whid = -1;
 	int		last_whid;
-	int 	state;
-	int 	nn_dummy_sock;					// dummy needed to drive send
+	int		state;
+	int		nn_dummy_sock;					// dummy needed to drive send
 	int		size;
 	int		i;
+	int		flags = 0;						// flags needed to pass to rtc funcitons
 	void*	p;
 	char*	payload_str;
 
 	ctx = mk_dummy_ctx();									// in the si world we need some rings in the context
+	pctx = mk_dummy_ctx();
 
 	ctx->max_plen = RMR_MAX_RCV_BYTES + sizeof( uta_mhdr_t );
 	ctx->max_mlen = ctx->max_plen + sizeof( uta_mhdr_t );
@@ -114,8 +117,7 @@
 	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 );
-mbuf = rmr_alloc_msg( ctx, 2048 );
+		mbuf = rmr_alloc_msg( ctx, 2048 );
 	}
 
 	//size = 2048 - em_hdr_size();		// emulated receive allocates 2K buffers -- subtract off header size
@@ -126,7 +128,12 @@
 	rmr_free_msg( mbuf );
 
 
-	// ---- drive rtc in a 'static' (not pthreaded) mode to get some coverage; no 'results' to be verified -----
+	// -------- drive rtc in a 'static' (not pthreaded) mode to get some coverage; no 'results' to be verified -----
+	/*
+		It is impossible to drive the message loop of the rtc from a unit test because
+		we cannot generate a message that will arrive on its private RMR context.
+	*/
+
 	setenv( ENV_RTG_RAW, "0", 1 );								// rtc is never raw under SI
 	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 );
@@ -136,7 +143,10 @@
 	}
 	ctx->shutdown = 1;			// should force rtc to quit on first pass
 	rtc( NULL );				// coverage test with nil pointer
-/*
+
+	rtc_file( NULL );			// the static file only collector
+	rtc_file( ctx );
+
 	rtc( ctx );
 
 	setenv( "RMR_RTG_SVC", "4567", 1 );		// drive for edge case coverage to ensure no nil pointer etc
@@ -145,7 +155,17 @@
 	rtc( ctx );
 	setenv( "RMR_RTG_SVC", "tcp:4567:error", 1 );
 	rtc( ctx );
-*/
+	setenv( "RMR_RTG_SVC", "localhost:4589", 1 );		// should force a request to be sent though no reponse back.
+	rtc( ctx );
+
+	payload_str = "newrt|start|abc-def\nmse|10|-1|host1:43086\nmse|20|-1|host1:43086\nnewrt|end|2\n";
+	mbuf = mk_populated_msg( 1024, 0, 20, -2, strlen( payload_str ) );
+	memcpy( mbuf->payload, payload_str, mbuf->len );
+	rtc_parse_msg( ctx, pctx, mbuf, 5, &flags );
+
+	mbuf = mk_populated_msg( 1024, 0, 90, -2, strlen( payload_str ) );		// drive with invalid message type for coverage
+	rtc_parse_msg( ctx, pctx, mbuf, 5, &flags );
+
 
 	// ------------- reallocation tests ------------------------------------------------------------
 	// we use mk_populated_msg() to create a message with mid/sid/plen pushed into the transport
diff --git a/test/test_ctx_support.c b/test/test_ctx_support.c
index 277f741..2a66c04 100644
--- a/test/test_ctx_support.c
+++ b/test/test_ctx_support.c
@@ -63,10 +63,12 @@
 	}
 
 	memset( ctx, 0, sizeof( *ctx ) );
-	
+
 	ctx->mring = uta_mk_ring( 4096 );				// message ring is always on for si
 	ctx->zcb_mring = uta_mk_ring( 128 );		// zero copy buffer mbuf ring to reduce malloc/free calls
 	ctx->si_ctx = malloc( 1024 );
+	ctx->my_name = strdup( "hostname1" );
+	ctx->my_ip = strdup( "123.45.67.89" );
 
 	return ctx;
 }
diff --git a/test/unit_test.ksh b/test/unit_test.ksh
index ad21465..718055a 100755
--- a/test/unit_test.ksh
+++ b/test/unit_test.ksh
@@ -190,7 +190,7 @@
 		}
 	}
 
-	/-:/ { 				# skip unexecutable lines
+	/-:/ {				# skip unexecutable lines
 		spit_line()
 		seq++					# allow blank lines in a sequence group
 		next
@@ -345,7 +345,8 @@
 quiet=0
 gen_xml=0
 replace_flags=1									# replace ##### in gcov for discounted lines
-run_nano_tests=0
+run_nano_tests=0								# can nolonger be turned on
+run_nng_tests=0									# -N will enable
 always_gcov=0									# -a sets to always run gcov even if failure
 save_gcov=1										# -o turns this off
 out_dir=${UT_COVERAGE_DIR:-/tmp/rmr_gcov}		# -O changes output directory
@@ -360,7 +361,7 @@
 		-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)
-		-N)	run_nano_tests=1;;
+		-N)	run_nng_tests=1;;
 		-O)	out_dir=$2; shift;;
 
 		-a)	always_gcov=1;;
@@ -385,9 +386,9 @@
 			;;
 
 
-		-h) 	usage; exit 0;;
+		-h)		usage; exit 0;;
 		--help) usage; exit 0;;
-		-\?) 	usage; exit 0;;
+		-\?)	usage; exit 0;;
 
 		*)	echo "unrecognised option: $1" >&2
 			usage >&2
@@ -399,7 +400,7 @@
 done
 
 
-if (( strict )) 		# if in strict mode, coverage shortcomings are failures
+if (( strict ))			# if in strict mode, coverage shortcomings are failures
 then
 	cfail="FAIL"
 else
@@ -418,11 +419,16 @@
 	do
 		if [[ $tfile != *"static_test.c" ]]
 		then
-			if(( ! run_nano_tests )) && [[ $tfile == *"nano"* ]]
+			if (( ! run_nng_tests )) && [[ $tfile == *"nng"* ]]		# drop any nng file unless -N given
+			then
+				continue
+			fi
+			if [[ $tfile == *"nano"* ]]			# no longer support nano tests; drop regardless
 			then
 				continue
 			fi
 
+			echo "<INFO> add test: $tfile" >&2
 			flist="${flist}$tfile "
 		fi
 	done
@@ -445,6 +451,11 @@
 ut_errors=0			# unit test errors (not coverage errors)
 errors=0
 
+if ! touch /tmp/PPID$$.noise
+then
+	echo "<ERR> unable to write to /tmp???"
+fi
+
 for tfile in $flist
 do
 	for x in *.gcov
@@ -455,14 +466,14 @@
 		fi
 	done
 
+	echo "$tfile --------------------------------------"
 	(	# all noise is now captured into a tmp file to support quiet mode
-		echo "$tfile --------------------------------------"
 		bcmd=$( printf "$builder" "${tfile%.c}" )
 		if ! $bcmd >/tmp/PID$$.log 2>&1
 		then
 			echo "[FAIL] cannot build $tfile"
 			cat /tmp/PID$$.log
-			rm -f /tmp/PID$$
+			# do NOT remove tmp files; bash seens to not gen a new PID for subshells
 			exit 1
 		fi
 
@@ -612,7 +623,6 @@
 						}
 					}
 				}
-
 			}
 
 			END {
@@ -623,7 +633,7 @@
 		rc=$?
 		cat /tmp/PID$$.log
 
-		if (( rc  || force_discounting )) 	# didn't pass, or forcing, see if discounting helps
+		if (( rc  || force_discounting ))	# didn't pass, or forcing, see if discounting helps
 		then
 			if (( ! verbose ))
 			then
@@ -645,7 +655,7 @@
 
 				tail -1 /tmp/PID$$.disc | grep '\['
 
-				if (( verbose > 1 )) 			# updated file was generated, keep here
+				if (( verbose > 1 ))			# updated file was generated, keep here
 				then
 					echo "[INFO] discounted coverage info in: ${tfile##*/}.dcov"
 				fi
@@ -653,7 +663,7 @@
 				mv /tmp/PID$$.disc ${name##*/}.dcov
 			done
 		fi
- 	)>/tmp/PID$$.noise 2>&1
+	)>/tmp/PID$$.noise 2>&1
 	if (( $? != 0 ))
 	then
 		(( ut_errors++ ))
@@ -685,7 +695,7 @@
 	if [[ $xx != *"test"* ]]
 	then
 		of=${xx%.gcov}.dcov
-	 	discount_an_checks $xx  >$of
+		discount_an_checks $xx  >$of
 		if [[ -n $of ]]
 		then
 			tail -1 $of |  grep '\['