15c6deeed4e9e2464bfe14f69d0ab9daf24d9c9c
[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    *buf2;
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
130
131         #ifndef NNG_UNDER_TEST
132                 si_ctx_t* si_ctx = NULL;
133         #endif
134
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 );
137         if( i >= 0 ) {
138                 write( i, "2\n", 2 );
139                 close( i );
140         }
141
142
143         /*
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.)
147
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
154         */
155         gcounts[0] = 1;                                                 // first entry has 1 group with 2 endpoints; message type 0, sid 0
156         ecounts[0] = 2;
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
160
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++;
167
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++;
173
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++;
179
180         gcounts[4] = 1;                                 // three entries for mt==3 with different sids
181         ecounts[4] = 1;
182         mtypes[4] = build_key( 3, 0 );
183         entries[enu].group = 0; entries[enu].ep_name = "localhost:5500"; enu++;
184
185         gcounts[5] = 1;
186         ecounts[5] = 1;
187         mtypes[5] = build_key( 3, 11 );
188         entries[enu].group = 0; entries[enu].ep_name = "localhost:5511"; enu++;
189
190         gcounts[6] = 1;
191         ecounts[6] = 1;
192         mtypes[6] = build_key( 3, -1 );
193         entries[enu].group = 0; entries[enu].ep_name = "localhost:5512"; enu++;
194
195
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" );
199                 exit( 1 );
200         }
201
202         enu = 0;
203         rte = NULL;
204         for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) {                                // add entries defined above
205                 if( gcounts[i] ) {
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" );
209                                 exit( 1 );
210                         }
211                 } else {
212                         if( rte == NULL ) {
213                                 fprintf( stderr, "<SNAFU> internal testing error -- rte was nil for gcount == 0\n" );
214                                 exit( 1 );
215                         }
216                 }
217
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" );
221                         enu++;
222                 }
223         }
224
225         // ----- end hacking together a route table ---------------------------------------------------
226
227
228         crt = uta_rt_clone( rt );                                                               // clone only the endpoint entries
229         errors += fail_if_nil( crt, "cloned route table" );
230         if( crt ) {
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)" );
234
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" );
237                 uta_rt_drop( crt );
238         }
239
240
241         crt = uta_rt_clone_all( rt );                                                   // clone all entries
242         errors += fail_if_nil( crt, "cloned all route table" );
243
244         if( crt ) {
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)" );
248
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)" );
252                 uta_rt_drop( crt );
253         }
254
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 ) );
258                         ctx->rtable = rt;
259                 } else {
260                         fprintf( stderr, "<FAIL> cannot acllocate a context, cannot continue rtable tests\n" );
261                         return errors;
262                 }
263         #else
264                 ctx = mk_dummy_ctx();
265         #endif
266
267         ctx->rtable = rt;
268
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)" );
273
274         ep = NULL;
275         #ifdef NNG_UNDER_TEST
276                 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, &ep );               // this should be found
277         #else
278                 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, &ep );      // this should be found
279         #endif
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" );
284
285         #if  NNG_UNDER_TEST
286                 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );             // test coverage on nil checks
287         #else
288                 state = uta_epsock_byname( NULL, "localhost:4561", &nn_sock, &ep );
289         #endif
290         errors += fail_not_equal( state, 0, "socket (by name) nil check returned true" );
291
292         ep->open = 1;
293         #if  NNG_UNDER_TEST
294                 state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, NULL );              // test coverage on nil checks
295         #else
296                 state = uta_epsock_byname( ctx, "localhost:4561", &nn_sock, NULL );
297         #endif
298         errors += fail_if_equal( state, 0, "socket (by name) open ep check returned false" );
299
300
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" );
304
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" );
307
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" );
310
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" );
313
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" );
316
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" );
320
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" );
324
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" );
327
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" );
330
331
332         alt_value = -1;
333         rte = uta_get_rte( rt, 0, 1, FALSE );                   // get an rte for the next loop
334         if( rte ) {
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
338                         #else
339                                 value = uta_epsock_rr( ctx, rte, 0, &more, &nn_sock, &ep );
340                         #endif
341
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" );
344                         alt_value = value;
345                 }
346         }
347
348         more = -1;
349         rte = uta_get_rte( rt, 0, 3, FALSE );                           // get an rte for the next loop
350         if( rte ) {
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
354                         #else
355                                 value = uta_epsock_rr( ctx, rte, 0, NULL, &nn_sock, &ep );
356                         #endif
357
358                         if( i ) {
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" );
361                         }
362                         alt_value = value;
363                 }
364         }
365
366         rte = uta_get_rte( rt, 11, 3, TRUE );
367         #ifdef NNG_UNDER_TEST
368                 state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
369         #else
370                 state = uta_epsock_rr( ctx, rte, 22, NULL, NULL, &ep );
371         #endif
372         errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
373
374
375         uta_rt_clone( NULL );                                                           // verify null parms don't crash things
376         uta_rt_drop( NULL );
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 );
380         #else
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" );
383
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" );
386
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" );
389         #endif
390         uta_add_rte( NULL, 99, 1 );
391         uta_get_rte( NULL, 0, 1000, TRUE );
392
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 );
396
397         buf = uta_fib( ".gitignore" );
398         errors += fail_if_nil( buf, "buffer from read file into buffer" );
399         if( buf ) {
400                 free( buf );
401         }
402         buf = uta_fib( "no-file" );
403         errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" );
404         if( buf ) {
405                 free( buf );
406         }
407
408         uta_rt_drop( rt );
409         rt = NULL;
410
411
412         if( ctx ) {
413                 if( (seed_fname = getenv( "RMR_SEED_RT" )) != NULL ) {
414                         read_static_rt( ctx, 0 );
415                         rt = ctx->rtable;
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
418                 }
419
420                 read_static_rt( ctx, 0 );                       // drive for not there coverage
421         }
422
423
424         buf = uta_fib( "no-suhch-file" );                       // drive some error checking for coverage
425         if( buf ) {
426                 free( buf );
427         }
428
429
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" );
434         ep->addr = NULL;
435         ep->notify = 1;
436         #ifdef NNG_UNDER_TEST
437                 state = uta_link2( ep );
438         #else
439                 state = uta_link2( ctx, ep );
440         #endif
441         errors += fail_if_true( state, "link2 did not return false when given a bad target name" );
442
443         #ifdef NNG_UNDER_TEST
444                 state = uta_link2( NULL );
445         #else
446                 state = uta_link2( ctx, NULL );
447                 errors += fail_if_true( state, "link2 did not return false when given nil ep pointer" );
448
449                 state = uta_link2( NULL, ep );
450         #endif
451         errors += fail_if_true( state, "link2 did not return false when given nil pointer" );
452
453         ep->name = strdup( "localhost:5512" );
454         ep->open = 1;
455         #ifdef NNG_UNDER_TEST
456                 state = uta_link2( ep );                        // drive for coverage
457         #else
458                 state = uta_link2( ctx, ep );
459         #endif
460         errors += fail_if_false( state, "link2 did returned false when given open ep" );
461
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
464                 ep->notify = 1;
465                 state = rt_link2_ep( NULL, ep );
466                 errors += fail_if_true( state, "rt_link2_ep returned true when given bad context" );
467
468                 state = rt_link2_ep( ctx, NULL );
469                 errors += fail_if_true( state, "rt_link2_ep returned true when given bad ep" );
470
471                 ep->open = 1;
472                 state = rt_link2_ep( ctx, ep );
473                 errors += fail_if_false( state, "rt_link2_ep returned false when given an open ep" );
474
475                 ep->open = 0;
476                 state = rt_link2_ep( ctx, ep );
477                 errors += fail_if_false( state, "rt_link2_ep returned false when given a closed ep" );
478
479                 ep->open = 1;
480                 uta_ep_failed( ep );
481                 errors += fail_if_true( ep->open, "uta_ep_failed didn't set open flag to false" );
482
483         #endif
484
485
486         // ----------------- test the meid support for looking up an endpoint based on the meid in the message -----
487
488         ctx->rtable = NULL;
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
492
493         mbuf = rmr_alloc_msg( ctx, 2048 );               //  buffer to play with
494         mbuf->len = 100;
495         rmr_str2meid( mbuf, "meid1" );                                  // id that we know is in the map
496
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" );
502
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" );
507         #else
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" );
512
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" );
516
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" );
521
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" );
524
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" );
527         #endif
528
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" );
533
534                 buf = ensure_nlterm( NULL );
535                 errors += fail_if_nil( buf, "ensure nlterm returned null pointer when given nil ptr" );
536                 if( buf ) {
537                         errors += fail_not_equal( strlen( buf ), 1, "ensure nlterm returned incorrect length string when given nil pointer" );
538                         free( buf );
539                 }
540
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" );
543                 if( buf ) {
544                         errors += fail_not_equal( strlen( buf ), 2, "ensure nlterm returned incorrect length string when given single char string" );
545                         free( buf );
546                 }
547
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" ) ) {
551                         errors++;
552                         free( buf2 );
553                 }
554                 free( buf );
555
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" ) ) {
559                         errors++;
560                         free( buf2 );
561                 }
562                 free( buf );
563
564                 buf = ensure_nlterm( strdup( "Stand up and cheer!" ) );                                 // force addition of newline
565                 if( buf ) {
566                         errors += fail_not_equal( strcmp( buf, "Stand up and cheer!\n" ), 0, "ensure nlterm didn't add newline" );
567                         free( buf );
568                         buf = NULL;
569                 }
570
571
572         // ------------- route manager request/response funcitons -------------------------------------------------------
573                 {
574                         rmr_mbuf_t*     smsg;
575
576                         smsg = rmr_alloc_msg( ctx, 1024 );
577                         send_rt_ack( ctx, smsg, "123456", 0, "no reason" );
578
579                         pctx = mk_dummy_ctx();
580                         ctx->rtg_whid = -1;
581                         state = send_update_req( pctx, ctx );
582                         errors += fail_not_equal( state, 0, "send_update_req did not return 0" );
583
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" );
587                 }
588
589
590         // ------------- si only; fd to ep conversion functions ---------------------------------------------------------
591         #ifndef NNG_UNDER_TEST
592                 ep2 = (endpoint_t *) malloc( sizeof( *ep ) );
593
594                 fd2ep_init( ctx );
595                 fd2ep_add( ctx, 10, ep2 );
596
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" );
600
601                 ep = fd2ep_get( ctx, 20 );
602                 errors += fail_not_nil( ep, "fd2ep did returned a pointer for unknown mapping" );
603
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" );
607
608                 ep = fd2ep_del( ctx, 20 );
609                 errors += fail_not_nil( ep, "fd2ep delete returned a pointer for unknown mapping" );
610         #endif
611
612         return !!errors;                        // 1 or 0 regardless of count
613 }