Add health check to the MC-listener application
[ric-app/mc.git] / sidecars / listener / unit_test.c
1 // vim: ts=4 sw=4 noet:
2 /*
3  --------------------------------------------------------------------------------
4         Copyright (c) 2018-2019 AT&T Intellectual Property.
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10            http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17  --------------------------------------------------------------------------------
18 */
19
20 /*
21         Mnemonic:       unit_test.c
22         Abstract:       Basic unit tests for the mc listener.
23         Date:           22 August 2019
24         Author:         E. Scott Daniels
25 */
26
27 #define FOREVER 0                       // allows forever loops in mcl code to escape after one loop
28
29 #include <unistd.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <time.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <signal.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39
40 #define TEST_MTYPE      1000            // message type for testing
41 #include "test_rmr_em.c"                // emulated rmr functions (for receives)
42
43 // this/these are what we are testing; include them directly (must be after forever def)
44 #include "mcl.c"
45 #include "rdc.c"
46
47 /*
48         Set up env things for the rdc setup call.
49 */
50 static void set_env() {
51         setenv( "MCL_RDC_ENABLE", "1", 1 );                                                                     // cause 'raw' capture to be setup
52         setenv( "MCL_RDC_STAGE", "/tmp/mc_listener_test/stage", 1 );            // unit test script should create and will purge after
53         setenv( "MCL_RDC_FINAL", "/tmp/mc_listener_test/final", 1 );
54         setenv( "MCL_RDC_SUFFIX", ".xxx", 1 );
55         setenv( "MCL_RDC_DONE", ".done", 1 );
56         setenv( "MCL_RDC_FREQ", "10", 1 );
57 }
58
59 /*
60         Parms:  [fifo-dir-name]
61 */
62 int main( int argc,  char** argv ) {
63         void*   ctx;
64         void*   bad_ctx;                                // context with a bad directory path for coverage/error testing
65         int             errors = 0;
66         char*   dname = "/tmp/fifos";
67         char*   port = "4560";
68         int             fd;
69         int             fd2;
70         int             rfd;                                    // a reader file des so we can read what we write
71         fifo_t* fref = NULL;                    // fifo type reference; we just verify that suss sets it
72         char    wbuf[2048];
73         char    payload[1024];
74         char*   bp;
75         void*   buf;
76         int state;
77         char    timestamp[1024];                // read will put a timestamp here
78
79         if( argc > 1 ) {
80                 dname = argv[1];
81         }
82         
83         set_env();                                                      // set env that setup_rdc() looks for
84
85         ctx = mcl_mk_context( dname );                  // allocate the context
86         if( ctx == NULL ) {
87                 fprintf( stderr, "[FAIL] couldn't make context\n" );
88                 exit( 1 );
89         }
90
91         mcl_set_sigh();                                                                         // prevent colobber from broken pipe
92
93         open_fifo( ctx, TEST_MTYPE, WRITER );                                           // open dummy to prevent blocking reader
94         rfd = open_fifo( ctx, TEST_MTYPE, READER );                             // open a reader to check fanout output
95         if( rfd < 0 ) {
96                 fprintf( stderr, "[FAIL] unable to open a pipe reader for type == 100\n" );
97                 errors++;
98         }
99
100         fd = suss_fifo( ctx, TEST_MTYPE, WRITER, &fref );                       // should open the file for writing and return the fdes
101         if( fd < 0 ) {
102                 fprintf( stderr, "[FAIL] suss_fifo did not return a valid fd\n" );
103                 errors++;
104         }
105
106         if( fref == NULL ) {
107                 fprintf( stderr, "[FAIL] suss_fifo did not set the fifo reference pointer\n" );
108                 errors++;
109         } else {
110                 chalk_ok( fref );
111                 chalk_error( fref );
112         }
113
114         fd2= suss_fifo( ctx, TEST_MTYPE, 0, NULL );                             // should open the file file for reading and return a different fd
115         if( fd < 0 ) {
116                 fprintf( stderr, "[FAIL] suss_fifo did not return a valid fd\n" );
117                 errors++;
118         }
119         if( fd == fd2 ) {
120                 fprintf( stderr, "[FAIL] reading and writing fifo file descriptors expected to differ; both were %d\n", fd );
121                 errors++;
122         }
123
124         mcl_start_listening( ctx, port, 0 );                    // start the listener
125
126         // under test, the FOREVER = 0 keeps fanout from blocking; drive several times to cover all cases
127         mcl_fifo_fanout( ctx, 5, 1 );                                   // first rmr receive call will simulate a timeout
128         mcl_fifo_fanout( ctx, 5, 1 );                                   // second receive simulates a health check
129         mcl_fifo_fanout( ctx, 5, 1 );                                   // 3-n return alternating timeout messages; drive so that 
130         mcl_fifo_fanout( ctx, 5, 1 );                                   // we will have several land in the FIFO
131         mcl_fifo_fanout( ctx, 5, 1 );
132         mcl_fifo_fanout( ctx, 5, 1 );
133         mcl_fifo_fanout( ctx, 5, 1 );
134
135         *timestamp = 0;
136         state = mcl_fifo_read1( ctx, TEST_MTYPE, payload, sizeof( payload ), TRUE );
137         if( state < 1 ) {
138                 fprintf( stderr, "[FAIL] fifo_read return positive value when expected to\n" );
139                 errors++;
140         }
141         state = mcl_fifo_tsread1( ctx, TEST_MTYPE, payload, sizeof( payload ), TRUE, timestamp );
142         if( state < 1 ) {
143                 fprintf( stderr, "[FAIL] fifo_read with timestamp return positive value when expected to\n" );
144                 errors++;
145         }
146
147         state = fifo_read1( NULL, TEST_MTYPE, payload, sizeof( payload ), 1, timestamp );               // coverage error check
148         if( state != 0 ) {
149                 fprintf( stderr, "[FAIL] fifo_read didn't return 0 when given a nil context to\n" );
150                 errors++;
151         }
152
153         mcl_fifo_fanout( ctx, 5, 0 );                                   // test with writing short header
154         mcl_fifo_fanout( ctx, 5, 0 );
155
156         // ------ some error/coverage testing ---------------------------
157         logit( LOG_CRIT, "critical message" );
158         logit( LOG_ERR, "error message" );
159         logit( LOG_WARN, "warning message" );
160         logit( LOG_STAT, "stats message" );
161
162         bad_ctx = mcl_mk_context( "/nosuchdirectoryinthesystem" );              // create a context where fifo opens should fail
163         if( bad_ctx == NULL ) {
164                 fprintf( stderr, "[FAIL] couldn't make 'bad' context" );
165                 exit( 1 );
166         }
167
168         fref = NULL;
169         fd = suss_fifo( bad_ctx, TEST_MTYPE, 1, &fref );                                // should fail to open the file for writing beacuse directory is bad
170         if( fd >= 0 ) {
171                 fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a context with a bad directory path\n" );
172                 errors++;
173         }
174         if( fref != NULL ) {
175                 fprintf( stderr, "[FAIL] suss_fifo returned an fref pointer when given a bad context\n" );
176                 errors++;
177         }
178
179         fd = suss_fifo( NULL, TEST_MTYPE, 1, &fref );                           // coverage nil pointer check
180         if( fd >= 0 ) {
181                 fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a nil context a bad directory path\n" );
182                 errors++;
183         }
184
185         fd = suss_fifo( ctx, -1, 1, &fref );                            // mad message type check
186         if( fd >= 0 ) {
187                 fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a bad message type\n" );
188                 errors++;
189         }
190
191         // -- buffer testing ------------------------------------------------------
192         bp = build_hdr( 1024, wbuf, 0 );
193         bp = build_hdr( 1024, NULL, 0 );
194         if( bp == NULL ) {
195                 fprintf( stderr, "[FAIL] build_hdr didn't return a buffer pointer when given a nil buffer\n" );
196                 errors++;
197         }
198         free( bp );
199
200         bp = build_hdr( 1024, wbuf, sizeof( wbuf ) );
201         if( bp == NULL ) {
202                 fprintf( stderr, "[FAIL] build_hdr didn't return a buffer pointer\n" );
203                 errors++;
204         }
205
206
207         // ----- msg receive testing ----------------------------------------------------
208         buf = mcl_get_msg( NULL, NULL, 1 );                     // drive nil pointer checks
209         if( buf != NULL ) {
210                 errors++;
211                 fprintf( stderr, "[FAIL], get_msg call with nil context returned a buffer pointer\n" );
212         }
213
214         buf = mcl_get_msg( ctx, NULL, 1 );                      // drive to force coverage; nothing is sent, so we can't validate buffer
215
216
217         mcl_fifo_one( NULL, NULL, 1, 1 );
218         mcl_fifo_one( ctx, NULL, 1, 1 );
219         mcl_fifo_one( ctx, wbuf, 0, 1 );
220         mcl_fifo_one( ctx, wbuf, 10, 100 );
221
222
223         // --- some rdc testing as best as we can without message generators --------
224         rdc_init( NULL, NULL, ".foo", ".bar" ); // coverage testing
225
226         ctx = setup_rdc();                                              // coverage test to ensure that it generates a context
227         if( ctx == NULL ) {
228                 fprintf( stderr, "[FAIL] setup_rdc did not return a context pointer\n" );
229                 errors++;
230         }
231
232         rdc_set_freq( NULL, 0 );                                        // error/nil test
233         rdc_set_freq( ctx, 0 );                                 // error/nil test
234         rdc_set_freq( ctx, 10 );                                // roll after 10seconds to test that
235
236         build_hdr( 1024, wbuf, sizeof( wbuf ) );
237         bp = NULL;
238         bp = rdc_init_buf( TEST_MTYPE, wbuf, 10, bp );                                  // set up for write
239         rdc_write( ctx, bp, payload, sizeof( payload ) );                               // write the raw data
240
241         fprintf( stderr, "[INFO] pausing to test rdc file rolling\n" );
242         sleep( 15 );
243         build_hdr( 1024, wbuf, sizeof( wbuf ) );
244         bp = NULL;
245         bp = rdc_init_buf( TEST_MTYPE, wbuf, 10, bp );
246         rdc_write( ctx, bp, payload, sizeof( payload ) );
247
248
249         // CAUTION:  filenames need to match those expected in the run script as it creates src, and will validate, destination files
250         state = copy_unlink( "/tmp/mc_listener_test/no-such-copy_src", "/tmp/mc_listener_test/copy_dest", 0664 );  // first couple drive for error and coverage
251         if( state >= 0 ) {
252                 fprintf( stderr, "[FAIL] copy-unlink of bad file didn't return bad state\n" );
253                 errors++;
254         }
255         state = copy_unlink( "/tmp/mc_listener_test/copy_src", "/tmp/mc_listener_test-nodir/copy_dest", 0664 );
256         if( state >= 0 ) {
257                 fprintf( stderr, "[FAIL] copy-unlink of bad target didn't return bad state\n" );
258                 errors++;
259         }
260         state = copy_unlink( "/tmp/mc_listener_test/copy_src", "/tmp/mc_listener_test/copy_dest", 0664 );  // drive for coverage; setup script can check contents
261         if( state < 0 ) {
262                 fprintf( stderr, "[FAIL] copy-unlink expected success but failed\n" );
263                 errors++;
264         }
265         state = mvocp( "/tmp/mc_listener_test/bad-src-mv_src", "/tmp/mc_listener_test/mv_dest" );
266         if( state >= 0 ) {
267                 fprintf( stderr, "[FAIL] mv or copy expected failure didn't set bad state\n" );
268                 errors++;
269         }
270         state = mvocp( "/tmp/mc_listener_test/mv_src", "/tmp/mc_listener_test/mv_dest" );
271         if( state < 0 ) {
272                 fprintf( stderr, "[FAIL] mv or copy expected to succeed  didn't set good state\n" );
273                 errors++;
274         }
275
276
277         // ---- finally, check error count, write nice cheerful message and exit ----
278         if( ! errors ) {
279                 fprintf( stderr, "[PASS] unit_test: everything looks peachy\n" );
280         } else {
281                 fprintf( stderr, "[FAIL] unit_test: there were %d errors\n", errors );
282         }
283
284         return errors != 0;
285 }
286