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
126 char* seed_fname; // seed file
127 SOCKET_TYPE nn_sock; // differnt in each transport (nng == struct, SI/Nano == int)
128 rmr_mbuf_t* mbuf; // message for meid route testing
130 #ifndef NNG_UNDER_TEST
131 si_ctx_t* si_ctx = NULL;
134 setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 ); // allow for verbose code in rtc to be driven
135 i = open( ".ut_rmr_verbose", O_RDWR | O_CREAT, 0644 );
137 write( i, "2\n", 2 );
143 The hacky code below calls the necessary rmr functions to create a route table
144 as though the following were read and parsed by the rmr functions. (This tests
145 the individual funcitons and avoids writing another parser, so it's not pretty.)
147 mse | 0 | 0 | yahoo.com:4561,localhost:4562
148 mse | 1 | 0 | localhost:4560,localhost:4568,localhost:4569; localhost:4561,localhost:4562
149 mse | 2 | 0 | localhost:4563,localhost:4564
150 mse | 3 | 0 | localhost:4565
151 mse | 3 | 11 | locahost:5511
152 mse | 3 | -1 | localhost:5500
154 gcounts[0] = 1; // first entry has 1 group with 2 endpoints; message type 0, sid 0
156 mtypes[0] = build_key( 0, 0 ); // mtype is now a key of mtype/sid
157 entries[enu].group = 0; entries[enu].ep_name = "yahoo.com:4561"; enu++; // use a dns resolvable name to test that
158 entries[enu].group = 0; entries[enu].ep_name = "localhost:4562"; enu++; // rest can default to some dummy ip
160 gcounts[1] = 2; // 2 groups
161 ecounts[1] = 3; // first has 3 endpoints
162 mtypes[1] = build_key( 1, 0 );
163 entries[enu].group = 0; entries[enu].ep_name = "localhost:4560"; enu++;
164 entries[enu].group = 0; entries[enu].ep_name = "localhost:4568"; enu++;
165 entries[enu].group = 0; entries[enu].ep_name = "localhost:4569"; enu++;
167 gcounts[2] = 0; // 0 means use same rte, this is the next group for the entry
168 ecounts[2] = 2; // 2 endpoints
169 mtypes[2] = 999; // ignored when appending to previous entry
170 entries[enu].group = 1; entries[enu].ep_name = "localhost:4561"; enu++;
171 entries[enu].group = 1; entries[enu].ep_name = "localhost:4562"; enu++;
173 gcounts[3] = 1; // next entry has 1 group
174 ecounts[3] = 2; // with 2 enpoints
175 mtypes[3] = build_key( 2, 0 );
176 entries[enu].group = 0; entries[enu].ep_name = "localhost:4563"; enu++;
177 entries[enu].group = 0; entries[enu].ep_name = "localhost:4564"; enu++;
179 gcounts[4] = 1; // three entries for mt==3 with different sids
181 mtypes[4] = build_key( 3, 0 );
182 entries[enu].group = 0; entries[enu].ep_name = "localhost:5500"; enu++;
186 mtypes[5] = build_key( 3, 11 );
187 entries[enu].group = 0; entries[enu].ep_name = "localhost:5511"; enu++;
191 mtypes[6] = build_key( 3, -1 );
192 entries[enu].group = 0; entries[enu].ep_name = "localhost:5512"; enu++;
195 rt = uta_rt_init( ); // get us a route table
196 if( (errors += fail_if_nil( rt, "pointer to route table" )) ) {
197 fprintf( stderr, "<FAIL> abort: cannot continue without a route table\n" );
203 for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) { // add entries defined above
205 rte = uta_add_rte( rt, mtypes[i], gcounts[i] ); // get/create entry for message type
206 if( (errors += fail_if_nil( rte, "route table entry" )) ) {
207 fprintf( stderr, "<FAIL> abort: cannot continue without a route table entry\n" );
212 fprintf( stderr, "<SNAFU> internal testing error -- rte was nil for gcount == 0\n" );
217 for( k = 0; k < ecounts[i]; k++ ) {
218 ep = uta_add_ep( rt, rte, entries[enu].ep_name, entries[enu].group );
219 errors += fail_if_nil( ep, "endpoint" );
224 // ----- end hacking together a route table ---------------------------------------------------
227 crt = uta_rt_clone( rt ); // clone only the endpoint entries
228 errors += fail_if_nil( crt, "cloned route table" );
230 c1 = count_entries( rt, 1 );
231 c2 = count_entries( crt, 1 );
232 errors += fail_not_equal( c1, c2, "cloned (endpoints) table entries space 1 count (b) did not match original table count (a)" );
234 c2 = count_entries( crt, 0 );
235 errors += fail_not_equal( c2, 0, "cloned (endpoints) table entries space 0 count (a) was not zero as expected" );
240 crt = uta_rt_clone_all( rt ); // clone all entries
241 errors += fail_if_nil( crt, "cloned all route table" );
244 c1 = count_entries( rt, 0 );
245 c2 = count_entries( crt, 0 );
246 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 0 count (b) did not match original table count (a)" );
248 c1 = count_entries( rt, 1 );
249 c2 = count_entries( crt, 1 );
250 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 1 count (b) did not match original table count (a)" );
254 #ifdef NNG_UNDER_TEST
255 if( (ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) )) != NULL ) { // get a "context" needed for si testing
256 memset( ctx, 0, sizeof( *ctx ) );
259 fprintf( stderr, "<FAIL> cannot acllocate a context, cannot continue rtable tests\n" );
263 ctx = mk_dummy_ctx();
268 ep = uta_get_ep( rt, "localhost:4561" );
269 errors += fail_if_nil( ep, "end point (fetch by name)" );
270 ep = uta_get_ep( rt, "bad_name:4560" );
271 errors += fail_not_nil( ep, "end point (fetch by name with bad name)" );
274 #ifdef NNG_UNDER_TEST
275 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, &ep ); // this should be found
277 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, &ep ); // this should be found
279 errors += fail_if_equal( state, 0, "socket (by name)" );
280 errors += fail_if_nil( ep, "epsock_byname did not populate endpoint pointer when expected to" );
281 //alt_value = uta_epsock_byname( rt, "localhost:4562" ); // we might do a memcmp on the two structs, but for now nothing
282 //errors += fail_if_equal( value, alt_value, "app1/app2 sockets" );
285 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep ); // test coverage on nil checks
287 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );
289 errors += fail_not_equal( state, 0, "socket (by name) nil check returned true" );
293 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, NULL ); // test coverage on nil checks
295 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, NULL );
297 errors += fail_if_equal( state, 0, "socket (by name) open ep check returned false" );
300 // --- test that the get_rte function finds expected keys, and retries to find 'bad' sid attempts for valid mtypes with no sid
301 rte = uta_get_rte( rt, 0, 1, TRUE ); // s=0 m=1 is defined, so this should return a pointer
302 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 true given" );
304 rte = uta_get_rte( rt, 0, 1, FALSE ); // the retry shouldn't apply, but ensure it does the righ thing
305 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 false given" );
307 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
308 errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 false given" );
310 rte = uta_get_rte( rt, 1000, 1, TRUE ); // this should also fail as there is no mt==1 sid==-1 defined
311 errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 true given" );
313 rte = uta_get_rte( rt, 0, 3, TRUE ); // mtype sid combo does exist; true/false should not matter
314 errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=3 true given" );
316 rte2 = uta_get_rte( rt, 11, 3, TRUE ); // same mtype as before, different (valid) group, rte should be different than before
317 errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=11 m=3 true given" );
318 errors += fail_if_true( rte == rte2, "get_rte for mtype==3 and different sids (0 and 11) returned the same rte pointer" );
320 rte2 = uta_get_rte( rt, 0, 3, FALSE ); // since the mtype/sid combo exists, setting false should return the same as before
321 errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=0 m=3 false given" );
322 errors += fail_if_false( rte == rte2, "get_rte did not return same pointer when mtype/sid combo given with different true/false" );
324 rte = uta_get_rte( rt, 12, 3, FALSE ); // this combo does not exist and should fail when alt-key is not allowed (false)
325 errors += fail_not_nil( rte, "get_rte returned a pointer for s=12, m=3, false" );
327 rte = uta_get_rte( rt, 12, 3, TRUE ); // this should return the entry for the 3/-1 combination
328 errors += fail_if_nil( rte, "get_rte did not return a pointer for s=12, m=3, true" );
332 rte = uta_get_rte( rt, 0, 1, FALSE ); // get an rte for the next loop
334 for( i = 0; i < 10; i++ ) { // round robin return value should be different each time
335 #ifdef NNG_UNDER_TEST
336 value = uta_epsock_rr( rte, 0, &more, &nn_sock, &ep ); // msg type 1, group 1
338 value = uta_epsock_rr( ctx, rte, 0, &more, &nn_sock, &ep );
341 errors += fail_if_equal( value, alt_value, "round robiin sockets with multiple end points" );
342 errors += fail_if_false( more, "more for mtype==1" );
348 rte = uta_get_rte( rt, 0, 3, FALSE ); // get an rte for the next loop
350 for( i = 0; i < 10; i++ ) { // this mtype has only one endpoint, so rr should be same each time
351 #ifdef NNG_UNDER_TEST
352 value = uta_epsock_rr( rte, 0, NULL, &nn_sock, &ep ); // also test ability to deal properly with nil more pointer
354 value = uta_epsock_rr( ctx, rte, 0, NULL, &nn_sock, &ep );
358 errors += fail_not_equal( value, alt_value, "round robin sockets with one endpoint" );
359 errors += fail_not_equal( more, -1, "more value changed in single group instance" );
365 rte = uta_get_rte( rt, 11, 3, TRUE );
366 #ifdef NNG_UNDER_TEST
367 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
369 state = uta_epsock_rr( ctx, rte, 22, NULL, NULL, &ep );
371 errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
374 uta_rt_clone( NULL ); // verify null parms don't crash things
376 #ifdef NNG_UNDER_TEST
377 uta_epsock_rr( NULL, 0, &more, &nn_sock, &ep ); // drive null case for coverage
378 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
380 state = uta_epsock_rr( NULL, NULL, 0, &more, &nn_sock, &ep ); // drive null case for coverage
381 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given nil ctx" );
383 state = uta_epsock_rr( ctx, NULL, 0, &more, &nn_sock, &ep );
384 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given nil rte" );
386 state = uta_epsock_rr( ctx, rte, 10000, &more, &nn_sock, &ep );
387 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given invalid group number" );
389 uta_add_rte( NULL, 99, 1 );
390 uta_get_rte( NULL, 0, 1000, TRUE );
392 fprintf( stderr, "[INFO] test: adding end points with nil data; warnings expected\n" );
393 uta_add_ep( NULL, NULL, "foo", 1 );
394 uta_add_ep( rt, NULL, "foo", 1 );
396 buf = uta_fib( ".gitignore" );
397 errors += fail_if_nil( buf, "buffer from read file into buffer" );
401 buf = uta_fib( "no-file" );
402 errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" );
412 if( (seed_fname = getenv( "RMR_SEED_RT" )) != NULL ) {
413 read_static_rt( ctx, 0 );
415 errors += fail_if_nil( rt, "read seed table didn't generate a rtable pointer in context" );
416 unsetenv( "RMR_SEED_RT" ); // remove for next test
419 read_static_rt( ctx, 0 ); // drive for not there coverage
423 buf = uta_fib( "no-suhch-file" ); // drive some error checking for coverage
429 ep = (endpoint_t *) malloc( sizeof( *ep ) );
430 memset( ep, 0, sizeof( ep ) );
431 pthread_mutex_init( &ep->gate, NULL );
432 ep->name = strdup( "worm" );
435 #ifdef NNG_UNDER_TEST
436 state = uta_link2( ep );
438 state = uta_link2( ctx, ep );
440 errors += fail_if_true( state, "link2 did not return false when given a bad target name" );
442 #ifdef NNG_UNDER_TEST
443 state = uta_link2( NULL );
445 state = uta_link2( ctx, NULL );
446 errors += fail_if_true( state, "link2 did not return false when given nil ep pointer" );
448 state = uta_link2( NULL, ep );
450 errors += fail_if_true( state, "link2 did not return false when given nil pointer" );
452 ep->name = strdup( "localhost:5512" );
454 #ifdef NNG_UNDER_TEST
455 state = uta_link2( ep ); // drive for coverage
457 state = uta_link2( ctx, ep );
459 errors += fail_if_false( state, "link2 did returned false when given open ep" );
461 #ifndef NNG_UNDER_TEST
462 ep->open = 0; // context is used only if ep not open, so to check this test close the ep
464 state = rt_link2_ep( NULL, ep );
465 errors += fail_if_true( state, "rt_link2_ep returned true when given bad context" );
467 state = rt_link2_ep( ctx, NULL );
468 errors += fail_if_true( state, "rt_link2_ep returned true when given bad ep" );
471 state = rt_link2_ep( ctx, ep );
472 errors += fail_if_false( state, "rt_link2_ep returned false when given an open ep" );
475 state = rt_link2_ep( ctx, ep );
476 errors += fail_if_false( state, "rt_link2_ep returned false when given a closed ep" );
480 errors += fail_if_true( ep->open, "uta_ep_failed didn't set open flag to false" );
485 // ----------------- test the meid support for looking up an endpoint based on the meid in the message -----
488 ctx->my_name = strdup( "my_host_name" ); // set up to load a rtable
489 ctx->my_ip = strdup( "192.168.1.30" );
490 gen_rt( ctx ); // generate a route table with meid entries and hang off ctx
492 mbuf = rmr_alloc_msg( ctx, 2048 ); // buffer to play with
494 rmr_str2meid( mbuf, "meid1" ); // id that we know is in the map
496 #ifdef NNG_UNDER_TEST
497 ep = NULL; // force to nil so we see it go non-nil
498 state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
499 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
500 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
502 rmr_str2meid( mbuf, "XXXmeid1" ); // id that we know is NOT in the map
503 state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
504 // it is NOT a valid check to test ep for nil -- epsock_mied doesn't guarentee ep is set/cleared when state is false
505 errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
507 ep = NULL; // force to nil so we see it go non-nil
508 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep );
509 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
510 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
512 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep ); // a second call to drive open == true check for coverage
513 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message; on open ep" );
514 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message; on open ep" );
516 rmr_str2meid( mbuf, "XXXmeid1" ); // id that we know is NOT in the map
517 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep );
518 // it is NOT a valid check to test ep for nil -- epsock_mied doesn't guarentee ep is set/cleared when state is false
519 errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
521 state = epsock_meid( NULL, ctx->rtable, mbuf, &nn_sock, &ep );
522 errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil context" );
524 state = epsock_meid( ctx, ctx->rtable, mbuf, NULL, &ep );
525 errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil socket pointer" );
528 // ------------ debugging and such; coverage only calls ----------------------------------------------------------
529 ep_stats( ctx->rtable, NULL, "name", NULL, NULL ); // ensure no crash when given nil pointer
530 rt_epcounts( ctx->rtable, "testing" );
531 rt_epcounts( NULL, "testing" );
533 buf = ensure_nlterm( strdup( "Stand up and cheer!" ) ); // force addition of newline
535 errors += fail_not_equal( strcmp( buf, "Stand up and cheer!\n" ), 0, "ensure nlterm didn't add newline" );
541 // ------------- route manager request/response funcitons -------------------------------------------------------
545 smsg = rmr_alloc_msg( ctx, 1024 );
546 send_rt_ack( ctx, smsg, "123456", 0, "no reason" );
548 pctx = mk_dummy_ctx();
550 state = send_update_req( pctx, ctx );
551 errors += fail_not_equal( state, 0, "send_update_req did not return 0" );
553 ctx->rtg_whid = rmr_wh_open( ctx, "localhost:19289" );
554 state = send_update_req( pctx, ctx );
555 errors += fail_if_equal( state, 0, "send_update_req to an open whid did not return 0" );
559 // ------------- si only; fd to ep conversion functions ---------------------------------------------------------
560 #ifndef NNG_UNDER_TEST
561 ep2 = (endpoint_t *) malloc( sizeof( *ep ) );
564 fd2ep_add( ctx, 10, ep2 );
566 ep = fd2ep_get( ctx, 10 );
567 errors += fail_if_nil( ep, "fd2ep did not return pointer for known mapping" );
568 errors += fail_if_false( ep == ep2, "fd2ep did not return same pointer that was added" );
570 ep = fd2ep_get( ctx, 20 );
571 errors += fail_not_nil( ep, "fd2ep did returned a pointer for unknown mapping" );
573 ep = fd2ep_del( ctx, 10 );
574 errors += fail_if_nil( ep, "fd2ep delete did not return pointer for known mapping" );
575 errors += fail_if_false( ep == ep2, "fd2ep delete did not return same pointer that was added" );
577 ep = fd2ep_del( ctx, 20 );
578 errors += fail_not_nil( ep, "fd2ep delete returned a pointer for unknown mapping" );
581 return !!errors; // 1 or 0 regardless of count