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