Fix rmr_rpobe command line bug; add test coverage
[ric-plt/lib/rmr.git] / test / rt_static_test.c
1 // : vi ts=4 sw=4 noet :
2 /*
3 ==================================================================================
4             Copyright (c) 2019 Nokia
5             Copyright (c) 2018-2019 AT&T Intellectual Property.
6
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
10
11            http://www.apache.org/licenses/LICENSE-2.0
12
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 ==================================================================================
19 */
20
21 /*
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.
25
26         Author:         E. Scott Daniels
27         Date:           3 April 2019
28 */
29
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <stdint.h>
37 #include <pthread.h>
38 #include <semaphore.h>
39
40 #include "rmr.h"
41 #include "rmr_agnostic.h"
42
43 typedef struct entry_info {
44         int group;
45         char* ep_name;
46 } ei_t;
47
48
49 /*
50         Driven by symtab foreach element of one space.
51         We count using the data as a counter.
52 */
53 static void count_things( void* st, void* entry, char const* name, void* thing, void* vdata ) {
54         int* counter;
55
56         if( thing ) {
57                 if( (counter = (int *) vdata) != NULL ) {
58                         *counter++;
59                 }
60         }
61 }
62
63 /*
64         Returns the number of entries in the table for the given class.
65 */
66 static int count_entries( route_table_t* rt, int class ) {
67         int counter = 0;
68
69         if( ! rt ) {
70                 return 0;
71         }
72         if( !rt->hash ) {
73                 return 0;
74         }
75
76         rmr_sym_foreach_class( rt->hash, class, count_things, &counter );       // run each and update counter
77
78         return counter;
79 }
80
81 /*
82         Builds a route table key.
83 */
84 static uint64_t build_key( uint32_t mtype, uint32_t sid ) {
85         uint64_t k;
86
87         k = (uint64_t) sid << 32;
88         k += mtype;
89
90 fprintf( stderr, "<INFO> build key: %x %x --> %llx\n", (int) mtype, (int) sid, (long long) k );
91         return k;
92 }
93
94 /*
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
97         situations).
98 */
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
105         rtable_ent_t*   rte2;
106         endpoint_t*     ep;                     // endpoint added
107         endpoint_t*     ep2;
108         int more = 0;                   // more flag from round robin
109         int errors = 0;                 // number errors found
110         int     i;
111         int k;
112         int     c1;                                     // general counters
113         int c2;
114         int mtype;
115         int value;
116         int alt_value;
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
121         char*   tok;
122         char*   nxt_tok;
123         int             enu = 0;
124         int             state;
125         char    *buf;
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
129
130         #ifndef NNG_UNDER_TEST
131                 si_ctx_t* si_ctx = NULL;
132         #endif
133
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 );
136         if( i >= 0 ) {
137                 write( i, "2\n", 2 );
138                 close( i );
139         }
140
141
142         /*
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.)
146
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
153         */
154         gcounts[0] = 1;                                                 // first entry has 1 group with 2 endpoints; message type 0, sid 0
155         ecounts[0] = 2;
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
159
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++;
166
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++;
172
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++;
178
179         gcounts[4] = 1;                                 // three entries for mt==3 with different sids
180         ecounts[4] = 1;
181         mtypes[4] = build_key( 3, 0 );
182         entries[enu].group = 0; entries[enu].ep_name = "localhost:5500"; enu++;
183
184         gcounts[5] = 1;
185         ecounts[5] = 1;
186         mtypes[5] = build_key( 3, 11 );
187         entries[enu].group = 0; entries[enu].ep_name = "localhost:5511"; enu++;
188
189         gcounts[6] = 1;
190         ecounts[6] = 1;
191         mtypes[6] = build_key( 3, -1 );
192         entries[enu].group = 0; entries[enu].ep_name = "localhost:5512"; enu++;
193
194
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" );
198                 exit( 1 );
199         }
200
201         enu = 0;
202         rte = NULL;
203         for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) {                                // add entries defined above
204                 if( gcounts[i] ) {
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" );
208                                 exit( 1 );
209                         }
210                 } else {
211                         if( rte == NULL ) {
212                                 fprintf( stderr, "<SNAFU> internal testing error -- rte was nil for gcount == 0\n" );
213                                 exit( 1 );
214                         }
215                 }
216
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" );
220                         enu++;
221                 }
222         }
223
224         // ----- end hacking together a route table ---------------------------------------------------
225
226
227         crt = uta_rt_clone( rt );                                                               // clone only the endpoint entries
228         errors += fail_if_nil( crt, "cloned route table" );
229         if( crt ) {
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)" );
233
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" );
236                 uta_rt_drop( crt );
237         }
238
239
240         crt = uta_rt_clone_all( rt );                                                   // clone all entries
241         errors += fail_if_nil( crt, "cloned all route table" );
242
243         if( crt ) {
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)" );
247
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)" );
251                 uta_rt_drop( crt );
252         }
253
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 ) );
257                         ctx->rtable = rt;
258                 } else {
259                         fprintf( stderr, "<FAIL> cannot acllocate a context, cannot continue rtable tests\n" );
260                         return errors;
261                 }
262         #else
263                 ctx = mk_dummy_ctx();
264         #endif
265
266         ctx->rtable = rt;
267
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)" );
272
273         ep = NULL;
274         #ifdef NNG_UNDER_TEST
275                 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, &ep );               // this should be found
276         #else
277                 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, &ep );      // this should be found
278         #endif
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" );
283
284         #if  NNG_UNDER_TEST
285                 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );             // test coverage on nil checks
286         #else
287                 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );
288         #endif
289         errors += fail_not_equal( state, 0, "socket (by name) nil check returned true" );
290
291         ep->open = 1;
292         #if  NNG_UNDER_TEST
293                 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, NULL );              // test coverage on nil checks
294         #else
295                 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, NULL );
296         #endif
297         errors += fail_if_equal( state, 0, "socket (by name) open ep check returned false" );
298
299
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" );
303
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" );
306
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" );
309
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" );
312
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" );
315
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" );
319
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" );
323
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" );
326
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" );
329
330
331         alt_value = -1;
332         rte = uta_get_rte( rt, 0, 1, FALSE );                   // get an rte for the next loop
333         if( rte ) {
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
337                         #else
338                                 value = uta_epsock_rr( ctx, rte, 0, &more, &nn_sock, &ep );
339                         #endif
340
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" );
343                         alt_value = value;
344                 }
345         }
346
347         more = -1;
348         rte = uta_get_rte( rt, 0, 3, FALSE );                           // get an rte for the next loop
349         if( rte ) {
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
353                         #else
354                                 value = uta_epsock_rr( ctx, rte, 0, NULL, &nn_sock, &ep );
355                         #endif
356
357                         if( i ) {
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" );
360                         }
361                         alt_value = value;
362                 }
363         }
364
365         rte = uta_get_rte( rt, 11, 3, TRUE );
366         #ifdef NNG_UNDER_TEST
367                 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
368         #else
369                 state = uta_epsock_rr( ctx, rte, 22, NULL, NULL, &ep );
370         #endif
371         errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
372
373
374         uta_rt_clone( NULL );                                                           // verify null parms don't crash things
375         uta_rt_drop( NULL );
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 );
379         #else
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" );
382
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" );
385
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" );
388         #endif
389         uta_add_rte( NULL, 99, 1 );
390         uta_get_rte( NULL, 0, 1000, TRUE );
391
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 );
395
396         buf = uta_fib( ".gitignore" );
397         errors += fail_if_nil( buf, "buffer from read file into buffer" );
398         if( buf ) {
399                 free( buf );
400         }
401         buf = uta_fib( "no-file" );
402         errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" );
403         if( buf ) {
404                 free( buf );
405         }
406
407         uta_rt_drop( rt );
408         rt = NULL;
409
410
411         if( ctx ) {
412                 if( (seed_fname = getenv( "RMR_SEED_RT" )) != NULL ) {
413                         read_static_rt( ctx, 0 );
414                         rt = ctx->rtable;
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
417                 }
418
419                 read_static_rt( ctx, 0 );                       // drive for not there coverage
420         }
421
422
423         buf = uta_fib( "no-suhch-file" );                       // drive some error checking for coverage
424         if( buf ) {
425                 free( buf );
426         }
427
428
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" );
433         ep->addr = NULL;
434         ep->notify = 1;
435         #ifdef NNG_UNDER_TEST
436                 state = uta_link2( ep );
437         #else
438                 state = uta_link2( ctx, ep );
439         #endif
440         errors += fail_if_true( state, "link2 did not return false when given a bad target name" );
441
442         #ifdef NNG_UNDER_TEST
443                 state = uta_link2( NULL );
444         #else
445                 state = uta_link2( ctx, NULL );
446                 errors += fail_if_true( state, "link2 did not return false when given nil ep pointer" );
447
448                 state = uta_link2( NULL, ep );
449         #endif
450         errors += fail_if_true( state, "link2 did not return false when given nil pointer" );
451
452         ep->name = strdup( "localhost:5512" );
453         ep->open = 1;
454         #ifdef NNG_UNDER_TEST
455                 state = uta_link2( ep );                        // drive for coverage
456         #else
457                 state = uta_link2( ctx, ep );
458         #endif
459         errors += fail_if_false( state, "link2 did returned false when given open ep" );
460
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
463                 ep->notify = 1;
464                 state = rt_link2_ep( NULL, ep );
465                 errors += fail_if_true( state, "rt_link2_ep returned true when given bad context" );
466
467                 state = rt_link2_ep( ctx, NULL );
468                 errors += fail_if_true( state, "rt_link2_ep returned true when given bad ep" );
469
470                 ep->open = 1;
471                 state = rt_link2_ep( ctx, ep );
472                 errors += fail_if_false( state, "rt_link2_ep returned false when given an open ep" );
473
474                 ep->open = 0;
475                 state = rt_link2_ep( ctx, ep );
476                 errors += fail_if_false( state, "rt_link2_ep returned false when given a closed ep" );
477
478                 ep->open = 1;
479                 uta_ep_failed( ep );
480                 errors += fail_if_true( ep->open, "uta_ep_failed didn't set open flag to false" );
481
482         #endif
483
484
485         // ----------------- test the meid support for looking up an endpoint based on the meid in the message -----
486
487         ctx->rtable = NULL;
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
491
492         mbuf = rmr_alloc_msg( ctx, 2048 );               //  buffer to play with
493         mbuf->len = 100;
494         rmr_str2meid( mbuf, "meid1" );                                  // id that we know is in the map
495
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" );
501
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" );
506         #else
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" );
511
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" );
515
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" );
520
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" );
523
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" );
526         #endif
527
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" );
532
533                 buf = ensure_nlterm( strdup( "Stand up and cheer!" ) );                                 // force addition of newline
534                 if( buf ) {
535                         errors += fail_not_equal( strcmp( buf, "Stand up and cheer!\n" ), 0, "ensure nlterm didn't add newline" );
536                         free( buf );
537                         buf = NULL;
538                 }
539
540
541         // ------------- route manager request/response funcitons -------------------------------------------------------
542                 {
543                         rmr_mbuf_t*     smsg;
544
545                         smsg = rmr_alloc_msg( ctx, 1024 );
546                         send_rt_ack( ctx, smsg, "123456", 0, "no reason" );
547
548                         pctx = mk_dummy_ctx();
549                         ctx->rtg_whid = -1;
550                         state = send_update_req( pctx, ctx );
551                         errors += fail_not_equal( state, 0, "send_update_req did not return 0" );
552
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" );
556                 }
557
558
559         // ------------- si only; fd to ep conversion functions ---------------------------------------------------------
560         #ifndef NNG_UNDER_TEST
561                 ep2 = (endpoint_t *) malloc( sizeof( *ep ) );
562
563                 fd2ep_init( ctx );
564                 fd2ep_add( ctx, 10, ep2 );
565
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" );
569
570                 ep = fd2ep_get( ctx, 20 );
571                 errors += fail_not_nil( ep, "fd2ep did returned a pointer for unknown mapping" );
572
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" );
576
577                 ep = fd2ep_del( ctx, 20 );
578                 errors += fail_not_nil( ep, "fd2ep delete returned a pointer for unknown mapping" );
579         #endif
580
581         return !!errors;                        // 1 or 0 regardless of count
582 }