Add CMake to drive unit tests in sidecars
[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         fprintf( stderr, "[INFO] expected create fail message should follow\n" );
163         bad_ctx = mcl_mk_context( "/nosuchdirectoryinthesystem" );              // create a context where fifo opens should fail
164         if( bad_ctx == NULL ) {
165                 fprintf( stderr, "[FAIL] couldn't make 'bad' context" );
166                 exit( 1 );
167         }
168
169         fref = NULL;
170         fd = suss_fifo( bad_ctx, TEST_MTYPE, 1, &fref );                                // should fail to open the file for writing beacuse directory is bad
171         if( fd >= 0 ) {
172                 fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a context with a bad directory path\n" );
173                 errors++;
174         }
175         if( fref != NULL ) {
176                 fprintf( stderr, "[FAIL] suss_fifo returned an fref pointer when given a bad context\n" );
177                 errors++;
178         }
179
180         fd = suss_fifo( NULL, TEST_MTYPE, 1, &fref );                           // coverage nil pointer check
181         if( fd >= 0 ) {
182                 fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a nil context a bad directory path\n" );
183                 errors++;
184         }
185
186         fd = suss_fifo( ctx, -1, 1, &fref );                            // mad message type check
187         if( fd >= 0 ) {
188                 fprintf( stderr, "[FAIL] suss_fifo returned a valid fd when given a bad message type\n" );
189                 errors++;
190         }
191
192         // -- buffer testing ------------------------------------------------------
193         bp = build_hdr( 1024, wbuf, 0 );
194         bp = build_hdr( 1024, NULL, 0 );
195         if( bp == NULL ) {
196                 fprintf( stderr, "[FAIL] build_hdr didn't return a buffer pointer when given a nil buffer\n" );
197                 errors++;
198         }
199         free( bp );
200
201         bp = build_hdr( 1024, wbuf, sizeof( wbuf ) );
202         if( bp == NULL ) {
203                 fprintf( stderr, "[FAIL] build_hdr didn't return a buffer pointer\n" );
204                 errors++;
205         }
206
207
208         // ----- msg receive testing ----------------------------------------------------
209         buf = mcl_get_msg( NULL, NULL, 1 );                     // drive nil pointer checks
210         if( buf != NULL ) {
211                 errors++;
212                 fprintf( stderr, "[FAIL], get_msg call with nil context returned a buffer pointer\n" );
213         }
214
215         buf = mcl_get_msg( ctx, NULL, 1 );                      // drive to force coverage; nothing is sent, so we can't validate buffer
216
217
218         mcl_fifo_one( NULL, NULL, 1, 1 );
219         mcl_fifo_one( ctx, NULL, 1, 1 );
220         mcl_fifo_one( ctx, wbuf, 0, 1 );
221         mcl_fifo_one( ctx, wbuf, 10, 100 );
222
223
224         // --- some rdc testing as best as we can without message generators --------
225         rdc_init( NULL, NULL, ".foo", ".bar" ); // coverage testing
226
227         ctx = setup_rdc();                                              // coverage test to ensure that it generates a context
228         if( ctx == NULL ) {
229                 fprintf( stderr, "[FAIL] setup_rdc did not return a context pointer\n" );
230                 errors++;
231         }
232
233         rdc_set_freq( NULL, 0 );                                        // error/nil test
234         rdc_set_freq( ctx, 0 );                                 // error/nil test
235         rdc_set_freq( ctx, 10 );                                // roll after 10seconds to test that
236
237         build_hdr( 1024, wbuf, sizeof( wbuf ) );
238         bp = NULL;
239         bp = rdc_init_buf( TEST_MTYPE, wbuf, 10, bp );                                  // set up for write
240         rdc_write( ctx, bp, payload, sizeof( payload ) );                               // write the raw data
241
242         fprintf( stderr, "[INFO] pausing to test rdc file rolling\n" );
243         sleep( 15 );
244         build_hdr( 1024, wbuf, sizeof( wbuf ) );
245         bp = NULL;
246         bp = rdc_init_buf( TEST_MTYPE, wbuf, 10, bp );
247         rdc_write( ctx, bp, payload, sizeof( payload ) );
248
249
250         // CAUTION:  filenames need to match those expected in the run script as it creates src, and will validate, destination files
251         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
252         if( state >= 0 ) {
253                 fprintf( stderr, "[FAIL] copy-unlink of bad file didn't return bad state\n" );
254                 errors++;
255         }
256         state = copy_unlink( "/tmp/mc_listener_test/copy_src", "/tmp/mc_listener_test-nodir/copy_dest", 0664 );
257         if( state >= 0 ) {
258                 fprintf( stderr, "[FAIL] copy-unlink of bad target didn't return bad state\n" );
259                 errors++;
260         }
261         state = copy_unlink( "/tmp/mc_listener_test/copy_src", "/tmp/mc_listener_test/copy_dest", 0664 );  // drive for coverage; setup script can check contents
262         if( state < 0 ) {
263                 fprintf( stderr, "[FAIL] copy-unlink expected success but failed\n" );
264                 errors++;
265         }
266         state = mvocp( "/tmp/mc_listener_test/bad-src-mv_src", "/tmp/mc_listener_test/mv_dest" );
267         if( state >= 0 ) {
268                 fprintf( stderr, "[FAIL] mv or copy expected failure didn't set bad state\n" );
269                 errors++;
270         }
271         state = mvocp( "/tmp/mc_listener_test/mv_src", "/tmp/mc_listener_test/mv_dest" );
272         if( state < 0 ) {
273                 fprintf( stderr, "[FAIL] mv or copy expected to succeed  didn't set good state\n" );
274                 errors++;
275         }
276
277
278         // ---- finally, check error count, write nice cheerful message and exit ----
279         if( ! errors ) {
280                 fprintf( stderr, "[PASS] unit_test: everything looks peachy\n" );
281         } else {
282                 fprintf( stderr, "[FAIL] unit_test: there were %d errors\n", errors );
283         }
284
285         return errors != 0;
286 }
287