--- /dev/null
+# 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
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" )
%.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
--- /dev/null
+// 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;
+}
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;
}
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++;
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 );
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 );
+ }
}
}
// --------------- private ------------------------------------------------
-// --------------- builders -----------------------------------------------
+// --------------- builders/operators -------------------------------------
/*
Create a new message wrapper for an existing RMR msg buffer.
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.
*/
}
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(){
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;
}
/*
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;
}
/*
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;
}
/*
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
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.
*/
rmr_close( mrc );
}
- free( listen_port );
+ if( listen_port != NULL ) {
+ free( listen_port );
+ }
}
/*
*/
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;
}
/*
#include <rmr/rmr.h>
-//#include "callback.hpp"
-//#include "default_cb.hpp" // default callback prototypes
#include "message.hpp"
#ifndef RMR_FALSE
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 );
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
}
/*
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
for( i = 0; i < nthreads - 1; i++ ) { // wait for others to stop
tinfo[i]->join();
}
+
+ delete tinfo;
}
/*
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( );
}
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 ) {
/*
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;
}
}
+/*
+ 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();
}
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)
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;
}
./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$$.*