From 0b08d9dc1639e926c1df4535703b67903a86aad7 Mon Sep 17 00:00:00 2001 From: "E. Scott Daniels" Date: Fri, 27 Mar 2020 10:18:37 -0400 Subject: [PATCH] Unit test improvements, add example programme 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 Change-Id: I9220d75fa6690feebf439cfe97dda43572f20c4e --- CHANGES | 6 + CMakeLists.txt | 2 +- examples/Makefile | 9 +- examples/rmr_dump.cpp | 259 ++++++++++++++++++++++++++++++++++++++++++++ examples/xapp_t2.cpp | 24 +++- src/messaging/message.cpp | 142 ++++++++++++++++++------ src/messaging/message.hpp | 4 + src/messaging/messenger.cpp | 66 +++++++++-- src/messaging/messenger.hpp | 8 +- src/xapp/xapp.cpp | 5 +- src/xapp/xapp.hpp | 4 + test/rmr_em.c | 21 +++- test/unit_test.cpp | 83 +++++++++++++- test/unit_test.sh | 1 + 14 files changed, 568 insertions(+), 66 deletions(-) create mode 100644 CHANGES create mode 100644 examples/rmr_dump.cpp diff --git a/CHANGES b/CHANGES new file mode 100644 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e59870..8e65e5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" ) diff --git a/examples/Makefile b/examples/Makefile index 1371871..a56760a 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -24,10 +24,7 @@ %.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 index 0000000..5cced21 --- /dev/null +++ b/examples/rmr_dump.cpp @@ -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 +#include +#include + +#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 pcount; // messages processed + std::atomic icount; // messages ignored + std::atomic 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, " 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 %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, " %-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 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, " listening on port: %s\n", port ); + + // create xapp, wait for route table if forwarding + x = std::unique_ptr( new Xapp( port, cbi->forward ) ); + + // register callback for each type on the command line + while( ai < argc ) { + mtype = atoi( argv[ai] ); + ai++; + fprintf( stderr, " capturing messages for type %d\n", mtype ); + x->Add_msg_cb( mtype, cb1, cbi ); + ncb++; + } + + if( ncb < 1 ) { + fprintf( stderr, " 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, " 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; +} diff --git a/examples/xapp_t2.cpp b/examples/xapp_t2.cpp index 6270f92..dd24d2b 100644 --- a/examples/xapp_t2.cpp +++ b/examples/xapp_t2.cpp @@ -45,12 +45,14 @@ extern int main( int argc, char** argv ) { std::unique_ptr 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, " 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, " 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 ); + } } } diff --git a/src/messaging/message.cpp b/src/messaging/message.cpp index d149cc0..30a0fbc 100644 --- a/src/messaging/message.cpp +++ b/src/messaging/message.cpp @@ -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 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; } /* diff --git a/src/messaging/message.hpp b/src/messaging/message.hpp index ce654d5..0db0927 100644 --- a/src/messaging/message.hpp +++ b/src/messaging/message.hpp @@ -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 Copy_payload( ); // copy the payload; deletable smart pointer diff --git a/src/messaging/messenger.cpp b/src/messaging/messenger.cpp index 6951f7d..24fe164 100644 --- a/src/messaging/messenger.cpp +++ b/src/messaging/messenger.cpp @@ -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 Messenger::Receive( int timeout ) { rmr_mbuf_t* mbuf = NULL; - //std::unique_ptr m; - - if( mrc == NULL ) { - return NULL; - } + std::unique_ptr 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( 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( new Message( mbuf, mrc ) ); + } } - return NULL; + return m; } /* diff --git a/src/messaging/messenger.hpp b/src/messaging/messenger.hpp index c8e4f13..0810aeb 100644 --- a/src/messaging/messenger.hpp +++ b/src/messaging/messenger.hpp @@ -39,8 +39,6 @@ #include -//#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 ); diff --git a/src/xapp/xapp.cpp b/src/xapp/xapp.cpp index 02cea30..8feb52e 100644 --- a/src/xapp/xapp.cpp +++ b/src/xapp/xapp.cpp @@ -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; } /* diff --git a/src/xapp/xapp.hpp b/src/xapp/xapp.hpp index a3d5e97..83540ec 100644 --- a/src/xapp/xapp.hpp +++ b/src/xapp/xapp.hpp @@ -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( ); diff --git a/test/rmr_em.c b/test/rmr_em.c index 581248e..c9f85cf 100644 --- a/test/rmr_em.c +++ b/test/rmr_em.c @@ -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 ) { diff --git a/test/unit_test.cpp b/test/unit_test.cpp index 7ba91d5..b0612d8 100644 --- a/test/unit_test.cpp +++ b/test/unit_test.cpp @@ -20,7 +20,12 @@ /* 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 @@ -42,7 +47,10 @@ #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 x ) { - fprintf( stderr, ">>>> killer is waiting in the shadows\n" ); - sleep( 5 ); - fprintf( stderr, ">>>> killer is on the loose\n" ); + fprintf( stderr, " killer is waiting in the shadows\n" ); + sleep( 2 ); + fprintf( stderr, " 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 msg2 = x->Alloc_msg( 2048 ); + std::unique_ptr 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, " 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, " copy string (%s) \n", (char *) (msg4.Get_payload()).get() ); // and payload should be coppied + if( msg4.Get_available_size() != 4096 ) { + errors++; + fprintf( stderr, " 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, " 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, " 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, " 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, " 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, " 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, " message move operator payload len smells: expected 21, got %d\n", msg5.Get_len() ); + } + return errors > 0; } diff --git a/test/unit_test.sh b/test/unit_test.sh index 9e0564b..7144f9c 100755 --- a/test/unit_test.sh +++ b/test/unit_test.sh @@ -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$$.* -- 2.16.6