CI: Add silent cmake SonarCloud scan
[ric-plt/lib/rmr.git] / test / rmr_nng_api_static_test.c
1 // : vi ts=4 sw=4 noet :
2 /*
3 ==================================================================================
4             Copyright (c) 2019-2021 Nokia
5             Copyright (c) 2018-2021 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:       rmr_api_static_test.c
23         Abstract:       Specific tests related to the API functions in rmr_nng.c/rmr.c.
24                                 This should be included by a driver, but only the main RMr
25                                 driver and there will likely not be a specific stand alone driver
26                                 for just this small set of tests because of the depth of the
27                                 library needed to test at this level.
28
29                                 This can be used for both the nng and nanomsg outward facing
30                                 RMr API functions.
31
32                                 The message buffer specific API tests are in a different static
33                                 module.  API functions tested here are:
34                                          rmr_close
35                                          rmr_get_rcvfd
36                                          rmr_ready
37                                          rmr_init
38                                          rmr_set_rtimeout
39                                          rmr_set_stimeout
40                                          rmr_rcv_specific
41                                          rmr_torcv_msg
42                                          rmr_rcv_msg
43                                          rmr_call
44                                          rmr_rts_msg
45                                          rmr_send_msg
46                                          rmr_mtosend_msg
47                                          rmr_free_msg
48
49         Author:         E. Scott Daniels
50         Date:           5 April 2019
51 */
52
53 #include <unistd.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <strings.h>
57 #include <errno.h>
58 #include <string.h>
59 #include <stdint.h>
60 #include <pthread.h>
61 #include <semaphore.h>
62
63 #include "rmr.h"
64 #include "rmr_agnostic.h"
65
66 /*
67         Send a 'burst' of messages to drive some send retry failures to increase RMr coverage
68         by handling the retry caee.
69 */
70 static void send_n_msgs( void* ctx, int n ) {
71         rmr_mbuf_t*     msg;                    // message buffers
72         int i;
73
74         msg = rmr_alloc_msg( ctx,  1024 );
75         if( ! msg ) {
76                 return;
77         }
78
79         for( i = 0; i < n; i++ ) {
80                 //fprintf( stderr, "mass send\n" );
81                 msg->len = 100;
82                 msg->mtype = 1;
83                 msg->state = 999;
84                 errno = 999;
85                 msg = rmr_send_msg( ctx, msg );
86         }
87 }
88
89 /*
90         Refresh or allocate a message with some default values
91 */
92 static rmr_mbuf_t* fresh_msg( void* ctx, rmr_mbuf_t* msg ) {
93         if( ! msg )  {
94                 msg = rmr_alloc_msg( ctx, 2048 );
95         }
96
97         msg->mtype = 0;
98         msg->sub_id = -1;
99         msg->state = 0;
100         msg->len = 100;
101
102         return msg;
103 }
104
105 static int rmr_api_test( ) {
106         int             errors = 0;
107         void*   rmc;                            // route manager context
108         void*   rmc2;                           // second context for non-listener init
109         rmr_mbuf_t*     msg;                    // message buffers
110         rmr_mbuf_t*     msg2;
111         int             v = 0;                                  // some value
112         char    wbuf[128];
113         int             i;
114         int             state;
115
116         v = rmr_ready( NULL );
117         errors += fail_if( v != 0, "rmr_ready returned true before initialisation "  );
118
119         em_set_long_hostname( 1 );
120         if( (rmc = rmr_init( "4560", 1024, FL_NOTHREAD )) == NULL ) {
121                 fail_if_nil( rmc, "rmr_init returned a nil pointer "  );
122                 return 1;
123         }
124
125         setenv( "RMR_SRC_ID", "somehost", 1 );                                                          // context should have this as source
126         if( (rmc2 = rmr_init( ":6789", 1024, FL_NOTHREAD )) == NULL ) {         // init without starting a thread
127                 errors += fail_if_nil( rmc, "rmr_init returned a nil pointer for non-threaded init "  );
128         }
129
130         fprintf( stderr, "<INFO> with RMR_SRC_ID env set, source name in context: (%s)\n", ((uta_ctx_t *) rmc2)->my_name );
131         v = strcmp( ((uta_ctx_t *) rmc2)->my_name, "somehost:6789" );
132         errors += fail_not_equal( v, 0, "source name not set from environment variable (see previous info)" );
133         free_ctx( rmc2 );                       // coverage
134         
135         unsetenv( "RMR_SRC_ID" );                                                                                               // context should NOT have our artificial name 
136         if( (rmc2 = rmr_init( NULL, 1024, FL_NOTHREAD )) == NULL ) {                    // drive default port selector code
137                 errors += fail_if_nil( rmc, "rmr_init returned a nil pointer when driving for default port "  );
138         }
139
140         fprintf( stderr, "<INFO> after unset of RMR_SRC_ID, source name in context: (%s)\n", ((uta_ctx_t *) rmc2)->my_name );
141         v = strcmp( ((uta_ctx_t *) rmc2)->my_name, "somehost:6789" );
142         errors += fail_if_equal( v, 0, "source name smells when removed from environment (see previous info)" );
143         free_ctx( rmc2 );                       // attempt to reduce leak check errors
144
145         v = rmr_ready( rmc );           // unknown return; not checking at the moment
146
147         msg = rmr_alloc_msg( NULL,  1024 );                                                                     // should return nil pointer
148         errors += fail_not_nil( msg, "rmr_alloc_msg didn't return nil when given nil context "  );
149
150         
151         msg = rmr_alloc_msg( rmc, 2048 );                               // allocate larger than default size given on init
152         errors += fail_if_nil( msg, "rmr_alloc_msg returned nil msg pointer "  );
153         if( msg ) {
154                 rmr_get_srcip( msg, wbuf );
155                 errors += fail_if_equal( 0, strlen( wbuf ), "rmr_get_srcip did not did not return string with length (b) after alloc_msg" );
156                 fprintf( stderr, "<INFO> ip: %s\n", wbuf );
157         }
158
159
160         v = rmr_payload_size( NULL );
161         errors += fail_if( v >= 0, "rmr_payload_size returned valid size for nil message "  );
162         errors += fail_if( errno == 0, "rmr_payload_size did not set errno on failure "  );
163
164         v = rmr_payload_size( msg );
165         if( v >= 0 ) {
166                 errors += fail_not_equal( v, 2048, "rmr_payload_size returned invalid size (a) instead of expected size (b) "  );
167                 errors += fail_if( errno != 0, "rmr_payload_size did not clear errno on success "  );
168         } else {
169                 errors += fail_if( v < 0, "rmr_payload_size returned invalid size for good message "  );
170         }
171
172 #ifdef EMULATE_NNG
173         // this is only supported in nng, so behavour is different depending on the underlying library being tested
174         v = rmr_get_rcvfd( NULL );
175         errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor when given nil context "  );
176         v = rmr_get_rcvfd( rmc );
177         errors += fail_if( v < 0, "rmr_get_rcvfd did not return a valid file descriptor "  );
178 #else
179         v = rmr_get_rcvfd( NULL );
180         errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor when given nil context "  );
181         v = rmr_get_rcvfd( rmc );
182         errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor (not supported in nanomsg)"  );
183 #endif
184
185         msg2 = rmr_send_msg( NULL, NULL );                      // drive for coverage
186         errors += fail_not_nil( msg2, "send_msg returned msg pointer when given a nil message and context "  );
187
188         msg->state = 0;
189         msg = rmr_send_msg( NULL, msg );
190         errors += fail_if( msg->state == 0, "rmr_send_msg did not set msg state when msg given with nil context "  );
191
192         // --- sends will fail with a no endpoint error until a dummy route table is set, so we test fail case first.
193         msg->len = 100;
194         msg->mtype = 1;
195         msg->state = 999;
196         msg->tp_state = 999;
197         errno = 999;
198         msg = rmr_send_msg( rmc, msg );
199         errors += fail_if_nil( msg, "send_msg_ did not return a message on send "  );
200         if( msg ) {
201                 errors += fail_not_equal( msg->state, RMR_ERR_NOENDPT, "send_msg did not return no endpoints before rtable added "  );
202                 errors += fail_if( errno == 0, "send_msg did not set errno "  );
203                 errors += fail_if( msg->tp_state == 999, "send_msg did not set tp_state (1)" );
204         }
205
206         gen_rt( rmc );          // --- after this point there is a dummy route table so send and rts calls should be ok
207
208         msg->len = 100;
209         msg->mtype = 1;
210         msg->state = 999;
211         msg->tp_state = 999;
212         errno = 999;
213         msg = rmr_send_msg( rmc, msg );
214         errors += fail_if_nil( msg, "send_msg_ did not return a message on send "  );
215         if( msg ) {
216                 errors += fail_not_equal( msg->state, RMR_OK, "send_msg returned bad status for send that should work "  );
217                 errors += fail_if( errno != 0, "send_msg set errno for send that should work "  );
218                 v = rmr_payload_size( msg );
219                 errors += fail_if( v != 2048, "send_msg did not allocate new buffer with correct size "  );
220                 errors += fail_if( msg->tp_state == 999, "send_msg did not set tp_state (2)" );
221         }
222
223         rmr_set_stimeout( NULL, 0 );
224         rmr_set_stimeout( rmc, 20 );
225         rmr_set_stimeout( rmc, -1 );
226         rmr_set_rtimeout( NULL, 0 );
227         rmr_set_rtimeout( rmc, 20 );
228         rmr_set_rtimeout( rmc, -1 );
229
230         msg2 = rmr_rcv_msg( NULL, NULL );
231         errors += fail_if( msg2 != NULL, "rmr_rcv_msg returned msg when given nil context and msg "  );
232
233         msg2 = rmr_rcv_msg( rmc, NULL );
234         errors += fail_if( msg2 == NULL, "rmr_rcv_msg returned nil msg when given nil msg "  );
235         if( msg2 ) {
236                 if( msg2->state != RMR_ERR_EMPTY ) {
237                         errors += fail_not_equal( msg2->state, RMR_OK, "receive given nil message did not return msg with good state (not empty) "  );
238                 }
239         }
240
241
242         msg = rmr_rcv_msg( rmc, msg );
243         if( msg ) {
244                 errors += fail_not_equal( msg->state, RMR_OK, "rmr_rcv_msg did not return an ok state "  );
245                 errors += fail_not_equal( msg->len, 220, "rmr_rcv_msg returned message with invalid len "  );
246         } else {
247                 errors += fail_if_nil( msg, "rmr_rcv_msg returned a nil pointer "  );
248         }
249
250         rmr_rts_msg( NULL, NULL );                      // drive for coverage
251         rmr_rts_msg( rmc, NULL );
252         errors += fail_if( errno == 0, "rmr_rts_msg did not set errno when given a nil message "  );
253
254         msg->state = 0;
255         msg = rmr_rts_msg( NULL, msg );                 // should set state in msg
256         if( msg ) {
257                 errors += fail_if_equal( msg->state, 0, "rmr_rts_msg did not set state when given valid message but no context "  );
258         } else {
259                 errors += fail_if_nil( msg,  "rmr_rts_msg returned a nil msg when given a good one" );
260         }
261
262
263         msg = rmr_rts_msg( rmc, msg );                  // return the buffer to the sender
264         errors += fail_if_nil( msg, "rmr_rts_msg did not return a message pointer "  );
265         errors += fail_not_equal( msg->state, 0, "rts_msg did not return a good state (a) when expected" );
266         errors += fail_not_equal( errno, 0, "rmr_rts_msg did not reset errno (a) expected (b)"  );
267
268
269
270         msg->state = 0;
271         msg = rmr_call( NULL, msg );
272         errors += fail_if( msg->state == 0, "rmr_call did not set message state when given message with nil context "  );
273
274         snprintf( msg->xaction, 17, "%015d", 16 );              // dummy transaction id (emulation generates, this should arrive after a few calls to recv)
275         msg->mtype = 0;
276         msg->sub_id = -1;
277         em_set_rcvcount( 0 );                                                   // reset message counter
278         em_set_rcvdelay( 1 );                                                   // force slow msg rate during mt testing
279         msg = rmr_call( rmc, msg );                                             // dummy nng/nano function will sequentually add xactions and should match or '16'
280         errors += fail_if_nil( msg, "rmr_call returned a nil message on call expected to succeed "  );
281         if( msg ) {
282                 errors += fail_not_equal( msg->state, RMR_OK, "rmr_call did not properly set state on successful return "  );
283                 errors += fail_not_equal( errno, 0, "rmr_call did not properly set errno (a) on successful return "  );
284         }
285
286         snprintf( wbuf, 17, "%015d", 14 );                              // while waiting, the queued messages should have #14, so issue a few receives looking for it
287         for( i = 0; i < 16; i++ ) {                                             // it should be in the first 15
288                 msg = rmr_rcv_msg( rmc, msg );
289                 if( msg ) {
290                         if( strcmp( wbuf, msg->xaction ) == 0 ) {               // found the queued message
291                                 break;
292                         }
293                         fprintf( stderr, "<INFO> msg: %s\n", msg->xaction );
294                 } else {
295                         errors += fail_if_nil( msg, "receive returnd nil msg while looking for queued message "  );
296                 }
297         }
298
299         errors += fail_if( i >= 16, "did not find expected message on queue "  );
300
301         if( ! msg ) {
302                 msg = rmr_alloc_msg( rmc, 2048 );                               // something buggered above; get a new one
303         }
304         msg->mtype = 0;
305         msg->sub_id = -1;
306         msg = rmr_call( rmc, msg );                                                     // make a call that we never expect a response on (nil pointer back)
307         errors += fail_not_nil( msg, "rmr_call returned a nil message on call expected not to receive a response "  );
308         errors += fail_if( errno == 0, "rmr_call did not set errno on failure "  );
309
310         rmr_free_msg( NULL );                   // drive for coverage; nothing to check
311         rmr_free_msg( msg2 );
312
313
314         msg2 = rmr_torcv_msg( NULL, NULL, 10 );
315         errors += fail_not_nil( msg2, "rmr_torcv_msg returned a pointer when given nil information "  );
316         msg2 = rmr_torcv_msg( rmc, NULL, 10 );
317         errors += fail_if_nil( msg2, "rmr_torcv_msg did not return a message pointer when given a nil old msg "  );
318
319         // ---  test timeout receive; our dummy epoll function will return 1 ready on first call and 0 ready (timeout emulation) on second
320         //              however we must drain the swamp (queue) first, so run until we get a timeout error, or 20 and report error if we get to 20.
321         msg = NULL;
322         for( i = 0; i < 40; i++ ) {
323                 msg = rmr_torcv_msg( rmc, msg, 10 );
324                 errors += fail_if_nil( msg, "torcv_msg returned nil msg when message expected "  );
325                 if( msg ) {
326                         if( msg->state == RMR_ERR_TIMEOUT || msg->state == RMR_ERR_EMPTY ) {            // queue drained and we've seen both states from poll if we get a timeout
327                                 break;
328                         }
329                 }
330         }
331         errors += fail_if( i >= 40, "torcv_msg never returned a timeout "  );
332
333
334         // ---- trace things that are not a part of the mbuf_api functions and thus must be tested here -------
335         state = rmr_init_trace( NULL, 37 );                                             // coverage test nil context
336         errors += fail_not_equal( state, 0, "attempt to initialise trace with nil context returned non-zero state (a) "  );
337         errors += fail_if_equal( errno, 0, "attempt to initialise trace with nil context did not set errno as expected "  );
338
339         state = rmr_init_trace( rmc, 37 );
340         errors += fail_if_equal( state, 0, "attempt to set trace len in context was not successful "  );
341         errors += fail_not_equal( errno, 0, "attempt to set trace len in context did not clear errno "  );
342
343         msg = rmr_tralloc_msg( rmc, 1024, 17, "1904308620110417" );
344         errors += fail_if_nil( msg, "attempt to allocate message with trace data returned nil message "  );
345         state = rmr_get_trace( msg, wbuf, 17 );
346         errors += fail_not_equal( state, 17, "len of trace data (a) returned after msg allocation was not expected size (b) "  );
347         state = strcmp( wbuf, "1904308620110417" );
348         errors += fail_not_equal( state, 0, "trace data returned after tralloc was not correct "  );
349
350         em_send_failures = 1;
351         send_n_msgs( rmc, 30 );                 // send 30 messages with emulation failures
352         em_send_failures = 0;
353
354
355         ((uta_ctx_t *)rmc)->shutdown = 1;
356         rmr_close( NULL );                      // drive for coverage
357         rmr_close( rmc );                       // no return to check; drive for coverage
358
359
360         // -- allocate a new context for mt-call and drive that stuff -----------------------------------------
361 #ifdef EMULATE_NNG
362         msg = fresh_msg( rmc, msg );                                                    // ensure we have one with known contents
363
364         msg->state = 0;
365         msg = rmr_mt_call( rmc, msg, 3, 10 );                                                   // drive when not in mt setup
366         if( msg ) {
367                 errors += fail_not_equal( msg->state, RMR_ERR_NOTSUPP, "rmr_mt_call did not set not supported error when not mt initialised" );
368         } else {
369                 errors += fail_if_nil( msg, "rmr_mt_call returned nil pointer when not mt initialised" );
370         }
371         msg = fresh_msg( rmc, msg );
372
373         msg = rmr_mt_rcv( rmc, msg, 10 );                                                               // gen not supported error if ctx not set for mt
374         if( msg ) {
375                 errors += fail_not_equal( msg->state, RMR_ERR_NOTSUPP, "rmr_mt_rcv did not return not supported state when mt not initialised" );
376         } else {
377                 errors += fail_if_nil( msg, "nil pointer from rmr_mt_rcv when mt not initialised\n" );
378         }
379         msg = fresh_msg( rmc, msg );
380
381         msg->state = 0;
382         if( msg ) {
383                 msg = rmr_mt_call( rmc, msg, 1000, 10 );                                                        // thread id out of range
384                 errors += fail_if_equal( msg->state, 0, "rmr_mt_call did not set an error when given an invalid call-id" );
385         } else {
386                 errors += fail_if_nil( msg, "rmr_mt_call returned a nil pointer when given an invalid call-id" );
387         }
388         msg = fresh_msg( rmc, msg );
389
390         state = init_mtcall( NULL );                                    // drive for coverage
391         errors += fail_not_equal( state, 0, "init_mtcall did not return false (a) when given a nil context pointer" );
392
393
394         if( (rmc = rmr_init( NULL, 1024, FL_NOTHREAD | RMRFL_MTCALL )) == NULL ) {                      // drive multi-call setup code without rtc thread
395                 errors += fail_if_nil( rmc, "rmr_init returned a nil pointer when driving for mt-call setup "  );
396         }
397
398         gen_rt( rmc );                                                                  // must attach a route table so sends succeed
399
400         fprintf( stderr, "<INFO> enabling mt messages\n" );
401         em_set_rcvdelay( 1 );                                                   // force slow msg rate during mt testing
402         em_set_mtc_msgs( 1 );                                                   // emulated nngrcv will now generate messages with call-id and call flag
403
404         msg->state = 0;
405         msg = rmr_mt_call( NULL, msg, 3, 10 );                  // should timeout
406         if( msg ) {
407                 errors += fail_if( msg->state == 0, "rmr_mt_call did not set message state when given message with nil context "  );
408         }
409         msg = fresh_msg( rmc, msg );
410
411         fprintf( stderr, "<INFO> invoking mt_call with timout == 2999\n" );
412         msg = rmr_mt_call( rmc, msg, 2, 2999 );                 // long timeout to drive time building code, should receive
413         if( msg ) {
414                 if( msg->state != RMR_OK ) {
415                         fprintf( stderr, "<INFO> rmr_mt_call returned error in mbuf: %d\n", msg->state );
416                 } else {
417                         errors += fail_not_nil( msg, "rmr_mt_call did not return a nil pointer on read timeout" );
418                 }
419         }
420         msg = fresh_msg( rmc, msg );
421
422         msg = rmr_mt_rcv( NULL, NULL, 10 );
423         errors += fail_not_nil( msg, "rmr_mt_rcv returned a non-nil message when given nil message and nil context" );
424
425         fprintf( stderr, "<INFO> invoking mt_rcv with timout == 2999\n" );
426         msg = fresh_msg( rmc, msg );
427         msg = rmr_mt_rcv( rmc, msg, 2999 );
428         if( !msg ) {
429                 errors += fail_if_nil( msg, "rmr_mt_rcv returned a nil message when given valid message and timeout of 29999" );
430         }
431         msg = fresh_msg( rmc, msg );
432
433         msg = rmr_mt_rcv( rmc, msg, -1 );
434         if( !msg ) {
435                 errors += fail_if_nil( msg, "rmr_mt_rcv returned a nil message when given valid message unlimited timeout" );
436         }
437         msg = fresh_msg( rmc, msg );
438
439         fprintf( stderr, "<INFO> waiting 20.0 seconds for a known call xaction to arrive (%ld)\n", time( NULL ) );
440         snprintf( msg->xaction, 17, "%015d", 5 );               // we'll reset receive counter before calling mt_call so this will arrive again
441         em_set_rcvcount( 0 );
442         msg = rmr_mt_call( rmc, msg, 2, 15000 );                // we need about 10s to get the message with the 'slow rate'
443         if( msg ) {
444                 errors += fail_not_equal( msg->state, RMR_OK, "mt_call with known xaction id bad state (a)" );
445         } else {
446                 errors += fail_if_nil( msg, "mt_call with known xaction id returned nil message" );
447         }
448         fprintf( stderr, "<INFO> time check: %ld\n", time( NULL ) );
449
450         em_set_mtc_msgs( 0 );                                                   // turn off 
451         em_set_rcvdelay( 0 );                                                   // full speed receive rate to overflow the ring
452
453         fprintf( stderr, "<INFO> pausing 5s to allow mt-call receive ring to fill  %ld\n", time( NULL ) );
454         sleep( 2 );
455         fprintf( stderr, "<INFO> tests continuing %ld\n", time( NULL ) );
456         em_set_rcvdelay( 1 );                                                   // restore slow receive pace for any later tests
457         ((uta_ctx_t *)rmc)->shutdown = 1;                               // force the mt-reciver attached to the context to stop
458 #endif
459
460
461         // --------------- phew, done ------------------------------------------------------------------------------
462
463         if( ! errors ) {
464                 fprintf( stderr, "<INFO> all RMr API tests pass\n" );
465         }
466         return errors;
467 }