From 8dd46415b94b33fa960bdd5732e909ffc4859520 Mon Sep 17 00:00:00 2001 From: "E. Scott Daniels" Date: Tue, 16 Apr 2019 20:47:54 +0000 Subject: [PATCH] test(unit): Extend unit tests Change-Id: I28148ce6ef070ad85cb80fc21f090ff47e856d79 Signed-off-by: E. Scott Daniels --- test/Makefile | 14 +- test/README | 69 +++++--- test/hdr_static_test.c | 112 ++++++++++++ test/mbuf_api_static_test.c | 188 ++++++++++++++++++++ test/mbuf_api_test.c | 58 ++++++ test/ring_static_test.c | 164 +++++++++++++++++ test/ring_test.c | 131 ++------------ test/rmr_nng_api_static_test.c | 274 ++++++++++++++++++++++++++++ test/rmr_nng_test.c | 130 ++++++++++++++ test/rt_static_test.c | 231 ++++++++++++++++++++++++ test/sr_nng_static_test.c | 188 ++++++++++++++++++++ test/symtab_static_test.c | 142 +++++++++++++++ test/test_nng_em.c | 393 +++++++++++++++++++++++++++++++++++++++++ test/test_support.c | 21 ++- test/tools_static_test.c | 154 ++++++++++++++++ test/tools_test.c | 20 --- test/unit_test.ksh | 151 +++++++++++++--- test/wormhole_static_test.c | 151 ++++++++++++++++ 18 files changed, 2394 insertions(+), 197 deletions(-) create mode 100644 test/hdr_static_test.c create mode 100644 test/mbuf_api_static_test.c create mode 100644 test/mbuf_api_test.c create mode 100644 test/ring_static_test.c create mode 100644 test/rmr_nng_api_static_test.c create mode 100644 test/rmr_nng_test.c create mode 100644 test/rt_static_test.c create mode 100644 test/sr_nng_static_test.c create mode 100644 test/symtab_static_test.c create mode 100644 test/test_nng_em.c create mode 100644 test/tools_static_test.c create mode 100644 test/wormhole_static_test.c diff --git a/test/Makefile b/test/Makefile index 20d461e..f23f838 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,18 +3,28 @@ CC = gcc coverage_opts = -ftest-coverage -fprofile-arcs -libs = ../build/librmr_nng.a -L ../build/lib -lnng -lpthread -lm +#libs = ../build/librmr_nng.a -L ../build/lib -lnng -lpthread -lm +libs = -L ../build/lib -lnng -lpthread -lm + +#sa_tests = sa_tools_test.o %.o:: %.c $(CC) -g $< -c %:: %.c - $(CC) $(coverage_opts) -fPIC -g $< -o $@ $(libs) + $(CC) -I ../src/common/src/ -I ../src/common/include -I ../src/nng/include $(coverage_opts) -fPIC -g $< -o $@ $(libs) # catch all all: echo "run unit_test.ksh to make and run things here" + +#sa_tools_test.o: sa_tools_test.c +# $(CC) -I ../src/common/src/ -I ../src/common/include -I $(coverage_opts) ../src/nng/include -fPIC -g $< -c + +#.PHONY: sa_tests +#sa_tests: $(sa_tests) + # remove intermediates clean: rm -f *.gcov *.gcda *.dcov *.gcno diff --git a/test/README b/test/README index 85f96ed..5adc3f8 100644 --- a/test/README +++ b/test/README @@ -1,21 +1,25 @@ Unit test -The means to unit testing the RMr library is contained in -this directory. It is somewhat difficult to accurately generate -coverage information for parts of the library because the library -is a fair amount of static functions (to keep them from being -visible to the user programme). +This directory contains the unit test support for the RMr +library. The basic test is run with the follwing command: -To run the tests: - ksh unit_test.sh [specific-test] + ksh unit_test.ksh -If a specific test (e.g. ring_test.c) is not given on the command line, -all *_test.c files are sussed out and an attempt to build and run them -is made by the script. +To run a specific test (e.g. ring_test.c) run: + ksh unit_test.ksh ring_test.c -Output is an interpretation of the resulting gcov output (in more useful -and/or easier to read format). For example: +The script runs the unit test(s) given, and if they pass then +runs an analysis on the .gcov files generated to generate +coverage information. By default, pass/fail of the test is +based only on the success or failure of the unit tests which +are testing functionality. The unit test script can report +an overall failure if coverage is below the indicated threshold +when given the strict option (-s). + +The analysis of .gcov files generates output shown below which +is thought to be more straight forward than the typical stuff +gcov produces: unit_test.ksh ring_test.c ring_test.c -------------------------------------- @@ -26,18 +30,29 @@ ring_test.c -------------------------------------- [PASS] 91% ../src/common/src/ring_static.c -The output shows, for each function, the coverage (column 2) and an +The output shows, for each function, the coverage (column 2) and an interpretation (ok or low) wthin an overall pass or fail. -File Names -The unit test script will find all files named *_test.c and assume that -they can be compiled and executed using the local Makefile. Files -which are needed by these programmes (e.g. common functions) are expected -to reside in this directory as test_*.c and test_*.h files and should -be directly included by the test programmes (not built and linked). This -allows the unit test script to isngore the functions, and files, when -generating coverage reports. +Because of the static nature of the RMr library, tests with the +intent of providing coverage information, as opposed just to providing +functional verification, are a bit trickier. To that end, the test +files in this directory are organised with three file name formats: + + test_*.c tools for testing, not tests + + *_test.c main test programmes which can be compiled in + a stand-alone manner (e.g. gcc foo_test.c) + + *_static_test.c Test functions which are real tests and are + included by one or more stand-alone driver. + +The unit_test script will search only for *_test.c and will ignore +*_static_test.c files when building it's list for testing. + + +Use the command 'unit_test.ksh -?' to see the usage information +and complete set of options available. Discounting @@ -51,13 +66,13 @@ option is given, an augmented coverage listing is saved in .dcov which shows the discounted lines with a string of equal signs (====) rather than the gcov hash string (###). -The discount check is applied only if an entire module has a lower -than accepted coverage rate, and can be forced for all modules with +The discount check is applied only if an entire module has a lower +than accepted coverage rate, and can be forced for all modules with the -f option. To illustrate, the following code checks the return from the system library strdup() call which is very unlikely to fail under test without -going to extremes and substituting for the system lib. Thus, the +going to extremes and substituting for the system lib. Thus, the block which checks for a nil pointer has been discounted: -: 354: @@ -70,13 +85,13 @@ block which checks for a nil pointer has been discounted: Target Coverage By default, a target coverage of 80% is used. For some modules this may -be impossible to achieve, so to prevent always failing these modules +be impossible to achieve, so to prevent always failing these modules may be listed in the .targets file with their expected minimum coverage. Module names need to be qualified (e.g. ../src/common/src/foo.c. ----------------------------------------------------------------------- A note about ksh (A.K.A Korn shell, or kshell) -Ksh is preferred for more complex scripts such as the unit test +Ksh is preferred for more complex scripts such as the unit test script as it does not have some of the limitations that bash -(and other knock-offs) have. +(and other knock-offs) have. diff --git a/test/hdr_static_test.c b/test/hdr_static_test.c new file mode 100644 index 0000000..9d7a38c --- /dev/null +++ b/test/hdr_static_test.c @@ -0,0 +1,112 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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. +================================================================================== +*/ + +/* + Mmemonic: hdr_static_test.c + Abstract: This tests specific properties of the message header + + Author: E. Scott Daniels + Date: 12 April 2019 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../src/common/include/rmr.h" +#include "../src/common/include/rmr_agnostic.h" +#include "../src/nng/include/rmr_nng_private.h" + +#define EMULATE_NNG +#include "test_nng_em.c" +#include "../src/nng/src/sr_nng_static.c" + +#include "test_support.c" + +/* + Dummy for testing here +*/ +extern void rmr_free_msg( rmr_mbuf_t* mbuf ) { +} + +static int hdr_test( ) { + int errors = 0; + uta_ctx_t* ctx; + rmr_mbuf_t* msg; + uta_mhdr_t* hdr; + int hlen; + int len; + int payload_len = 2049; + int trace_len = 37; + + ctx = (uta_ctx_t *) malloc( sizeof( *ctx ) ); + ctx->trace_data_len = 0; + ctx->my_name = strdup( "my-dummy-host-name-and-port:xxxx" ); + + msg = alloc_zcmsg( ctx, NULL, payload_len, 0 ); // header len here should just be len of our struct + hdr = (uta_mhdr_t *) msg->header; + hlen = RMR_HDR_LEN( hdr ); + + fprintf( stderr, " struct len= %d msg len= %d %d\n", (int) sizeof( uta_mhdr_t ), hlen, htonl( hlen ) ); + errors += fail_not_equal( hlen, (int) sizeof( uta_mhdr_t ), "header len (a) not size of struct when no trace data is present" ); + + len = (int) sizeof( uta_mhdr_t ) + payload_len; // expected size of transport buffer allocated + errors += fail_not_equal( len, msg->alloc_len, "alloc len (a) not expected size" ); + + + ctx->trace_data_len = trace_len; // alloc messages with tracing buffer in place + msg = alloc_zcmsg( ctx, NULL, payload_len, 0 ); // header len here should just be len of our struct + hdr = (uta_mhdr_t *) msg->header; + hlen = RMR_HDR_LEN( hdr ); + fprintf( stderr, " with trace data: struct+trace len= %d msg len= %d %d\n", (int) sizeof( uta_mhdr_t )+trace_len, hlen, htonl( hlen ) ); + errors += fail_not_equal( hlen, (int) sizeof( uta_mhdr_t ) + trace_len, "header len (a) was not header + trace data size (b)" ); + + len = RMR_TR_LEN( hdr ); + errors += fail_not_equal( len, trace_len, "trace len in header (a) not expected value (b)" ); + + len = RMR_D1_LEN( hdr ); + errors += fail_not_equal( len, 0, "d1 len in header (a) not expected value (b)" ); + + len = RMR_D2_LEN( hdr ); + errors += fail_not_equal( len, 0, "d2 len in header (a) not expected value (b)" ); + + + // ------------------------------------------------------------------------------------------- + + if( ! errors ) { + fprintf( stderr, " all msg header tests pass\n" ); + } + return !! errors; +} + +int main() { + return hdr_test(); +} diff --git a/test/mbuf_api_static_test.c b/test/mbuf_api_static_test.c new file mode 100644 index 0000000..4c0cd5e --- /dev/null +++ b/test/mbuf_api_static_test.c @@ -0,0 +1,188 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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. +================================================================================== +*/ + +/* + Mmemonic: mbuf_api_static_test.c + Abstract: Test the message buffer funcitons. These are meant to be included at compile + time by the test driver. + + Author: E. Scott Daniels + Date: 3 April 2019 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/common/include/rmr.h" +#include "../src/common/include/rmr_agnostic.h" + + +int mbuf_api_test( ) { + unsigned char* c; + int i; + int errors = 0; + rmr_mbuf_t* mbuf; + unsigned char src_buf[256]; + + mbuf = (rmr_mbuf_t *) malloc( sizeof( *mbuf ) ); + if( mbuf == NULL ) { + fprintf( stderr, "[FAIL] tester cannot allocate memory: mbuf\n" ); + exit( 1 ); + } + + mbuf->payload = (void *) malloc( sizeof( char ) * 1024 ); // add a dummy payload + mbuf->tp_buf = mbuf->payload; + mbuf->header = mbuf->payload; + mbuf->alloc_len = 1024; + + memset( src_buf, 0, sizeof( src_buf ) ); + rmr_bytes2payload( mbuf, NULL, strlen( src_buf) ); // errno should be set on return + errors += fail_if( errno == 0, "buf copy to payload with nil src returned good errno" ); + + rmr_bytes2payload( NULL, src_buf, strlen( src_buf) ); // errno should be set on return + errors += fail_if( errno == 0, "buf copy to payload with nil mbuf returned good errno" ); + + mbuf->state = 1; // force it to something to test that it was set + rmr_bytes2payload( mbuf, src_buf, strlen( src_buf) ); + errors += fail_if( mbuf->state != RMR_OK, "buf copy to payload returned bad state in mbuf" ); + + rmr_bytes2payload( mbuf, src_buf, 8192 ); // bust the limit + errors += fail_if( mbuf->state == RMR_OK, "huge buf copy to payload returned good state in mbuf" ); + errors += fail_if( errno == 0, "huge buf copy to payload returned good state in errno" ); + + + snprintf( src_buf, sizeof( src_buf ), "This is some text in the buffer" ); + rmr_str2payload( mbuf, src_buf ); // this uses bytes2payload, so only one invocation needed + + errno = 0; + i = rmr_bytes2meid( NULL, src_buf, RMR_MAX_MEID ); + errors += fail_if( errno == 0, "(errno) attempt to copy bytes to meid with nil message" ); + errors += fail_if( i > 0, "(rv) attempt to copy bytes to meid with nil message" ); + + errno = 0; + i = rmr_bytes2meid( mbuf, NULL, RMR_MAX_MEID ); + errors += fail_if( errno == 0, "(errno) attempt to copy bytes to meid with nil source buffer" ); + errors += fail_if( i > 0, "(rv) attempt to copy bytes to meid with nil message" ); + + errno = 0; + i = rmr_bytes2meid( mbuf, src_buf, RMR_MAX_MEID + 1 ); + errors += fail_if( errno == 0, "(errno) attempt to copy bytes to meid with large source buffer" ); + errors += fail_if( i != RMR_MAX_MEID, "(rv) attempt to copy bytes to meid with large source buffer" ); + + errno = 0; + i = rmr_bytes2meid( mbuf, src_buf, RMR_MAX_MEID ); + errors += fail_if( errno != 0, "copy bytes to meid; expected errno to be ok" ); + errors += fail_if( i != RMR_MAX_MEID, "copy bytes to meid; expected return value to be max meid len" ); + + + + errno = 0; + snprintf( src_buf, sizeof( src_buf ), "meid-fits" ); + i = rmr_str2meid( NULL, src_buf ); + errors += fail_if( errno == 0, "(errno) attempt to copy string to meid with nil message" ); + errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to meid with nil message" ); + + errno = 0; + i = rmr_str2meid( mbuf, NULL ); + errors += fail_if( errno == 0, "(errno) attempt to copy string to meid with nil source buffer" ); + errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to meid with nil message" ); + + errno = 0; + i = rmr_str2meid( mbuf, src_buf ); + errors += fail_if( errno != 0, "copy string to meid; expected errno to be ok" ); + errors += fail_if( i != RMR_OK, "copy string to meid; expected return value to be RMR_OK" ); + + errno = 0; + snprintf( src_buf, sizeof( src_buf ), "meid-should-be-too-large-to-fit-in-the-meid" ); + i = rmr_str2meid( mbuf, src_buf ); + errors += fail_if( errno == 0, "(errno) attempt to copy string to meid with large source buffer" ); + errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to meid with large source buffer" ); + + + errno = 0; + i = rmr_bytes2xact( NULL, src_buf, RMR_MAX_XID ); + errors += fail_if( errno == 0, "(errno) attempt to copy bytes to xact with nil message" ); + errors += fail_if( i > 0, "(rv) attempt to copy bytes to xact with nil message" ); + + errno = 0; + i = rmr_bytes2xact( mbuf, NULL, RMR_MAX_XID ); + errors += fail_if( errno == 0, "(errno) attempt to copy bytes to xact with nil source buffer" ); + errors += fail_if( i > 0, "(rv) attempt to copy bytes to xact with nil message" ); + + errno = 0; + i = rmr_bytes2xact( mbuf, src_buf, RMR_MAX_XID + 1 ); + errors += fail_if( errno == 0, "(errno) attempt to copy bytes to xact with large source buffer" ); + errors += fail_if( i != RMR_MAX_XID, "(rv) attempt to copy bytes to xact with large source buffer" ); + + errno = 0; + i = rmr_bytes2xact( mbuf, src_buf, RMR_MAX_XID ); + errors += fail_if( errno != 0, "copy bytes to xact; expected errno to be ok" ); + errors += fail_if( i != RMR_MAX_XID, "copy bytes to xact; expected return value to be max xact len" ); + + + + errno = 0; + snprintf( src_buf, sizeof( src_buf ), "xact-fits" ); + i = rmr_str2xact( NULL, src_buf ); + errors += fail_if( errno == 0, "(errno) attempt to copy string to xact with nil message" ); + errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to xact with nil message" ); + + errno = 0; + i = rmr_str2xact( mbuf, NULL ); + errors += fail_if( errno == 0, "(errno) attempt to copy string to xact with nil source buffer" ); + errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to xact with nil message" ); + + errno = 0; + i = rmr_str2xact( mbuf, src_buf ); + errors += fail_if( errno != 0, "copy string to xact; expected errno to be ok" ); + errors += fail_if( i != RMR_OK, "copy string to xact; expected return value to be RMR_OK" ); + + errno = 0; + snprintf( src_buf, sizeof( src_buf ), "xact-should-be-too-large-to-fit-in-the-xact" ); + i = rmr_str2xact( mbuf, src_buf ); + errors += fail_if( errno == 0, "(errno) attempt to copy string to xact with large source buffer" ); + errors += fail_if( i == RMR_OK, "(rv) attempt to copy string to xact with large source buffer" ); + + + snprintf( src_buf, sizeof( src_buf ), "test-meid" ); + rmr_str2meid( mbuf, src_buf ); + + errno = 0; + c = rmr_get_meid( NULL, NULL ); + errors += fail_if( c != NULL, "get meid with nil message buffer" ); + errors += fail_if( errno == 0, "(errno bad) get meid with nil msg buffer" ); + + + c = rmr_get_meid( mbuf, NULL ); // should allocate and return c + errors += fail_if( c == NULL, "get meid with nil dest pointer (did not allocate a buffer)" ); + errors += fail_if( strcmp( c, "test-meid" ) != 0, "did not get expected meid from mbuffer" ); + + c = rmr_get_meid( mbuf, c ); + errors += fail_if( c == NULL, "get meid with a dest pointer returned no pointer" ); + errors += fail_if( strcmp( c, "test-meid" ) != 0, "did not get expected meid from mbuffer" ); + + + return errors > 0; // overall exit code bad if errors +} diff --git a/test/mbuf_api_test.c b/test/mbuf_api_test.c new file mode 100644 index 0000000..858efdc --- /dev/null +++ b/test/mbuf_api_test.c @@ -0,0 +1,58 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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: mbuf_api_test.c + Abstract: Unit tests for the mbuf common API functions. + Author: E. Scott Daniels + Date: 2 April 2019 +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "../src/common/include/rmr.h" +#include "../src/common/include/rmr_agnostic.h" + +#include "../src/common/src/mbuf_api.c" // module under test + +#include "test_support.c" // our private library of test tools +#include "mbuf_api_static_test.c" // test functions + +int main( ) { + int errors = 0; + + errors += mbuf_api_test( ); + + if( errors ) { + fprintf( stderr, " mbuf_api tests failed\n" ); + } else { + fprintf( stderr, " mbuf_api tests pass\n" ); + } +} diff --git a/test/ring_static_test.c b/test/ring_static_test.c new file mode 100644 index 0000000..482244c --- /dev/null +++ b/test/ring_static_test.c @@ -0,0 +1,164 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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. +================================================================================== +*/ + +/* + Mmemonic: ring_static_test.c + Abstract: Test the ring funcitons. These are meant to be included at compile + time by the test driver. + + Author: E. Scott Daniels + Date: 3 April 2019 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/common/include/rmr.h" +#include "../src/common/include/rmr_agnostic.h" +//#include "../src/common/src/ring_static.c" + + +/* + Conduct a series of interleaved tests inserting i-factor + values before beginning to pull values (i-factor must be + size - 2 smaller than the ring. + Returns 0 on success, 1 on insert failure and 2 on pull failure. +*/ +static int ie_test( void* r, int i_factor, long inserts ) { + int i; + int* dp; + int data[29]; + + for( i = 0; i < inserts; i++ ) { + data[i%29] = i; + if( ! uta_ring_insert( r, &data[i%29] ) ) { + fprintf( stderr, " interleaved insert failed on ifactor=%d i=%d\n", i_factor, i ); + return 1; + } + if( i > i_factor-1 ) { + dp = uta_ring_extract( r ); + if( *dp != data[(i-i_factor)%29] ) { + fprintf( stderr, " interleaved exctract failed on ifactor=%d i=%d expected=%d got=%d\n", i_factor, i, data[(i-i_factor)%29], *dp ); + return 2; + } + } + } + //fprintf( stderr, " interleaved insert/extract test passed for insert factor %d\n", i_factor ); + + return 0; +} + +static int ring_test( ) { + void* r; + int i; + int j; + int data[20]; + int* dp; + int size = 18; + + r = uta_mk_ring( 0 ); // should return nil + if( r != NULL ) { + fprintf( stderr, " attempt to make a ring with size 0 returned a pointer\n" ); + return 1; + } + r = uta_mk_ring( -1 ); // should also return nil + if( r != NULL ) { + fprintf( stderr, " attempt to make a ring with size <0 returned a pointer\n" ); + return 1; + } + + r = uta_mk_ring( 18 ); + if( r == NULL ) { + fprintf( stderr, " unable to make ring with 17 entries\n" ); + return 1; + } + + for( i = 0; i < 20; i++ ) { // test to ensure it reports full when head/tail start at 0 + data[i] = i; + if( ! uta_ring_insert( r, &data[i] ) ) { + break; + } + } + + if( i > size ) { + fprintf( stderr, " didn not report table full: i=%d\n", i ); + return 1; + } + + fprintf( stderr, " reported table full at i=%d as expected\n", i ); + + + for( i = 0; i < size + 3; i++ ) { // ensure they all come back in order, and we don't get 'extras' + if( (dp = uta_ring_extract( r )) == NULL ) { + if( i < size-1 ) { + fprintf( stderr, " nil pointer at i=%d\n", i ); + return 1; + } else { + break; + } + } + + if( *dp != i ) { + fprintf( stderr, " data at i=% isnt right; expected %d got %d\n", i, i, *dp ); + } + } + if( i > size ) { + fprintf( stderr, " got too many values on extract: %d\n", i ); + return 1; + } + fprintf( stderr, " extracted values were sane, got: %d\n", i-1 ); + + uta_ring_free( NULL ); // ensure this doesn't blow up + uta_ring_free( r ); + for( i = 2; i < 15; i++ ) { + r = uta_mk_ring( 16 ); + if( ie_test( r, i, 101 ) != 0 ) { // modest number of inserts + fprintf( stderr, " ie test for 101 inserts didn't return 0\n" ); + return 1; + } + + uta_ring_free( r ); + } + fprintf( stderr, " all modest insert/exctract tests pass\n" ); + + size = 5; + for( j = 0; j < 20; j++ ) { + for( i = 2; i < size - 2; i++ ) { + r = uta_mk_ring( size ); + if( ie_test( r, i, 66000 ) != 0 ) { // should force the 16bit head/tail indexes to roll over + fprintf( stderr, " ie test for 66K inserts didn't return 0\n" ); + return 1; + } + + uta_ring_free( r ); + } + fprintf( stderr, " all large insert/exctract tests pass ring size=%d\n", size ); + + size++; + } + + fprintf( stderr, " all ring tests pass\n" ); + return 0; +} diff --git a/test/ring_test.c b/test/ring_test.c index 331f976..a06668f 100644 --- a/test/ring_test.c +++ b/test/ring_test.c @@ -20,9 +20,12 @@ /* Mmemonic: ring_test.c - Abstract: Test the ring funcitons. + Abstract: This is a stand alone test driver for the ring module. It + includes the static tests after setting up the environment + then invokes it. + Author: E. Scott Daniels - Date: 31 July 2017 + Date: 3 April 2019 */ #include @@ -37,123 +40,19 @@ #include "../src/common/include/rmr_agnostic.h" #include "../src/common/src/ring_static.c" +#include "test_support.c" // things like fail_if() +#include "ring_static_test.c" // the actual tests -/* - Conduct a series of interleaved tests inserting i-factor - values before beginning to pull values (i-factor must be - size - 2 smaller than the ring. - Returns 0 on success, 1 on insert failure and 2 on pull failure. -*/ -static int ie_test( void* r, int i_factor, long inserts ) { - int i; - int* dp; - int data[29]; - - for( i = 0; i < inserts; i++ ) { - data[i%29] = i; - if( ! uta_ring_insert( r, &data[i%29] ) ) { - fprintf( stderr, "[FAIL] interleaved insert failed on ifactor=%d i=%d\n", i_factor, i ); - return 1; - } - if( i > i_factor-1 ) { - dp = uta_ring_extract( r ); - if( *dp != data[(i-i_factor)%29] ) { - fprintf( stderr, "[FAIL] interleaved exctract failed on ifactor=%d i=%d expected=%d got=%d\n", i_factor, i, data[(i-i_factor)%29], *dp ); - return 2; - } - } - } - //fprintf( stderr, "[OK] interleaved insert/extract test passed for insert factor %d\n", i_factor ); - - return 0; -} - -int main( void ) { - void* r; - int i; - int j; - int data[20]; - int* dp; - int size = 18; - - r = uta_mk_ring( 0 ); // should return nil - if( r != NULL ) { - fprintf( stderr, "[FAIL] attempt to make a ring with size 0 returned a pointer\n" ); - exit( 1 ); - } - r = uta_mk_ring( -1 ); // should also return nil - if( r != NULL ) { - fprintf( stderr, "[FAIL] attempt to make a ring with size <0 returned a pointer\n" ); - exit( 1 ); - } - - r = uta_mk_ring( 18 ); - if( r == NULL ) { - fprintf( stderr, "[FAIL] unable to make ring with 17 entries\n" ); - exit( 1 ); - } - - for( i = 0; i < 20; i++ ) { // test to ensure it reports full when head/tail start at 0 - data[i] = i; - if( ! uta_ring_insert( r, &data[i] ) ) { - break; - } - } - - if( i > size ) { - fprintf( stderr, "[FAIL] didn not report table full: i=%d\n", i ); - exit( 1 ); - } - - fprintf( stderr, "[OK] reported table full at i=%d as expected\n", i ); - - - for( i = 0; i < size + 3; i++ ) { // ensure they all come back in order, and we don't get 'extras' - if( (dp = uta_ring_extract( r )) == NULL ) { - if( i < size-1 ) { - fprintf( stderr, "[FAIL] nil pointer at i=%d\n", i ); - exit( 1 ); - } else { - break; - } - } - - if( *dp != i ) { - fprintf( stderr, "[FAIL] data at i=% isnt right; expected %d got %d\n", i, i, *dp ); - } - } - if( i > size ) { - fprintf( stderr, "[FAIL] got too many values on extract: %d\n", i ); - exit( 1 ); - } - fprintf( stderr, "[OK] extracted values were sane, got: %d\n", i-1 ); - - uta_ring_free( NULL ); // ensure this doesn't blow up - uta_ring_free( r ); - for( i = 2; i < 15; i++ ) { - r = uta_mk_ring( 16 ); - if( ie_test( r, i, 101 ) != 0 ) { // modest number of inserts - exit( 1 ); - } - - uta_ring_free( r ); - } - fprintf( stderr, "[OK] all modest insert/exctract tests pass\n" ); +int main( ) { + int errors = 0; - size = 5; - for( j = 0; j < 20; j++ ) { - for( i = 2; i < size - 2; i++ ) { - r = uta_mk_ring( size ); - if( ie_test( r, i, 66000 ) != 0 ) { // should force the 16bit head/tail indexes to roll over - exit( 1 ); - } - - uta_ring_free( r ); - } - fprintf( stderr, "[OK] all large insert/exctract tests pass ring size=%d\n", size ); + errors += ring_test( ); - size++; + if( errors ) { + fprintf( stderr, " ring tests failed\n" ); + } else { + fprintf( stderr, " ring tests pass\n" ); } - return 0; + return errors; } diff --git a/test/rmr_nng_api_static_test.c b/test/rmr_nng_api_static_test.c new file mode 100644 index 0000000..e9ecb72 --- /dev/null +++ b/test/rmr_nng_api_static_test.c @@ -0,0 +1,274 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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. +================================================================================== +*/ + +/* + Mmemonic: rmr_api_static_test.c + Abstract: Specific tests related to the API functions in rmr_nng.c/rmr.c. + This should be included by a driver, but only the main RMr + driver and there likely not be a specific stand alone driver + for just this small set of tests because of the depth of the + library needed to test at this level. + + The message buffer specific API tests are in a different static + module. API functions tested here are: + rmr_close + rmr_get_rcvfd + rmr_ready + rmr_init + rmr_set_rtimeout + rmr_set_stimeout + rmr_rcv_specific + rmr_torcv_msg + rmr_rcv_msg + rmr_call + rmr_rts_msg + rmr_send_msg + rmr_mtosend_msg + rmr_free_msg + + Author: E. Scott Daniels + Date: 5 April 2019 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/common/include/rmr.h" +#include "../src/common/include/rmr_agnostic.h" +//#include "../src/common/src/ring_static.c" + +/* + Send a 'burst' of messages to drive some send retry failures to increase RMr coverage + by handling the retry caee. +*/ +static void send_n_msgs( void* ctx, int n ) { + rmr_mbuf_t* msg; // message buffers + int i; + + msg = rmr_alloc_msg( ctx, 1024 ); + if( ! msg ) { + return; + } + + for( i = 0; i < n; i++ ) { +fprintf( stderr, "mass send\n" ); + msg->len = 100; + msg->mtype = 1; + msg->state = 999; + errno = 999; + msg = rmr_send_msg( ctx, msg ); + } +} + +static int rmr_api_test( ) { + int errors = 0; + void* rmc; // route manager context + void* rmc2; // second context for non-listener init + rmr_mbuf_t* msg; // message buffers + rmr_mbuf_t* msg2; + int v = 0; // some value + char wbuf[128]; + int i; + + v = rmr_ready( NULL ); + errors += fail_if( v != 0, "rmr_ready returned true before initialisation" ); + + if( (rmc = rmr_init( "4560", 1024, FL_NOTHREAD )) == NULL ) { + fail_if_nil( rmc, "rmr_init returned a nil pointer" ); + return 1; + } + + if( (rmc2 = rmr_init( ":6789", 1024, FL_NOTHREAD )) == NULL ) { // init without starting a thread + errors += fail_if_nil( rmc, "rmr_init returned a nil pointer for non-threaded init" ); + } + + free_ctx( rmc2 ); // coverage + + if( (rmc2 = rmr_init( NULL, 1024, FL_NOTHREAD )) == NULL ) { // drive default port selector code + errors += fail_if_nil( rmc, "rmr_init returned a nil pointer when driving for default port" ); + } + + v = rmr_ready( rmc ); // unknown return; not checking at the moment + + msg = rmr_alloc_msg( NULL, 1024 ); // should return nil pointer + errors += fail_not_nil( msg, "rmr_alloc_msg didn't return nil when given nil context" ); + + msg = rmr_alloc_msg( rmc, 2048 ); // allocate larger than default size given on init + errors += fail_if_nil( msg, "rmr_alloc_msg returned nil msg pointer" ); + + v = rmr_payload_size( NULL ); + errors += fail_if( v >= 0, "rmr_payload_size returned valid size for nil message" ); + errors += fail_if( errno == 0, "rmr_payload_size did not set errno on failure" ); + + v = rmr_payload_size( msg ); + if( v >= 0 ) { + errors += fail_not_equal( v, 2048, "rmr_payload_size returned invalid size (a) instead of expected size (b)" ); + errors += fail_if( errno != 0, "rmr_payload_size did not clear errno on success" ); + } else { + errors += fail_if( v < 0, "rmr_payload_size returned invalid size for good message" ); + } + + v = rmr_get_rcvfd( NULL ); + errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor when given nil context" ); + v = rmr_get_rcvfd( rmc ); + errors += fail_if( v < 0, "rmr_get_rcvfd did not return a valid file descriptor" ); + + msg2 = rmr_send_msg( NULL, NULL ); // drive for coverage + errors += fail_not_nil( msg2, "send_msg returned msg pointer when given a nil message and context" ); + + // --- sends will fail with a no endpoint error until a dummy route table is set, so we test fail case first. + msg->len = 100; + msg->mtype = 1; + msg->state = 999; + errno = 999; + msg = rmr_send_msg( rmc, msg ); + errors += fail_if_nil( msg, "send_msg_ did not return a message on send" ); + if( msg ) { + errors += fail_not_equal( msg->state, RMR_ERR_NOENDPT, "send_msg did not return no endpoints before rtable added" ); + errors += fail_if( errno == 0, "send_msg did not set errno" ); + } + + gen_rt( rmc ); // --- after this point there is a dummy route table so send and rts calls should be ok + + msg->len = 100; + msg->mtype = 1; + msg->state = 999; + errno = 999; + msg = rmr_send_msg( rmc, msg ); + errors += fail_if_nil( msg, "send_msg_ did not return a message on send" ); + if( msg ) { + errors += fail_not_equal( msg->state, RMR_OK, "send_msg returned bad status for send that should work" ); + errors += fail_if( errno != 0, "send_msg set errno for send that should work" ); + v = rmr_payload_size( msg ); + errors += fail_if( v != 2048, "send_msg did not allocate new buffer with correct size" ); + } + + rmr_set_stimeout( NULL, 0 ); + rmr_set_stimeout( rmc, 20 ); + rmr_set_stimeout( rmc, -1 ); + rmr_set_rtimeout( NULL, 0 ); + rmr_set_rtimeout( rmc, 20 ); + rmr_set_rtimeout( rmc, -1 ); + + msg2 = rmr_rcv_msg( NULL, NULL ); + errors += fail_if( msg2 != NULL, "rmr_rcv_msg returned msg when given nil context and msg" ); + + msg2 = rmr_rcv_msg( rmc, NULL ); + errors += fail_if( msg2 == NULL, "rmr_rcv_msg returned nil msg when given nil msg" ); + if( msg2 ) { + errors += fail_not_equal( msg2->state, RMR_OK, "receive given nil message did not return msg with good state" ); + } + + msg = rmr_rcv_msg( rmc, msg ); + if( msg ) { + errors += fail_if( msg->state != RMR_OK, "rmr_rcv_msg did not return an ok state" ); + errors += fail_not_equal( msg->len, 129, "rmr_rcv_msg returned message with invalid len" ); + } else { + errors += fail_if_nil( msg, "rmr_rcv_msg returned a nil pointer" ); + } + + rmr_rts_msg( NULL, NULL ); // drive for coverage + rmr_rts_msg( rmc, NULL ); + errors += fail_if( errno == 0, "rmr_rts_msg did not set errno when given a nil message" ); + + msg = rmr_rts_msg( rmc, msg ); // return the buffer to the sender + errors += fail_if_nil( msg, "rmr_rts_msg did not return a message pointer" ); + errors += fail_if( errno != 0, "rmr_rts_msg did not reset errno" ); + + + snprintf( msg->xaction, 17, "%015d", 16 ); // dummy transaction id (emulation generates, this should arrive after a few calls to recv) + msg = rmr_call( rmc, msg ); // this call should return a message as we can anticipate a dummy message in + errors += fail_if_nil( msg, "rmr_call returned a nil message on call expected to succeed" ); + if( msg ) { + errors += fail_not_equal( msg->state, RMR_OK, "rmr_call did not properly set state on successful return" ); + errors += fail_if( errno != 0, "rmr_call did not properly set errno on successful return" ); + } + + snprintf( wbuf, 17, "%015d", 14 ); // if we call receive we should find this in the first 15 tries + for( i = 0; i < 16; i++ ) { + msg = rmr_rcv_msg( rmc, msg ); + if( msg ) { + if( strcmp( wbuf, msg->xaction ) == 0 ) { // found the queued message + break; + } + fprintf( stderr, " msg: %s\n", msg->xaction ); + } else { + errors += fail_if_nil( msg, "receive returnd nil msg while looking for queued message" ); + } + } + + errors += fail_if( i >= 16, "did not find expected message on queue" ); + + if( ! msg ) { + msg = rmr_alloc_msg( rmc, 2048 ); // something buggered above; get a new one + } + msg = rmr_call( rmc, msg ); // make a call that we never expect a response on + errors += fail_not_nil( msg, "rmr_call returned a non-nil message on call expected not to receive a response" ); + if( msg ) { + errors += fail_not_equal( msg->state, RMR_OK, "rmr_call did not properly set state on queued message receive" ); + errors += fail_if( errno != 0, "rmr_call did not properly set errno on queued message receivesuccessful" ); + } + + msg = rmr_call( rmc, msg ); // this should "timeout" because the message xaction id won't ever appear again + errors += fail_not_nil( msg, "rmr_call returned a non-nil message on call expected to fail" ); + errors += fail_if( errno == 0, "rmr_call did not set errno on failure" ); + + rmr_free_msg( NULL ); // drive for coverage; nothing to check + rmr_free_msg( msg2 ); + + + // --- test timeout receive; our dummy epoll function will return 1 ready on first call and 0 ready (timeout emulation) on second + // however we must drain the swamp (queue) first, so run until we get a timeout error, or 20 and report error if we get to 20. + msg = NULL; + for( i = 0; i < 40; i++ ) { + msg = rmr_torcv_msg( rmc, msg, 10 ); + errors += fail_if_nil( msg, "torcv_msg returned nil msg when message expected" ); + if( msg ) { + if( msg->state == RMR_ERR_TIMEOUT ) { // queue drained and we've seen both states from poll if we get a timeout + break; + } + } + } + errors += fail_if( i >= 40, "torcv_msg never returned a timeout" ); + + + em_send_failures = 1; + send_n_msgs( rmc, 30 ); // send 30 messages with emulation failures + em_send_failures = 0; + + + rmr_close( NULL ); // drive for coverage + rmr_close( rmc ); // no return to check; drive for coverage + +//extern rmr_mbuf_t* rmr_mtosend_msg( void* vctx, rmr_mbuf_t* msg, int max_to ) { +//extern rmr_mbuf_t* rmr_torcv_msg( void* vctx, rmr_mbuf_t* old_msg, int ms_to ) { + + + + if( ! errors ) { + fprintf( stderr, " all RMr API tests pass\n" ); + } + return !!errors; +} diff --git a/test/rmr_nng_test.c b/test/rmr_nng_test.c new file mode 100644 index 0000000..1ebdb94 --- /dev/null +++ b/test/rmr_nng_test.c @@ -0,0 +1,130 @@ +// :vi sw=4 ts=4 noet: +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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. +================================================================================== +*/ + +/* + Mmemonic: rmr_nng_test.c + Abstract: This tests the whole rmr nng implementation. This driver + includes all of the module specific unit test static files + (e.g. wormhole_static_test.c) and drives the tests contained + there. The individual modules allow them to be driven by + a standalone driver, and to be maintained separately. We must + test by inclusion because of the static nature of the internal + functions of the library. + + Author: E. Scott Daniels + Date: 18 January 2018 (IMO HRTL) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 1 + +#include +#include +#include +#include +#include + +#define EMULATE_NNG +#include "test_nng_em.c" // nng/nn emulation (before including things under test) + + +#include "../src/common/include/rmr.h" // things the users see +#include "../src/common/include/rmr_symtab.h" +#include "../src/common/include/rmr_agnostic.h" // transport agnostic header +#include "../src/nng/include/rmr_nng_private.h" // transport specific + +#include "../src/common/src/symtab.c" +#include "../src/nng/src/rmr_nng.c" + +static void gen_rt( uta_ctx_t* ctx ); // defined in sr_nng_static_test, but used by a few others (eliminate order requirement below) + + // specific test tools in this directory +#include "test_support.c" // things like fail_if() + // and finally.... +#include "tools_static_test.c" // local test functions pulled directly because of static nature of things +#include "symtab_static_test.c" +#include "ring_static_test.c" +#include "rt_static_test.c" +#include "sr_nng_static_test.c" +#include "wormhole_static_test.c" +#include "rmr_nng_api_static_test.c" + + +/* + Drive each of the separate tests and report. +*/ +int main() { + int errors = 0; + + fprintf( stderr, " starting tool tests\n" ); + errors += tools_test(); + fprintf( stderr, " error count: %d\n", errors ); + + fprintf( stderr, " starting ring tests (%d)\n", errors ); + errors += ring_test(); + fprintf( stderr, " error count: %d\n", errors ); + + fprintf( stderr, " starting symtab tests\n" ); + errors += symtab_test( ); + fprintf( stderr, " error count: %d\n", errors ); + + fprintf( stderr, " starting rtable tests\n" ); + errors += rt_test(); // route table tests + fprintf( stderr, " error count: %d\n", errors ); + + fprintf( stderr, " starting RMr API tests\n" ); + errors += rmr_api_test(); + fprintf( stderr, " error count: %d\n", errors ); + + fprintf( stderr, " starting wormhole tests\n" ); + errors += worm_test(); // test wormhole funcitons + fprintf( stderr, " error count: %d\n", errors ); + + fprintf( stderr, " starting send/receive tests\n" ); + errors += sr_nng_test(); // test the send/receive static functions + fprintf( stderr, " error count: %d\n", errors ); + + if( errors == 0 ) { + fprintf( stderr, " all tests were OK\n" ); + } else { + fprintf( stderr, " %d modules reported errors\n", errors ); + } + + return !!errors; +} diff --git a/test/rt_static_test.c b/test/rt_static_test.c new file mode 100644 index 0000000..f1e6981 --- /dev/null +++ b/test/rt_static_test.c @@ -0,0 +1,231 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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. +================================================================================== +*/ + +/* + Mmemonic: rt_static_test.c + Abstract: Test the route table funcitons. These are meant to be included at compile + time by the test driver. + + Author: E. Scott Daniels + Date: 3 April 2019 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/common/include/rmr.h" +#include "../src/common/include/rmr_agnostic.h" + +typedef struct entry_info { + int group; + char* ep_name; +} ei_t; + + +/* + This is the main route table test. It sets up a very specific table + for testing (not via the generic setup function for other test + situations). +*/ +static int rt_test( ) { + uta_ctx_t* ctx; // context needed to test load static rt + route_table_t* rt; // route table + route_table_t* crt; // cloned route table + rtable_ent_t* rte; // entry in the table + endpoint_t* ep; // endpoint added + int more = 0; // more flag from round robin + int errors = 0; // number errors found + int i; + int k; + int mtype; + int value; + int alt_value; + ei_t entries[50]; // end point information + int gcounts[5]; // number of groups in this set + int ecounts[5]; // number of elements per group + int mtypes[5]; // msg type for each group set + char* tok; + char* nxt_tok; + int enu = 0; + int state; + char *buf; + char* seed_fname; // seed file + nng_socket nn_sock; // this is a struct in nng, so difficult to validate + + setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 ); // allow for verbose code in rtc to be driven + i = open( ".rmr_verbose", O_CREAT, 0664 ); + if( i >= 0 ) { + close( i ); + } + + gcounts[0] = 1; // build entry info -- this is hackish, but saves writing another parser + ecounts[0] = 2; + mtypes[0] = 0; + entries[enu].group = 0; entries[enu].ep_name = "yahoo.com:4561"; enu++; // use a dns resolvable name to test that + entries[enu].group = 0; entries[enu].ep_name = "localhost:4562"; enu++; // rest can default to some dummy ip + + gcounts[1] = 2; + ecounts[1] = 3; + mtypes[1] = 1; + entries[enu].group = 0; entries[enu].ep_name = "localhost:4561"; enu++; + entries[enu].group = 0; entries[enu].ep_name = "localhost:4568"; enu++; + entries[enu].group = 0; entries[enu].ep_name = "localhost:4569"; enu++; + + gcounts[2] = 0; // 0 groups means use same rte, this is the next gropup + ecounts[2] = 2; + mtypes[2] = 1; + entries[enu].group = 1; entries[enu].ep_name = "localhost:4561"; enu++; + entries[enu].group = 1; entries[enu].ep_name = "localhost:4562"; enu++; + + gcounts[3] = 1; // 0 groups means use same rte, this is the next gropup + ecounts[3] = 2; + mtypes[3] = 2; + entries[enu].group = 0; entries[enu].ep_name = "localhost:4563"; enu++; + entries[enu].group = 0; entries[enu].ep_name = "localhost:4564"; enu++; + + gcounts[4] = 1; // 0 groups means use same rte, this is the next gropup + ecounts[4] = 1; + mtypes[4] = 3; + entries[enu].group = 0; entries[enu].ep_name = "localhost:4565"; enu++; + + + + rt = uta_rt_init( ); // get us a route table + if( (errors += fail_if_nil( rt, "pointer to route table" )) ) { + fprintf( stderr, " abort: cannot continue without a route table\n" ); + exit( 1 ); + } + + enu = 0; + rte = NULL; + for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) { // add entries defined above + if( gcounts[i] ) { + rte = uta_add_rte( rt, mtypes[i], gcounts[i] ); // get/create entry for message type + if( (errors += fail_if_nil( rte, "route table entry" )) ) { + fprintf( stderr, " abort: cannot continue without a route table entry\n" ); + exit( 1 ); + } + } else { + if( rte == NULL ) { + fprintf( stderr, " internal testing error -- rte was nil for gcount == 0\n" ); + exit( 1 ); + } + } + + for( k = 0; k < ecounts[i]; k++ ) { + ep = uta_add_ep( rt, rte, entries[enu].ep_name, entries[enu].group ); + errors += fail_if_nil( ep, "endpoint" ); + enu++; + } + } + + crt = uta_rt_clone( rt ); + errors += fail_if_nil( crt, "cloned route table" ); + + ep = uta_get_ep( rt, "localhost:4561" ); + errors += fail_if_nil( ep, "end point (fetch by name)" ); + ep = uta_get_ep( rt, "bad_name:4560" ); + errors += fail_not_nil( ep, "end point (fetch by name with bad name)" ); + + state = uta_epsock_byname( rt, "localhost:4561", &nn_sock ); // this should be found + errors += fail_if_equal( state, 0, "socket (by name)" ); + //alt_value = uta_epsock_byname( rt, "localhost:4562" ); // we might do a memcmp on the two structs, but for now nothing + //errors += fail_if_equal( value, alt_value, "app1/app2 sockets" ); + + alt_value = -1; + for( i = 0; i < 10; i++ ) { // round robin return value should be different each time + value = uta_epsock_rr( rt, 1, 0, &more, &nn_sock ); // msg type 1, group 1 + errors += fail_if_equal( value, alt_value, "round robiin sockets with multiple end points" ); + errors += fail_if_false( more, "more for mtype==1" ); + alt_value = value; + } + + more = -1; + for( i = 0; i < 10; i++ ) { // this mtype has only one endpoint, so rr should be same each time + value = uta_epsock_rr( rt, 3, 0, NULL, &nn_sock ); // also test ability to deal properly with nil more pointer + if( i ) { + errors += fail_not_equal( value, alt_value, "round robin sockets with one endpoint" ); + errors += fail_not_equal( more, -1, "more value changed in single group instance" ); + } + alt_value = value; + } + + value = uta_epsock_rr( rt, 9, 0, &more, &nn_sock ); // non-existant message type; should return false (0) + errors += fail_not_equal( value, 0, "socket for bad mtype was valid" ); + + uta_rt_clone( NULL ); // verify null parms don't crash things + uta_rt_drop( NULL ); + uta_epsock_rr( NULL, 1, 0, &more, &nn_sock ); // drive null case for coverage + uta_add_rte( NULL, 99, 1 ); + + fprintf( stderr, "[INFO] test: adding end points with nil data; warnings expected\n" ); + uta_add_ep( NULL, NULL, "foo", 1 ); + uta_add_ep( rt, NULL, "foo", 1 ); + + buf = uta_fib( ".gitignore" ); + errors += fail_if_nil( buf, "buffer from read file into buffer" ); + if( buf ) { + free( buf ); + } + buf = uta_fib( "no-file" ); + errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" ); + if( buf ) { + free( buf ); + } + + uta_rt_drop( rt ); + + uta_rt_drop( crt ); + + if( (ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) )) != NULL ) { + memset( ctx, 0, sizeof( *ctx ) ); + + if( (seed_fname = getenv( "RMR_SEED_RT" )) != NULL ) { + if( ! (fail_if_nil( rt, "pointer to rt for load test" )) ) { + errors++; + read_static_rt( ctx, 0 ); + unsetenv( "RMR_SEED_RT" ); // unset to test the does not exist condition + read_static_rt( ctx, 0 ); + } else { + fprintf( stderr, " cannot gen rt for load test\n" ); + } + } else { + read_static_rt( ctx, 0 ); // not defined, just drive for that one case + } + } + + uta_fib( "no-suhch-file" ); // drive some error checking for coverage + +/* + if( ctx ) { + if( ctx->rtg_addr ) { + free( ctx->rtg_addr ); + } + free( ctx ); + } +*/ + + return !!errors; // 1 or 0 regardless of count +} diff --git a/test/sr_nng_static_test.c b/test/sr_nng_static_test.c new file mode 100644 index 0000000..69090a1 --- /dev/null +++ b/test/sr_nng_static_test.c @@ -0,0 +1,188 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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. +================================================================================== +*/ + +/* + Mmemonic: sr_nng_static_test.c + Abstract: Test the send/receive funcitons. These are meant to be included at compile + time by the test driver. + + Author: E. Scott Daniels + Date: 3 April 2019 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/common/include/rmr.h" +#include "../src/common/include/rmr_agnostic.h" + +/* + Generate a simple route table (for all but direct route table testing). +*/ +static void gen_rt( uta_ctx_t* ctx ) { + int fd; + char* rt_stuff; // strings for the route table + + rt_stuff = + "newrt|start\n" // false start to drive detection + "xxx|badentry to drive default case" + "newrt|start\n" + "rte|0|localhost:4560,localhost:4562\n" + "rte|1|localhost:4562;localhost:4561,localhost:4569\n" + "rte|2|localhost:4562\n" + "rte|4|localhost:4561\n" + "rte|5|localhost:4563\n" + "rte|6|localhost:4562\n" + "newrt|end\n"; + + fd = open( "utesting.rt", O_WRONLY | O_CREAT, 0600 ); + if( fd < 0 ) { + fprintf( stderr, " unable to open file for testing route table gen\n" ); + return; + } + + setenv( "RMR_SEED_RT", "utesting.rt", 1 ); + write( fd, rt_stuff, strlen( rt_stuff ) ); + close( fd ); + read_static_rt( ctx, 0 ); + unlink( "utesting.rt" ); +} + + +/* + Drive the send and receive functions. We also drive as much of the route + table collector as is possible without a real rtg process running somewhere. + + Send and receive functions are indirectly exercised from the rmr_nng_static_test + module as it tests the user facing send/receive/call/rts functions. These tests + should exercise specific cases for the internal functions as they will not + specifically be driven elsewhere. +*/ +static int sr_nng_test() { + uta_ctx_t* ctx; // context needed to test load static rt + uta_ctx_t* real_ctx; // real one to force odd situations for error testing + int errors = 0; // number errors found + rmr_mbuf_t* mbuf; // mbuf to send/receive + rmr_mbuf_t* mb2; // error capturing msg buf + int whid = -1; + int last_whid; + int state; + nng_socket nn_dummy_sock; // dummy needed to drive send + int size; + int i; + + //ctx = rmr_init( "tcp:4360", 2048, 0 ); // do NOT call init -- that starts the rtc thread which isn't good here + ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) ); // alloc the context manually + memset( ctx, 0, sizeof( uta_ctx_t ) ); + + ctx->mring = NULL; //uta_mk_ring( 128 ); + ctx->max_plen = RMR_MAX_RCV_BYTES + sizeof( uta_mhdr_t ); + ctx->max_mlen = ctx->max_plen + sizeof( uta_mhdr_t ); + ctx->my_name = strdup( "dummy-test" ); + uta_lookup_rtg( ctx ); + + gen_rt( ctx ); // forces a static load with some known info since we don't start the rtc() + + state = rmr_ready( NULL ); + errors += fail_if_true( state, "reported ready when given a nil context" ); + state = rmr_ready( ctx ); + errors += fail_if_false( state, "reported not ready when it should be" ); + + mbuf = rcv_msg( ctx, NULL ); + errors += fail_if_nil( mbuf, "no mbuf returned on receive test" ); + + mbuf->len = 10; + mbuf->mtype = 1; + + mb2 = clone_msg( mbuf ); + errors += fail_if_nil( mb2, "clone message returned nil pointer" ); + errors += fail_not_equal( mbuf->flags, mb2->flags, "clone did not duplicate flags" ); + errors += fail_not_equal( mbuf->alloc_len, mb2->alloc_len, "clone did not dup alloc-len" ); + errors += fail_not_equal( mbuf->state, mb2->state, "clone did not dup state" ); + rmr_free_msg( mb2 ); + + mbuf = rmr_send_msg( NULL, mbuf ); + errors += fail_if_nil( mbuf, "send with nil context but buffere didn't return buffer" ); + if( mbuf ) { + errors += fail_not_equal( mbuf->state, RMR_ERR_BADARG, "send with buffer but nil context didn't return right state" ); + } else { + mbuf = rmr_rcv_msg( ctx, NULL ); + } + + size = 2048 - sizeof( uta_mhdr_t ); // emulated nng receive allocates 2K payloads + state = rmr_payload_size( mbuf ); + errors += fail_not_equal( state, size, "payload size didn't return expected value" ); // receive should always give 4k buffer + + rmr_free_msg( mbuf ); + + + state = xlate_nng_state( NNG_EAGAIN, 99 ); + errors += fail_if( state == 99, "xlate_nng_state returned default for nng_eagain" ); + errors += fail_if( errno != EAGAIN, "xlate_nng_state did not set errno to eagain for nng_eagain" ); + + state = xlate_nng_state( NNG_ETIMEDOUT, 99 ); + errors += fail_if( state == 99, "xlate_nng_state returned default for nng_timeout" ); + errors += fail_if( errno != EAGAIN, "xlate_nng_state did not set errno to eagain for nng_timeout" ); + + state = xlate_nng_state( NNG_ENOTSUP, 99 ); + errors += fail_if( state != 99, "xlate_nng_state did not return default for nng_notsup" ); + + state = xlate_nng_state( NNG_ENOTSUP, 99 ); + errors += fail_if( state != 99, "xlate_nng_state did not return default for nng_notsup" ); + errors += fail_if( errno == 0, "xlate_nng_state did not set errno (1)" ); + + state = xlate_nng_state( NNG_EINVAL, 99 ); + errors += fail_if( state != 99, "xlate_nng_state did not return default for nng_inval" ); + errors += fail_if( errno == 0, "xlate_nng_state did not set errno (2)" ); + + state = xlate_nng_state( NNG_ENOMEM, 99 ); + errors += fail_if( state != 99, "xlate_nng_state did not return default for nng_nomem" ); + errors += fail_if( errno == 0, "xlate_nng_state did not set errno (3)" ); + + state = xlate_nng_state( NNG_ESTATE, 99 ); + errors += fail_if( state != 99, "xlate_nng_state did not return default for nng_state" ); + errors += fail_if( errno == 0, "xlate_nng_state did not set errno (4)" ); + + state = xlate_nng_state( NNG_ECLOSED, 99 ); + errors += fail_if( state != 99, "xlate_nng_state did not return default for nng_closed" ); + errors += fail_if( errno == 0, "xlate_nng_state did not set errno (5)" ); + + state = xlate_nng_state( 999, 99 ); + errors += fail_if( state != 99, "xlate_nng_state did not return default for unknown error" ); + errors += fail_if( errno == 0, "xlate_nng_state did not set errno (6)" ); + + // ---- drive rtc in a 'static' (not pthreaded) mode ----- + setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 ); // allow for verbose code in rtc to be driven + i = open( ".rmr_verbose", O_CREAT, 0664 ); + if( i >= 0 ) { + write( i, "0\n", 2 ); + close( i ); + } + ctx->shutdown = 1; // should force rtc to quit on first pass + rtc( ctx ); + + + return !!errors; +} diff --git a/test/symtab_static_test.c b/test/symtab_static_test.c new file mode 100644 index 0000000..181cd13 --- /dev/null +++ b/test/symtab_static_test.c @@ -0,0 +1,142 @@ +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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: symtab_static_test.c + Abstract: This is the static function that should be included by + any test that wants to test the symbol table. It must + be included in the compile, and not built to object. + + Date: 1 April 2019 + Author: E. Scott Daniels +*/ + +#include "../src/common/include/rmr_symtab.h" +// -- parent must include if needed #include "../src/common/src/symtab.c" + +#include "test_support.c" + +#ifndef GOOD +#define GOOD 0 +#define BAD 1 +#endif + +int symtab_state = GOOD; // overall pass/fail state 0==fail +int symtab_counter = 0; // global counter for for-each tests + +static void st_fetch( void* st, char* key, int class, int expected ) { + char* val; + + val = rmr_sym_get( st, key, class ); + if( val ) { + fprintf( stderr, "<%s> get returns key=%s val=%s\n", !expected ? "FAIL" : "OK", key, val ); + if( !expected ) { + symtab_state = BAD; + } + + } else { + fprintf( stderr, "<%s> string key st_fetch return nil\n", expected ? "FAIL" : "OK" ); + if( expected ) { + symtab_state = BAD; + } + } +} + +static void st_nfetch( void* st, int key, int expected ) { + char* val; + + val = rmr_sym_pull( st, key ); + if( val ) { + fprintf( stderr, "<%s> get returns key=%d val=%s\n", !expected ? "FAIL" : "OK", key, val ); + if( !expected ) { + symtab_state = BAD; + } + } else { + fprintf( stderr, "<%s> get return nil for key=%d\n", expected ? "FAIL" : "OK", key ); + if( expected ) { + symtab_state = BAD; + } + } +} + + +/* + Driven by foreach class -- just incr the counter. +*/ +static void each_counter( void* a, void* b, const char* c, void* d, void* e ) { + symtab_counter++; +} + +static int symtab_test( ) { + void* st; + char* foo = "foo"; + char* bar = "bar"; + char* goo = "goo"; // name not in symtab + int i; + int class = 1; + int s; + void* p; + int errors = 0; + + st = rmr_sym_alloc( 10 ); // alloc with small value to force adjustment inside + errors += fail_if_nil( st, "symtab pointer" ); + + s = rmr_sym_put( st, foo, class, bar ); // add entry with string key; returns 1 if it was inserted + errors += fail_if_false( s, "insert foo existed" ); + + s = rmr_sym_put( st, foo, class+1, bar ); // add to table with a different class + errors += fail_if_false( s, "insert foo existed" ); + + s = rmr_sym_put( st, foo, class, bar ); // inserted above, should return not inserted (0) + errors += fail_if_true( s, "insert foo existed" ); + + st_fetch( st, foo, class, 1 ); + st_fetch( st, goo, class, 0 ); // st_fetch non existant + rmr_sym_stats( st, 4 ); // early stats at verbose level 4 so chatter is minimised + rmr_sym_dump( st ); + + for( i = 2000; i < 3000; i++ ) { // bunch of dummy things to force chains in the table + rmr_sym_map( st, i, foo ); // add entry with unsigned integer key + } + rmr_sym_stats( st, 0 ); // just the small facts to verify the 1000 we stuffed in + rmr_sym_ndel( st, 2001 ); // force a numeric key delete + rmr_sym_ndel( st, 12001 ); // delete numeric key not there + + s = rmr_sym_map( st, 1234, foo ); // add known entries with unsigned integer key + errors += fail_if_false( s, "numeric add of key 1234 should not have existed" ); + s = rmr_sym_map( st, 2345, bar ); + errors += fail_if_true( s, "numeric add of key 2345 should have existed" ); + + symtab_counter = 0; + rmr_sym_foreach_class( st, 0, each_counter, NULL ); + errors += fail_if_false( symtab_counter, "expected counter after foreach to be non-zero" ); + + st_nfetch( st, 1234, 1 ); + st_nfetch( st, 2345, 1 ); + + rmr_sym_del( st, foo, 0 ); // drive for coverage + rmr_sym_stats( st, 0 ); + + rmr_sym_free( NULL ); // ensure it doesn't barf when given a nil pointer + rmr_sym_free( st ); + + return !!( errors + symtab_state ); +} + diff --git a/test/test_nng_em.c b/test/test_nng_em.c new file mode 100644 index 0000000..76fe72b --- /dev/null +++ b/test/test_nng_em.c @@ -0,0 +1,393 @@ +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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: test_nng_em.c + Abstract: A nano/NNG message emulator for testing without needing to + actually have nanomsg, nng, or external processes. + We also emulate the epoll_wait() function for controlled + poll related testing. + + This module must be directly included to be used. + Date: 11 February 2019 + Author: E. Scott Daniels +*/ + +// ---------------------- emulated nng functions --------------------------- + + +#ifndef _em_nn +#define _em_nn + +static int em_send_failures = 0; // test programme can set this to emulate eagain send failures + +// ----------- epoll emulation --------------------------------------------- + +// CAUTION: sys/epoll.h must be included before this define and function will properly compile. +#define epoll_wait em_wait +/* + Every other call returns 1 ready; alternate calls return 0 ready. + Mostly for testing the timeout receive call. First call should return + something ready and the second should return nothing ready so we can + drive both cases. +*/ +static int em_wait( int fd, void* events, int n, int to ) { + static int ready = 0; + + ready = !ready; + return ready; +} + + + +//-------------------------------------------------------------------------- +#ifdef EMULATE_NNG +struct nn_msghdr { + int boo; +}; + +static int return_value = 0; + +/* + Test app can call this to have all emulated functions return failure instead + of success. +*/ +static void en_set_retur( int rv ) { + return_value = rv; +} + + + +static int em_nng_foo() { + fprintf( stderr, "emulated functions in play" ); +} + + +/* + Simulated v1 message for receive to return. This needs to match the RMr header + so that we can fill in length, type and xaction id things. +#define MSG_VER 1 +struct em_msg { + int32_t mtype; // message type ("long" network integer) + int32_t plen; // payload length + int32_t rmr_ver; // our internal message version number + unsigned char xid[32]; // space for user transaction id or somesuch + unsigned char sid[32]; // sender ID for return to sender needs + unsigned char src[16]; // name of the sender (source) + unsigned char meid[32]; // managed element id. + struct timespec ts; // timestamp ??? +}; +*/ + +/* + v2 message; should be able to use it for everything that is set up here as + we don't add a payload even if setting a v1 type. +*/ +#define ALT_MSG_VER 1 // alternate every so often +#define MSG_VER 2 // default version to insert +struct em_msg { + int32_t mtype; // message type ("long" network integer) + int32_t plen; // payload length + int32_t rmr_ver; // our internal message version number + unsigned char xid[32]; // space for user transaction id or somesuch + unsigned char sid[32]; // sender ID for return to sender needs + unsigned char src[64]; // name of the sender (source) + unsigned char meid[32]; // managed element id. + struct timespec ts; // timestamp ??? + + // V2 extension + int32_t flags; // HFL_* constants + int32_t len0; // length of the RMr header data + int32_t len1; // length of the tracing data + int32_t len2; // length of data 1 (d1) + int32_t len3; // length of data 2 (d2) + +}; + +/* + Receive message must allocate a new buffer and return the pointer into *m. + Every 9 messages or so we'll simulate an old version message +*/ +static int em_nng_recvmsg( nng_socket s, nng_msg ** m, int i ) { + void* b; + struct em_msg* msg; + static int count = 0; // we'll simulate a message going in by dropping an rmr-ish msg with transaction id only + int trace_size = 0; + + //sleep( 1 ); + + b = (void *) malloc( 2048 ); + if( m != NULL ) { + memset( b, 0, 2048 ); + *m = (nng_msg *) b; + msg = (struct em_msg *) b; + if( count % 10 == 9 ) { + //msg->rmr_ver = htonl( MSG_VER ); + msg->rmr_ver = ALT_MSG_VER; // emulate the bug in RMr v1 + } else { + msg->rmr_ver = htonl( MSG_VER ); + } + msg->mtype = htonl( 1 ); + msg->plen = htonl( 129 ); + msg->len0 = htonl( sizeof( struct em_msg ) ); + msg->len1 = htonl( trace_size ); + snprintf( msg->xid, 32, "%015d", count++ ); // simple transaction id so we can test receive specific and ring stuff + snprintf( msg->src, 16, "localhost:4562" ); // set src id (unrealistic) so that rts() can be tested + } + + //fprintf( stderr, ">>> simulated received message: %s\n", msg->xid ); + return return_value; +} + +static void* em_msg_body( nng_msg* msg ) { + return (void *) msg; // we don't manage a real msg, so body is just the buffer we allocated +} + +static size_t em_msg_len( const nng_msg* msg ) { + if( msg ) { + return 2048; + } + + return 0; +} + + +static int em_nng_pull_open(nng_socket * s ) { + return return_value; +} +static int em_nng_pull0_open(nng_socket * s ) { + return return_value; +} +static int em_nng_listen(nng_socket s, const char * c, nng_listener * l, int i ) { + return return_value; +} +static int em_nng_close(nng_socket s ) { + return return_value; +} +static int em_nng_push0_open(nng_socket * s ) { + return return_value; +} +static int em_nng_dial(nng_socket s, const char * c, nng_dialer * d, int i ) { + //fprintf( stderr, " === simulated dialing: %s\n", c ); + return return_value; +} +static int em_nng_setopt(nng_socket s, const char * c, const void * p, size_t t ) { + return return_value; +} +static int em_nng_sub_open(nng_socket * s ) { + return return_value; +} +static int em_nng_sub0_open(nng_socket * s ) { + return return_value; +} +static int em_nng_recv(nng_socket s, void * v, size_t * t, int i ) { + return return_value; +} +static int em_nng_send( nng_socket s, void* m, int l, int f ) { + return return_value; +} + +/* + Emulate sending a message. If the global em_send_failures is set, + then every so often we fail with an EAGAIN to drive that part + of the code in RMr. +*/ +static int em_sendmsg( nng_socket s, nng_msg* m, int i ) { + static int count = 0; + + if( em_send_failures && (count++ % 15 == 14) ) { + //fprintf( stderr, ">>>> failing send\n\n" ); + return NNG_EAGAIN; + } + + return return_value; +} + +static void* em_nng_alloc( size_t len ) { + return malloc( len ); +} + +static int em_nng_msg_alloc( nng_msg** mp, size_t l ) { + void* p; + + if( !mp || return_value != 0 ) { + return -1; + } + + p = (void *) malloc( sizeof( char ) * l ); + *mp = (nng_msg *) p; + + return return_value; +} + +/* + We just free the buffer here as it was a simple malloc. +*/ +static void em_nng_free( void* p, size_t l ) { + if( p ) { + //fprintf( stderr, ">>>>> not freed: %p\n", p ); + free( p ); + } +} +static void em_nng_msg_free( void* p ) { + if( p ) { + //fprintf( stderr, ">>>>> not freed: %p\n", p ); + free( p ); + } +} + +static int em_dialer_create( void* d, nng_socket s, char* stuff ) { + //fprintf( stderr, ">>>> emulated dialer create\n\n" ); + return 0; +} + +static int em_dialer_start( nng_dialer d, int i ) { + //fprintf( stderr, ">>>> emulated dialer start\n\n" ); + return return_value; +} + + +static int em_dialer_setopt_ms( nng_dialer dialer, void* option, int ms ) { + return return_value; +} + +static int em_nng_getopt_int( nng_socket s, void* con, int* target ) { + if( target ) { + *target = 0; + } + return return_value; +} + + + +// nng redefines some of these to point directly to various 'versions' of the function (ugg, function versions, really?) +#undef nng_recvmsg +#undef nng_free +#undef nng_pull_open +#undef nng_pull0_open +#undef nng_listen +#undef nng_close +#undef nng_getopt_int +#undef nng_push0_open +#undef nng_dial +#undef nng_setopt +#undef nng_sub_open +#undef nng_sub0_open +#undef nng_recv +#undef nng_alloc + +#define nng_msg_alloc em_nng_msg_alloc +#define nng_recvmsg em_nng_recvmsg +#define nng_free em_nng_free +#define nng_free em_nng_free +#define nng_msg_free em_nng_msg_free +#define nng_pull_open em_nng_pull_open +#define nng_pull0_open em_nng_pull0_open +#define nng_listen em_nng_listen +#define nng_close em_nng_close +#define nng_getopt_int em_nng_getopt_int +#define nng_push0_open em_nng_push0_open +#define nng_dial em_nng_dial +#define nng_setopt em_nng_setopt +#define nng_sub_open em_nng_sub_open +#define nng_sub0_open em_nng_sub0_open +#define nng_recv em_nng_recv +#define nng_send em_nng_send +#define nng_sendmsg em_sendmsg +#define nng_alloc em_nng_alloc +#define nng_free em_nng_free +#define nng_dialer_setopt_ms em_dialer_setopt_ms +#define nng_dialer_start em_dialer_start +#define nng_dialer_create em_dialer_create +#define nng_msg_body em_msg_body +#define nng_msg_len em_msg_len + + +#else + + +// ----------------------- emulated nano functions -------------------------- +struct em_nn_msghdr { + int dummy; +}; + +static int em_nn_socket (int domain, int protocol ) { + static int s = 1; + + return ++s; +} + +static int em_nn_close (int s ) { + return 1; +} + +static int em_nn_setsockopt (int s, int level, int option, const void *optval, size_t optvallen ) { + return 1; +} + +static int em_nn_getsockopt (int s, int level, int option, void *optval, size_t *optvallen ) { + return 1; +} + +static int em_nn_bind (int s, const char *addr ) { +fprintf( stderr, ">>> ===== emulated bind called ====\n" ); + return 1; +} + +static int em_nn_connect (int s, const char *addr ) { + return 1; +} + +static int em_nn_shutdown (int s, int how ) { + return 1; +} + +static int em_nn_send (int s, const void *buf, size_t len, int flags ) { + return 1; +} + +static int em_nn_recv (int s, void *buf, size_t len, int flags ) { + return 1; +} + +static int em_sendmsg (int s, const struct em_nn_msghdr *msghdr, int flags ) { + return 1; +} + +static int em_nn_recvmsg (int s, struct nn_msghdr *msghdr, int flags ) { + return 1; +} + +// nanomsg +#define nn_socket em_nn_socket +#define nn_close em_nn_close +#define nn_setsockopt em_nn_setsockopt +#define nn_getsockopt em_nn_getsockopt +#define nn_bind em_nn_bind +#define nn_connect em_nn_connect +#define nn_shutdown em_nn_shutdown +#define nn_send em_nn_send +#define nn_recv em_nn_recv +#define nn_sendmsg em_nn_sendmsg +#define nn_recvmsg em_nn_recvmsg + +#endif + + +#endif diff --git a/test/test_support.c b/test/test_support.c index d9a5f47..4ed976c 100644 --- a/test/test_support.c +++ b/test/test_support.c @@ -27,10 +27,15 @@ Date: 6 January 2019 */ +#ifndef _test_support_c +#define _test_support_c + #include #include #include #include +#include +#include #ifndef BAD #define BAD 1 // these are exit codes unless user overrides @@ -81,21 +86,21 @@ static void set_signals( void ) { static int fail_if_nil( void* p, char* what ) { if( !p ) { - fprintf( stderr, "[FAIL] pointer to '%s' was nil\n", what ); + fprintf( stderr, " %s: pointer was nil\n", what ); } return p ? GOOD : BAD; } static int fail_not_nil( void* p, char* what ) { if( p ) { - fprintf( stderr, "[FAIL] pointer to '%s' was not nil\n", what ); + fprintf( stderr, " %s: pointer was not nil\n", what ); } return !p ? GOOD : BAD; } static int fail_if_false( int bv, char* what ) { if( !bv ) { - fprintf( stderr, "[FAIL] boolean was false (%d) %s\n", bv, what ); + fprintf( stderr, " %s: expected true, boolean test was false (%d)\n", what, bv ); } return bv ? GOOD : BAD; @@ -103,7 +108,7 @@ static int fail_if_false( int bv, char* what ) { static int fail_if_true( int bv, char* what ) { if( bv ) { - fprintf( stderr, "[FAIL] boolean was true (%d) %s\n", bv, what ); + fprintf( stderr, " %s: expected false, boolean test was true (%d)\n", what, bv ); } return bv ? BAD : GOOD; } @@ -114,21 +119,23 @@ static int fail_if_true( int bv, char* what ) { static int fail_if( int bv, char* what ) { if( bv ) { - fprintf( stderr, "[FAIL] boolean was true (%d) %s\n", bv, what ); + fprintf( stderr, " %s: expected false, boolean test was true (%d)\n", what, bv ); } return bv ? BAD : GOOD; } static int fail_not_equal( int a, int b, char* what ) { if( a != b ) { - fprintf( stderr, "[FAIL] %s values were not equal a=%d b=%d\n", what, a, b ); + fprintf( stderr, " %s: values were not equal a=%d b=%d\n", what, a, b ); } return a == b ? GOOD : BAD; // user may override good/bad so do NOT return a==b directly! } static int fail_if_equal( int a, int b, char* what ) { if( a == b ) { - fprintf( stderr, "[FAIL] %s values were equal a=%d b=%d\n", what, a, b ); + fprintf( stderr, " %s values were equal a=%d b=%d\n", what, a, b ); } return a != b ? GOOD : BAD; // user may override good/bad so do NOT return a==b directly! } + +#endif diff --git a/test/tools_static_test.c b/test/tools_static_test.c new file mode 100644 index 0000000..66ddeb6 --- /dev/null +++ b/test/tools_static_test.c @@ -0,0 +1,154 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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: tools_static_test.c + Abstract: Unit tests for the RMr tools module. This file is a static include + that is pulle in at compile time by the test driver. The driver is + expected to include necessary rmr*.h and test_support files before + including this file. In addition, a context struct, or dummy, must + be provided based on the type of testing being done. + + Author: E. Scott Daniels + Date: 3 April 2019 +*/ + + +static int tools_test( ) { + 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; + uta_ctx_t ctx; // context for uta_lookup test + void* if_list; + + + // ------------------ tokenise tests ----------------------------------------------------------- + dbuf = strdup( buf ); + i = uta_tokenise( dbuf, tokens, 127, ',' ); + 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] ); + errors += fail_if_nil( tokens[j], "token from buffer" ); + } + errors += fail_not_equal( strcmp( tokens[4], "Betty" ), 0, "4th token wasn't 'Betty'" ); + + free( dbuf ); + dbuf = strdup( buf ); + i = uta_tokenise( dbuf, tokens, 127, '|' ); + 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" ); + + hname = uta_h2ip( "yahoo.com" ); + errors += fail_if_nil( hname, "h2ip did not return a pointer" ); + + hname = uta_h2ip( "yahoo.com:1234" ); // should ignore the port + errors += fail_if_nil( hname, "h2ip did not return a pointer" ); + + // ------------ rtg lookup test ------------------------------------------------------------- + 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" ); + + 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" ); + + 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" ); + +/* +//==== moved out of generic tools ========== + // -------------- test link2 stuff ---------------------------------------------------------- + i = uta_link2( "bad" ); // should fail + errors += fail_if_true( i >= 0, "uta_link2 didn't fail when given bad address" ); + + i = uta_link2( "nohost:-1234" ); + errors += fail_if_true( i >= 0, "uta_link2 did not failed when given a bad (negative) port " ); + + i = uta_link2( "nohost:1234" ); // nn should go off and set things up, but it will never successd, but uta_ call should + errors += fail_if_true( i < 0, "uta_link2 failed when not expected to" ); +*/ + + // ------------ my ip stuff ----------------------------------------------------------------- + + if_list = mk_ip_list( "1235" ); + errors += fail_if_nil( if_list, "mk_ip_list returned nil pointer" ); + + i = has_myip( NULL, NULL, ',', 128 ); // should be false if pointers are nil + errors += fail_if_true( i, "has_myip returned true when given nil buffer" ); + + i = has_myip( "buffer contents not valid", NULL, ',', 128 ); // should be false if pointers are nil + errors += fail_if_true( i, "has_myip returned true when given nil list" ); + + i = has_myip( "buffer contents not valid", NULL, ',', 1 ); // should be false if max < 2 + errors += fail_if_true( i, "has_myip returned true when given small max value" ); + + i = has_myip( "buffer.contents.not.valid", if_list, ',', 128 ); // should be false as there is nothing valid in the list + errors += fail_if_true( i, "has_myip returned true when given a buffer with no valid info" ); + + + setenv( "RMR_BIND_IF", "192.168.4.30", 1 ); // drive the case where we have a hard set interface; and set known interface in list + if_list = mk_ip_list( "1235" ); + errors += fail_if_nil( if_list, "mk_ip_list with env set returned nil pointer" ); + + i = has_myip( "192.168.1.2:1235,192.168.4.30:1235,192.168.2.19:4567", if_list, ',', 128 ); // should find our ip in middle + errors += fail_if_false( i, "has_myip did not find IP in middle of list" ); + + i = has_myip( "192.168.4.30:1235,192.168.2.19:4567,192.168.2.19:2222", if_list, ',', 128 ); // should find our ip at head + errors += fail_if_false( i, "has_myip did not find IP at head of list" ); + + i = has_myip( "192.168.23.45:4444,192.168.1.2:1235,192.168.4.30:1235", if_list, ',', 128 ); // should find our ip at end + errors += fail_if_false( i, "has_myip did not find IP at tail of list" ); + + i = has_myip( "192.168.4.30:1235", if_list, ',', 128 ); // should find our ip when only in list + errors += fail_if_false( i, "has_myip did not find IP when only one in list" ); + + return !!errors; // 1 or 0 regardless of count +} diff --git a/test/tools_test.c b/test/tools_test.c index 786d2e6..a29c0e2 100644 --- a/test/tools_test.c +++ b/test/tools_test.c @@ -36,14 +36,6 @@ #include #include -/* -#include -#include -#include -#include -#include -*/ - #include "../src/common/include/rmr.h" #include "../src/common/include/rmr_agnostic.h" #include "test_support.c" // our private library of test tools @@ -148,18 +140,6 @@ int main( ) { i = uta_lookup_rtg( &ctx ); errors += fail_if_true( i, "rtg lookup returned that it found something when not expected to" ); -/* -//==== moved out of generic tools ========== - // -------------- test link2 stuff ---------------------------------------------------------- - i = uta_link2( "bad" ); // should fail - errors += fail_if_true( i >= 0, "uta_link2 didn't fail when given bad address" ); - - i = uta_link2( "nohost:-1234" ); - errors += fail_if_true( i >= 0, "uta_link2 did not failed when given a bad (negative) port " ); - - i = uta_link2( "nohost:1234" ); // nn should go off and set things up, but it will never successd, but uta_ call should - errors += fail_if_true( i < 0, "uta_link2 failed when not expected to" ); -*/ // ------------ my ip stuff ----------------------------------------------------------------- diff --git a/test/unit_test.ksh b/test/unit_test.ksh index fb5fa2a..745cf07 100755 --- a/test/unit_test.ksh +++ b/test/unit_test.ksh @@ -51,17 +51,26 @@ # but only the result of the discount test is taken into # consideration with regard to overall success. # +# Overall Pass/Fail +# By default the overall state is based only on the success +# or failure of the unit tests and NOT on the perceived +# state of coverage. If the -s (strict) option is given, then +# overall state will be failure if code coverage expectations +# are not met. +# # Date: 16 January 2018 # Author: E. Scott Daniels # ------------------------------------------------------------------------- function usage { - echo "usage: $0 [-G|-M|-C custom-command-string] [-c cov-target] [-f] [-v] [files]" + echo "usage: $0 [-G|-M|-C custom-command-string] [-c cov-target] [-f] [-F] [-v] [files]" echo " if -C is used to provide a custom build command then it must " echo " contain a %s which will be replaced with the unit test file name." echo ' e.g.: -C "mk -a %s"' echo " -c allows user to set the target coverage for a module to pass; default is 80" echo " -f forces a discount check (normally done only if coverage < target)" + echo " -F show only failures at the function level" + echo " -s strict mode; code coverage must also pass to result in a good exit code" echo " -v will write additional information to the tty and save the disccounted file if discount run or -f given" } @@ -82,7 +91,7 @@ function add_ignored_func { } ' | while read f do - iflist+="$f " + iflist="${iflist}$f " done } @@ -90,7 +99,7 @@ function add_ignored_func { # Parse the .gcov file and discount any unexecuted lines which are in if() # blocks that are testing the result of alloc/malloc calls, or testing for # nil pointers. The feeling is that these might not be possible to drive -# and shoudn't contribute to coverage deficencies. +# and shoudn't contribute to coverage deficiencies. # # In verbose mode, the .gcov file is written to stdout and any unexecuted # line which is discounted is marked with ===== replacing the ##### marking @@ -114,9 +123,11 @@ function discount_an_checks { fi awk -v module_cov_target=$mct \ + -v cfail=${cfail:-WARN} \ + -v show_all=$show_all \ -v full_name="${1}" \ -v module="${f%.*}" \ - -v chatty=$verbose \ + -v chatty=1 \ ' function spit_line( ) { if( chatty ) { @@ -201,13 +212,15 @@ function discount_an_checks { net = unexec - discount orig_cov = ((nexec-unexec)/nexec)*100 # original coverage adj_cov = ((nexec-net)/nexec)*100 # coverage after discount - pass_fail = adj_cov < module_cov_target ? "FAIL" : "PASS" + pass_fail = adj_cov < module_cov_target ? cfail : "PASS" rc = adj_cov < module_cov_target ? 1 : 0 - if( chatty ) { - printf( "[%s] %s executable=%d unexecuted=%d discounted=%d net_unex=%d cov=%d% ==> %d%%% target=%d%%\n", - pass_fail, full_name ? full_name : module, nexec, unexec, discount, net, orig_cov, adj_cov, module_cov_target ) - } else { - printf( "[%s] %d%% (%d%%) %s\n", pass_fail, adj_cov, orig_cov, full_name ? full_name : module ) + if( pass_fail == cfail || show_all ) { + if( chatty ) { + printf( "[%s] %s executable=%d unexecuted=%d discounted=%d net_unex=%d cov=%d% ==> %d%%% target=%d%%\n", + pass_fail, full_name ? full_name : module, nexec, unexec, discount, net, orig_cov, adj_cov, module_cov_target ) + } else { + printf( "[%s] %d%% (%d%%) %s\n", pass_fail, adj_cov, orig_cov, full_name ? full_name : module ) + } } exit( rc ) @@ -232,12 +245,27 @@ function get_mct { # ------------------------------------------------------------------------ +# we assume that the project has been built in the ../[.]build directory +if [[ -d ../build/lib ]] +then + export LD_LIBRARY_PATH=../build/lib +else + if [[ -d ../.build/lib ]] + then + export LD_LIBRARY_PATH=../.build/lib + else + echo "[WARN] cannot find ../[.]build/lib; things might not work" + echo "" + fi +fi + export C_INCLUDE_PATH="../src/common/include" module_cov_target=80 builder="make -B %s" # default to plain ole make verbose=0 -trigger_discount_str="FAIL" +show_all=1 # show all things -F sets to show failures only +strict=0 # -s (strict) will set; when off, coverage state ignored in final pass/fail while [[ $1 == "-"* ]] do @@ -248,9 +276,12 @@ do -c) module_cov_target=$2; shift;; -f) force_discounting=1; - trigger_discount_str="FAIL|PASS" # check all outcomes for each module + trigger_discount_str="WARN|FAIL|PASS" # check all outcomes for each module ;; + -F) show_all=0;; + + -s) strict=1;; # coverage counts toward pass/fail state -v) (( verbose++ ));; -h) usage; exit 0;; @@ -266,17 +297,35 @@ do shift done + +if (( strict )) # if in strict mode, coverage shortcomings are failures +then + cfail="FAIL" +else + cfail="WARN" +fi +if [[ -z $trigger_discount_str ]] +then + trigger_discount_str="$cfail" +fi + + if [[ -z $1 ]] then flist="" for tfile in *_test.c do - flist+="$tfile " + if [[ $tfile != *"static_test.c" ]] + then + flist="${flist}$tfile " + fi done else flist="$@" fi + +ut_errors=0 # unit test errors (not coverage errors) errors=0 for tfile in $flist do @@ -293,12 +342,18 @@ do iflist="main sig_clean_exit " # ignore external functions from our tools add_ignored_func $tfile # ignore all static functions in our test driver add_ignored_func test_support.c # ignore all static functions in our test tools + add_ignored_func test_nng_em.c # the nng/nano emulated things + for f in *_static_test.c # all static modules here + do + add_ignored_func $f + done if ! ${tfile%.c} >/tmp/PID$$.log 2>&1 then echo "[FAIL] unit test failed for: $tfile" cat /tmp/PID$$.log - continue + (( ut_errors++ )) # cause failure even if not in strict mode + continue # skip coverage tests for this fi ( @@ -306,11 +361,14 @@ do sed '/^#/ d; /^$/ d; s/^/TARGET: /' ./.targets gcov -f ${tfile%.c} | sed "s/'//g" ) | awk \ + -v cfail=$cfail \ + -v show_all=$show_all \ -v ignore_list="$iflist" \ -v module_cov_target=$module_cov_target \ -v chatty=$verbose \ ' BEGIN { + announce_target = 1; nignore = split( ignore_list, ignore, " " ) for( i = 1; i <= nignore; i++ ) { imap[ignore[i]] = 1 @@ -359,30 +417,48 @@ do pct = a[2]+0 if( file ) { + if( announce_target ) { # announce default once at start + announce_target = 0; + printf( "\n[INFO] default target coverage for modules is %d%%\n", module_cov_target ) + } + if( target[fname] ) { mct = target[fname] + announce_target = 1; } else { mct = module_cov_target } - if( chatty ) { + + if( announce_target ) { # annoucne for module if different from default printf( "[INFO] target coverage for %s is %d%%\n", fname, mct ) } + if( pct < mct ) { - printf( "[FAIL] %3d%% %s\n\n", pct, fname ) # CAUTION: write only 3 things here + printf( "[%s] %3d%% %s\n", cfail, pct, fname ) # CAUTION: write only 3 things here exit_code = 1 } else { - printf( "[PASS] %3d%% %s\n\n", pct, fname ) + printf( "[PASS] %3d%% %s\n", pct, fname ) } + + announce_target = 0; } else { - if( pct < 80 ) { + if( pct < 70 ) { printf( "[LOW] %3d%% %s\n", pct, fname ) } else { - printf( "[OK] %3d%% %s\n", pct, fname ) + if( pct < 80 ) { + printf( "[MARG] %3d%% %s\n", pct, fname ) + } else { + if( show_all ) { + printf( "[OK] %3d%% %s\n", pct, fname ) + } + } } } + } END { + printf( "\n" ); exit( exit_code ) } ' >/tmp/PID$$.log # capture output to run discount on failures @@ -390,27 +466,52 @@ do cat /tmp/PID$$.log if (( rc || force_discounting )) # didn't pass, or forcing, see if discounting helps then + show_all=1 + if (( ! verbose )) + then + echo "[INFO] checking to see if discounting improves coverage for failures listed above" + fi + egrep "$trigger_discount_str" /tmp/PID$$.log | while read state junk name do - echo "[INFO] checking to see if discounting improves coverage for $name" if ! discount_an_checks $name.gcov >/tmp/PID$$.disc then (( errors++ )) fi + tail -1 /tmp/PID$$.disc - if (( verbose )) # updated file was generated, keep here + + if (( verbose > 1 )) # updated file was generated, keep here then echo "[INFO] discounted coverage info in: ${tfile##*/}.dcov" - mv /tmp/PID$$.disc ${tfile##*/}.dcov fi + + mv /tmp/PID$$.disc ${name##*/}.dcov done fi done +state=0 # final state rm -f /tmp/PID$$.* -if (( errors )) +if (( strict )) # fail if some coverage failed too then - exit 1 + if (( errors + ut_errors )) + then + state=1 + fi +else # not strict; fail only if unit tests themselves failed + if (( ut_errors )) + then + state=1 + fi +fi + +echo"" +if (( state )) +then + echo "[FAIL] overall unit testing fails: coverage errors=$errors unit test errors=$ut_errors" +else + echo "[PASS] overall unit testing passes" fi -exit 0 +exit $state diff --git a/test/wormhole_static_test.c b/test/wormhole_static_test.c new file mode 100644 index 0000000..7a4246f --- /dev/null +++ b/test/wormhole_static_test.c @@ -0,0 +1,151 @@ +// : vi ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2019 Nokia + Copyright (c) 2018-2019 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. +================================================================================== +*/ + +/* + Mmemonic: wormhole_static.c + Abstract: Specific tests for wormhole. This module is included directly by + the test driver at compile time. + + Author: E. Scott Daniels + Date: 3 April 2019 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "../src/common/include/rmr.h" +#include "../src/common/include/rmr_agnostic.h" + + +/* + Note that the last tests in this function destroy the context and message so + any tests added MUST be ahead of those tests. +*/ +static int worm_test( ) { + uta_ctx_t* ctx; // context needed to test load static rt + char wbuf[1024]; + int errors = 0; // number errors found + int i; + + rmr_mbuf_t* mbuf; // mbuf to send to peer + int whid = -1; + int last_whid; + + ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) ); + if( ctx == NULL ) { + fail_if_nil( ctx, "could not allocate dummy context" ); + return 1; + } + memset( ctx, 0, sizeof( *ctx ) ); + ctx->my_name = strdup( "tester" ); + + gen_rt( ctx ); + + whid = rmr_wh_open( NULL, NULL ); + errors += fail_not_equal( whid, -1, "call to wh_open with invalid values did not return bad whid" ); + + + whid = rmr_wh_open( ctx, NULL ); + errors += fail_not_equal( whid, -1, "call to wh_open with invalid target did not return bad whid" ); + + whid = rmr_wh_open( ctx, "" ); + errors += fail_not_equal( whid, -1, "call to wh_open with empty target did not return bad whid" ); + + whid = rmr_wh_open( ctx, "localhost:89219" ); + errors += fail_if_equal( whid, -1, "call to wh_open with valid target failed" ); + + rmr_wh_close( ctx, 4 ); // test for coverage only; [5] should have nil pointer + rmr_wh_close( ctx, 50 ); // test for coverage only; more than allocated reference + + last_whid = whid; + whid = rmr_wh_open( ctx, "localhost:89219" ); + errors += fail_not_equal( whid, last_whid, "call to wh_open with duplicate target did not return the same whid" ); + + for( i = 0; i < 20; i++ ) { // test ability to extend the table + snprintf( wbuf, sizeof( wbuf ), "localhost:864%02d", i ); // new address for each so whid is different + whid = rmr_wh_open( ctx, wbuf ); + snprintf( wbuf, sizeof( wbuf ), "call to wh_open failed for iteration = %d", i ); + errors += fail_if_equal( whid, -1, wbuf ); + if( i ) { + snprintf( wbuf, sizeof( wbuf ), "call to wh_open for iteration = %d returned same whid: %d", i, whid ); + errors += fail_if_equal( whid, last_whid, wbuf ); + } + + last_whid = whid; + } + + rmr_wh_close( ctx, 3 ); // close one, then open a new one to verify that hole is found + whid = rmr_wh_open( ctx, "localhost:21961" ); + errors += fail_not_equal( whid, 3, "attempt to fill in a hole didn't return expected" ); + + rmr_wh_send_msg( NULL, 0, NULL ); // tests for coverage + rmr_wh_send_msg( ctx, 0, NULL ); + + mbuf = rmr_alloc_msg( ctx, 2048 ); // get an muf to pass round + errors += fail_if_nil( mbuf, "unable to allocate mbuf for send tests (giving up on send tests)" ); + while( mbuf ) { + if( !(mbuf = rmr_wh_send_msg( ctx, 50, mbuf )) ) { // test for coverage + errors += fail_if_nil( mbuf, "send didn't return an mbuf (skip rest of send tests)" ); + break; + } + + mbuf = rmr_wh_send_msg( ctx, 4, mbuf ); + errors += fail_not_equal( mbuf->state, RMR_OK, "valid wormhole send failed" ); + errors += fail_not_equal( errno, 0, "errno after valid wormhole send was not 0" ); + + rmr_wh_close( ctx, 4 ); + mbuf = rmr_wh_send_msg( ctx, 4, mbuf ); + rmr_wh_send_msg( ctx, 4, mbuf ); + errors += fail_not_equal( mbuf->state, RMR_ERR_WHID, "send on closed wormhole didn't set correct state in msg" ); + + break; + } + + + // WARNING: these tests destroy the context, so they MUST be last + if( mbuf ) { // only if we got an mbuf + errno = 0; + mbuf->header = NULL; + mbuf = rmr_wh_send_msg( ctx, 5, mbuf ); // coverage test on mbuf header check + errors += fail_not_equal( errno, EBADMSG, "wh_send didn't set errno after bad mbuf send" ); + errors += fail_not_equal( mbuf->state, RMR_ERR_NOHDR, "send with bad header did now set msg state correctly" ); + + errno = 0; + wh_nuke( ctx ); + ctx->wormholes = NULL; + mbuf = rmr_wh_send_msg( ctx, 4, mbuf ); // coverage test on mbuf header check + errors += fail_not_equal( errno, EINVAL, "wh_send didn't set errno after send without wormole reference" ); + errors += fail_not_equal( mbuf->state, RMR_ERR_NOWHOPEN, "wh_send didn't set msg state after send without wormole reference" ); + + rmr_free_msg( mbuf ); + } + + if( ctx ) { + free( ctx->my_name ); + free( ctx ); + } + + return !!errors; // 1 or 0 regardless of count +} -- 2.16.6