enhance(API): Add multi-threaded call
[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 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:       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         if( (rmc = rmr_init( "4560", 1024, FL_NOTHREAD )) == NULL ) {
120                 fail_if_nil( rmc, "rmr_init returned a nil pointer "  );
121                 return 1;
122         }
123
124         if( (rmc2 = rmr_init( ":6789", 1024, FL_NOTHREAD )) == NULL ) {         // init without starting a thread
125                 errors += fail_if_nil( rmc, "rmr_init returned a nil pointer for non-threaded init "  );
126         }
127
128         free_ctx( rmc2 );                       // coverage
129
130         if( (rmc2 = rmr_init( NULL, 1024, FL_NOTHREAD )) == NULL ) {                    // drive default port selector code
131                 errors += fail_if_nil( rmc, "rmr_init returned a nil pointer when driving for default port "  );
132         }
133
134
135         v = rmr_ready( rmc );           // unknown return; not checking at the moment
136
137         msg = rmr_alloc_msg( NULL,  1024 );                                                                     // should return nil pointer
138         errors += fail_not_nil( msg, "rmr_alloc_msg didn't return nil when given nil context "  );
139
140         msg = rmr_alloc_msg( rmc, 2048 );                               // allocate larger than default size given on init
141         errors += fail_if_nil( msg, "rmr_alloc_msg returned nil msg pointer "  );
142
143         v = rmr_payload_size( NULL );
144         errors += fail_if( v >= 0, "rmr_payload_size returned valid size for nil message "  );
145         errors += fail_if( errno == 0, "rmr_payload_size did not set errno on failure "  );
146
147         v = rmr_payload_size( msg );
148         if( v >= 0 ) {
149                 errors += fail_not_equal( v, 2048, "rmr_payload_size returned invalid size (a) instead of expected size (b) "  );
150                 errors += fail_if( errno != 0, "rmr_payload_size did not clear errno on success "  );
151         } else {
152                 errors += fail_if( v < 0, "rmr_payload_size returned invalid size for good message "  );
153         }
154
155 #ifdef EMULATE_NNG
156         // this is only supported in nng, so behavour is different depending on the underlying library being tested
157         v = rmr_get_rcvfd( NULL );
158         errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor when given nil context "  );
159         v = rmr_get_rcvfd( rmc );
160         errors += fail_if( v < 0, "rmr_get_rcvfd did not return a valid file descriptor "  );
161 #else
162         v = rmr_get_rcvfd( NULL );
163         errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor when given nil context "  );
164         v = rmr_get_rcvfd( rmc );
165         errors += fail_if( v >= 0, "rmr_get_rcvfd returned a valid file descriptor (not supported in nanomsg)"  );
166 #endif
167
168         msg2 = rmr_send_msg( NULL, NULL );                      // drive for coverage
169         errors += fail_not_nil( msg2, "send_msg returned msg pointer when given a nil message and context "  );
170
171         msg->state = 0;
172         msg = rmr_send_msg( NULL, msg );
173         errors += fail_if( msg->state == 0, "rmr_send_msg did not set msg state when msg given with nil context "  );
174
175         // --- sends will fail with a no endpoint error until a dummy route table is set, so we test fail case first.
176         msg->len = 100;
177         msg->mtype = 1;
178         msg->state = 999;
179         errno = 999;
180         msg = rmr_send_msg( rmc, msg );
181         errors += fail_if_nil( msg, "send_msg_ did not return a message on send "  );
182         if( msg ) {
183                 errors += fail_not_equal( msg->state, RMR_ERR_NOENDPT, "send_msg did not return no endpoints before rtable added "  );
184                 errors += fail_if( errno == 0, "send_msg did not set errno "  );
185         }
186
187         gen_rt( rmc );          // --- after this point there is a dummy route table so send and rts calls should be ok
188
189         msg->len = 100;
190         msg->mtype = 1;
191         msg->state = 999;
192         errno = 999;
193         msg = rmr_send_msg( rmc, msg );
194         errors += fail_if_nil( msg, "send_msg_ did not return a message on send "  );
195         if( msg ) {
196                 errors += fail_not_equal( msg->state, RMR_OK, "send_msg returned bad status for send that should work "  );
197                 errors += fail_if( errno != 0, "send_msg set errno for send that should work "  );
198                 v = rmr_payload_size( msg );
199                 errors += fail_if( v != 2048, "send_msg did not allocate new buffer with correct size "  );
200         }
201
202         rmr_set_stimeout( NULL, 0 );
203         rmr_set_stimeout( rmc, 20 );
204         rmr_set_stimeout( rmc, -1 );
205         rmr_set_rtimeout( NULL, 0 );
206         rmr_set_rtimeout( rmc, 20 );
207         rmr_set_rtimeout( rmc, -1 );
208
209         msg2 = rmr_rcv_msg( NULL, NULL );
210         errors += fail_if( msg2 != NULL, "rmr_rcv_msg returned msg when given nil context and msg "  );
211
212         msg2 = rmr_rcv_msg( rmc, NULL );
213         errors += fail_if( msg2 == NULL, "rmr_rcv_msg returned nil msg when given nil msg "  );
214         if( msg2 ) {
215                 if( msg2->state != RMR_ERR_EMPTY ) {
216                         errors += fail_not_equal( msg2->state, RMR_OK, "receive given nil message did not return msg with good state (not empty) "  );
217                 }
218                 //errors += fail_not_equal( msg2->state, RMR_OK, "receive given nil message did not return msg with good state "  );
219         }
220
221
222         msg = rmr_rcv_msg( rmc, msg );
223         if( msg ) {
224                 errors += fail_not_equal( msg->state, RMR_OK, "rmr_rcv_msg did not return an ok state "  );
225                 errors += fail_not_equal( msg->len, 220, "rmr_rcv_msg returned message with invalid len "  );
226         } else {
227                 errors += fail_if_nil( msg, "rmr_rcv_msg returned a nil pointer "  );
228         }
229
230         rmr_rts_msg( NULL, NULL );                      // drive for coverage
231         rmr_rts_msg( rmc, NULL );
232         errors += fail_if( errno == 0, "rmr_rts_msg did not set errno when given a nil message "  );
233
234         msg->state = 0;
235         msg = rmr_rts_msg( NULL, msg );                 // should set state in msg
236         errors += fail_if_equal( msg->state, 0, "rmr_rts_msg did not set state when given valid message but no context "  );
237
238
239         msg = rmr_rts_msg( rmc, msg );                  // return the buffer to the sender
240         errors += fail_if_nil( msg, "rmr_rts_msg did not return a message pointer "  );
241         errors += fail_if( errno != 0, "rmr_rts_msg did not reset errno "  );
242
243
244
245         msg->state = 0;
246         msg = rmr_call( NULL, msg );
247         errors += fail_if( msg->state == 0, "rmr_call did not set message state when given message with nil context "  );
248
249         snprintf( msg->xaction, 17, "%015d", 16 );              // dummy transaction id (emulation generates, this should arrive after a few calls to recv)
250         msg->mtype = 0;
251         msg->sub_id = -1;
252         msg = rmr_call( rmc, msg );                                             // dummy nng/nano function will sequentually add xactions and should match or '16'
253         errors += fail_if_nil( msg, "rmr_call returned a nil message on call expected to succeed "  );
254         if( msg ) {
255                 errors += fail_not_equal( msg->state, RMR_OK, "rmr_call did not properly set state on successful return "  );
256                 errors += fail_not_equal( errno, 0, "rmr_call did not properly set errno (a) on successful return "  );
257         }
258
259         snprintf( wbuf, 17, "%015d", 14 );                              // while waiting, the queued messages should have #14, so issue a few receives looking for it
260         for( i = 0; i < 16; i++ ) {                                             // it should be in the first 15
261                 msg = rmr_rcv_msg( rmc, msg );
262                 if( msg ) {
263                         if( strcmp( wbuf, msg->xaction ) == 0 ) {               // found the queued message
264                                 break;
265                         }
266                         fprintf( stderr, "<INFO> msg: %s\n", msg->xaction );
267                 } else {
268                         errors += fail_if_nil( msg, "receive returnd nil msg while looking for queued message "  );
269                 }
270         }
271
272         errors += fail_if( i >= 16, "did not find expected message on queue "  );
273
274         if( ! msg ) {
275                 msg = rmr_alloc_msg( rmc, 2048 );                               // something buggered above; get a new one
276         }
277         msg->mtype = 0;
278         msg->sub_id = -1;
279         msg = rmr_call( rmc, msg );                                                     // make a call that we never expect a response on (nil pointer back)
280         errors += fail_not_nil( msg, "rmr_call returned a nil message on call expected not to receive a response "  );
281         errors += fail_if( errno == 0, "rmr_call did not set errno on failure "  );
282
283         rmr_free_msg( NULL );                   // drive for coverage; nothing to check
284         rmr_free_msg( msg2 );
285
286
287         msg2 = rmr_torcv_msg( NULL, NULL, 10 );
288         errors += fail_not_nil( msg2, "rmr_torcv_msg returned a pointer when given nil information "  );
289         msg2 = rmr_torcv_msg( rmc, NULL, 10 );
290         errors += fail_if_nil( msg2, "rmr_torcv_msg did not return a message pointer when given a nil old msg "  );
291
292         // ---  test timeout receive; our dummy epoll function will return 1 ready on first call and 0 ready (timeout emulation) on second
293         //              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.
294         msg = NULL;
295         for( i = 0; i < 40; i++ ) {
296                 msg = rmr_torcv_msg( rmc, msg, 10 );
297                 errors += fail_if_nil( msg, "torcv_msg returned nil msg when message expected "  );
298                 if( msg ) {
299                         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
300                                 break;
301                         }
302                 }
303         }
304         errors += fail_if( i >= 40, "torcv_msg never returned a timeout "  );
305
306
307         // ---- trace things that are not a part of the mbuf_api functions and thus must be tested here -------
308         state = rmr_init_trace( NULL, 37 );                                             // coverage test nil context
309         errors += fail_not_equal( state, 0, "attempt to initialise trace with nil context returned non-zero state (a) "  );
310         errors += fail_if_equal( errno, 0, "attempt to initialise trace with nil context did not set errno as expected "  );
311
312         state = rmr_init_trace( rmc, 37 );
313         errors += fail_if_equal( state, 0, "attempt to set trace len in context was not successful "  );
314         errors += fail_not_equal( errno, 0, "attempt to set trace len in context did not clear errno "  );
315
316         msg = rmr_tralloc_msg( rmc, 1024, 17, "1904308620110417" );
317         errors += fail_if_nil( msg, "attempt to allocate message with trace data returned nil message "  );
318         state = rmr_get_trace( msg, wbuf, 17 );
319         errors += fail_not_equal( state, 17, "len of trace data (a) returned after msg allocation was not expected size (b) "  );
320         state = strcmp( wbuf, "1904308620110417" );
321         errors += fail_not_equal( state, 0, "trace data returned after tralloc was not correct "  );
322
323         em_send_failures = 1;
324         send_n_msgs( rmc, 30 );                 // send 30 messages with emulation failures
325         em_send_failures = 0;
326
327
328         ((uta_ctx_t *)rmc)->shutdown = 1;
329         rmr_close( NULL );                      // drive for coverage
330         rmr_close( rmc );                       // no return to check; drive for coverage
331
332
333         // -- allocate a new context for mt-call and drive that stuff -----------------------------------------
334 #ifdef EMULATE_NNG
335         msg = fresh_msg( rmc, msg );                                                    // ensure we have one with known contents
336
337         msg->state = 0;
338         msg = rmr_mt_call( rmc, msg, 3, 10 );                                                   // drive when not in mt setup
339         if( msg ) {
340                 errors += fail_not_equal( msg->state, RMR_ERR_NOTSUPP, "rmr_mt_call did not set not supported error when not mt initialised" );
341         } else {
342                 errors += fail_if_nil( msg, "rmr_mt_call returned nil pointer when not mt initialised" );
343         }
344         msg = fresh_msg( rmc, msg );
345
346         msg = rmr_mt_rcv( rmc, msg, 10 );                                                               // gen not supported error if ctx not set for mt
347         if( msg ) {
348                 errors += fail_not_equal( msg->state, RMR_ERR_NOTSUPP, "rmr_mt_rcv did not return not supported state when mt not initialised" );
349         } else {
350                 errors += fail_if_nil( msg, "nil pointer from rmr_mt_rcv when mt not initialised\n" );
351         }
352         msg = fresh_msg( rmc, msg );
353
354         msg->state = 0;
355         if( msg ) {
356                 msg = rmr_mt_call( rmc, msg, 1000, 10 );                                                        // thread id out of range
357                 errors += fail_if_equal( msg->state, 0, "rmr_mt_call did not set an error when given an invalid call-id" );
358         } else {
359                 errors += fail_if_nil( msg, "rmr_mt_call returned a nil pointer when given an invalid call-id" );
360         }
361         msg = fresh_msg( rmc, msg );
362
363         state = init_mtcall( NULL );                                    // drive for coverage
364         errors += fail_not_equal( state, 0, "init_mtcall did not return false (a) when given a nil context pointer" );
365
366
367         if( (rmc = rmr_init( NULL, 1024, FL_NOTHREAD | RMRFL_MTCALL )) == NULL ) {                      // drive multi-call setup code without rtc thread
368                 errors += fail_if_nil( rmc, "rmr_init returned a nil pointer when driving for mt-call setup "  );
369         }
370
371         gen_rt( rmc );                                                                  // must attach a route table so sends succeed
372
373         fprintf( stderr, "<INFO> enabling mt messages\n" );
374         em_set_rcvdelay( 1 );                                                   // force slow msg rate during mt testing
375         em_set_mtc_msgs( 1 );                                                   // emulated nngrcv will now generate messages with call-id and call flag
376
377         msg->state = 0;
378         msg = rmr_mt_call( NULL, msg, 3, 10 );                  // should timeout
379         if( msg ) {
380                 errors += fail_if( msg->state == 0, "rmr_mt_call did not set message state when given message with nil context "  );
381         }
382         msg = fresh_msg( rmc, msg );
383
384         fprintf( stderr, "<INFO> invoking mt_call with timout == 2999\n" );
385         msg = rmr_mt_call( rmc, msg, 2, 2999 );                 // long timeout to drive time building code, should receive
386         if( msg ) {
387                 if( msg->state != RMR_OK ) {
388                         fprintf( stderr, "<INFO> rmr_mt_call returned error in mbuf: %d\n", msg->state );
389                 } else {
390                         errors += fail_not_nil( msg, "rmr_mt_call did not return a nil pointer on read timeout" );
391                 }
392         }
393         msg = fresh_msg( rmc, msg );
394
395         msg = rmr_mt_rcv( NULL, NULL, 10 );
396         errors += fail_not_nil( msg, "rmr_mt_rcv returned a non-nil message when given nil message and nil context" );
397
398         fprintf( stderr, "<INFO> invoking mt_rcv with timout == 2999\n" );
399         msg = fresh_msg( rmc, msg );
400         msg = rmr_mt_rcv( rmc, msg, 2999 );
401         if( !msg ) {
402                 errors += fail_if_nil( msg, "rmr_mt_rcv returned a nil message when given valid message and timeout of 29999" );
403         }
404         msg = fresh_msg( rmc, msg );
405
406         msg = rmr_mt_rcv( rmc, msg, -1 );
407         if( !msg ) {
408                 errors += fail_if_nil( msg, "rmr_mt_rcv returned a nil message when given valid message unlimited timeout" );
409         }
410         msg = fresh_msg( rmc, msg );
411
412         fprintf( stderr, "<INFO> waiting 20.0 seconds for a known call xaction to arrive (%ld)\n", time( NULL ) );
413         snprintf( msg->xaction, 17, "%015d", 5 );               // we'll reset receive counter before calling mt_call so this will arrive again
414         em_set_rcvcount( 0 );
415         msg = rmr_mt_call( rmc, msg, 2, 15000 );                // we need about 10s to get the message with the 'slow rate'
416         if( msg ) {
417                 errors += fail_not_equal( msg->state, RMR_OK, "mt_call with known xaction id bad state (a)" );
418         } else {
419                 errors += fail_if_nil( msg, "mt_call with known xaction id returned nil message" );
420         }
421         fprintf( stderr, "<INFO> time check: %ld\n", time( NULL ) );
422                 
423
424         em_set_mtc_msgs( 0 );                                                   // turn off 
425         em_set_rcvdelay( 0 );                                                   // full speed receive rate
426         ((uta_ctx_t *)rmc)->shutdown = 1;                               // force the mt-reciver attached to the context to stop
427 #endif
428
429
430         // --------------- phew, done ------------------------------------------------------------------------------
431
432         if( ! errors ) {
433                 fprintf( stderr, "<INFO> all RMr API tests pass\n" );
434         }
435         return !!errors;
436 }