Unit test improvements, add example programme 34/3034/1
authorE. Scott Daniels <daniels@research.att.com>
Fri, 27 Mar 2020 14:18:37 +0000 (10:18 -0400)
committerE. Scott Daniels <daniels@research.att.com>
Fri, 27 Mar 2020 14:18:37 +0000 (10:18 -0400)
This change addresses some sonar flagged issues (move/copy
builders missing), and adds unit tests to support the
addition of those.  The code was slightly adjusted to
better support testing coverage.

The rmr_dump programme was added to the examples.

Issue-ID: RIC-148

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

14 files changed:
CHANGES [new file with mode: 0644]
CMakeLists.txt
examples/Makefile
examples/rmr_dump.cpp [new file with mode: 0644]
examples/xapp_t2.cpp
src/messaging/message.cpp
src/messaging/message.hpp
src/messaging/messenger.cpp
src/messaging/messenger.hpp
src/xapp/xapp.cpp
src/xapp/xapp.hpp
test/rmr_em.c
test/unit_test.cpp
test/unit_test.sh

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..05014d1
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,6 @@
+# This file contains a brief summary of each version bump.
+
+2020 March 27; version 0.1.2
+       Changes identified by sonar (missing copy/move builders)
+       rmr_dump example programme
+       Improvements to code for better test coverage
index 9e59870..8e65e5f 100644 (file)
@@ -30,7 +30,7 @@ cmake_minimum_required( VERSION 3.5 )
 
 set( major_version "0" )               # should be automatically populated from git tag later, but until CI process sets a tag we use this
 set( minor_version "1" )
-set( patch_level "1" )
+set( patch_level "2" )
 
 set( install_root "${CMAKE_INSTALL_PREFIX}" )
 set( install_inc "include/ricxfcpp" )
index 1371871..a56760a 100644 (file)
 %.o:: %.cpp %.hpp
        g++ -g ${prereq%% *} -c 
 
-all:: xapp_t1 xapp_t2
+% :: %.cpp
+       g++ $< -g -o $@  -lricxfcpp -lrmr_si -lpthread -lm
 
-xapp_t1:: xapp_t1.cpp
-       g++ xapp_t1.cpp -g -o xapp_t1 -lricxfcpp -lrmr_si -lpthread -lm
-
-xapp_t2:: xapp_t2.cpp
-       g++ xapp_t2.cpp -g -o xapp_t2 -lricxfcpp -lrmr_si -lpthread -lm
+all:: xapp_t1 xapp_t2 rmr_dump
diff --git a/examples/rmr_dump.cpp b/examples/rmr_dump.cpp
new file mode 100644 (file)
index 0000000..5cced21
--- /dev/null
@@ -0,0 +1,259 @@
+// vi: ts=4 sw=4 noet:
+/*
+==================================================================================
+       Copyright (c) 2020 Nokia
+       Copyright (c) 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:       rmr_dump.cpp
+       Abstract:       This is a sample xapp which will behave somewhat like tcpdump
+                               as it receives rmr messages.  The list of message types which should
+                               be processed are given on the command line, following any flag
+                               options (e.g. -p port).  Any of those messages received are reported
+                               on by the application.  The verbosity level may be used to increase
+                               the amount of detail given for the tracked messages.
+
+                               This writes to the TTY which is slow, so do not expect it to be able 
+                               to process and report on a high rate of messages. Also, forwarded
+                               messages will reach the intended target, however if the target 
+                               attempts to send a response the response will come back to THIS
+                               application, and not the message origination; this cannot be a bit
+                               of middleware in it's current form.
+
+       Date:           25 March 2020
+       Author:         E. Scott Daniels
+
+*/
+#include <stdio.h>
+#include <unistd.h>
+#include <atomic>
+
+#include "ricxfcpp/xapp.hpp"
+
+/*
+       Information that the callback needs outside
+       of what is given to it via parms on a call
+       by the framework.
+*/
+typedef struct {
+       int             vlevel;                         // verbosity level
+       bool    forward;                        // if true, message is forwarded
+       int             stats_freq;                     // header/stats after n messages
+       std::atomic<long>       pcount; // messages processed
+       std::atomic<long>       icount; // messages ignored
+       std::atomic<int>        hdr;    // number of messages before next header
+} cb_info_t;
+
+// ----------------------------------------------------------
+
+/*
+       Dump bytes to tty.
+*/
+void dump( unsigned const char* buf, int len ) {
+       int             i;
+       int             j;
+       char    cheater[17];
+
+       fprintf( stdout, "<RD> 0000 | " );
+       j = 0;
+       for( i = 0; i < len; i++ ) {
+               cheater[j++] =  isprint( buf[i] ) ? buf[i] : '.';
+               fprintf( stdout, "%02x ", buf[i] );
+                       
+               if( j == 16 ) {
+                       cheater[j] = 0;
+                       fprintf( stdout, " | %s\n<RD> %04x | ", cheater, i+1 ); 
+                       j = 0;
+               }
+       }
+
+       if( j ) {
+               i = 16 - (i % 16);
+               for( ; i > 0; i-- ) {
+                       fprintf( stdout, "   " );
+               }
+               cheater[j] = 0;
+               fprintf( stdout, " | %s\n", cheater );  
+       }
+}
+
+/*
+       generate stats when the hdr count reaches 0. Only one active
+       thread will ever see it be exactly 0, so this is thread safe.
+*/
+void stats( cb_info_t& cbi ) {
+       int curv;                                       // current stat trigger value
+
+       curv = cbi.hdr--;
+
+       if( curv == 0 ) {                                       // stats when we reach 0
+               fprintf( stdout, "ignored: %ld  processed: %ld\n", 
+                       cbi.icount.load(), cbi.pcount.load() );
+               if( cbi.vlevel > 0 ) {
+                       fprintf( stdout, "\n     %5s %5s %2s %5s\n", 
+                               "MTYPE", "SUBID", "ST", "PLLEN" );
+               }
+
+               cbi.hdr = cbi.stats_freq;               // reset must be last
+       }
+}
+
+void cb1( Message& mbuf, int mtype, int subid, int len,
+                               Msg_component payload,  void* data ) {
+       cb_info_t*      cbi;
+       long total_count;
+
+       if( (cbi = (cb_info_t *) data) == NULL ) {
+               return;
+       }
+
+       cbi->pcount++;
+       stats( *cbi );                  // gen stats & header if needed
+
+       if( cbi->vlevel > 0 ) {
+               fprintf( stdout, "<RD> %-5d %-5d %02d %-5d \n",
+                               mtype, subid, mbuf.Get_state(), len );
+
+               if( cbi->vlevel > 1 ) {
+                       dump(  payload.get(), len > 64 ? 64 : len );
+               }
+       }
+
+       if( cbi->forward ) {
+               // forward with no change to len or payload
+               mbuf.Send_msg( Message::NO_CHANGE, NULL );
+       }
+}
+
+/*
+       registered as the default callback; it counts the
+       messages that we aren't giving details about.
+*/
+void cbd( Message& mbuf, int mtype, int subid, int len,
+                               Msg_component payload,  void* data ) {
+       cb_info_t*      cbi;
+
+       if( (cbi = (cb_info_t *) data) == NULL ) {
+               return;
+       }
+
+       cbi->icount++;
+       stats( *cbi );
+
+       if( cbi->forward ) {
+               // forward with no change to len or payload
+               mbuf.Send_msg( Message::NO_CHANGE, NULL );
+       }
+}
+
+int main( int argc, char** argv ) {
+       std::unique_ptr<Xapp> x;
+       char*   port = (char *) "4560";
+       int ai = 1;                                     // arg processing index
+       cb_info_t*      cbi;
+       int             ncb = 0;                        // number of callbacks registered
+       int             mtype;
+       int             nthreads = 1;
+
+       cbi = (cb_info_t *) malloc( sizeof( *cbi ) );
+       cbi->pcount = 0;
+       cbi->icount = 0;
+       cbi->stats_freq = 10;
+
+       ai = 1;
+       // very simple flag parsing (no error/bounds checking)
+       while( ai < argc ) {
+               if( argv[ai][0] != '-' )  {             // break on first non-flag
+                       break;
+               }
+
+               // very simple arg parsing; each must be separate -x -y not -xy.
+               switch( argv[ai][1] ) {
+                       case 'f':                                       // enable packet forwarding
+                               cbi->forward = true;
+                               break;
+
+                       case 'p':                                       // define port
+                               port = argv[ai+1];
+                               ai++;
+                               break;
+
+                       case 's':                                               // stats frequency
+                               cbi->stats_freq = atoi( argv[ai+1] );
+                               if( cbi->stats_freq < 5 ) {     // enforce sanity
+                                       cbi->stats_freq = 5;
+                               }
+                               ai++;
+                               break;
+
+                       case 't':                                               // thread count
+                               nthreads = atoi( argv[ai+1] );
+                               if( nthreads < 1 ) {
+                                       nthreads = 1;
+                               }
+                               ai++;
+                               break;
+
+                       case 'v':                       // simple verbose bump
+                               cbi->vlevel++;
+                               break;
+
+                       case 'V':                       // explicit verbose level
+                               cbi->vlevel = atoi( argv[ai+1] );
+                               ai++;
+                               break;
+
+                       default:
+                               fprintf( stderr, "unrecognised option: %s\n", argv[ai] );
+                               fprintf( stderr, "usage: %s [-f] [-p port] [-s stats-freq]  [-t thread-count] [-v | -V n] msg-type1 ... msg-typen\n", argv[0] );
+                               fprintf( stderr, "\tstats frequency is in number of messages received\n" );
+                               fprintf( stderr, "\tverbose levels (-V) 0 counts only, 1 message info 2 payload dump\n" );
+                               exit( 1 );
+               }
+
+               ai++;
+       }
+
+       cbi->hdr = cbi->stats_freq;
+       fprintf( stderr, "<RD> listening on port: %s\n", port );
+
+       // create xapp, wait for route table if forwarding
+       x = std::unique_ptr<Xapp>( new Xapp( port, cbi->forward ) );
+
+       // register callback for each type on the command line
+       while( ai < argc ) {    
+               mtype = atoi( argv[ai] );
+               ai++;
+               fprintf( stderr, "<RD> capturing messages for type %d\n", mtype );
+               x->Add_msg_cb( mtype, cb1, cbi );
+               ncb++;
+       }
+
+       if( ncb < 1 ) {
+               fprintf( stderr, "<RD> no message types specified on the command line\n" );
+               exit( 1 );
+       }
+
+       x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, cbi );         // register default cb
+
+       fprintf( stderr, "<RD> starting driver\n" );
+       x->Run( nthreads );
+
+       // return from run() is not expected, but some compilers might
+       // compilain if there isn't a return value here.
+       return 0;
+}
index 6270f92..dd24d2b 100644 (file)
@@ -45,12 +45,14 @@ extern int main( int argc, char** argv ) {
        std::unique_ptr<Message> msg;
        Msg_component payload;                          // special type of unique pointer to the payload 
 
-       int mtype;
        int     sz;
        int i;
        int ai;
        int response_to = 0;                            // max timeout wating for a response
        char*   port = (char *) "4555";
+       int     mtype = 0;
+       int rmtype;                                                     // received message type
+       int delay = 1000000;                            // mu-sec delay; default 1s
        
 
        ai = 1;
@@ -60,6 +62,11 @@ extern int main( int argc, char** argv ) {
                }
 
                switch( argv[ai][1] ) {                 // we only support -x so -xy must be -x -y
+                       case 'd':                                       // delay between messages (mu-sec)
+                               delay = atoi( argv[ai+1] );
+                               ai++;
+                               break;
+                               
                        case 'p': 
                                port = argv[ai+1];      
                                ai++;
@@ -80,6 +87,11 @@ extern int main( int argc, char** argv ) {
        msg = xfw->Alloc_msg( 2048 );
 
        for( i = 0; i < 100; i++ ) {
+               mtype++;
+               if( mtype > 10 ) {
+                       mtype = 0;
+               }
+
                sz = msg->Get_available_size();                 // we'll reuse a message if we received one back; ensure it's big enough
                if( sz < 2048 ) {
                        fprintf( stderr, "<SNDR> fail: message returned did not have enough size: %d [%d]\n", sz, i );
@@ -90,19 +102,21 @@ extern int main( int argc, char** argv ) {
                snprintf( (char *) payload.get(), 2048, "This is message %d\n", i );    // something silly to send
 
                // payload updated in place, nothing to copy from, so payload parm is nil
-               if ( ! msg->Send_msg( 1, Message::NO_SUBID, strlen( (char *) payload.get() )+1, NULL )) {
+               if ( ! msg->Send_msg( mtype, Message::NO_SUBID, strlen( (char *) payload.get() )+1, NULL )) {
                        fprintf( stderr, "<SNDR> send failed: %d\n", i );
                }
 
                msg = xfw->Receive( response_to );
                if( msg != NULL ) {
-                       mtype = msg->Get_mtype();
+                       rmtype = msg->Get_mtype();
                        payload = msg->Get_payload();
-                       fprintf( stderr, "got: mtype=%d payload=(%s)\n", mtype, (char *) payload.get() );
+                       fprintf( stderr, "got: mtype=%d payload=(%s)\n", rmtype, (char *) payload.get() );
                } else {
                        msg = xfw->Alloc_msg( 2048 );                           // nothing back, need a new message to send
                }
 
-               sleep( 1 );
+               if( delay > 0 ) {
+                       usleep( delay );
+               }
        }
 }
index d149cc0..30a0fbc 100644 (file)
@@ -43,7 +43,7 @@
 
 // --------------- private ------------------------------------------------
 
-// --------------- builders -----------------------------------------------
+// --------------- builders/operators  -------------------------------------
 
 /*
        Create a new message wrapper for an existing RMR msg buffer.
@@ -58,6 +58,73 @@ Message::Message( void* mrc, int payload_len ) {
        this->mbuf = rmr_alloc_msg( mrc, payload_len );
 }
 
+/*
+       Copy builder.  Given a source object instance (soi), create a copy.
+       Creating a copy should be avoided as it can be SLOW!
+*/
+Message::Message( const Message& soi ) {
+       int payload_size;
+
+       mrc = soi.mrc;
+       payload_size = rmr_payload_size( soi.mbuf );            // rmr can handle a nil pointer
+       mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE );      
+}
+
+/*
+       Assignment operator. Simiolar to the copycat, but "this" object exists and
+       may have data that needs to be released prior to making a copy of the soi.
+*/
+Message& Message::operator=( const Message& soi ) {
+       int     payload_size;
+
+       if( this != &soi ) {                            // cannot do self assignment
+               if( mbuf != NULL ) {
+                       rmr_free_msg( mbuf );           // release the old one so we don't leak
+               }
+       
+               payload_size = rmr_payload_size( soi.mbuf );            // rmr can handle a nil pointer
+               mrc = soi.mrc;
+               mbuf = rmr_realloc_payload( soi.mbuf, payload_size, RMR_COPY, RMR_CLONE );      
+       }
+
+       return *this;
+}
+
+/*
+       Move builder.  Given a source object instance (soi), move the information from
+       the soi ensuring that the destriction of the soi doesn't trash things from
+       under us.
+*/
+Message::Message( Message&& soi ) {
+       mrc = soi.mrc;
+       mbuf = soi.mbuf;
+
+       soi.mrc = NULL;         // prevent closing of RMR stuff on soi destroy
+       soi.mbuf = NULL;
+}
+
+/*
+       Move Assignment operator. Move the message data to the existing object
+       ensure the object reference is cleaned up, and ensuring that the source
+       object references are removed.
+*/
+Message& Message::operator=( Message&& soi ) {
+       if( this != &soi ) {                            // cannot do self assignment
+               if( mbuf != NULL ) {
+                       rmr_free_msg( mbuf );           // release the old one so we don't leak
+               }
+       
+               mrc = soi.mrc;
+               mbuf = soi.mbuf;
+
+               soi.mrc = NULL;
+               soi.mbuf = NULL;
+       }
+
+       return *this;
+}
+
+
 /*
        Destroyer.
 */
@@ -140,11 +207,13 @@ std::unique_ptr<unsigned char> Message::Get_src(){
 }
 
 int    Message::Get_state( ){
+       int state = INVALID_STATUS;
+
        if( mbuf != NULL ) {
-               return mbuf->state;
+               state =  mbuf->state;
        }
 
-       return INVALID_STATUS;
+       return state;
 }
 
 int    Message::Get_subid(){
@@ -220,12 +289,14 @@ void Message::Set_subid( int new_subid ){
        Exposed to the user, but not expected to be frequently used.
 */
 bool Message::Send( ) {
-       if( mbuf == NULL ) {
-               return false;
+       bool state = false;
+
+       if( mbuf != NULL ) {
+               mbuf =  rmr_send_msg( mrc, mbuf );      // send and pick up new mbuf
+               state = mbuf->state == RMR_OK;          // overall state for caller
        }
 
-       mbuf =  rmr_send_msg( mrc, mbuf );
-       return mbuf->state == RMR_OK;
+       return state;
 }
 
 /*
@@ -233,12 +304,14 @@ bool Message::Send( ) {
        Exposed to the user, but not expected to be frequently used.
 */
 bool Message::Reply( ) {
-       if( mbuf == NULL ) {
-               return false;
+       bool state = false;
+
+       if( mbuf != NULL ) {
+               mbuf =  rmr_rts_msg( mrc, mbuf );               // send and pick up new mbuf
+               state = mbuf->state == RMR_OK;                  // state for caller based on send
        }
 
-       mbuf =  rmr_rts_msg( mrc, mbuf );
-       return mbuf->state == RMR_OK;
+       return state;
 }
 
 /*
@@ -253,38 +326,39 @@ bool Message::Reply( ) {
        This is public, but most users should use Send_msg or Send_response functions.
 */
 bool Message::Send( int mtype, int subid, int payload_len, unsigned char* payload, int stype ) {
+       bool state = false;
 
-       if( mbuf == NULL ) {
-               return false;
-       }
+       if( mbuf != NULL ) {
+               if( mtype != NO_CHANGE ) {
+                       mbuf->mtype = mtype;
+               }
+               if( subid != NO_CHANGE ) {
+                       mbuf->sub_id = subid;
+               }
 
-       if( mtype != NO_CHANGE ) {
-               mbuf->mtype = mtype;
-       }
-       if( subid != NO_CHANGE ) {
-               mbuf->sub_id = subid;
-       }
+               if( payload_len != NO_CHANGE ) {
+                       mbuf->len = payload_len;
+               }
 
-       if( payload_len != NO_CHANGE ) {
-               mbuf->len = payload_len;
-       }
+               if( payload != NULL ) {                 // if we have a payload, ensure msg has room, realloc if needed, then copy
+                       mbuf = rmr_realloc_payload( mbuf, payload_len, RMR_NO_COPY, RMR_NO_CLONE );             // ensure message is large enough
+                       if( mbuf == NULL ) {
+                               return false;
+                       }
 
-       if( payload != NULL ) {                 // if we have a payload, ensure msg has room, realloc if needed, then copy
-               mbuf = rmr_realloc_payload( mbuf, payload_len, RMR_NO_COPY, RMR_NO_CLONE );             // ensure message is large enough
-               if( mbuf == NULL ) {
-                       return false;
+                       memcpy( mbuf->payload, payload, mbuf->len );
                }
 
-               memcpy( mbuf->payload, payload, mbuf->len );
-       }
+               if( stype == RESPONSE ) {
+                       mbuf = rmr_rts_msg( mrc, mbuf );
+               } else {
+                       mbuf = rmr_send_msg( mrc, mbuf );
+               }
 
-       if( stype == RESPONSE ) {
-               mbuf = rmr_rts_msg( mrc, mbuf );
-       } else {
-               mbuf = rmr_send_msg( mrc, mbuf );
+               state = mbuf->state == RMR_OK;
        }
 
-       return mbuf->state == RMR_OK;
+       return state;
 }
 
 /*
index ce654d5..0db0927 100644 (file)
@@ -69,6 +69,10 @@ class Message {
 
                Message( rmr_mbuf_t* mbuf, void* mrc );         // builders
                Message( void* mrc, int payload_len );
+               Message( const Message& soi );                          // copy cat
+               Message& operator=( const Message& soi );       // copy operator
+               Message( Message&& soi );                               // mover
+               Message& operator=( Message&& soi );    // move operator
                ~Message();                                                                     // destroyer
 
                std::unique_ptr<unsigned char>  Copy_payload( );                // copy the payload; deletable smart pointer
index 6951f7d..24fe164 100644 (file)
@@ -78,6 +78,52 @@ Messenger::Messenger( char* port, bool wait4table ) {
        ok_2_run = true;
 }
 
+/*
+       Move support. We DO allow the instance to be moved as only one copy 
+       remains following the move.
+       Given a source object instance (soi) we move the information to 
+       the new object, and then DELETE what was moved so that when the
+       user frees the soi, it doesn't destroy what we snarfed.
+*/
+Messenger::Messenger( Messenger&& soi ) {
+       mrc = soi.mrc;
+       listen_port = soi.listen_port;
+       ok_2_run = soi.ok_2_run;
+       gate = soi.gate;
+               cb_hash = soi.cb_hash;                          // this seems dodgy
+       
+       soi.gate = NULL;
+       soi.listen_port = NULL;
+       soi.mrc = NULL;
+}
+
+/*
+       Move operator. Given a source object instance, movee it's contents
+       to this insance.  We must first clean up this instance.
+*/
+Messenger& Messenger::operator=( Messenger&& soi ) {
+       if( this != &soi ) {                            // cannot move onto ourself
+               if( mrc != NULL ) {
+                       rmr_close( mrc );
+               }
+               if( listen_port != NULL ) {
+                       free( listen_port );
+               }
+
+               mrc = soi.mrc;
+               listen_port = soi.listen_port;
+               ok_2_run = soi.ok_2_run;
+               gate = soi.gate;
+                       cb_hash = soi.cb_hash;                          // this seems dodgy
+               
+               soi.gate = NULL;
+               soi.listen_port = NULL;
+               soi.mrc = NULL;
+       }
+
+       return *this;
+}
+
 /*
        Destroyer.
 */
@@ -86,7 +132,9 @@ Messenger::~Messenger() {
                rmr_close( mrc );
        }
 
-       free( listen_port );
+       if( listen_port != NULL ) {
+               free( listen_port );
+       }
 }
 
 /*
@@ -165,18 +213,16 @@ void Messenger::Listen( ) {
 */
 std::unique_ptr<Message>  Messenger::Receive( int timeout ) {
        rmr_mbuf_t*     mbuf = NULL;
-       //std::unique_ptr<Message> m;
-
-       if( mrc == NULL ) {
-               return NULL;
-       }
+       std::unique_ptr<Message> m = NULL;
 
-       mbuf = rmr_torcv_msg( mrc, mbuf, timeout );             // future: do we want to reuse the mbuf here?
-       if( mbuf != NULL ) {
-               return std::unique_ptr<Message>( new Message( mbuf, mrc ) );
+       if( mrc != NULL ) {
+               mbuf = rmr_torcv_msg( mrc, mbuf, timeout );             // future: do we want to reuse the mbuf here?
+               if( mbuf != NULL ) {
+                       m = std::unique_ptr<Message>( new Message( mbuf, mrc ) );
+               }
        }
 
-       return NULL;
+       return m;
 }
 
 /*
index c8e4f13..0810aeb 100644 (file)
@@ -39,8 +39,6 @@
 
 #include <rmr/rmr.h>
 
-//#include "callback.hpp"
-//#include "default_cb.hpp"            // default callback prototypes
 #include "message.hpp"
 
 #ifndef RMR_FALSE
@@ -58,12 +56,18 @@ class Messenger {
                void*           mrc;                                    // message router context
                char*           listen_port;                    // port we ask msg router to listen on
 
+               // copy and assignment are PRIVATE so that they fail if xapp tries; messenger cannot be copied!
+               Messenger( const Messenger& soi );      
+               Messenger& operator=( const Messenger& soi );
+
        public:
                // -- constants which need an instance; that happens as a global in the .cpp file (wtf C++)
                static const int MAX_PAYLOAD;                   // max message size we'll handle
                static const int DEFAULT_CALLBACK;              // parm for add callback to set default
 
                Messenger( char* port, bool wait4table );       // builder
+               Messenger( Messenger&& soi );                           // move construction
+               Messenger& operator=( Messenger&& soi );        // move operator
                ~Messenger();                                                           // destroyer
 
                void Add_msg_cb( int mtype, user_callback fun_name, void* data );
index 02cea30..8feb52e 100644 (file)
@@ -55,7 +55,7 @@
        If port is nil, then the default port is used (4560).
 */
 Xapp::Xapp( char* port, bool wait4table ) : Messenger( port, wait4table ) {
-       // what's left to do?
+       // nothing to do; all handled in Messenger constructor
 }
 
 /*
@@ -72,7 +72,6 @@ Xapp::~Xapp() {
        function won't return unless that listener crashes.
 */
 void Xapp::Run( int nthreads ) {
-       int joined;                                             // at end, number of threads joined back
        int i;
        std::thread** tinfo;                            // each thread we'll start
 
@@ -87,6 +86,8 @@ void Xapp::Run( int nthreads ) {
        for( i = 0; i < nthreads - 1; i++ ) {                           // wait for others to stop
                tinfo[i]->join();
        }
+
+       delete tinfo;
 }
 
 /*
index a3d5e97..83540ec 100644 (file)
@@ -47,6 +47,10 @@ class Xapp : public Messenger {
        private:
                std::string name;
 
+               // copy and assignment are PRIVATE because we cant "clone" the listen environment
+               Xapp( const Xapp& soi );
+               Xapp& operator=( const Xapp& soi );
+
        public:
                Xapp( char* listen_port, bool wait4rt );        // builder
                Xapp( );
index 581248e..c9f85cf 100644 (file)
@@ -186,7 +186,26 @@ rmr_mbuf_t* rmr_rts_msg( void* mrc, rmr_mbuf_t* mbuf ) {
 }
 
 rmr_mbuf_t* rmr_realloc_payload( rmr_mbuf_t* mbuf, int payload_len, int copy, int clone ) {             // ensure message is large enough
-       return rmr_alloc_msg( NULL, payload_len );
+       rmr_mbuf_t* nmb;
+       unsigned char* payload;
+
+       if( mbuf == NULL ) {
+               return NULL;
+       }
+
+       nmb = rmr_alloc_msg( NULL, payload_len );
+       if( copy ) {
+               memcpy( nmb->payload, mbuf->payload, mbuf->len );
+               nmb->len = mbuf->len;
+       } else {
+               nmb->len = 0;
+       }
+       nmb->state = mbuf->state;
+
+       if( ! clone ) {
+               free( mbuf );
+       }
+       return  nmb;
 }
 
 void rmr_close( void*  mrc ) {
index 7ba91d5..b0612d8 100644 (file)
 
 /*
        Mnemonic:       Unit_test.cpp
-       Abstract:       This is the unit test driver for the C++ xAPP framework
+       Abstract:       This is the unit test driver for the C++ xAPP framework. It 
+                               operates by including all of the modules directly (in order
+                               to build them with the necessary coverage flags), then 
+                               drives all that it can.  The RMR emulation module provides
+                               emulated RMR functions which simulate the creation, sending
+                               and receiving of messages etc.
 
        Date:           20 March 2020
        Author:         E. Scott Daniels
 #include "../src/messaging/messenger.cpp"
 #include "../src/xapp/xapp.cpp"
 
-// callback error counts are global for ease
+/*
+       callback error counts are global for ease. They track the number of times each callback
+       was invoked with the expected message type(s) and any times they were not.
+*/
 int err_cb1 = 0;
 int err_cb2 = 0;
 int err_cbd = 0;
@@ -80,10 +88,17 @@ void cbd( Message& mbuf, int mtype, int subid, int len, Msg_component payload,
        }
 }
 
+/*
+       The Xapp Run() function only returns when Xapp is asked to stop, and that
+       isn't supported from inside any of the callbacks.  This funciton is 
+       started in a thread and after a few seconds it will drive the halt 
+       function in the Xapp instance to stop the run function and allow the
+       unit test to finish.
+*/
 void killer( std::shared_ptr<Xapp> x ) {
-       fprintf( stderr, ">>>> killer is waiting in the shadows\n" );
-       sleep( 5 );
-       fprintf( stderr, ">>>> killer is on the loose\n" );
+       fprintf( stderr, "<INFO> killer is waiting in the shadows\n" );
+       sleep( 2 );
+       fprintf( stderr, "<INFO> killer is on the loose\n" );
        x->Halt();
 }
 
@@ -99,7 +114,9 @@ int main( int argc, char** argv ) {
        int             ai = 1;                                                 // arg processing index
        int             nthreads = 2;                                   // ensure the for loop is executed in setup
        int             i;
+       int             len;
        int             errors = 0;
+       char    wbuf[256];
 
        ai = 1;
        while( ai < argc ) {                            // very simple flag processing (no bounds/error checking)
@@ -225,5 +242,61 @@ int main( int argc, char** argv ) {
                errors++;
        }
 
+       // -----  specific move/copy coverage drivers ---------------------------
+       
+       Messenger m1( (char *) "1234", false );         // messenger class does NOT permit copies, so no need to test
+       Messenger m2( (char *) "9999", false );
+       m1 = std::move( m2 );                                           // drives move operator= function
+       Messenger m3 = std::move( m1 );                         // drives move constructor function
+
+       std::unique_ptr<Message> msg2 = x->Alloc_msg( 2048 );
+       std::unique_ptr<Message> msg3 = x->Alloc_msg( 4096 );
+
+       snprintf( wbuf, sizeof( wbuf ), "Stand up and cheer!!" );
+       msg3->Set_len( strlen( wbuf ) );
+       strcpy( (char *) (msg3->Get_payload()).get(), wbuf );                   // populate the payload to vet copy later
+       fprintf( stderr, "<DBUG> set string (%s) \n", (char *) (msg3->Get_payload()).get() );
+
+       Message msg4 = *(msg3.get());                                                                   // drive copy builder; msg4 should have a 4096 byte payload
+       fprintf( stderr, "<DBUG> copy string (%s) \n", (char *) (msg4.Get_payload()).get() );   // and payload should be coppied
+       if( msg4.Get_available_size() != 4096 ) {
+               errors++;
+               fprintf( stderr, "<FAIL> message copy builder payload size smells: expected 4096, got %d\n", msg4.Get_available_size() );
+       }
+       if( strcmp( (char *) msg4.Get_payload().get(), wbuf ) != 0 ) {
+               errors++;
+               fprintf( stderr, "<FAIL> message copy builder payload of copy not the expected string\n" );
+       }
+
+       snprintf( wbuf, sizeof( wbuf ), "Rambling Wreck; GT!" );                // different string into msg 2 to ensure copy replaced msg3 string
+       strcpy( (char *) (msg2->Get_payload()).get(), wbuf );                   // populate the msg2 payload to vet copy
+       msg2->Set_len( strlen( wbuf ) );
+       *msg3 = *msg2;                                                                                                  // drive the copy operator= function
+       if( msg3->Get_available_size() != 2048 ) {
+               errors++;
+               fprintf( stderr, "<FAIL> message copy operator payload size smells: expected 2048, got %d\n", msg3->Get_available_size() );
+       }
+       if( strcmp( (char *) msg3->Get_payload().get(), wbuf ) != 0 ) {
+               errors++;
+               fprintf( stderr, "<FAIL> message copy builder payload of copy not the expected string\n" );
+       }
+
+       Message msg5 = std::move( *(msg3.get()) );                                      // drive move constructor
+       if( msg5.Get_available_size() != 2048 ) {
+               errors++;
+               fprintf( stderr, "<FAIL> message copy operator payload size smells: expected 2048, got %d\n", msg5.Get_available_size() );
+       }
+       if( strcmp( (char *) msg5.Get_payload().get(), wbuf ) != 0 ) {
+               errors++;
+               fprintf( stderr, "<FAIL> message copy builder payload of copy not the expected string\n" );
+       }
+
+       msg5.Set_len( 2 );                                      // bogus len for vetting later
+       msg5 = std::move( *(msg3.get()) );
+       if( msg5.Get_len() == 21 ) {
+               errors++;
+               fprintf( stderr, "<FAIL> message move operator payload len smells: expected 21, got %d\n", msg5.Get_len() );
+       }
+
        return errors > 0;
 }
index 9e0564b..7144f9c 100755 (executable)
@@ -82,6 +82,7 @@ gcov unit_test >/tmp/PID$$.gcov_log 2>&1      # suss out our gcov files
 ./scrub_gcov.sh                                                                # remove cruft
 
 list=$( mk_list )
+echo "[INFO] coverage stats, discounted (raw), for the various modules:"
 ./parse_gcov.sh $list                                          # generate simple, short, coverage stats
 
 rm -f /tmp/PID$$.*