1 // : vi ts=4 sw=4 noet :
3 ==================================================================================
4 Copyright (c) 2019 Nokia
5 Copyright (c) 2018-2019 AT&T Intellectual Property.
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
11 http://www.apache.org/licenses/LICENSE-2.0
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 ==================================================================================
22 Mmemonic: rt_static_test.c
23 Abstract: Test the route table funcitons. These are meant to be included at compile
24 time by the test driver.
26 Author: E. Scott Daniels
38 #include <semaphore.h>
41 #include "rmr_agnostic.h"
43 typedef struct entry_info {
50 Driven by symtab foreach element of one space.
51 We count using the data as a counter.
53 static void count_things( void* st, void* entry, char const* name, void* thing, void* vdata ) {
57 if( (counter = (int *) vdata) != NULL ) {
64 Returns the number of entries in the table for the given class.
66 static int count_entries( route_table_t* rt, int class ) {
76 rmr_sym_foreach_class( rt->hash, class, count_things, &counter ); // run each and update counter
82 Builds a route table key.
84 static uint64_t build_key( uint32_t mtype, uint32_t sid ) {
87 k = (uint64_t) sid << 32;
90 fprintf( stderr, "<INFO> build key: %x %x --> %llx\n", (int) mtype, (int) sid, (long long) k );
95 This is the main route table test. It sets up a very specific table
96 for testing (not via the generic setup function for other test
99 static int rt_test( ) {
100 uta_ctx_t* ctx; // context needed to test load static rt
101 uta_ctx_t* pctx; // "private" context for route manager communication tests
102 route_table_t* rt; // route table
103 route_table_t* crt; // cloned route table
104 rtable_ent_t* rte; // route table entries from table
106 endpoint_t* ep; // endpoint added
108 int more = 0; // more flag from round robin
109 int errors = 0; // number errors found
112 int c1; // general counters
117 ei_t entries[50]; // end point information
118 int gcounts[7]; // number of groups in this set
119 int ecounts[7]; // number of elements per group
120 uint64_t mtypes[7]; // mtype/sid 'key' in the modern RMR world
127 char* seed_fname; // seed file
128 SOCKET_TYPE nn_sock; // differnt in each transport (nng == struct, SI/Nano == int)
129 rmr_mbuf_t* mbuf; // message for meid route testing
131 #ifndef NNG_UNDER_TEST
132 si_ctx_t* si_ctx = NULL;
135 setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 ); // allow for verbose code in rtc to be driven
136 i = open( ".ut_rmr_verbose", O_RDWR | O_CREAT, 0644 );
138 write( i, "2\n", 2 );
144 The hacky code below calls the necessary rmr functions to create a route table
145 as though the following were read and parsed by the rmr functions. (This tests
146 the individual funcitons and avoids writing another parser, so it's not pretty.)
148 mse | 0 | 0 | yahoo.com:4561,localhost:4562
149 mse | 1 | 0 | localhost:4560,localhost:4568,localhost:4569; localhost:4561,localhost:4562
150 mse | 2 | 0 | localhost:4563,localhost:4564
151 mse | 3 | 0 | localhost:4565
152 mse | 3 | 11 | locahost:5511
153 mse | 3 | -1 | localhost:5500
155 gcounts[0] = 1; // first entry has 1 group with 2 endpoints; message type 0, sid 0
157 mtypes[0] = build_key( 0, 0 ); // mtype is now a key of mtype/sid
158 entries[enu].group = 0; entries[enu].ep_name = "yahoo.com:4561"; enu++; // use a dns resolvable name to test that
159 entries[enu].group = 0; entries[enu].ep_name = "localhost:4562"; enu++; // rest can default to some dummy ip
161 gcounts[1] = 2; // 2 groups
162 ecounts[1] = 3; // first has 3 endpoints
163 mtypes[1] = build_key( 1, 0 );
164 entries[enu].group = 0; entries[enu].ep_name = "localhost:4560"; enu++;
165 entries[enu].group = 0; entries[enu].ep_name = "localhost:4568"; enu++;
166 entries[enu].group = 0; entries[enu].ep_name = "localhost:4569"; enu++;
168 gcounts[2] = 0; // 0 means use same rte, this is the next group for the entry
169 ecounts[2] = 2; // 2 endpoints
170 mtypes[2] = 999; // ignored when appending to previous entry
171 entries[enu].group = 1; entries[enu].ep_name = "localhost:4561"; enu++;
172 entries[enu].group = 1; entries[enu].ep_name = "localhost:4562"; enu++;
174 gcounts[3] = 1; // next entry has 1 group
175 ecounts[3] = 2; // with 2 enpoints
176 mtypes[3] = build_key( 2, 0 );
177 entries[enu].group = 0; entries[enu].ep_name = "localhost:4563"; enu++;
178 entries[enu].group = 0; entries[enu].ep_name = "localhost:4564"; enu++;
180 gcounts[4] = 1; // three entries for mt==3 with different sids
182 mtypes[4] = build_key( 3, 0 );
183 entries[enu].group = 0; entries[enu].ep_name = "localhost:5500"; enu++;
187 mtypes[5] = build_key( 3, 11 );
188 entries[enu].group = 0; entries[enu].ep_name = "localhost:5511"; enu++;
192 mtypes[6] = build_key( 3, -1 );
193 entries[enu].group = 0; entries[enu].ep_name = "localhost:5512"; enu++;
196 rt = uta_rt_init( NULL );
197 errors += fail_if_false( rt == NULL, "rt_init given a nil context didn't return nil" );
199 ctx = mk_dummy_ctx(); // make a dummy with rtgate mutex
200 rt = uta_rt_init( ctx ); // get us a route table
201 if( (errors += fail_if_nil( rt, "pointer to route table" )) ) {
202 fprintf( stderr, "<FAIL> abort: cannot continue without a route table\n" );
208 for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) { // add entries defined above
210 rte = uta_add_rte( rt, mtypes[i], gcounts[i] ); // get/create entry for message type
211 if( (errors += fail_if_nil( rte, "route table entry" )) ) {
212 fprintf( stderr, "<FAIL> abort: cannot continue without a route table entry\n" );
217 fprintf( stderr, "<SNAFU> internal testing error -- rte was nil for gcount == 0\n" );
222 for( k = 0; k < ecounts[i]; k++ ) {
223 ep = uta_add_ep( rt, rte, entries[enu].ep_name, entries[enu].group );
224 errors += fail_if_nil( ep, "endpoint" );
229 // ----- end hacking together a route table ---------------------------------------------------
232 crt = uta_rt_clone( ctx, rt, NULL, 0 ); // create a new rt and clone only the me entries
233 errors += fail_if_nil( crt, "cloned route table" );
235 c1 = count_entries( rt, 1 );
236 c2 = count_entries( crt, 1 );
237 errors += fail_not_equal( c1, c2, "cloned (endpoints) table entries space 1 count (b) did not match original table count (a)" );
239 c2 = count_entries( crt, 0 );
240 errors += fail_not_equal( c2, 0, "cloned (endpoints) table entries space 0 count (a) was not zero as expected" );
242 errors += fail_if_false( crt->ephash == rt->ephash, "ephash pointer in cloned table is not right" );
247 crt = uta_rt_clone( ctx, rt, NULL, 1 ); // clone all entries (MT and ME)
248 errors += fail_if_nil( crt, "cloned (all) route table" );
251 c1 = count_entries( rt, 0 );
252 c2 = count_entries( crt, 0 );
253 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 0 count (b) did not match original table count (a)" );
255 c1 = count_entries( rt, 1 );
256 c2 = count_entries( crt, 1 );
257 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 1 count (b) did not match original table count (a)" );
259 errors += fail_if_false( crt->ephash == rt->ephash, "ephash pointer in cloned table (all) is not right" );
263 #ifdef NNG_UNDER_TEST
264 if( (ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) )) != NULL ) { // get a "context" needed for si testing
265 memset( ctx, 0, sizeof( *ctx ) );
268 fprintf( stderr, "<FAIL> cannot acllocate a context, cannot continue rtable tests\n" );
272 ctx = mk_dummy_ctx();
277 ep = uta_get_ep( rt, "localhost:4561" );
278 errors += fail_if_nil( ep, "end point (fetch by name)" );
279 ep = uta_get_ep( rt, "bad_name:4560" );
280 errors += fail_not_nil( ep, "end point (fetch by name with bad name)" );
283 #ifdef NNG_UNDER_TEST
284 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, &ep ); // this should be found
286 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, &ep ); // this should be found
288 errors += fail_if_equal( state, 0, "socket (by name)" );
289 errors += fail_if_nil( ep, "epsock_byname did not populate endpoint pointer when expected to" );
290 //alt_value = uta_epsock_byname( rt, "localhost:4562" ); // we might do a memcmp on the two structs, but for now nothing
291 //errors += fail_if_equal( value, alt_value, "app1/app2 sockets" );
294 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep ); // test coverage on nil checks
296 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );
298 errors += fail_not_equal( state, 0, "socket (by name) nil check returned true" );
300 if( ep ) { // if previous test fails, cant run this
303 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, NULL ); // test coverage on nil checks
305 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, NULL );
307 errors += fail_if_equal( state, 0, "socket (by name) open ep check returned false" );
311 // --- test that the get_rte function finds expected keys, and retries to find 'bad' sid attempts for valid mtypes with no sid
312 rte = uta_get_rte( rt, 0, 1, TRUE ); // s=0 m=1 is defined, so this should return a pointer
313 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 true given" );
315 rte = uta_get_rte( rt, 0, 1, FALSE ); // the retry shouldn't apply, but ensure it does the righ thing
316 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 false given" );
318 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
319 errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 false given" );
321 rte = uta_get_rte( rt, 1000, 1, TRUE ); // this should also fail as there is no mt==1 sid==-1 defined
322 errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 true given" );
324 rte = uta_get_rte( rt, 0, 3, TRUE ); // mtype sid combo does exist; true/false should not matter
325 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=3 true given" );
327 rte2 = uta_get_rte( rt, 11, 3, TRUE ); // same mtype as before, different (valid) group, rte should be different than before
328 errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=11 m=3 true given" );
329 errors += fail_if_true( rte == rte2, "get_rte for mtype==3 and different sids (0 and 11) returned the same rte pointer" );
331 rte2 = uta_get_rte( rt, 0, 3, FALSE ); // since the mtype/sid combo exists, setting false should return the same as before
332 errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=0 m=3 false given" );
333 errors += fail_if_false( rte == rte2, "get_rte did not return same pointer when mtype/sid combo given with different true/false" );
335 rte = uta_get_rte( rt, 12, 3, FALSE ); // this combo does not exist and should fail when alt-key is not allowed (false)
336 errors += fail_not_nil( rte, "get_rte returned a pointer for s=12, m=3, false" );
338 rte = uta_get_rte( rt, 12, 3, TRUE ); // this should return the entry for the 3/-1 combination
339 errors += fail_if_nil( rte, "get_rte did not return a pointer for s=12, m=3, true" );
343 rte = uta_get_rte( rt, 0, 1, FALSE ); // get an rte for the next loop
345 for( i = 0; i < 10; i++ ) { // round robin return value should be different each time
346 #ifdef NNG_UNDER_TEST
347 value = uta_epsock_rr( rte, 0, &more, &nn_sock, &ep ); // msg type 1, group 1
349 value = uta_epsock_rr( ctx, rte, 0, &more, &nn_sock, &ep );
352 errors += fail_if_equal( value, alt_value, "round robiin sockets with multiple end points" );
353 errors += fail_if_false( more, "more for mtype==1" );
359 rte = uta_get_rte( rt, 0, 3, FALSE ); // get an rte for the next loop
361 for( i = 0; i < 10; i++ ) { // this mtype has only one endpoint, so rr should be same each time
362 #ifdef NNG_UNDER_TEST
363 value = uta_epsock_rr( rte, 0, NULL, &nn_sock, &ep ); // also test ability to deal properly with nil more pointer
365 value = uta_epsock_rr( ctx, rte, 0, NULL, &nn_sock, &ep );
369 errors += fail_not_equal( value, alt_value, "round robin sockets with one endpoint" );
370 errors += fail_not_equal( more, -1, "more value changed in single group instance" );
376 rte = uta_get_rte( rt, 11, 3, TRUE );
377 #ifdef NNG_UNDER_TEST
378 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
380 state = uta_epsock_rr( ctx, rte, 22, NULL, NULL, &ep );
382 errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
385 uta_rt_clone( ctx, NULL, NULL, 0 ); // verify null parms don't crash things
387 #ifdef NNG_UNDER_TEST
388 uta_epsock_rr( NULL, 0, &more, &nn_sock, &ep ); // drive null case for coverage
389 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
391 state = uta_epsock_rr( NULL, NULL, 0, &more, &nn_sock, &ep ); // drive null case for coverage
392 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given nil ctx" );
394 state = uta_epsock_rr( ctx, NULL, 0, &more, &nn_sock, &ep );
395 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given nil rte" );
397 state = uta_epsock_rr( ctx, rte, 10000, &more, &nn_sock, &ep );
398 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given invalid group number" );
400 uta_add_rte( NULL, 99, 1 );
401 uta_get_rte( NULL, 0, 1000, TRUE );
403 fprintf( stderr, "[INFO] test: adding end points with nil data; warnings expected\n" );
404 uta_add_ep( NULL, NULL, "foo", 1 );
405 uta_add_ep( rt, NULL, "foo", 1 );
407 buf = uta_fib( ".gitignore" );
408 errors += fail_if_nil( buf, "buffer from read file into buffer" );
412 buf = uta_fib( "no-file" );
413 errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" );
418 fprintf( stderr, ">>>>>> test is overtly dropping rt table at %p\n", rt );
424 if( (seed_fname = getenv( "RMR_SEED_RT" )) != NULL ) {
425 read_static_rt( ctx, 0 );
427 errors += fail_if_nil( rt, "read seed table didn't generate a rtable pointer in context" );
428 unsetenv( "RMR_SEED_RT" ); // remove for next test
431 read_static_rt( ctx, 0 ); // drive for not there coverage
435 buf = uta_fib( "no-suhch-file" ); // drive some error checking for coverage
441 ep = (endpoint_t *) malloc( sizeof( *ep ) );
442 memset( ep, 0, sizeof( ep ) );
443 pthread_mutex_init( &ep->gate, NULL );
444 ep->name = strdup( "worm" );
447 #ifdef NNG_UNDER_TEST
448 state = uta_link2( ep );
450 state = uta_link2( ctx, ep );
452 errors += fail_if_true( state, "link2 did not return false when given a bad target name" );
454 #ifdef NNG_UNDER_TEST
455 state = uta_link2( NULL );
457 state = uta_link2( ctx, NULL );
458 errors += fail_if_true( state, "link2 did not return false when given nil ep pointer" );
460 state = uta_link2( NULL, ep );
462 errors += fail_if_true( state, "link2 did not return false when given nil pointer" );
464 ep->name = strdup( "localhost:5512" );
466 #ifdef NNG_UNDER_TEST
467 state = uta_link2( ep ); // drive for coverage
469 state = uta_link2( ctx, ep );
471 errors += fail_if_false( state, "link2 did returned false when given open ep" );
473 #ifndef NNG_UNDER_TEST
474 ep->open = 0; // context is used only if ep not open, so to check this test close the ep
476 state = rt_link2_ep( NULL, ep );
477 errors += fail_if_true( state, "rt_link2_ep returned true when given bad context" );
479 state = rt_link2_ep( ctx, NULL );
480 errors += fail_if_true( state, "rt_link2_ep returned true when given bad ep" );
483 state = rt_link2_ep( ctx, ep );
484 errors += fail_if_false( state, "rt_link2_ep returned false when given an open ep" );
487 state = rt_link2_ep( ctx, ep );
488 errors += fail_if_false( state, "rt_link2_ep returned false when given a closed ep" );
492 errors += fail_if_true( ep->open, "uta_ep_failed didn't set open flag to false" );
497 // ----------------- test the meid support for looking up an endpoint based on the meid in the message -----
500 ctx->my_name = strdup( "my_host_name" ); // set up to load a rtable
501 ctx->my_ip = strdup( "192.168.1.30" );
502 gen_rt( ctx ); // generate a route table with meid entries and hang off ctx
504 mbuf = rmr_alloc_msg( ctx, 2048 ); // buffer to play with
506 rmr_str2meid( mbuf, "meid1" ); // id that we know is in the map
508 #ifdef NNG_UNDER_TEST
509 ep = NULL; // force to nil so we see it go non-nil
510 state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
511 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
512 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
514 rmr_str2meid( mbuf, "XXXmeid1" ); // id that we know is NOT in the map
515 state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
516 // it is NOT a valid check to test ep for nil -- epsock_mied doesn't guarentee ep is set/cleared when state is false
517 errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
519 ep = NULL; // force to nil so we see it go non-nil
520 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep );
521 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
522 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
524 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep ); // a second call to drive open == true check for coverage
525 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message; on open ep" );
526 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message; on open ep" );
528 rmr_str2meid( mbuf, "XXXmeid1" ); // id that we know is NOT in the map
529 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep );
530 // it is NOT a valid check to test ep for nil -- epsock_mied doesn't guarentee ep is set/cleared when state is false
531 errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
533 state = epsock_meid( NULL, ctx->rtable, mbuf, &nn_sock, &ep );
534 errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil context" );
536 state = epsock_meid( ctx, ctx->rtable, mbuf, NULL, &ep );
537 errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil socket pointer" );
540 // ------------ debugging and such; coverage only calls ----------------------------------------------------------
541 ep_stats( ctx->rtable, NULL, "name", NULL, NULL ); // ensure no crash when given nil pointer
542 rt_epcounts( ctx->rtable, "testing" );
543 rt_epcounts( NULL, "testing" );
545 buf = ensure_nlterm( NULL );
546 errors += fail_if_nil( buf, "ensure nlterm returned null pointer when given nil ptr" );
548 errors += fail_not_equal( strlen( buf ), 1, "ensure nlterm returned incorrect length string when given nil pointer" );
552 buf = ensure_nlterm( strdup( "x" ) ); // should return "x\n"
553 errors += fail_if_nil( buf, "ensure nlterm returned null pointer when given single char string" );
555 errors += fail_not_equal( strlen( buf ), 2, "ensure nlterm returned incorrect length string when given single char string" );
559 buf = strdup( "x\n" );
560 buf2 = ensure_nlterm( buf ); // buffer returned should be the same
561 if( fail_not_pequal( buf, buf2, "ensure nlterm returned new buffer for one char string with newline" ) ) {
567 buf = strdup( "Missing our trips to Gloria's for papossas.\n" );
568 buf2 = ensure_nlterm( buf ); // buffer returned should be the same
569 if( fail_not_pequal( buf, buf2, "ensure nlterm returned new buffer for string with newline" ) ) {
575 buf = ensure_nlterm( strdup( "Stand up and cheer!" ) ); // force addition of newline
577 errors += fail_not_equal( strcmp( buf, "Stand up and cheer!\n" ), 0, "ensure nlterm didn't add newline" );
583 // ------------- route manager request/response funcitons -------------------------------------------------------
587 smsg = rmr_alloc_msg( ctx, 1024 );
588 send_rt_ack( ctx, smsg, "123456", 0, "no reason" );
590 pctx = mk_dummy_ctx();
592 state = send_update_req( pctx, ctx );
593 errors += fail_not_equal( state, 0, "send_update_req did not return 0" );
595 ctx->rtg_whid = rmr_wh_open( ctx, "localhost:19289" );
596 state = send_update_req( pctx, ctx );
597 errors += fail_if_equal( state, 0, "send_update_req to an open whid did not return 0" );
601 // ------------- si only; fd to ep conversion functions ---------------------------------------------------------
602 #ifndef NNG_UNDER_TEST
603 ep2 = (endpoint_t *) malloc( sizeof( *ep ) );
606 fd2ep_add( ctx, 10, ep2 );
608 ep = fd2ep_get( ctx, 10 );
609 errors += fail_if_nil( ep, "fd2ep did not return pointer for known mapping" );
610 errors += fail_if_false( ep == ep2, "fd2ep did not return same pointer that was added" );
612 ep = fd2ep_get( ctx, 20 );
613 errors += fail_not_nil( ep, "fd2ep did returned a pointer for unknown mapping" );
615 ep = fd2ep_del( ctx, 10 );
616 errors += fail_if_nil( ep, "fd2ep delete did not return pointer for known mapping" );
617 errors += fail_if_false( ep == ep2, "fd2ep delete did not return same pointer that was added" );
619 ep = fd2ep_del( ctx, 20 );
620 errors += fail_not_nil( ep, "fd2ep delete returned a pointer for unknown mapping" );
623 return !!errors; // 1 or 0 regardless of count