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( ); // get us a route table
197 if( (errors += fail_if_nil( rt, "pointer to route table" )) ) {
198 fprintf( stderr, "<FAIL> abort: cannot continue without a route table\n" );
204 for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) { // add entries defined above
206 rte = uta_add_rte( rt, mtypes[i], gcounts[i] ); // get/create entry for message type
207 if( (errors += fail_if_nil( rte, "route table entry" )) ) {
208 fprintf( stderr, "<FAIL> abort: cannot continue without a route table entry\n" );
213 fprintf( stderr, "<SNAFU> internal testing error -- rte was nil for gcount == 0\n" );
218 for( k = 0; k < ecounts[i]; k++ ) {
219 ep = uta_add_ep( rt, rte, entries[enu].ep_name, entries[enu].group );
220 errors += fail_if_nil( ep, "endpoint" );
225 // ----- end hacking together a route table ---------------------------------------------------
228 crt = uta_rt_clone( rt ); // clone only the endpoint entries
229 errors += fail_if_nil( crt, "cloned route table" );
231 c1 = count_entries( rt, 1 );
232 c2 = count_entries( crt, 1 );
233 errors += fail_not_equal( c1, c2, "cloned (endpoints) table entries space 1 count (b) did not match original table count (a)" );
235 c2 = count_entries( crt, 0 );
236 errors += fail_not_equal( c2, 0, "cloned (endpoints) table entries space 0 count (a) was not zero as expected" );
241 crt = uta_rt_clone_all( rt ); // clone all entries
242 errors += fail_if_nil( crt, "cloned all route table" );
245 c1 = count_entries( rt, 0 );
246 c2 = count_entries( crt, 0 );
247 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 0 count (b) did not match original table count (a)" );
249 c1 = count_entries( rt, 1 );
250 c2 = count_entries( crt, 1 );
251 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 1 count (b) did not match original table count (a)" );
255 #ifdef NNG_UNDER_TEST
256 if( (ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) )) != NULL ) { // get a "context" needed for si testing
257 memset( ctx, 0, sizeof( *ctx ) );
260 fprintf( stderr, "<FAIL> cannot acllocate a context, cannot continue rtable tests\n" );
264 ctx = mk_dummy_ctx();
269 ep = uta_get_ep( rt, "localhost:4561" );
270 errors += fail_if_nil( ep, "end point (fetch by name)" );
271 ep = uta_get_ep( rt, "bad_name:4560" );
272 errors += fail_not_nil( ep, "end point (fetch by name with bad name)" );
275 #ifdef NNG_UNDER_TEST
276 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, &ep ); // this should be found
278 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, &ep ); // this should be found
280 errors += fail_if_equal( state, 0, "socket (by name)" );
281 errors += fail_if_nil( ep, "epsock_byname did not populate endpoint pointer when expected to" );
282 //alt_value = uta_epsock_byname( rt, "localhost:4562" ); // we might do a memcmp on the two structs, but for now nothing
283 //errors += fail_if_equal( value, alt_value, "app1/app2 sockets" );
286 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep ); // test coverage on nil checks
288 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );
290 errors += fail_not_equal( state, 0, "socket (by name) nil check returned true" );
294 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, NULL ); // test coverage on nil checks
296 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, NULL );
298 errors += fail_if_equal( state, 0, "socket (by name) open ep check returned false" );
301 // --- test that the get_rte function finds expected keys, and retries to find 'bad' sid attempts for valid mtypes with no sid
302 rte = uta_get_rte( rt, 0, 1, TRUE ); // s=0 m=1 is defined, so this should return a pointer
303 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 true given" );
305 rte = uta_get_rte( rt, 0, 1, FALSE ); // the retry shouldn't apply, but ensure it does the righ thing
306 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 false given" );
308 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
309 errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 false given" );
311 rte = uta_get_rte( rt, 1000, 1, TRUE ); // this should also fail as there is no mt==1 sid==-1 defined
312 errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 true given" );
314 rte = uta_get_rte( rt, 0, 3, TRUE ); // mtype sid combo does exist; true/false should not matter
315 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=3 true given" );
317 rte2 = uta_get_rte( rt, 11, 3, TRUE ); // same mtype as before, different (valid) group, rte should be different than before
318 errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=11 m=3 true given" );
319 errors += fail_if_true( rte == rte2, "get_rte for mtype==3 and different sids (0 and 11) returned the same rte pointer" );
321 rte2 = uta_get_rte( rt, 0, 3, FALSE ); // since the mtype/sid combo exists, setting false should return the same as before
322 errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=0 m=3 false given" );
323 errors += fail_if_false( rte == rte2, "get_rte did not return same pointer when mtype/sid combo given with different true/false" );
325 rte = uta_get_rte( rt, 12, 3, FALSE ); // this combo does not exist and should fail when alt-key is not allowed (false)
326 errors += fail_not_nil( rte, "get_rte returned a pointer for s=12, m=3, false" );
328 rte = uta_get_rte( rt, 12, 3, TRUE ); // this should return the entry for the 3/-1 combination
329 errors += fail_if_nil( rte, "get_rte did not return a pointer for s=12, m=3, true" );
333 rte = uta_get_rte( rt, 0, 1, FALSE ); // get an rte for the next loop
335 for( i = 0; i < 10; i++ ) { // round robin return value should be different each time
336 #ifdef NNG_UNDER_TEST
337 value = uta_epsock_rr( rte, 0, &more, &nn_sock, &ep ); // msg type 1, group 1
339 value = uta_epsock_rr( ctx, rte, 0, &more, &nn_sock, &ep );
342 errors += fail_if_equal( value, alt_value, "round robiin sockets with multiple end points" );
343 errors += fail_if_false( more, "more for mtype==1" );
349 rte = uta_get_rte( rt, 0, 3, FALSE ); // get an rte for the next loop
351 for( i = 0; i < 10; i++ ) { // this mtype has only one endpoint, so rr should be same each time
352 #ifdef NNG_UNDER_TEST
353 value = uta_epsock_rr( rte, 0, NULL, &nn_sock, &ep ); // also test ability to deal properly with nil more pointer
355 value = uta_epsock_rr( ctx, rte, 0, NULL, &nn_sock, &ep );
359 errors += fail_not_equal( value, alt_value, "round robin sockets with one endpoint" );
360 errors += fail_not_equal( more, -1, "more value changed in single group instance" );
366 rte = uta_get_rte( rt, 11, 3, TRUE );
367 #ifdef NNG_UNDER_TEST
368 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
370 state = uta_epsock_rr( ctx, rte, 22, NULL, NULL, &ep );
372 errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
375 uta_rt_clone( NULL ); // verify null parms don't crash things
377 #ifdef NNG_UNDER_TEST
378 uta_epsock_rr( NULL, 0, &more, &nn_sock, &ep ); // drive null case for coverage
379 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
381 state = uta_epsock_rr( NULL, NULL, 0, &more, &nn_sock, &ep ); // drive null case for coverage
382 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given nil ctx" );
384 state = uta_epsock_rr( ctx, NULL, 0, &more, &nn_sock, &ep );
385 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given nil rte" );
387 state = uta_epsock_rr( ctx, rte, 10000, &more, &nn_sock, &ep );
388 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given invalid group number" );
390 uta_add_rte( NULL, 99, 1 );
391 uta_get_rte( NULL, 0, 1000, TRUE );
393 fprintf( stderr, "[INFO] test: adding end points with nil data; warnings expected\n" );
394 uta_add_ep( NULL, NULL, "foo", 1 );
395 uta_add_ep( rt, NULL, "foo", 1 );
397 buf = uta_fib( ".gitignore" );
398 errors += fail_if_nil( buf, "buffer from read file into buffer" );
402 buf = uta_fib( "no-file" );
403 errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" );
413 if( (seed_fname = getenv( "RMR_SEED_RT" )) != NULL ) {
414 read_static_rt( ctx, 0 );
416 errors += fail_if_nil( rt, "read seed table didn't generate a rtable pointer in context" );
417 unsetenv( "RMR_SEED_RT" ); // remove for next test
420 read_static_rt( ctx, 0 ); // drive for not there coverage
424 buf = uta_fib( "no-suhch-file" ); // drive some error checking for coverage
430 ep = (endpoint_t *) malloc( sizeof( *ep ) );
431 memset( ep, 0, sizeof( ep ) );
432 pthread_mutex_init( &ep->gate, NULL );
433 ep->name = strdup( "worm" );
436 #ifdef NNG_UNDER_TEST
437 state = uta_link2( ep );
439 state = uta_link2( ctx, ep );
441 errors += fail_if_true( state, "link2 did not return false when given a bad target name" );
443 #ifdef NNG_UNDER_TEST
444 state = uta_link2( NULL );
446 state = uta_link2( ctx, NULL );
447 errors += fail_if_true( state, "link2 did not return false when given nil ep pointer" );
449 state = uta_link2( NULL, ep );
451 errors += fail_if_true( state, "link2 did not return false when given nil pointer" );
453 ep->name = strdup( "localhost:5512" );
455 #ifdef NNG_UNDER_TEST
456 state = uta_link2( ep ); // drive for coverage
458 state = uta_link2( ctx, ep );
460 errors += fail_if_false( state, "link2 did returned false when given open ep" );
462 #ifndef NNG_UNDER_TEST
463 ep->open = 0; // context is used only if ep not open, so to check this test close the ep
465 state = rt_link2_ep( NULL, ep );
466 errors += fail_if_true( state, "rt_link2_ep returned true when given bad context" );
468 state = rt_link2_ep( ctx, NULL );
469 errors += fail_if_true( state, "rt_link2_ep returned true when given bad ep" );
472 state = rt_link2_ep( ctx, ep );
473 errors += fail_if_false( state, "rt_link2_ep returned false when given an open ep" );
476 state = rt_link2_ep( ctx, ep );
477 errors += fail_if_false( state, "rt_link2_ep returned false when given a closed ep" );
481 errors += fail_if_true( ep->open, "uta_ep_failed didn't set open flag to false" );
486 // ----------------- test the meid support for looking up an endpoint based on the meid in the message -----
489 ctx->my_name = strdup( "my_host_name" ); // set up to load a rtable
490 ctx->my_ip = strdup( "192.168.1.30" );
491 gen_rt( ctx ); // generate a route table with meid entries and hang off ctx
493 mbuf = rmr_alloc_msg( ctx, 2048 ); // buffer to play with
495 rmr_str2meid( mbuf, "meid1" ); // id that we know is in the map
497 #ifdef NNG_UNDER_TEST
498 ep = NULL; // force to nil so we see it go non-nil
499 state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
500 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
501 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
503 rmr_str2meid( mbuf, "XXXmeid1" ); // id that we know is NOT in the map
504 state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
505 // it is NOT a valid check to test ep for nil -- epsock_mied doesn't guarentee ep is set/cleared when state is false
506 errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
508 ep = NULL; // force to nil so we see it go non-nil
509 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep );
510 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
511 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
513 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep ); // a second call to drive open == true check for coverage
514 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message; on open ep" );
515 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message; on open ep" );
517 rmr_str2meid( mbuf, "XXXmeid1" ); // id that we know is NOT in the map
518 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep );
519 // it is NOT a valid check to test ep for nil -- epsock_mied doesn't guarentee ep is set/cleared when state is false
520 errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
522 state = epsock_meid( NULL, ctx->rtable, mbuf, &nn_sock, &ep );
523 errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil context" );
525 state = epsock_meid( ctx, ctx->rtable, mbuf, NULL, &ep );
526 errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil socket pointer" );
529 // ------------ debugging and such; coverage only calls ----------------------------------------------------------
530 ep_stats( ctx->rtable, NULL, "name", NULL, NULL ); // ensure no crash when given nil pointer
531 rt_epcounts( ctx->rtable, "testing" );
532 rt_epcounts( NULL, "testing" );
534 buf = ensure_nlterm( NULL );
535 errors += fail_if_nil( buf, "ensure nlterm returned null pointer when given nil ptr" );
537 errors += fail_not_equal( strlen( buf ), 1, "ensure nlterm returned incorrect length string when given nil pointer" );
541 buf = ensure_nlterm( strdup( "x" ) ); // should return "x\n"
542 errors += fail_if_nil( buf, "ensure nlterm returned null pointer when given single char string" );
544 errors += fail_not_equal( strlen( buf ), 2, "ensure nlterm returned incorrect length string when given single char string" );
548 buf = strdup( "x\n" );
549 buf2 = ensure_nlterm( buf ); // buffer returned should be the same
550 if( fail_not_pequal( buf, buf2, "ensure nlterm returned new buffer for one char string with newline" ) ) {
556 buf = strdup( "Missing our trips to Gloria's for papossas.\n" );
557 buf2 = ensure_nlterm( buf ); // buffer returned should be the same
558 if( fail_not_pequal( buf, buf2, "ensure nlterm returned new buffer for string with newline" ) ) {
564 buf = ensure_nlterm( strdup( "Stand up and cheer!" ) ); // force addition of newline
566 errors += fail_not_equal( strcmp( buf, "Stand up and cheer!\n" ), 0, "ensure nlterm didn't add newline" );
572 // ------------- route manager request/response funcitons -------------------------------------------------------
576 smsg = rmr_alloc_msg( ctx, 1024 );
577 send_rt_ack( ctx, smsg, "123456", 0, "no reason" );
579 pctx = mk_dummy_ctx();
581 state = send_update_req( pctx, ctx );
582 errors += fail_not_equal( state, 0, "send_update_req did not return 0" );
584 ctx->rtg_whid = rmr_wh_open( ctx, "localhost:19289" );
585 state = send_update_req( pctx, ctx );
586 errors += fail_if_equal( state, 0, "send_update_req to an open whid did not return 0" );
590 // ------------- si only; fd to ep conversion functions ---------------------------------------------------------
591 #ifndef NNG_UNDER_TEST
592 ep2 = (endpoint_t *) malloc( sizeof( *ep ) );
595 fd2ep_add( ctx, 10, ep2 );
597 ep = fd2ep_get( ctx, 10 );
598 errors += fail_if_nil( ep, "fd2ep did not return pointer for known mapping" );
599 errors += fail_if_false( ep == ep2, "fd2ep did not return same pointer that was added" );
601 ep = fd2ep_get( ctx, 20 );
602 errors += fail_not_nil( ep, "fd2ep did returned a pointer for unknown mapping" );
604 ep = fd2ep_del( ctx, 10 );
605 errors += fail_if_nil( ep, "fd2ep delete did not return pointer for known mapping" );
606 errors += fail_if_false( ep == ep2, "fd2ep delete did not return same pointer that was added" );
608 ep = fd2ep_del( ctx, 20 );
609 errors += fail_not_nil( ep, "fd2ep delete returned a pointer for unknown mapping" );
612 return !!errors; // 1 or 0 regardless of count