Address potential error state after good send

This change addresses the potential to "hide" the good
status of a send in the case where there are multiple
round-robin groups for a message type. The send algorithm
was modified to eliminate the potential edge case and
to report a successful result if the message was accepted
for delivery to at least one group.

Two issues identified by valgrind (use of a potentially
uninitialised variable in a jump) were also corrected.

Intermediate commits:
Signed-off-by: E. Scott Daniels <daniels@research.att.com>

Add error tag to msg

Missing ERR on failure message.

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

Prevent NPE in symtab

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

Adjust unit tests, fix valgrind inddicated issues

Unit tests were added to verify the new get rte function.
Valgrind indicated several places where a conditional was
using a potentially uninitialised variable; these were fixed.

Signed-off-by: E. Scott Daniels <daniels@research.att.com>
Change-Id: I7c0a49713ab3d6fbb8f66e3d286049762433dc58
diff --git a/test/rt_static_test.c b/test/rt_static_test.c
index 13a216b..6d416ef 100644
--- a/test/rt_static_test.c
+++ b/test/rt_static_test.c
@@ -79,6 +79,19 @@
 }
 
 /*
+	Builds a route table key.
+*/
+static uint64_t build_key( uint32_t mtype, uint32_t sid ) {
+	uint64_t k;
+
+	k = (uint64_t) sid << 32;
+	k += mtype;
+
+fprintf( stderr, "<INFO> build key: %x %x --> %llx\n", (int) mtype, (int) sid, (long long) k );
+	return k;
+}
+
+/*
 	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).
@@ -87,7 +100,8 @@
 	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
+	rtable_ent_t*	rte;	// route table entries from table
+	rtable_ent_t*	rte2;
 	endpoint_t*	ep;			// endpoint added
 	int more = 0;			// more flag from round robin
 	int errors = 0;			// number errors found
@@ -99,9 +113,9 @@
 	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
+	int		gcounts[7];		// number of groups in this set
+	int		ecounts[7];		// number of elements per group
+	uint64_t	mtypes[7];	// mtype/sid 'key' in the modern RMR world
 	char*	tok;
 	char*	nxt_tok;
 	int		enu = 0;
@@ -113,40 +127,62 @@
 	setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 );			// allow for verbose code in rtc to be driven
 	i = open( ".ut_rmr_verbose", O_RDWR | O_CREAT, 0644 );
 	if( i >= 0 ) {
-		write( 1, "2\n", 2 );
+		write( i, "2\n", 2 );
 		close( i );
 	}
 
-	gcounts[0] = 1;			// build entry info -- this is hackish, but saves writing another parser
+							
+	/*
+		The hacky code below calls the necessary rmr functions to create a route table
+		as though the following were read and parsed by the rmr functions. (This tests
+		the individual funcitons and avoids writing another parser, so it's not pretty.)
+	
+		mse | 0 | 0 | yahoo.com:4561,localhost:4562
+		mse | 1 | 0 | localhost:4560,localhost:4568,localhost:4569; localhost:4561,localhost:4562
+		mse | 2 | 0 | localhost:4563,localhost:4564
+		mse | 3 | 0 | localhost:4565
+		mse | 3 | 11 | locahost:5511
+		mse | 3 | -1 | localhost:5500
+	*/
+	gcounts[0] = 1;							// first entry has 1 group with 2 endpoints; message type 0, sid 0
 	ecounts[0] = 2;
-	mtypes[0] = 0;
+	mtypes[0] = build_key( 0, 0 );			// mtype is now a key of mtype/sid
 	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
+	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++;
+	gcounts[1] = 2;				// 2 groups
+	ecounts[1] = 3;				// first has 3 endpoints
+	mtypes[1] = build_key( 1, 0 );
+	entries[enu].group = 0; entries[enu].ep_name = "localhost:4560"; 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;
+	gcounts[2] = 0;					// 0 means use same rte, this is the next group for the entry
+	ecounts[2] = 2;					// 2 endpoints
+	mtypes[2] = 999;				// ignored when appending to previous entry
 	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;
+	gcounts[3] = 1;					// next entry has 1 group
+	ecounts[3] = 2;					// with 2 enpoints
+	mtypes[3] = build_key( 2, 0 );
 	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
+	gcounts[4] = 1;					// three entries for mt==3 with different sids
 	ecounts[4] = 1;
-	mtypes[4] = 3;
-	entries[enu].group = 0; entries[enu].ep_name = "localhost:4565"; enu++;
+	mtypes[4] = build_key( 3, 0 );
+	entries[enu].group = 0; entries[enu].ep_name = "localhost:5500"; enu++;
 
+	gcounts[5] = 1;
+	ecounts[5] = 1;
+	mtypes[5] = build_key( 3, 11 );
+	entries[enu].group = 0; entries[enu].ep_name = "localhost:5511"; enu++;
+
+	gcounts[6] = 1;
+	ecounts[6] = 1;
+	mtypes[6] = build_key( 3, -1 );
+	entries[enu].group = 0; entries[enu].ep_name = "localhost:5512"; enu++;
 
 
 	rt = uta_rt_init( );										// get us a route table
@@ -178,6 +214,9 @@
 		}
 	}
 
+	// ----- end hacking together a route table ---------------------------------------------------
+
+
 	crt = uta_rt_clone( rt );								// clone only the endpoint entries
 	errors += fail_if_nil( crt, "cloned route table" );
 	if( crt ) {
@@ -216,31 +255,70 @@
 	//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" );
 
+
+	// --- test that the get_rte function finds expected keys, and retries to find 'bad' sid attempts for valid mtypes with no sid
+	rte = uta_get_rte( rt, 0, 1, TRUE );			// s=0 m=1 is defined, so this should return a pointer
+	errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 true given" );
+
+	rte = uta_get_rte( rt, 0, 1, FALSE );			// the retry shouldn't apply, but ensure it does the righ thing
+	errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 false given" );
+
+	rte = uta_get_rte( rt, 1000, 1, FALSE );		// s=1000 does not exist for any msg type; should return nil as not allowed to drop sid
+	errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 false given" );
+
+	rte = uta_get_rte( rt, 1000, 1, TRUE );			// this should also fail as there is no mt==1 sid==-1 defined
+	errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 true given" );
+
+	rte = uta_get_rte( rt, 0, 3, TRUE );			// mtype sid combo does exist; true/false should not matter
+	errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=3 true given" );
+
+	rte2 = uta_get_rte( rt, 11, 3, TRUE );			// same mtype as before, different (valid) group, rte should be different than before
+	errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=11 m=3 true given" );
+	errors += fail_if_true( rte == rte2, "get_rte for mtype==3 and different sids (0 and 11) returned the same rte pointer" );
+
+	rte2 = uta_get_rte( rt, 0, 3, FALSE );			// since the mtype/sid combo exists, setting false should return the same as before
+	errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=0 m=3 false given" );
+	errors += fail_if_false( rte == rte2, "get_rte did not return same pointer when mtype/sid combo given with different true/false" );
+
+	rte = uta_get_rte( rt, 12, 3, FALSE );			// this combo does not exist and should fail when alt-key is not allowed (false)
+	errors += fail_not_nil( rte, "get_rte returned a pointer for s=12, m=3, false" );
+
+	rte = uta_get_rte( rt, 12, 3, TRUE );			// this should return the entry for the 3/-1 combination 
+	errors += fail_if_nil( rte, "get_rte did not return a pointer for s=12, m=3, true" );
+
 	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;
+	rte = uta_get_rte( rt, 0, 1, FALSE );			// get an rte for the next loop
+	if( rte ) {
+		for( i = 0; i < 10; i++ ) {									// round robin return value should be different each time
+			value = uta_epsock_rr( rte, 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" );
+	rte = uta_get_rte( rt, 0, 3, FALSE );				// get an rte for the next loop
+	if( rte ) {
+		for( i = 0; i < 10; i++ ) {								// this mtype has only one endpoint, so rr should be same each time
+			value = uta_epsock_rr( rte, 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;
 		}
-		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" );
+	rte = uta_get_rte( rt, 11, 3, TRUE );
+	state = uta_epsock_rr( rte, 22, NULL, NULL );
+	errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
 
 	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_epsock_rr( NULL, 0,  &more, &nn_sock );			// drive null case for coverage
 	uta_add_rte( NULL, 99, 1 );
+	uta_get_rte( NULL, 0, 1000, TRUE );
 
 	fprintf( stderr, "[INFO] test: adding end points with nil data; warnings expected\n" );
 	uta_add_ep( NULL, NULL, "foo", 1 );
@@ -258,25 +336,26 @@
 	}
 
 	uta_rt_drop( rt );
+	rt = NULL;
 
 	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, "<FAIL> cannot gen rt for load test\n" );
-			}
-		} else {
-			read_static_rt( ctx, 0 );		// not defined, just drive for that one case
+			read_static_rt( ctx, 0 );
+			rt = ctx->rtable;
+			errors += fail_if_nil( rt, "read seed table didn't generate a rtable pointer in context" );
+			unsetenv( "RMR_SEED_RT" );				// remove for next test
 		}
+
+		read_static_rt( ctx, 0 );			// drive for not there coverage
 	}
 
-	uta_fib( "no-suhch-file" );			// drive some error checking for coverage
+
+	buf = uta_fib( "no-suhch-file" );			// drive some error checking for coverage
+	if( buf ) {
+		free( buf );
+	}
 
 
 	ep = (endpoint_t *) malloc( sizeof( *ep ) );
@@ -286,12 +365,6 @@
 	state = uta_link2( ep );
 	errors += fail_if_true( state, "link2 did not return false when given nil pointers" );
 
-	state = uta_epsock_rr( rt, 122, 0, NULL, NULL );
-	errors += fail_if_true( state, "uta_epsock_rr returned bad state when given nil socket pointer" );
-
-	rt = uta_rt_init( );										// get us a route table
-	state = uta_epsock_rr( rt, 0, -1, NULL, &nn_sock );
-	errors += fail_if_true( state, "uta_epsock_rr returned bad state (true) when given negative group number" );
 
 	return !!errors;			// 1 or 0 regardless of count
 }