Ensure RT incremental update not applied early
[ric-plt/lib/rmr.git] / test / rt_static_test.c
1
2 // : vi ts=4 sw=4 noet :
3 /*
4 ==================================================================================
5             Copyright (c) 2019 Nokia
6             Copyright (c) 2018-2019 AT&T Intellectual Property.
7
8    Licensed under the Apache License, Version 2.0 (the "License");
9    you may not use this file except in compliance with the License.
10    You may obtain a copy of the License at
11
12            http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19 ==================================================================================
20 */
21
22 /*
23         Mmemonic:       rt_static_test.c
24         Abstract:       Test the route table funcitons. These are meant to be included at compile
25                                 time by the test driver.
26
27         Author:         E. Scott Daniels
28         Date:           3 April 2019
29 */
30
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <strings.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <stdint.h>
38 #include <pthread.h>
39 #include <semaphore.h>
40
41 #include "rmr.h"
42 #include "rmr_agnostic.h"
43
44 typedef struct entry_info {
45         int group;
46         char* ep_name;
47 } ei_t;
48
49
50 /*
51         Driven by symtab foreach element of one space.
52         We count using the data as a counter.
53 */
54 static void count_things( void* st, void* entry, char const* name, void* thing, void* vdata ) {
55         int* counter;
56
57         if( thing ) {
58                 if( (counter = (int *) vdata) != NULL ) {
59                         *counter++;
60                 }
61         }
62 }
63
64 /*
65         Returns the number of entries in the table for the given class.
66 */
67 static int count_entries( route_table_t* rt, int class ) {
68         int counter = 0;
69
70         if( ! rt ) {
71                 return 0;
72         }
73         if( !rt->hash ) {
74                 return 0;
75         }
76
77         rmr_sym_foreach_class( rt->hash, class, count_things, &counter );       // run each and update counter
78
79         return counter;
80 }
81
82 /*
83         Builds a route table key.
84 */
85 static uint64_t build_key( uint32_t mtype, uint32_t sid ) {
86         uint64_t k;
87
88         k = (uint64_t) sid << 32;
89         k += mtype;
90
91         fprintf( stderr, "<INFO> build key: %x %x --> %llx\n", (int) mtype, (int) sid, (long long) k );
92         return k;
93 }
94
95 /*
96         Create a very large set of things to clone and ensure that the colleciton
97         buffers are properly resized without errors.
98 */
99 static int lg_clone_test( ) {
100         int             errors = 0;
101         uta_ctx_t*      ctx;
102         char*   old_env;
103         route_table_t*  p;
104
105         old_env = getenv( "RMR_SEED_RT" );
106         setenv( "RMR_SEED_RT", "./large_meid.rt", 1 );
107
108         ctx = mk_dummy_ctx();
109
110         read_static_rt( ctx, 0 );
111         p = uta_rt_clone( ctx, ctx->rtable, NULL, 1 );                                          // clone to force the copy from the existing table
112         errors += fail_if_nil( p, "clone of large table returned nil" );
113         if( p != NULL ) {
114                 errors += fail_not_equal( p->error, 0, "clone of large table had error" );
115         }
116
117         setenv( "RMR_SEED_RT", old_env, 1 );
118
119         return errors;
120 }
121
122 /*
123         This is the main route table test. It sets up a very specific table
124         for testing (not via the generic setup function for other test
125         situations).
126 */
127 static int rt_test( ) {
128         uta_ctx_t* ctx;                 // context needed to test load static rt
129         uta_ctx_t* pctx;                // "private" context for route manager communication tests
130         route_table_t* rt;              // route table
131         route_table_t* crt;             // cloned route table
132         rtable_ent_t*   rte;    // route table entries from table
133         rtable_ent_t*   rte2;
134         endpoint_t*     ep;                     // endpoint added
135         endpoint_t*     ep2;
136         int more = 0;                   // more flag from round robin
137         int errors = 0;                 // number errors found
138         int     i;
139         int k;
140         int     c1;                                     // general counters
141         int c2;
142         int mtype;
143         int value;
144         int alt_value;
145         ei_t    entries[50];    // end point information
146         int             gcounts[7];             // number of groups in this set
147         int             ecounts[7];             // number of elements per group
148         uint64_t        mtypes[7];      // mtype/sid 'key' in the modern RMR world
149         char*   tok;
150         char*   nxt_tok;
151         int             enu = 0;
152         int             state;
153         char    *buf;
154         char    *buf2;
155         char*   seed_fname;             // seed file
156         SOCKET_TYPE     nn_sock;        // differnt in each transport (nng == struct, SI/Nano == int)
157         rmr_mbuf_t*     mbuf;           // message for meid route testing
158         void*   p;                              // generic pointer
159
160         #ifndef NNG_UNDER_TEST
161                 si_ctx_t* si_ctx = NULL;
162         #endif
163
164         setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 );                     // allow for verbose code in rtc to be driven
165         i = open( ".ut_rmr_verbose", O_RDWR | O_CREAT, 0644 );
166         if( i >= 0 ) {
167                 write( i, "2\n", 2 );
168                 close( i );
169         }
170
171
172         /*
173                 The hacky code below calls the necessary rmr functions to create a route table
174                 as though the following were read and parsed by the rmr functions. (This tests
175                 the individual funcitons and avoids writing another parser, so it's not pretty.)
176
177                 mse | 0 | 0 | yahoo.com:4561,localhost:4562
178                 mse | 1 | 0 | localhost:4560,localhost:4568,localhost:4569; localhost:4561,localhost:4562
179                 mse | 2 | 0 | localhost:4563,localhost:4564
180                 mse | 3 | 0 | localhost:4565
181                 mse | 3 | 11 | locahost:5511
182                 mse | 3 | -1 | localhost:5500
183         */
184         gcounts[0] = 1;                                                 // first entry has 1 group with 2 endpoints; message type 0, sid 0
185         ecounts[0] = 2;
186         mtypes[0] = build_key( 0, 0 );                  // mtype is now a key of mtype/sid
187         entries[enu].group = 0; entries[enu].ep_name = "yahoo.com:4561"; enu++;         // use a dns resolvable name to test that
188         entries[enu].group = 0; entries[enu].ep_name = "localhost:4562"; enu++;         // rest can default to some dummy ip
189
190         gcounts[1] = 2;                         // 2 groups
191         ecounts[1] = 3;                         // first has 3 endpoints
192         mtypes[1] = build_key( 1, 0 );
193         entries[enu].group = 0; entries[enu].ep_name = "localhost:4560"; enu++;
194         entries[enu].group = 0; entries[enu].ep_name = "localhost:4568"; enu++;
195         entries[enu].group = 0; entries[enu].ep_name = "localhost:4569"; enu++;
196
197         gcounts[2] = 0;                                 // 0 means use same rte, this is the next group for the entry
198         ecounts[2] = 2;                                 // 2 endpoints
199         mtypes[2] = 999;                                // ignored when appending to previous entry
200         entries[enu].group = 1; entries[enu].ep_name = "localhost:4561"; enu++;
201         entries[enu].group = 1; entries[enu].ep_name = "localhost:4562"; enu++;
202
203         gcounts[3] = 1;                                 // next entry has 1 group
204         ecounts[3] = 2;                                 // with 2 enpoints
205         mtypes[3] = build_key( 2, 0 );
206         entries[enu].group = 0; entries[enu].ep_name = "localhost:4563"; enu++;
207         entries[enu].group = 0; entries[enu].ep_name = "localhost:4564"; enu++;
208
209         gcounts[4] = 1;                                 // three entries for mt==3 with different sids
210         ecounts[4] = 1;
211         mtypes[4] = build_key( 3, 0 );
212         entries[enu].group = 0; entries[enu].ep_name = "localhost:5500"; enu++;
213
214         gcounts[5] = 1;
215         ecounts[5] = 1;
216         mtypes[5] = build_key( 3, 11 );
217         entries[enu].group = 0; entries[enu].ep_name = "localhost:5511"; enu++;
218
219         gcounts[6] = 1;
220         ecounts[6] = 1;
221         mtypes[6] = build_key( 3, -1 );
222         entries[enu].group = 0; entries[enu].ep_name = "localhost:5512"; enu++;
223
224
225         rt = uta_rt_init( NULL );
226         errors += fail_if_false( rt == NULL, "rt_init given a nil context didn't return nil" );
227
228         ctx = mk_dummy_ctx();           // make a dummy with rtgate mutex
229         rt = uta_rt_init( ctx );                                                                                // get us a route table
230         if( (errors += fail_if_nil( rt, "pointer to route table" )) ) {
231                 fprintf( stderr, "<FAIL> abort: cannot continue without a route table\n" );
232                 exit( 1 );
233         }
234
235         enu = 0;
236         rte = NULL;
237         for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) {                                // add entries defined above
238                 if( gcounts[i] ) {
239                         rte = uta_add_rte( rt, mtypes[i], gcounts[i] );                                 // get/create entry for message type
240                         if( (errors += fail_if_nil( rte, "route table entry" )) ) {
241                                 fprintf( stderr, "<FAIL> abort: cannot continue without a route table entry\n" );
242                                 exit( 1 );
243                         }
244                 } else {
245                         if( rte == NULL ) {
246                                 fprintf( stderr, "<SNAFU> internal testing error -- rte was nil for gcount == 0\n" );
247                                 exit( 1 );
248                         }
249                 }
250
251                 for( k = 0; k < ecounts[i]; k++ ) {
252                         ep = uta_add_ep( rt, rte, entries[enu].ep_name, entries[enu].group );
253                         errors += fail_if_nil( ep, "endpoint" );
254                         enu++;
255                 }
256         }
257
258         // ----- end hacking together a route table ---------------------------------------------------
259
260
261         crt = uta_rt_clone( ctx, rt, NULL, 0 );                                                         // create a new rt and clone only the me entries
262         errors += fail_if_nil( crt, "cloned route table" );
263         if( crt ) {
264                 c1 = count_entries( rt, 1 );
265                 c2 = count_entries( crt, 1 );
266                 errors += fail_not_equal( c1, c2, "cloned (endpoints) table entries space 1 count (b) did not match original table count (a)" );
267
268                 c2 = count_entries( crt, 0 );
269                 errors += fail_not_equal( c2, 0, "cloned (endpoints) table entries space 0 count (a) was not zero as expected" );
270
271                 errors += fail_if_false( crt->ephash == rt->ephash, "ephash pointer in cloned table is not right" );
272                 uta_rt_drop( crt );
273         }
274
275
276         crt = uta_rt_clone( ctx, rt, NULL, 1 );                                                 // clone all entries (MT and ME)
277         errors += fail_if_nil( crt, "cloned (all) route table" );
278
279         if( crt ) {
280                 c1 = count_entries( rt, 0 );
281                 c2 = count_entries( crt, 0 );
282                 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 0 count (b) did not match original table count (a)" );
283
284                 c1 = count_entries( rt, 1 );
285                 c2 = count_entries( crt, 1 );
286                 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 1 count (b) did not match original table count (a)" );
287
288                 errors += fail_if_false( crt->ephash == rt->ephash, "ephash pointer in cloned table (all) is not right" );
289                 uta_rt_drop( crt );
290         }
291
292         #ifdef NNG_UNDER_TEST
293                 if( (ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) )) != NULL ) {             // get a "context" needed for si testing
294                         memset( ctx, 0, sizeof( *ctx ) );
295                         ctx->rtable = rt;
296                 } else {
297                         fprintf( stderr, "<FAIL> cannot acllocate a context, cannot continue rtable tests\n" );
298                         return errors;
299                 }
300         #else
301                 ctx = mk_dummy_ctx();
302         #endif
303
304         ctx->rtable = rt;
305
306         ep = uta_get_ep( rt, "localhost:4561" );
307         errors += fail_if_nil( ep, "end point (fetch by name)" );
308         ep = uta_get_ep( rt, "bad_name:4560" );
309         errors += fail_not_nil( ep, "end point (fetch by name with bad name)" );
310
311         ep = NULL;
312         #ifdef NNG_UNDER_TEST
313                 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, &ep );               // this should be found
314         #else
315                 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, &ep );              // this should be found
316         #endif
317         errors += fail_if_equal( state, 0, "socket (by name)" );
318         errors += fail_if_nil( ep, "epsock_byname did not populate endpoint pointer when expected to" );
319         //alt_value = uta_epsock_byname( rt, "localhost:4562" );                        // we might do a memcmp on the two structs, but for now nothing
320         //errors += fail_if_equal( value, alt_value, "app1/app2 sockets" );
321
322         #if  NNG_UNDER_TEST
323                 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );             // test coverage on nil checks
324         #else
325                 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );
326                 errors += fail_not_equal( state, 0, "socket (by name) nil context check returned true" );
327
328                 p = ctx->si_ctx;
329                 ctx->si_ctx = NULL;             // set to drive second test
330                 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, &ep );
331                 ctx->si_ctx = p;
332         #endif
333         errors += fail_not_equal( state, 0, "socket (by name) nil check returned true" );
334
335         if( ep ) {                                      // if previous test fails, cant run this
336                 ep->open = 1;
337                 #if  NNG_UNDER_TEST
338                         state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, NULL );              // test coverage on nil checks
339                 #else
340                         state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, NULL );
341                 #endif
342                 errors += fail_if_equal( state, 0, "socket (by name) open ep check returned false" );
343         }
344
345
346         // --- test that the get_rte function finds expected keys, and retries to find 'bad' sid attempts for valid mtypes with no sid
347         rte = uta_get_rte( rt, 0, 1, TRUE );                    // s=0 m=1 is defined, so this should return a pointer
348         errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 true given" );
349
350         rte = uta_get_rte( rt, 0, 1, FALSE );                   // the retry shouldn't apply, but ensure it does the righ thing
351         errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 false given" );
352
353         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
354         errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 false given" );
355
356         rte = uta_get_rte( rt, 1000, 1, TRUE );                 // this should also fail as there is no mt==1 sid==-1 defined
357         errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 true given" );
358
359         rte = uta_get_rte( rt, 0, 3, TRUE );                    // mtype sid combo does exist; true/false should not matter
360         errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=3 true given" );
361
362         rte2 = uta_get_rte( rt, 11, 3, TRUE );                  // same mtype as before, different (valid) group, rte should be different than before
363         errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=11 m=3 true given" );
364         errors += fail_if_true( rte == rte2, "get_rte for mtype==3 and different sids (0 and 11) returned the same rte pointer" );
365
366         rte2 = uta_get_rte( rt, 0, 3, FALSE );                  // since the mtype/sid combo exists, setting false should return the same as before
367         errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=0 m=3 false given" );
368         errors += fail_if_false( rte == rte2, "get_rte did not return same pointer when mtype/sid combo given with different true/false" );
369
370         rte = uta_get_rte( rt, 12, 3, FALSE );                  // this combo does not exist and should fail when alt-key is not allowed (false)
371         errors += fail_not_nil( rte, "get_rte returned a pointer for s=12, m=3, false" );
372
373         rte = uta_get_rte( rt, 12, 3, TRUE );                   // this should return the entry for the 3/-1 combination
374         errors += fail_if_nil( rte, "get_rte did not return a pointer for s=12, m=3, true" );
375
376
377         alt_value = -1;
378         rte = uta_get_rte( rt, 0, 1, FALSE );                   // get an rte for the next loop
379         if( rte ) {
380                 for( i = 0; i < 10; i++ ) {                                                                     // round robin return value should be different each time
381                         #ifdef NNG_UNDER_TEST
382                                 value = uta_epsock_rr( rte, 0, &more, &nn_sock, &ep );          // msg type 1, group 1
383                         #else
384                                 value = uta_epsock_rr( ctx, rte, 0, &more, &nn_sock, &ep );
385                         #endif
386
387                         errors += fail_if_equal( value, alt_value, "round robiin sockets with multiple end points" );
388                         errors += fail_if_false( more, "more for mtype==1" );
389                         alt_value = value;
390                 }
391         }
392
393         more = -1;
394         rte = uta_get_rte( rt, 0, 3, FALSE );                           // get an rte for the next loop
395         if( rte ) {
396                 for( i = 0; i < 10; i++ ) {                                                             // this mtype has only one endpoint, so rr should be same each time
397                         #ifdef NNG_UNDER_TEST
398                                 value = uta_epsock_rr( rte, 0, NULL, &nn_sock, &ep );           // also test ability to deal properly with nil more pointer
399                         #else
400                                 value = uta_epsock_rr( ctx, rte, 0, NULL, &nn_sock, &ep );
401                         #endif
402
403                         if( i ) {
404                                 errors += fail_not_equal( value, alt_value, "round robin sockets with one endpoint" );
405                                 errors += fail_not_equal( more, -1, "more value changed in single group instance" );
406                         }
407                         alt_value = value;
408                 }
409         }
410
411         rte = uta_get_rte( rt, 11, 3, TRUE );
412         #ifdef NNG_UNDER_TEST
413                 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
414         #else
415                 state = uta_epsock_rr( ctx, rte, 22, NULL, NULL, &ep );
416         #endif
417         errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
418
419
420         uta_rt_clone( ctx, NULL, NULL, 0 );                                                             // verify null parms don't crash things
421         uta_rt_drop( NULL );
422         #ifdef NNG_UNDER_TEST
423                 uta_epsock_rr( NULL, 0,  &more, &nn_sock, &ep );                        // drive null case for coverage
424                 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
425         #else
426                 state = uta_epsock_rr( NULL, NULL, 0,  &more, &nn_sock, &ep );                  // drive null case for coverage
427                 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given nil ctx" );
428
429                 state = uta_epsock_rr( ctx, NULL, 0,  &more, &nn_sock, &ep );
430                 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given nil rte" );
431
432                 state = uta_epsock_rr( ctx, rte, 10000,  &more, &nn_sock, &ep );
433                 errors += fail_not_equal( state, 0, "uta_epsock_rr did not return false when given invalid group number" );
434         #endif
435         uta_add_rte( NULL, 99, 1 );
436         uta_get_rte( NULL, 0, 1000, TRUE );
437
438         fprintf( stderr, "[INFO] test: adding end points with nil data; warnings expected\n" );
439         uta_add_ep( NULL, NULL, "foo", 1 );
440         uta_add_ep( rt, NULL, "foo", 1 );
441
442         buf = uta_fib( ".gitignore" );
443         errors += fail_if_nil( buf, "buffer from read file into buffer" );
444         if( buf ) {
445                 free( buf );
446         }
447         buf = uta_fib( "no-file" );
448         errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" );
449         if( buf ) {
450                 free( buf );
451         }
452
453         fprintf( stderr, "<INFO> test is overtly dropping rt table at %p\n", rt );
454         ctx->rtable = NULL;
455         uta_rt_drop( rt );
456         rt = NULL;
457
458
459         // --- force the load of a RT which has some edge case forcing issues
460         if( ctx ) {
461                 char*   rt_stuff =
462                                 "newrt | start | dummy-seed\n"
463                                 "mse | 1  | -1 | localhost:84306\n"
464                                 "mse | 10  | -1 | localhost:84306\n"
465                                 "mse | 10  | 1 | localhost:84306\n"
466                                 "# should cause failure because there aren't 10 entries above\n"
467                                 "newrt | end | 10\n"
468
469                                 "# this table has no end\n"
470                                 "newrt | start | dummy-seed\n"
471                                 "mse | 1  | -1 | localhost:84306\n"
472                                 "mse | 10  | -1 | localhost:84306\n"
473                                 "mse | 10  | 1 | localhost:84306\n"
474
475                                 "# this table should be ok\n"
476                                 "newrt | start | dummy-seed\n"
477                                 "mse | 1  | -1 | localhost:84306\n"
478                                 "mse | 10  | -1 | localhost:84306\n"
479                                 "mse | 10  | 1 | localhost:84306\n"
480                                 "newrt | end | 3\n"
481
482                                 "# for an update to the existing table\n"
483                                 "# not in progress; drive that exception check\n"
484                                 "update | end | 23\n"
485
486                                 "update | start | dummy-seed\n"
487                                 "mse | 3 | 2 | localhost:2222\n"
488                                 "# short record to drive test\n"
489                                 "del\n"
490                                 "# no table end for exception handling\n"
491
492                                 "update | start | dummy-seed\n"
493                                 "mse | 2 | 2 | localhost:2222\n"
494                                 "del | 10 | 1\n"
495                                 "update | end | 2\n";
496
497                 fprintf( stderr, "<INFO> loading RT from edge case static table\n" );
498                 fprintf( stderr, "<INFO> %s\n", rt_stuff );
499                 gen_custom_rt( ctx, rt_stuff );
500                 fprintf( stderr, "<INFO> edge case load completed\n" );
501                 errors += fail_if_nil( ctx->rtable, "edge case route table didn't generate a pointer into the context" );
502
503                 unsetenv( "RMR_SEED_RT" );                      // remove for next read try
504                 if( ctx && ctx->seed_rt_fname != NULL ) {
505                         free( ctx->seed_rt_fname );
506                         ctx->seed_rt_fname = NULL;
507                 }
508                 read_static_rt( ctx, 0 );                       // drive for not there coverage
509         }
510
511
512         buf = uta_fib( "no-suhch-file" );                       // drive some error checking for coverage
513         if( buf ) {
514                 free( buf );
515         }
516
517
518         ep = (endpoint_t *) malloc( sizeof( *ep ) );
519         memset( ep, 0, sizeof( ep ) );
520         pthread_mutex_init( &ep->gate, NULL );
521         ep->name = strdup( "worm" );
522         ep->addr = NULL;
523         ep->notify = 1;
524         #ifdef NNG_UNDER_TEST
525                 state = uta_link2( ep );
526         #else
527                 state = uta_link2( ctx, ep );
528         #endif
529         errors += fail_if_true( state, "link2 did not return false when given a bad target name" );
530
531         #ifdef NNG_UNDER_TEST
532                 state = uta_link2( NULL );
533         #else
534                 state = uta_link2( ctx, NULL );
535                 errors += fail_if_true( state, "link2 did not return false when given nil ep pointer" );
536
537                 state = uta_link2( NULL, ep );
538         #endif
539         errors += fail_if_true( state, "link2 did not return false when given nil pointer" );
540
541         ep->name = strdup( "localhost:5512" );
542         ep->open = 1;
543         #ifdef NNG_UNDER_TEST
544                 state = uta_link2( ep );                        // drive for coverage
545         #else
546                 state = uta_link2( ctx, ep );
547         #endif
548         errors += fail_if_false( state, "link2 did returned false when given open ep" );
549
550         #ifndef NNG_UNDER_TEST
551                 ep->open = 0;                                                   // context is used only if ep not open, so to check this test close the ep
552                 ep->notify = 1;
553                 state = rt_link2_ep( NULL, ep );
554                 errors += fail_if_true( state, "rt_link2_ep returned true when given bad context" );
555
556                 state = rt_link2_ep( ctx, NULL );
557                 errors += fail_if_true( state, "rt_link2_ep returned true when given bad ep" );
558
559                 ep->open = 1;
560                 state = rt_link2_ep( ctx, ep );
561                 errors += fail_if_false( state, "rt_link2_ep returned false when given an open ep" );
562
563                 ep->open = 0;
564                 state = rt_link2_ep( ctx, ep );
565                 errors += fail_if_false( state, "rt_link2_ep returned false when given a closed ep" );
566
567                 ep->open = 1;
568                 uta_ep_failed( ep );
569                 errors += fail_if_true( ep->open, "uta_ep_failed didn't set open flag to false" );
570
571         #endif
572
573
574         // ----------------- test the meid support for looking up an endpoint based on the meid in the message -----
575
576         ctx->rtable = NULL;
577         ctx->my_name = strdup( "my_host_name" );                // set up to load a rtable
578         ctx->my_ip = strdup( "192.168.1.30" );
579         if( ctx && ctx->seed_rt_fname != NULL ) {
580                 free( ctx->seed_rt_fname );
581                 ctx->seed_rt_fname = NULL;
582         }
583         gen_rt( ctx );                                                                  // generate a route table with meid entries and hang off ctx
584
585         mbuf = rmr_alloc_msg( ctx, 2048 );               //  buffer to play with
586         mbuf->len = 100;
587         rmr_str2meid( mbuf, "meid1" );                                  // id that we know is in the map
588
589         #ifdef NNG_UNDER_TEST
590                 ep = NULL;                                                                              // force to nil so we see it go non-nil
591                 state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
592                 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
593                 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
594
595                 rmr_str2meid( mbuf, "XXXmeid1" );                               // id that we know is NOT in the map
596                 state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
597                 // it is NOT a valid check to test ep for nil -- epsock_mied doesn't guarentee ep is set/cleared when state is false
598                 errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
599         #else
600                 ep = NULL;                                                                              // force to nil so we see it go non-nil
601                 state = epsock_meid( ctx, ctx->rtable,  mbuf, &nn_sock, &ep );
602                 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
603                 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
604
605                 state = epsock_meid( ctx, ctx->rtable,  mbuf, &nn_sock, &ep );          // a second call to drive open == true check for coverage
606                 errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message; on open ep" );
607                 errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message; on open ep" );
608
609                 rmr_str2meid( mbuf, "XXXmeid1" );                               // id that we know is NOT in the map
610                 state = epsock_meid( ctx, ctx->rtable, mbuf, &nn_sock, &ep );
611                 // it is NOT a valid check to test ep for nil -- epsock_mied doesn't guarentee ep is set/cleared when state is false
612                 errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
613
614                 state = epsock_meid( NULL, ctx->rtable,  mbuf, &nn_sock, &ep );
615                 errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil context" );
616
617                 state = epsock_meid( ctx, ctx->rtable,  mbuf, NULL, &ep );
618                 errors += fail_not_equal( state, 0, "epsock_meid returned true when given nil socket pointer" );
619         #endif
620
621         // ------------  debugging and such; coverage only calls ----------------------------------------------------------
622                 ep_stats( ctx->rtable, NULL, "name", NULL, NULL );                      // ensure no crash when given nil pointer
623                 rt_epcounts( ctx->rtable, "testing" );
624                 rt_epcounts( NULL, "testing" );
625
626                 buf = ensure_nlterm( NULL );
627                 errors += fail_if_nil( buf, "ensure nlterm returned null pointer when given nil ptr" );
628                 if( buf ) {
629                         errors += fail_not_equal( strlen( buf ), 1, "ensure nlterm returned incorrect length string when given nil pointer" );
630                         free( buf );
631                 }
632
633                 buf = ensure_nlterm( strdup( "x" ) );                   // should return "x\n"
634                 errors += fail_if_nil( buf, "ensure nlterm returned null pointer when given single char string" );
635                 if( buf ) {
636                         errors += fail_not_equal( strlen( buf ), 2, "ensure nlterm returned incorrect length string when given single char string" );
637                         free( buf );
638                 }
639
640                 buf = strdup( "x\n" );
641                 buf2 = ensure_nlterm( buf );                                    // buffer returned should be the same
642                 if( fail_not_pequal( buf, buf2, "ensure nlterm returned new buffer for one char string with newline" ) ) {
643                         errors++;
644                         free( buf2 );
645                 }
646                 free( buf );
647
648                 buf = strdup( "Missing our trips to Gloria's for papossas.\n" );
649                 buf2 = ensure_nlterm( buf );                                                                                    // buffer returned should be the same
650                 if( fail_not_pequal( buf, buf2, "ensure nlterm returned new buffer for string with newline" ) ) {
651                         errors++;
652                         free( buf2 );
653                 }
654                 free( buf );
655
656                 buf = ensure_nlterm( strdup( "Stand up and cheer!" ) );                                 // force addition of newline
657                 if( buf ) {
658                         errors += fail_not_equal( strcmp( buf, "Stand up and cheer!\n" ), 0, "ensure nlterm didn't add newline" );
659                         free( buf );
660                         buf = NULL;
661                 }
662
663
664         // ------------- route manager request/response funcitons -------------------------------------------------------
665                 {
666                         rmr_mbuf_t*     smsg;
667
668                         smsg = rmr_alloc_msg( ctx, 1024 );
669                         send_rt_ack( ctx, smsg, "123456", 0, "no reason" );
670
671                         pctx = mk_dummy_ctx();
672                         ctx->rtg_whid = -1;
673                         state = send_update_req( pctx, ctx );
674                         errors += fail_not_equal( state, 0, "send_update_req did not return 0" );
675
676                         ctx->rtg_whid = rmr_wh_open( ctx, "localhost:19289" );
677                         state = send_update_req( pctx, ctx );
678                         errors += fail_if_equal( state, 0, "send_update_req to an open whid did not return 0" );
679                 }
680
681
682         // ------------- si only; fd to ep conversion functions ---------------------------------------------------------
683         #ifndef NNG_UNDER_TEST
684                 ep2 = (endpoint_t *) malloc( sizeof( *ep ) );
685
686                 fd2ep_init( ctx );
687                 fd2ep_add( ctx, 10, ep2 );
688
689                 ep = fd2ep_get( ctx, 10 );
690                 errors += fail_if_nil( ep, "fd2ep did not return pointer for known mapping" );
691                 errors += fail_if_false( ep == ep2,  "fd2ep did not return same pointer that was added" );
692
693                 ep = fd2ep_get( ctx, 20 );
694                 errors += fail_not_nil( ep, "fd2ep did returned a pointer for unknown mapping" );
695
696                 ep = fd2ep_del( ctx, 10 );
697                 errors += fail_if_nil( ep, "fd2ep delete did not return pointer for known mapping" );
698                 errors += fail_if_false( ep == ep2,  "fd2ep delete did not return same pointer that was added" );
699
700                 ep = fd2ep_del( ctx, 20 );
701                 errors += fail_not_nil( ep, "fd2ep delete returned a pointer for unknown mapping" );
702         #endif
703
704         // ---------------- misc coverage tests --------------------------------------------------------------------------
705         collect_things( NULL, NULL, NULL, NULL, NULL );                         // these both return null, these test NP checks
706         collect_things( NULL, NULL, NULL, NULL, (void *) 1234 );                // the last is an invalid pointer, but check needed to force check on previous param
707         del_rte( NULL, NULL, NULL, NULL, NULL );
708
709         ctx = mk_dummy_ctx();
710         roll_tables( ctx );                             // drive nil rt check
711
712
713
714         // ------ specific edge case tests -------------------------------------------------------------------------------
715         errors += lg_clone_test( );
716
717         return !!errors;                        // 1 or 0 regardless of count
718 }