Beef up tests to the extended header functions
[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         route_table_t* rt;              // route table
102         route_table_t* crt;             // cloned route table
103         rtable_ent_t*   rte;    // route table entries from table
104         rtable_ent_t*   rte2;
105         endpoint_t*     ep;                     // endpoint added
106         int more = 0;                   // more flag from round robin
107         int errors = 0;                 // number errors found
108         int     i;
109         int k;
110         int     c1;                                     // general counters
111         int c2;
112         int mtype;
113         int value;
114         int alt_value;
115         ei_t    entries[50];    // end point information
116         int             gcounts[7];             // number of groups in this set
117         int             ecounts[7];             // number of elements per group
118         uint64_t        mtypes[7];      // mtype/sid 'key' in the modern RMR world
119         char*   tok;
120         char*   nxt_tok;
121         int             enu = 0;
122         int             state;
123         char    *buf;
124         char*   seed_fname;             // seed file
125         nng_socket nn_sock;             // this is a struct in nng, so difficult to validate
126         rmr_mbuf_t*     mbuf;           // message for meid route testing
127
128         setenv( "ENV_VERBOSE_FILE", ".ut_rmr_verbose", 1 );                     // allow for verbose code in rtc to be driven
129         i = open( ".ut_rmr_verbose", O_RDWR | O_CREAT, 0644 );
130         if( i >= 0 ) {
131                 write( i, "2\n", 2 );
132                 close( i );
133         }
134
135                                                         
136         /*
137                 The hacky code below calls the necessary rmr functions to create a route table
138                 as though the following were read and parsed by the rmr functions. (This tests
139                 the individual funcitons and avoids writing another parser, so it's not pretty.)
140         
141                 mse | 0 | 0 | yahoo.com:4561,localhost:4562
142                 mse | 1 | 0 | localhost:4560,localhost:4568,localhost:4569; localhost:4561,localhost:4562
143                 mse | 2 | 0 | localhost:4563,localhost:4564
144                 mse | 3 | 0 | localhost:4565
145                 mse | 3 | 11 | locahost:5511
146                 mse | 3 | -1 | localhost:5500
147         */
148         gcounts[0] = 1;                                                 // first entry has 1 group with 2 endpoints; message type 0, sid 0
149         ecounts[0] = 2;
150         mtypes[0] = build_key( 0, 0 );                  // mtype is now a key of mtype/sid
151         entries[enu].group = 0; entries[enu].ep_name = "yahoo.com:4561"; enu++;         // use a dns resolvable name to test that
152         entries[enu].group = 0; entries[enu].ep_name = "localhost:4562"; enu++;         // rest can default to some dummy ip
153
154         gcounts[1] = 2;                         // 2 groups
155         ecounts[1] = 3;                         // first has 3 endpoints
156         mtypes[1] = build_key( 1, 0 );
157         entries[enu].group = 0; entries[enu].ep_name = "localhost:4560"; enu++;
158         entries[enu].group = 0; entries[enu].ep_name = "localhost:4568"; enu++;
159         entries[enu].group = 0; entries[enu].ep_name = "localhost:4569"; enu++;
160
161         gcounts[2] = 0;                                 // 0 means use same rte, this is the next group for the entry
162         ecounts[2] = 2;                                 // 2 endpoints
163         mtypes[2] = 999;                                // ignored when appending to previous entry
164         entries[enu].group = 1; entries[enu].ep_name = "localhost:4561"; enu++;
165         entries[enu].group = 1; entries[enu].ep_name = "localhost:4562"; enu++;
166
167         gcounts[3] = 1;                                 // next entry has 1 group
168         ecounts[3] = 2;                                 // with 2 enpoints
169         mtypes[3] = build_key( 2, 0 );
170         entries[enu].group = 0; entries[enu].ep_name = "localhost:4563"; enu++;
171         entries[enu].group = 0; entries[enu].ep_name = "localhost:4564"; enu++;
172
173         gcounts[4] = 1;                                 // three entries for mt==3 with different sids
174         ecounts[4] = 1;
175         mtypes[4] = build_key( 3, 0 );
176         entries[enu].group = 0; entries[enu].ep_name = "localhost:5500"; enu++;
177
178         gcounts[5] = 1;
179         ecounts[5] = 1;
180         mtypes[5] = build_key( 3, 11 );
181         entries[enu].group = 0; entries[enu].ep_name = "localhost:5511"; enu++;
182
183         gcounts[6] = 1;
184         ecounts[6] = 1;
185         mtypes[6] = build_key( 3, -1 );
186         entries[enu].group = 0; entries[enu].ep_name = "localhost:5512"; enu++;
187
188
189         rt = uta_rt_init( );                                                                            // get us a route table
190         if( (errors += fail_if_nil( rt, "pointer to route table" )) ) {
191                 fprintf( stderr, "<FAIL> abort: cannot continue without a route table\n" );
192                 exit( 1 );
193         }
194
195         enu = 0;
196         rte = NULL;
197         for( i = 0; i < sizeof( gcounts )/sizeof( int ); i++ ) {                                // add entries defined above
198                 if( gcounts[i] ) {
199                         rte = uta_add_rte( rt, mtypes[i], gcounts[i] );                                 // get/create entry for message type
200                         if( (errors += fail_if_nil( rte, "route table entry" )) ) {
201                                 fprintf( stderr, "<FAIL> abort: cannot continue without a route table entry\n" );
202                                 exit( 1 );
203                         }
204                 } else {
205                         if( rte == NULL ) {
206                                 fprintf( stderr, "<SNAFU> internal testing error -- rte was nil for gcount == 0\n" );
207                                 exit( 1 );
208                         }
209                 }
210
211                 for( k = 0; k < ecounts[i]; k++ ) {
212                         ep = uta_add_ep( rt, rte, entries[enu].ep_name, entries[enu].group );
213                         errors += fail_if_nil( ep, "endpoint" );
214                         enu++;
215                 }
216         }
217
218         // ----- end hacking together a route table ---------------------------------------------------
219
220
221         crt = uta_rt_clone( rt );                                                               // clone only the endpoint entries
222         errors += fail_if_nil( crt, "cloned route table" );
223         if( crt ) {
224                 c1 = count_entries( rt, 1 );
225                 c2 = count_entries( crt, 1 );
226                 errors += fail_not_equal( c1, c2, "cloned (endpoints) table entries space 1 count (b) did not match original table count (a)" );
227         
228                 c2 = count_entries( crt, 0 );
229                 errors += fail_not_equal( c2, 0, "cloned (endpoints) table entries space 0 count (a) was not zero as expected" );
230                 uta_rt_drop( crt );
231         }
232
233
234         crt = uta_rt_clone_all( rt );                                                   // clone all entries
235         errors += fail_if_nil( crt, "cloned all route table" );
236
237         if( crt ) {
238                 c1 = count_entries( rt, 0 );
239                 c2 = count_entries( crt, 0 );
240                 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 0 count (b) did not match original table count (a)" );
241         
242                 c1 = count_entries( rt, 1 );
243                 c2 = count_entries( crt, 1 );
244                 errors += fail_not_equal( c1, c2, "cloned (all) table entries space 1 count (b) did not match original table count (a)" );
245                 uta_rt_drop( crt );
246         }
247         
248
249         ep = uta_get_ep( rt, "localhost:4561" );
250         errors += fail_if_nil( ep, "end point (fetch by name)" );
251         ep = uta_get_ep( rt, "bad_name:4560" );
252         errors += fail_not_nil( ep, "end point (fetch by name with bad name)" );
253
254         ep = NULL;
255         state = uta_epsock_byname( rt, "localhost:4561", &nn_sock, &ep );               // this should be found
256         errors += fail_if_equal( state, 0, "socket (by name)" );
257         errors += fail_if_nil( ep, "epsock_byname did not populate endpoint pointer when expected to" );
258         //alt_value = uta_epsock_byname( rt, "localhost:4562" );                        // we might do a memcmp on the two structs, but for now nothing
259         //errors += fail_if_equal( value, alt_value, "app1/app2 sockets" );
260
261
262         // --- test that the get_rte function finds expected keys, and retries to find 'bad' sid attempts for valid mtypes with no sid
263         rte = uta_get_rte( rt, 0, 1, TRUE );                    // s=0 m=1 is defined, so this should return a pointer
264         errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 true given" );
265
266         rte = uta_get_rte( rt, 0, 1, FALSE );                   // the retry shouldn't apply, but ensure it does the righ thing
267         errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=1 false given" );
268
269         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
270         errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 false given" );
271
272         rte = uta_get_rte( rt, 1000, 1, TRUE );                 // this should also fail as there is no mt==1 sid==-1 defined
273         errors += fail_not_nil( rte, "get_rte returned a pointer when s=1000 m=1 true given" );
274
275         rte = uta_get_rte( rt, 0, 3, TRUE );                    // mtype sid combo does exist; true/false should not matter
276         errors += fail_if_nil( rte, "get_rte did not return a pointer when s=0 m=3 true given" );
277
278         rte2 = uta_get_rte( rt, 11, 3, TRUE );                  // same mtype as before, different (valid) group, rte should be different than before
279         errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=11 m=3 true given" );
280         errors += fail_if_true( rte == rte2, "get_rte for mtype==3 and different sids (0 and 11) returned the same rte pointer" );
281
282         rte2 = uta_get_rte( rt, 0, 3, FALSE );                  // since the mtype/sid combo exists, setting false should return the same as before
283         errors += fail_if_nil( rte2, "get_rte did not return a pointer when s=0 m=3 false given" );
284         errors += fail_if_false( rte == rte2, "get_rte did not return same pointer when mtype/sid combo given with different true/false" );
285
286         rte = uta_get_rte( rt, 12, 3, FALSE );                  // this combo does not exist and should fail when alt-key is not allowed (false)
287         errors += fail_not_nil( rte, "get_rte returned a pointer for s=12, m=3, false" );
288
289         rte = uta_get_rte( rt, 12, 3, TRUE );                   // this should return the entry for the 3/-1 combination 
290         errors += fail_if_nil( rte, "get_rte did not return a pointer for s=12, m=3, true" );
291
292         alt_value = -1;
293         rte = uta_get_rte( rt, 0, 1, FALSE );                   // get an rte for the next loop
294         if( rte ) {
295                 for( i = 0; i < 10; i++ ) {                                                                     // round robin return value should be different each time
296                         value = uta_epsock_rr( rte, 0, &more, &nn_sock, &ep );          // msg type 1, group 1
297                         errors += fail_if_equal( value, alt_value, "round robiin sockets with multiple end points" );
298                         errors += fail_if_false( more, "more for mtype==1" );
299                         alt_value = value;
300                 }
301         }
302
303         more = -1;
304         rte = uta_get_rte( rt, 0, 3, FALSE );                           // get an rte for the next loop
305         if( rte ) {
306                 for( i = 0; i < 10; i++ ) {                                                             // this mtype has only one endpoint, so rr should be same each time
307                         value = uta_epsock_rr( rte, 0, NULL, &nn_sock, &ep );           // also test ability to deal properly with nil more pointer
308                         if( i ) {
309                                 errors += fail_not_equal( value, alt_value, "round robin sockets with one endpoint" );
310                                 errors += fail_not_equal( more, -1, "more value changed in single group instance" );
311                         }
312                         alt_value = value;
313                 }
314         }
315
316         rte = uta_get_rte( rt, 11, 3, TRUE );
317         state = uta_epsock_rr( rte, 22, NULL, NULL, &ep );
318         errors += fail_if_true( state, "uta_epsock_rr returned bad (non-zero) state when given nil socket pointer" );
319
320
321         uta_rt_clone( NULL );                                                           // verify null parms don't crash things
322         uta_rt_drop( NULL );
323         uta_epsock_rr( NULL, 0,  &more, &nn_sock, &ep );                        // drive null case for coverage
324         uta_add_rte( NULL, 99, 1 );
325         uta_get_rte( NULL, 0, 1000, TRUE );
326
327         fprintf( stderr, "[INFO] test: adding end points with nil data; warnings expected\n" );
328         uta_add_ep( NULL, NULL, "foo", 1 );
329         uta_add_ep( rt, NULL, "foo", 1 );
330
331         buf = uta_fib( ".gitignore" );
332         errors += fail_if_nil( buf, "buffer from read file into buffer" );
333         if( buf ) {
334                 free( buf );
335         }
336         buf = uta_fib( "no-file" );
337         errors += fail_if_nil( buf, "buffer from read file into buffer (no file)" );
338         if( buf ) {
339                 free( buf );
340         }
341
342         uta_rt_drop( rt );
343         rt = NULL;
344
345         if( (ctx = (uta_ctx_t *) malloc( sizeof( uta_ctx_t ) )) != NULL ) {
346                 memset( ctx, 0, sizeof( *ctx ) );
347
348                 if( (seed_fname = getenv( "RMR_SEED_RT" )) != NULL ) {
349                         read_static_rt( ctx, 0 );
350                         rt = ctx->rtable;
351                         errors += fail_if_nil( rt, "read seed table didn't generate a rtable pointer in context" );
352                         unsetenv( "RMR_SEED_RT" );                              // remove for next test
353                 }
354
355                 read_static_rt( ctx, 0 );                       // drive for not there coverage
356         }
357
358
359         buf = uta_fib( "no-suhch-file" );                       // drive some error checking for coverage
360         if( buf ) {
361                 free( buf );
362         }
363
364
365         ep = (endpoint_t *) malloc( sizeof( *ep ) );
366         pthread_mutex_init( &ep->gate, NULL );
367         ep->name = strdup( "worm" );
368         ep->addr = NULL;
369         state = uta_link2( ep );
370         errors += fail_if_true( state, "link2 did not return false when given nil pointers" );
371
372         // ----------------- test the meid support for looking up an endpoint based on the meid in the message -----
373
374         ctx->my_name = strdup( "my_host_name" );                // set up to load a rtable
375         ctx->my_ip = strdup( "192.168.1.30" );
376         gen_rt( ctx );                                                                  // generate a route table with meid entries and hang off ctx
377
378         mbuf = rmr_alloc_msg( ctx, 2048 );               //  buffer to play with
379         mbuf->len = 100;
380         rmr_str2meid( mbuf, "meid1" );                                  // id that we know is in the map
381
382         state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
383         errors += fail_if_nil( ep, "ep was nil when looking up ep with known meid in message" );
384         errors += fail_not_equal( state, 1, "state was not true when looking up ep with known meid in message" );
385
386         rmr_str2meid( mbuf, "XXXmeid1" );                               // id that we know is NOT in the map
387         state = epsock_meid( ctx->rtable, mbuf, &nn_sock, &ep );
388         errors += fail_not_nil( ep, "ep was NOT nil when looking ep up with unknown meid in message" );
389         errors += fail_not_equal( state, 0, "state was not false when looking up ep with unknown meid in message" );
390
391         return !!errors;                        // 1 or 0 regardless of count
392 }