From: E. Scott Daniels Date: Mon, 19 Apr 2021 19:13:51 +0000 (-0400) Subject: Correct bug identified in static analysis X-Git-Tag: 4.7.3^0 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=8c6756d9d6f94beca0bc382f97383ca5e79d16c7;p=ric-plt%2Flib%2Frmr.git Correct bug identified in static analysis This change corrects a flag test bug identified during static analysis of the code. Several tests were added and/or enhanced to beef up the coverage. Issue-ID: RIC-777 Signed-off-by: E. Scott Daniels Change-Id: Icd56f9a669e9583446ac80a5120bc42193eb7adb Signed-off-by: E. Scott Daniels --- diff --git a/CHANGES_CORE.txt b/CHANGES_CORE.txt index cfa9ea3..d7590a4 100644 --- a/CHANGES_CORE.txt +++ b/CHANGES_CORE.txt @@ -5,6 +5,9 @@ # API and build change and fix summaries. Doc corrections # and/or changes are not mentioned here; see the commit messages. +2021 April 19; version 4.7.3 + Correct flag check bug in route table functions (RIC-777). + 2021 April 9; version 4.7.2 Ensure that route table update received from route generator is not applied before a full route table is received. diff --git a/CMakeLists.txt b/CMakeLists.txt index a0d1b7f..9f12da3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,7 @@ cmake_minimum_required( VERSION 3.5 ) 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 "7" ) -set( patch_level "2" ) +set( patch_level "3" ) set( install_root "${CMAKE_INSTALL_PREFIX}" ) set( install_inc "include/rmr" ) diff --git a/docs/rel-notes.rst b/docs/rel-notes.rst index dadc465..b7c67b0 100644 --- a/docs/rel-notes.rst +++ b/docs/rel-notes.rst @@ -22,6 +22,13 @@ the need to leap frog versions ceased, and beginning with version 4.0.0, the RMR versions should no longer skip. +2021 April 19; version 4.7.3 +---------------------------- + +Correct flag check bug in route table functions (RIC-777). + + + 2021 April 9; version 4.7.2 --------------------------- diff --git a/src/rmr/common/src/rt_generic_static.c b/src/rmr/common/src/rt_generic_static.c index dcedb49..a25a449 100644 --- a/src/rmr/common/src/rt_generic_static.c +++ b/src/rmr/common/src/rt_generic_static.c @@ -1081,7 +1081,7 @@ static void parse_rt_rec( uta_ctx_t* ctx, uta_ctx_t* pctx, char* buf, int vleve break; case 'u': // update current table, not a total replacement - if( ! ctx->flags & CFL_FULLRT ) { // we cannot update until we have a full table from route generator + if( ! (ctx->flags & CFL_FULLRT) ) { // we cannot update until we have a full table from route generator rmr_vlog( RMR_VL_WARN, "route table update ignored: full table not previously recevied" ); break; } diff --git a/src/rmr/si/include/rmr_si_private.h b/src/rmr/si/include/rmr_si_private.h index 414c649..8109acd 100644 --- a/src/rmr/si/include/rmr_si_private.h +++ b/src/rmr/si/include/rmr_si_private.h @@ -210,4 +210,7 @@ static endpoint_t* fd2ep_get( uta_ctx_t* ctx, int fd ); static void fd2ep_init( uta_ctx_t* ctx ); static void fd2ep_add( uta_ctx_t* ctx, int fd, endpoint_t* ep ); +// ------ misc --------------------------------------------------- +static inline void incr_ep_counts( int state, endpoint_t* ep ); // must declare for static includes, but after headers + #endif diff --git a/src/rmr/si/src/rmr_si.c b/src/rmr/si/src/rmr_si.c index 8f88cf8..54a1bba 100644 --- a/src/rmr/si/src/rmr_si.c +++ b/src/rmr/si/src/rmr_si.c @@ -1,8 +1,8 @@ // vim: ts=4 sw=4 noet : /* ================================================================================== - Copyright (c) 2019-2020 Nokia - Copyright (c) 2018-2020 AT&T Intellectual Property. + Copyright (c) 2019-2021 Nokia + Copyright (c) 2018-2021 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. @@ -56,11 +56,13 @@ #include "si95/socket_if.h" #include "si95/siproto.h" + #define SI95_BUILD 1 // we drop some common functions for si #include "rmr.h" // things the users see #include "rmr_agnostic.h" // agnostic things (must be included before private) #include "rmr_si_private.h" // things that we need too + #include "rmr_symtab.h" #include "rmr_logging.h" @@ -78,6 +80,28 @@ //------------------------------------------------------------------------------ +/* + If we have an EP, up the counters based on state. + This isn't needed, but it makes driving the code under unit test easier so we + induldge in the bean counter's desire for coverage numbers. +*/ +static inline void incr_ep_counts( int state, endpoint_t* ep ) { + if( ep != NULL ) { + switch( state ) { + case RMR_OK: + ep->scounts[EPSC_GOOD]++; + break; + + case RMR_ERR_RETRY: + ep->scounts[EPSC_TRANS]++; + break; + + default: + ep->scounts[EPSC_FAIL]++; + break; + } + } +} /* Clean up a context. @@ -339,29 +363,14 @@ extern rmr_mbuf_t* rmr_rts_msg( void* vctx, rmr_mbuf_t* msg ) { } } - msg->state = RMR_OK; // ensure it is clear before send hold_src = strdup( (char *) ((uta_mhdr_t *)msg->header)->src ); // the dest where we're returning the message to hold_ip = strdup( (char *) ((uta_mhdr_t *)msg->header)->srcip ); // both the src host and src ip zt_buf_fill( (char *) ((uta_mhdr_t *)msg->header)->src, ctx->my_name, RMR_MAX_SRC ); // must overlay the source to be ours msg = send_msg( ctx, msg, nn_sock, -1 ); if( msg ) { - if( ep != NULL ) { - switch( msg->state ) { - case RMR_OK: - ep->scounts[EPSC_GOOD]++; - break; - - case RMR_ERR_RETRY: - ep->scounts[EPSC_TRANS]++; - break; - - default: - // FIX ME uta_fd_failed( nn_sock ); // we don't have an ep so this requires a look up/search to mark it failed - ep->scounts[EPSC_FAIL]++; - break; - } - } + incr_ep_counts( msg->state, ep ); // update counts + zt_buf_fill( (char *) ((uta_mhdr_t *)msg->header)->src, hold_src, RMR_MAX_SRC ); // always replace original source & ip so rts can be called again zt_buf_fill( (char *) ((uta_mhdr_t *)msg->header)->srcip, hold_ip, RMR_MAX_SRC ); msg->flags |= MFL_ADDSRC; // if msg given to send() it must add source @@ -568,6 +577,35 @@ extern int rmr_set_rtimeout( void* vctx, int time ) { return 0; } +/* + Common cleanup on initialisation error. These are hard to force, and this helps to ensure + all code is tested by providing a callable rather than a block of "goto" code. + + There is a return value so that where we need this we get dinked only for one + uncovered line rather than two: + init_err(...); + return NULL; + + That's a hack, and is yet another example of the testing tail wagging the dog. +*/ +static inline void* init_err( char* msg, void* ctx, void* port, int errval ) { + if( errval != 0 ) { // if not letting it be what a sysllib set it to... + errno = errval; + } + + if( port ) { // free things if allocated + free( port ); + } + if( ctx ) { + free_ctx( ctx ); + } + + if( msg ) { // crit message if supplied + rmr_vlog( RMR_VL_CRIT, "rmr_init: %s: %s", msg, strerror( errno ) ); + } + + return NULL; +} /* This is the actual init workhorse. The user visible function meerly ensures that the @@ -617,13 +655,11 @@ static void* init( char* uproto_port, int def_msg_size, int flags ) { } if ( proto_port == NULL ){ - errno = ENOMEM; - return NULL; + return init_err( "unable to alloc proto port string", NULL, NULL, ENOMEM ); } if( (ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) )) == NULL ) { - errno = ENOMEM; - goto err; + return init_err( "unable to allocate context", ctx, proto_port, ENOMEM ); } memset( ctx, 0, sizeof( uta_ctx_t ) ); @@ -663,8 +699,7 @@ static void* init( char* uproto_port, int def_msg_size, int flags ) { ctx->si_ctx = SIinitialise( SI_OPT_FG ); // FIX ME: si needs to streamline and drop fork/bg stuff if( ctx->si_ctx == NULL ) { - rmr_vlog( RMR_VL_CRIT, "unable to initialise SI95 interface\n" ); - goto err; + return init_err( "unable to initialise SI95 interface\n", ctx, proto_port, 0 ); } if( (port = strchr( proto_port, ':' )) != NULL ) { @@ -698,8 +733,7 @@ static void* init( char* uproto_port, int def_msg_size, int flags ) { free( tok ); } else { if( (gethostname( wbuf, sizeof( wbuf ) )) != 0 ) { - rmr_vlog( RMR_VL_CRIT, "rmr_init: cannot determine localhost name: %s\n", strerror( errno ) ); - goto err; + return init_err( "cannot determine localhost name\n", ctx, proto_port, 0 ); } if( (tok = strchr( wbuf, '.' )) != NULL ) { *tok = 0; // we don't keep domain portion @@ -708,9 +742,7 @@ static void* init( char* uproto_port, int def_msg_size, int flags ) { ctx->my_name = (char *) malloc( sizeof( char ) * RMR_MAX_SRC ); if( snprintf( ctx->my_name, RMR_MAX_SRC, "%s:%s", wbuf, port ) >= RMR_MAX_SRC ) { // our registered name is host:port - rmr_vlog( RMR_VL_CRIT, "rmr_init: hostname + port must be less than %d characters; %s:%s is not\n", RMR_MAX_SRC, wbuf, port ); - errno = EINVAL; - goto err; + return init_err( "hostname + port is too long", ctx, proto_port, EINVAL ); } if( (tok = getenv( ENV_NAME_ONLY )) != NULL ) { @@ -745,7 +777,7 @@ static void* init( char* uproto_port, int def_msg_size, int flags ) { snprintf( bind_info, sizeof( bind_info ), "%s:%s", interface, port ); if( (state = SIlistener( ctx->si_ctx, TCP_DEVICE, bind_info )) < 0 ) { rmr_vlog( RMR_VL_CRIT, "rmr_init: unable to start si listener for %s: %s\n", bind_info, strerror( errno ) ); - goto err; + return init_err( NULL, ctx, proto_port, 0 ); } // finish all flag setting before threads to keep helgrind quiet @@ -760,9 +792,7 @@ static void* init( char* uproto_port, int def_msg_size, int flags ) { ctx->ephash = rmr_sym_alloc( 129 ); // host:port to ep symtab exists outside of any route table if( ctx->ephash == NULL ) { - rmr_vlog( RMR_VL_CRIT, "rmr_init: unable to allocate ep hash\n" ); - errno = ENOMEM; - goto err; + return init_err( "unable to allocate ep hash\n", ctx, proto_port, ENOMEM ); } ctx->rtable = rt_clone_space( ctx, NULL, NULL, 0 ); // create an empty route table so that wormhole/rts calls can be used @@ -790,11 +820,6 @@ static void* init( char* uproto_port, int def_msg_size, int flags ) { free( proto_port ); return (void *) ctx; - -err: - free( proto_port ); - free_ctx( ctx ); - return NULL; } /* diff --git a/src/rmr/si/src/sr_si_static.c b/src/rmr/si/src/sr_si_static.c index 2e24064..4f86728 100644 --- a/src/rmr/si/src/sr_si_static.c +++ b/src/rmr/si/src/sr_si_static.c @@ -281,27 +281,14 @@ static void ref_tpbuf( rmr_mbuf_t* msg, size_t alen ) { msg->header = ((char *) msg->tp_buf) + TP_HDR_LEN; v1hdr = (uta_v1mhdr_t *) msg->header; // v1 will always allow us to suss out the version - - if( v1hdr->rmr_ver == 1 ) { // bug in verion 1 didn't encode the version in network byte order - ver = 1; - v1hdr->rmr_ver = htonl( 1 ); // save it correctly in case we clone the message - } else { - ver = ntohl( v1hdr->rmr_ver ); - } + ver = ntohl( v1hdr->rmr_ver ); switch( ver ) { - case 1: - msg->len = ntohl( v1hdr->plen ); // length sender says is in the payload (received length could be larger) - msg->alloc_len = alen; // length of whole tp buffer (including header, trace and data bits) - msg->payload = msg->header + sizeof( uta_v1mhdr_t ); // point past header to payload (single buffer allocation above) + // version 1 is deprecated case 1: + // version 2 is deprecated case 2: - msg->xaction = &v1hdr->xid[0]; // point at transaction id in header area - msg->flags |= MFL_ZEROCOPY; // this is a zerocopy sendable message - msg->mtype = ntohl( v1hdr->mtype ); // capture and convert from network order to local order - msg->sub_id = UNSET_SUBID; // type 1 messages didn't have this - msg->state = RMR_OK; - hlen = sizeof( uta_v1mhdr_t ); - break; + case 3: + // fall-through default: // current version always lands here hdr = (uta_mhdr_t *) msg->header; @@ -351,12 +338,11 @@ static inline rmr_mbuf_t* clone_msg( rmr_mbuf_t* old_msg ) { nm->header = ((char *) nm->tp_buf) + TP_HDR_LEN; v1hdr = (uta_v1mhdr_t *) old_msg->header; // v1 will work to dig header out of any version switch( ntohl( v1hdr->rmr_ver ) ) { - case 1: - hdr = nm->header; - memcpy( hdr, old_msg->header, sizeof( *v1hdr ) ); // copy complete header - nm->payload = (void *) v1hdr + sizeof( *v1hdr ); - break; + // version 1 deprecated case 1: + // version 2 deprecated + case 3: + // fall-through default: // current message always caught here hdr = nm->header; memcpy( hdr, old_msg->header, RMR_HDR_LEN( old_msg->header ) + RMR_TR_LEN( old_msg->header ) + RMR_D1_LEN( old_msg->header ) + RMR_D2_LEN( old_msg->header )); // copy complete header, trace and other data @@ -423,11 +409,10 @@ static inline rmr_mbuf_t* realloc_msg( rmr_mbuf_t* old_msg, int tr_len ) { v1hdr = (uta_v1mhdr_t *) old_msg->header; // v1 will work to dig header out of any version switch( ntohl( v1hdr->rmr_ver ) ) { - case 1: - v1hdr = nm->header; - memcpy( v1hdr, old_msg->header, sizeof( *v1hdr ) ); // copy complete header - nm->payload = (void *) v1hdr + sizeof( *v1hdr ); - break; + // version 1 not supported + // version 2 not supported + case 3: + // fall-through default: // current message version always caught here hdr = nm->header; @@ -581,19 +566,6 @@ static inline rmr_mbuf_t* realloc_payload( rmr_mbuf_t* old_msg, int payload_len, return nm; } -/* - For SI95 based transport all receives are driven through the threaded - ring and thus this function should NOT be called. If it is we will panic - and abort straight away. -*/ -static rmr_mbuf_t* rcv_msg( uta_ctx_t* ctx, rmr_mbuf_t* old_msg ) { - -fprintf( stderr, "\n\n>>> rcv_msg: bad things just happened!\n\n>>>>>> abort! rcv_msg called and it shouldn't be\n" ); -exit( 1 ); - - return NULL; -} - /* This does the hard work of actually sending the message to the given socket. On success, a new message struct is returned. On error, the original msg is returned with the state @@ -808,21 +780,8 @@ static rmr_mbuf_t* mtosend_msg( void* vctx, rmr_mbuf_t* msg, int max_to ) { } } - if( ep != NULL && msg != NULL ) { - switch( msg->state ) { - case RMR_OK: - ep->scounts[EPSC_GOOD]++; - break; - - case RMR_ERR_RETRY: - ep->scounts[EPSC_TRANS]++; - break; - - default: - ep->scounts[EPSC_FAIL]++; - uta_ep_failed( ep ); // sending to ep failed; set up to reconnect - break; - } + if( msg != NULL ) { + incr_ep_counts( msg->state, ep ); } } else { if( DEBUG ) rmr_vlog( RMR_VL_DEBUG, "invalid socket for rte, setting no endpoint err: mtype=%d sub_id=%d\n", msg->mtype, msg->sub_id ); diff --git a/test/rmr_si_api_static_test.c b/test/rmr_si_api_static_test.c index 5424e98..5f0a4f4 100644 --- a/test/rmr_si_api_static_test.c +++ b/test/rmr_si_api_static_test.c @@ -66,6 +66,30 @@ #include "rmr.h" #include "rmr_agnostic.h" +/* + Driving ep counts is tricky when trying to do it as a part of the rts function, so + we drive that code on it's own. +*/ +static int test_ep_counts() { + int errors = 0; + struct endpoint ep; // need a dummy endpoint + + memset( &ep, 0, sizeof( ep ) ); + + incr_ep_counts( RMR_OK, &ep ); + errors += fail_if_false( ep.scounts[EPSC_GOOD] == 1, "ep inc good counter had bad value" ); + + incr_ep_counts( RMR_ERR_RETRY, &ep ); + errors += fail_if_false( ep.scounts[EPSC_TRANS] == 1, "ep inc trans counter had bad value" ); + + incr_ep_counts( 99, &ep ); // any non-retry/ok value + errors += fail_if_false( ep.scounts[EPSC_FAIL] == 1, "ep inc fail counter had bad value" ); + + incr_ep_counts( RMR_OK, NULL ); // ensure nil pointer doesn't crash us + + return errors; +} + static int rmr_api_test( ) { int errors = 0; void* rmc; // route manager context @@ -283,6 +307,11 @@ static int rmr_api_test( ) { setenv( "RMR_RTG_SVC", "-1", 1 ); // force into static table mode rmr_init( ":6789", 1024, 0 ); // threaded mode with static table + + // ---- some things must be pushed specifically for edge cases and such ------------------------------------ + errors += test_ep_counts(); + init_err( "test error message", rmc, rmc2, ENOMEM ); // drive for coverage + // --------------- phew, done ------------------------------------------------------------------------------ if( ! errors ) { diff --git a/test/si95_test.c b/test/si95_test.c index 70f13e8..809d9a1 100644 --- a/test/si95_test.c +++ b/test/si95_test.c @@ -181,7 +181,7 @@ static int cleanup() { 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 + SIconnect( si_ctx, "127.0.0.1:43086" ); // ensure context has a tp block to free on shutdown SIshutdown( NULL ); SIabort( si_ctx ); @@ -212,7 +212,7 @@ static int addr() { errors += fail_if_true( l != 0, "SIaddress given two null pointers didn't return 0 len" ); l = SIaddress( buf1, NULL, 0 ); errors += fail_if_true( l != 0, "SIaddress given null dest pointer didn't return 0 len" ); - l = SIaddress( NULL, buf1, 0 ); + l = SIaddress( NULL, (void *) &buf1, 0 ); errors += fail_if_true( l != 0, "SIaddress given null src pointer didn't return 0 len" ); net_addr = NULL; @@ -224,7 +224,7 @@ static int addr() { snprintf( buf1, sizeof( buf1 ), "[ff02::5]:4002" ); // v6 might not be supported so failure is OK here; driving for coverage l = SIaddress( buf1, &net_addr, AC_TOADDR6 ); if( l > 0 ) { - l = SIaddress( net_addr, &hr_addr, AC_TODOT ); // convert the address back to hr string + l = SIaddress( net_addr, (void *) &hr_addr, AC_TODOT ); // convert the address back to hr string errors += fail_if_true( l < 1, "v6 to dot conversion failed" ); errors += fail_if_nil( hr_addr, "v6 to dot conversion yields a nil pointer" ); free( net_addr ); @@ -234,7 +234,7 @@ static int addr() { l = SIaddress( buf1, (void **) &net_addr, AC_TOADDR ); errors += fail_if_true( l < 1, "v4 to addr conversion failed" ); - l = SIaddress( net_addr, &hr_addr, AC_TODOT ); // convert the address back to hr string + l = SIaddress( net_addr, (void *) &hr_addr, AC_TODOT ); // convert the address back to hr string errors += fail_if_true( l < 1, "to dot convdersion failed" ); errors += fail_if_nil( hr_addr, "v4 to dot conversion yields a nil pointer" ); free( net_addr ); diff --git a/test/sr_si_static_test.c b/test/sr_si_static_test.c index 0bbd045..213a08b 100644 --- a/test/sr_si_static_test.c +++ b/test/sr_si_static_test.c @@ -271,5 +271,12 @@ static int sr_si_test() { errors += fail_not_equal( strncmp( payload_str, mbuf->payload, strlen( payload_str )), 0, "realloc payload (clone+nocopy) validation of unchanged payload fails" ); + // ---------------------- misc coverage tests; nothing to verify other than they don't crash ----------------------- + payload_str = strdup( "The Marching 110 will play the OU fightsong after every touchdown or field goal; it is a common sound echoing from Peden Stadium in the fall." ); + + dump_n( payload_str, "A dump", strlen( payload_str ) ); + dump_40( payload_str, "another dump" ); + return !!errors; + } diff --git a/test/test_gen_rt.c b/test/test_gen_rt.c index 5748ca1..3afe47b 100644 --- a/test/test_gen_rt.c +++ b/test/test_gen_rt.c @@ -45,7 +45,6 @@ static void gen_rt( uta_ctx_t* ctx ) { int fd; char* rt_stuff; // strings for the route table -ctx->flags |= 0x08; fd = open( "utesting.rt", O_WRONLY | O_CREAT, 0600 ); if( fd < 0 ) { fprintf( stderr, " unable to open file for testing route table gen\n" ); @@ -53,6 +52,8 @@ ctx->flags |= 0x08; } rt_stuff = + "updatert|start\n" // update check before whole table received + "updatert|end\n" "newrt|end\n" // end of table check before start of table found "# comment to drive full comment test\n" "\n" // handle blank lines diff --git a/test/test_support.c b/test/test_support.c index fce0449..1e5e657 100644 --- a/test/test_support.c +++ b/test/test_support.c @@ -216,7 +216,6 @@ static int fail_not_equal( int a, int b, char* what ) { static int fail_if_equal( int a, int b, char* what ) { ts_tests_driven++; - fprintf( stderr, " %s %d\n", what, a==b ); if( a == b ) { fprintf( stderr, " %s values were equal a=%d b=%d\n", what, a, b ); } diff --git a/test/tools_static_test.c b/test/tools_static_test.c index 250f834..c6e4e8b 100644 --- a/test/tools_static_test.c +++ b/test/tools_static_test.c @@ -1,8 +1,9 @@ + if_addrs_t* ifl; // interface lis2 // : vi ts=4 sw=4 noet : /* ================================================================================== - Copyright (c) 2019 Nokia - Copyright (c) 2018-2019 AT&T Intellectual Property. + Copyright (c) 2019-2021 Nokia + Copyright (c) 2018-2021 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. @@ -31,91 +32,130 @@ Date: 3 April 2019 */ -// ------------ zero termianted buffer --------------------------------------------------------- -static int ztbf_test() { - int errors = 0; - char buf[128]; - char* sshort = "Stand up and cheer! Cheer long and loud for old Ohio."; - char* slong = "Now is the time for the bobcat in the forest to make its way back to Court St for a round of pints at the Pub."; - int l1; - - l1 = zt_buf_fill( buf, sshort, 64 ); - errors += fail_not_equal( l1, strlen( sshort ), "zt_buf_fill of short buf returned unexpected len" ); - errors += fail_not_equal( l1, strlen( buf ), "zt_buf_fill of short buf returned len did not match strlen" ); - - l1 = zt_buf_fill( buf, slong, 64 ); - errors += fail_if_equal( l1, strlen( slong ), "zt_buf_fill of long buf returned unexpected len" ); - errors += fail_not_equal( l1, strlen( buf ), "zt_buf_fill of long buf returned len did not match strlen" ); +#define MAX_TOKENS 127 - l1 = zt_buf_fill( buf, sshort, strlen( sshort ) ); // edge case of exact size - errors += fail_not_equal( l1, strlen( sshort )-1, "zt_buf_fill exact length edge case failed" ); +// -------------------- testing support internal functions ------------------------------ +/* + Returns an interface name that is valid in this environment (keeps us from + having to know/guess a name to test with. +*/ +static char* get_ifname( ) { + struct ifaddrs *ifs; // pointer to head + struct ifaddrs *ele; // pointer into the list + char* rstr = NULL; // return string + char octs[NI_MAXHOST+1]; - l1 = zt_buf_fill( buf, sshort, 1 ); // unrealistic edge case - errors += fail_not_equal( l1, 0, "zt_buf_fill dest len == 1 test failed" ); + getifaddrs( &ifs ); + for( ele = ifs; ele; ele = ele->ifa_next ) { + if( ele && strcmp( ele->ifa_name, "lo" ) ) { + memset( octs, 0, sizeof( octs ) ); + getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in6 ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST ); + if( *octs ) { + rstr = strdup( ele->ifa_name ); + fprintf( stderr, " found interface with address: %s\n", rstr ); + break; + } + } + } - return errors; + if( rstr == NULL ) { + fprintf( stderr, " no interface with an address was found!\n" ); + } + return rstr; } /* - Returns an interface name that is valid in this environment (keeps us from - having to know/guess a name to test with. + Build an if-addr list from what we "see" on the current system. Keeps us from + having to guess about what we _might_ find in some random test setup. + If the inc_lo0 boolean is true, then the loop back address(es) will be + included. */ -static char* get_ifname( ) { +static if_addrs_t* get_iflist( int inc_lo0 ) { if_addrs_t* l; struct ifaddrs *ifs; // pointer to head struct ifaddrs *ele; // pointer into the list - char* rstr = NULL; // return string char octs[NI_MAXHOST+1]; + int max_addrs = 128; if( (l = (if_addrs_t *) malloc( sizeof( if_addrs_t ) )) == NULL ) { + fprintf( stderr, " malloc of if_addrs failed\n" ); return NULL; } memset( l, 0, sizeof( if_addrs_t ) ); - l->addrs = (char **) malloc( sizeof( char* ) * 128 ); + l->addrs = (char **) malloc( sizeof( char* ) * max_addrs ); if( l->addrs == NULL ) { + fprintf( stderr, " malloc of if_addrs array failed\n" ); free( l ); return NULL; } getifaddrs( &ifs ); for( ele = ifs; ele; ele = ele->ifa_next ) { - if( ele && strcmp( ele->ifa_name, "lo" ) ) { + if( ele && (inc_lo0 || strcmp( ele->ifa_name, "lo" )) ) { memset( octs, 0, sizeof( octs ) ); getnameinfo( ele->ifa_addr, sizeof( struct sockaddr_in6 ), octs, NI_MAXHOST, NULL, 0, NI_NUMERICHOST ); - if( *octs ) { - rstr = strdup( ele->ifa_name ); - fprintf( stderr, " found interface with address: %s\n", rstr ); - break; + if( *octs && l->naddrs < max_addrs ) { + l->addrs[l->naddrs] = strdup( ele->ifa_name ); + l->naddrs++; } } } - free( l ); - if( rstr == NULL ) { - fprintf( stderr, " no interface with an address was found!\n" ); - } - return rstr; + return l; } -static int tools_test( ) { + + +// ------------ internal functions to drive various categories of tests -------------------------------------- + +static int ztbf_test() { + int errors = 0; + char buf[128]; + char* sshort = "Stand up and cheer! Cheer long and loud for old Ohio."; + char* slong = "Now is the time for the bobcat in the forest to make its way back to Court St for a round of pints at the Pub."; + int l1; + + l1 = zt_buf_fill( NULL, sshort, 64 ); // drive for coverage + errors += fail_not_equal( l1, -1, "nil check (buf) on zt_buf_fill did not return expected value" ); + l1 = zt_buf_fill( buf, NULL, 64 ); + errors += fail_not_equal( l1, -1, "nil check (str) on zt_buf_fill did not return expected value" ); + + l1 = zt_buf_fill( buf, sshort, 64 ); + errors += fail_not_equal( l1, strlen( sshort ), "zt_buf_fill of short buf returned unexpected len" ); + errors += fail_not_equal( l1, strlen( buf ), "zt_buf_fill of short buf returned len did not match strlen" ); + + l1 = zt_buf_fill( buf, slong, 64 ); + errors += fail_if_equal( l1, strlen( slong ), "zt_buf_fill of long buf returned unexpected len" ); + errors += fail_not_equal( l1, strlen( buf ), "zt_buf_fill of long buf returned len did not match strlen" ); + + l1 = zt_buf_fill( buf, sshort, strlen( sshort ) ); // edge case of exact size + errors += fail_not_equal( l1, strlen( sshort )-1, "zt_buf_fill exact length edge case failed" ); + + l1 = zt_buf_fill( buf, sshort, 1 ); // unrealistic edge case + errors += fail_not_equal( l1, 0, "zt_buf_fill dest len == 1 test failed" ); + + return errors; +} + +/* + various tokenising tests. +*/ +static int tok_tests( ) { int i; int j; - int errors = 0; - char* tokens[127]; - char* buf = "2,Fred,Wilma,Barney,Betty,Dino,Pebbles,Bambam,Mr. Slate,Gazoo"; char* dbuf; // duplicated buf since C marks a const string is unumtable - char* hname; - char* ip; // ip address string - uta_ctx_t ctx; // context for uta_lookup test - void* if_list; - + char* buf = "2,Fred,Wilma,Barney,Betty,Dino,Pebbles,Bambam,Mr. Slate,Gazoo"; + char* tokens[MAX_TOKENS]; + int errors = 0; + if_addrs_t* ifl; // interface list + int ntokens; - uta_dump_env(); + i = uta_tokenise( NULL, tokens, MAX_TOKENS, ',' ); // nil check coverage + errors += fail_not_equal( i, 0, "uta_tokenise did not fail when given nil pointer" ); - // ------------------ tokenise tests ----------------------------------------------------------- dbuf = strdup( buf ); - i = uta_tokenise( dbuf, tokens, 127, ',' ); + i = uta_tokenise( dbuf, tokens, MAX_TOKENS, ',' ); errors += fail_not_equal( i, 10, "unexpected number of tokens returned (comma sep)" ); for( j = 0; j < i; j++ ) { //fprintf( stderr, ">>>> [%d] (%s)\n", j, tokens[j] ); @@ -125,60 +165,31 @@ static int tools_test( ) { free( dbuf ); dbuf = strdup( buf ); - i = uta_tokenise( dbuf, tokens, 127, '|' ); + i = uta_tokenise( dbuf, tokens, MAX_TOKENS, '|' ); errors += fail_not_equal( i, 1, "unexpected number of tokens returned (bar sep)" ); free( dbuf ); - // ------------ has str tests ----------------------------------------------------------------- - j = uta_has_str( buf, "Mr. Slate", ',', 1 ); // should fail (-1) because user should use strcmp in this situation - errors += fail_if_true( j >= 0, "test to ensure has str rejects small max" ); - - j = uta_has_str( buf, "Mr. Slate", ',', 27 ); - errors += fail_if_true( j < 0, "has string did not find Mr. Slate" ); - - j = uta_has_str( buf, "Mrs. Slate", ',', 27 ); - errors += fail_if_true( j >= 0, "has string not found Mrs. Slate" ); - - // ------------ host name 2 ip tests --------------------------------------------------------- - hname = uta_h2ip( "192.168.1.2" ); - errors += fail_not_equal( strcmp( hname, "192.168.1.2" ), 0, "h2ip did not return IP address when given address" ); - errors += fail_if_nil( hname, "h2ip did not return a pointer" ); - free( hname ); - - hname = uta_h2ip( "yahoo.com" ); - errors += fail_if_nil( hname, "h2ip did not return a pointer" ); - free( hname ); - - hname = uta_h2ip( "yahoo.com:1234" ); // should ignore the port - errors += fail_if_nil( hname, "h2ip did not return a pointer" ); - free( hname ); - - // ------------ rtg lookup test ------------------------------------------------------------- -#ifdef KEEP - // pub/sub route table generator is deprecated and should be removed at this point - ctx.rtg_port = 0; - ctx.rtg_addr = NULL; - - i = uta_lookup_rtg( NULL ); // ensure it handles a nil context - errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to (nil context)" ); - - setenv( "RMR_RTG_SVC", "localhost:1234", 1); - i = uta_lookup_rtg( &ctx ); - errors += fail_if_false( i, "rtg lookup returned that it did not find something when expected to" ); - errors += fail_if_nil( ctx.rtg_addr, "rtg lookup did not return a pointer (with port)" ); - errors += fail_not_equal( ctx.rtg_port, 1234, "rtg lookup did not capture the port" ); + if( (ifl = get_iflist( 1 )) == NULL ) { + errors++; + fprintf( stderr, " unable to generate an interface list for tokenising tests\n" ); + return errors; + } - setenv( "RMR_RTG_SVC", "localhost", 1); // test ability to generate default port - uta_lookup_rtg( &ctx ); - errors += fail_if_nil( ctx.rtg_addr, "rtg lookup did not return a pointer (no port)" ); - errors += fail_not_equal( ctx.rtg_port, 5656, "rtg lookup did not return default port" ); + dbuf = strdup( "lo0,en0,en1,wlan0,wlan1" ); // must have a mutable string for call + ntokens = uta_rmip_tokenise( dbuf, ifl, tokens, MAX_TOKENS, ',' ); // should find at least lo0 + errors += fail_if_true( ntokens < 1, "rmip tokenise didn't find an interface in the list" ); - unsetenv( "RMR_RTG_SVC" ); // this should fail as the default name (rtg) will be unknown during testing - i = uta_lookup_rtg( &ctx ); - errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to" ); -#endif + return errors; +} - // ------------ my_ip stuff ----------------------------------------------------------------- +/* + Tests related to finding and validating my ip address. +*/ +static int my_ip() { + int i; + int errors = 0; + char* ip; // ip address string + void* if_list; if_list = mk_ip_list( "1235" ); errors += fail_if_nil( if_list, "mk_ip_list returned nil pointer" ); @@ -239,9 +250,88 @@ static int tools_test( ) { fprintf( stderr, " test skipped because no interface with address could be found on system" ); } - errors += ztbf_test(); // test the zero term buffer fill function + return errors; +} + +/* + String tools related tests. +*/ +static int str_tests() { + int j; + char* buf = "2,Fred,Wilma,Barney,Betty,Dino,Pebbles,Bambam,Mr. Slate,Gazoo"; + int errors = 0; + + j = uta_has_str( buf, "Mr. Slate", ',', 1 ); // should fail (-1) because user should use strcmp in this situation + errors += fail_if_true( j >= 0, "test to ensure has str rejects small max" ); + + j = uta_has_str( buf, "Mr. Slate", ',', 27 ); + errors += fail_if_true( j < 0, "has string did not find Mr. Slate" ); + + j = uta_has_str( buf, "Mrs. Slate", ',', 27 ); + errors += fail_if_true( j >= 0, "has string not found Mrs. Slate" ); + + return errors; +} + +/* + Tests related to host name tools. +*/ +static int hostname_tests() { + int errors = 0; + char* hname; + + + hname = uta_h2ip( "192.168.1.2" ); + errors += fail_not_equal( strcmp( hname, "192.168.1.2" ), 0, "h2ip did not return IP address when given address" ); + errors += fail_if_nil( hname, "h2ip did not return a pointer" ); + free( hname ); + + hname = uta_h2ip( "yahoo.com" ); + errors += fail_if_nil( hname, "h2ip did not return a pointer" ); + free( hname ); + + hname = uta_h2ip( "yahoo.com:1234" ); // should ignore the port + errors += fail_if_nil( hname, "h2ip did not return a pointer" ); + free( hname ); + + hname = uta_h2ip( "bugaboofoo.com:1234" ); // should not be there + errors += fail_not_nil( hname, "h2ip lookup returned non-nil when given bogus name" ); + + return errors; +} + +/* + Misc coverage mostly. +*/ +static int misc_tests() { + int errors = 0; + int v; + if_addrs_t* ifl; // interface list + + if( (ifl = get_iflist( 1 )) != NULL ) { + v = is_this_myip( ifl, NULL ); + errors += fail_if_false( v == 0, "is this my ip didn't fail when given nil address" ); + } + + return errors; +} +// ---------------------------------------------------------------------------------------------------------------------- + + +/* + Primary test function driven by the testing main(). +*/ +static int tools_test( ) { + int errors = 0; + + uta_dump_env(); -// ------------------------------------------------------------------------------------------------- + errors += tok_tests(); + errors += my_ip(); + errors += str_tests(); + errors += hostname_tests(); + errors += ztbf_test(); + test_summary( errors, "tools" ); return !!errors; // 1 or 0 regardless of count } diff --git a/test/tools_test.c b/test/tools_test.c index 70cce44..9ccc68d 100644 --- a/test/tools_test.c +++ b/test/tools_test.c @@ -49,13 +49,17 @@ #include "tools_static.c" #include "tools_static_test.c" +#include "wrapper_static_test.c" int main( ) { int errors = 0; - fprintf( stderr, ">>>> starting tools_test\n" ); + fprintf( stderr, " starting tools_test\n" ); errors += tools_test() > 0; + fprintf( stderr, " testing wrapper\n" ); + errors += wrapper_test(); + test_summary( errors, "tool tests" ); if( errors == 0 ) { fprintf( stderr, " all tool tests were OK\n\n" ); diff --git a/test/unit_test.ksh b/test/unit_test.ksh index d476ad9..575c1d8 100755 --- a/test/unit_test.ksh +++ b/test/unit_test.ksh @@ -443,7 +443,6 @@ else flist="$@" fi - if (( noexec )) then echo "no exec mode; would test these:" diff --git a/test/wrapper_static_test.c b/test/wrapper_static_test.c new file mode 100644 index 0000000..3ca71ba --- /dev/null +++ b/test/wrapper_static_test.c @@ -0,0 +1,63 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2021 Nokia + Copyright (c) 2021 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: wrapper_static_test.c + Abstract: Unit test for the wrapper module in common. + + Author: E. Scott Daniels + Date: 19 April 2020 +*/ + +#include + +/* + Called by the test driver (main). Returns the number of errors found. +*/ +static int wrapper_test( ) { + int errors = 0; + char* b; + int len; + + b = rmr_get_consts(); // function that builds constant json string for python-like things + + if( fail_if_equal( strlen( b ), 0, "wrapper buffer had nothing" ) ) { + return 1; // can't do any further checking + } + + errors += fail_if_true( *b != '{', "first character in buffer not valid json" ); + len = strlen( b ) - 1; + errors += fail_if_true( *(b+len) != '}', "last character in buffer not valid json" ); + free( b ); + + b = build_sval( "foobar", "value", 1 ); + errors += fail_if_equal( strlen( b ), 0, "build svalue with sep returned nil buffer" ); + errors += fail_not_equal( strcmp( b, "\"foobar\": \"value\"," ), 0, "svalue result not the expected string" ); + + b = build_sval( "foobar", "value", 0 ); + errors += fail_if_equal( strlen( b ), 0, "build svalue without sep returned nil buffer" ); + errors += fail_not_equal( strcmp( b, "\"foobar\": \"value\"" ), 0, "svalue result without sep not the expected string" ); + + + // ------------------------------------------------------------------------------------------------- + + return !!errors; // 1 or 0 regardless of count +}