Add json support
[ric-plt/xapp-frame-cpp.git] / test / jhash_test.cpp
1 // vim: ts=4 sw=4 noet :
2 /*
3 ==================================================================================
4        Copyright (c) 2020 Nokia
5        Copyright (c) 2020 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         Mnemonic:       json_test.cpp
23         Abstract:       Unit test for the json module. This expects that a static json
24                                 file exist in the current directory with a known set of fields,
25                                 arrays and objects that can be sussed out after parsing. The
26                                 expected file is test.json.
27
28         Date:           26 June 2020
29         Author:         E. Scott Daniels
30 */
31
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <string.h>
38
39 #include <string>
40 #include <memory>
41
42
43 /*
44         Very simple file reader. Reads up to 8k into a single buffer and
45         returns the buffer as char*.  Easier to put json test things in
46         a file than strings.
47 */
48 static char* read_jstring( char* fname ) {
49         char*   rbuf;
50         int     fd;
51         int len;
52
53         rbuf = (char *) malloc( sizeof( char ) * 8192 );
54         fd = open( fname, O_RDONLY, 0 );
55         if( fd < 0  ) {
56                 fprintf( stderr, "<ABORT> can't open test file: %s: %s\n", fname, strerror( errno ) );
57                 exit( 1 );
58         }
59
60         len = read( fd, rbuf, 8190 );
61         if( len < 0  ) {
62                 close( fd );
63                 fprintf( stderr, "<ABORT> read from file failed: %s: %s\n", fname, strerror( errno ) );
64                 exit( 1 );
65         }
66
67         rbuf[len] = 0;
68         close( fd );
69
70         return rbuf;
71 }
72
73 // this also tests jwrapper.c but that is built as a special object to link in
74 // rather than including here.
75 //
76 #include "../src/json/jhash.hpp"
77 #include "../src/json/jhash.cpp"
78
79 #include "ut_support.cpp"
80
81 int main( int argc, char** argv ) {
82         int             errors = 0;
83         Jhash*  jh;
84         char*   jstr;
85         std::string     sval;
86         double  val;
87         bool    state;
88         int             i;
89         int             len;
90         int             true_count = 0;
91
92         set_test_name( "jhash_test" );
93         jstr = read_jstring( (char *) "test.json" );                    // read and parse the json
94
95         fprintf( stderr, "read: (%s)\n", jstr );
96
97         jh = new Jhash( jstr );
98
99         if( jh == NULL ) {
100                 fprintf( stderr, "<FAIL> could not parse json string from: test.json\n" );
101                 exit( 1 );
102         }
103
104
105         sval = jh->String( (char *) "meeting_day" );
106         fprintf( stderr, "<INFO> sval=(%s)\n", sval.c_str() );
107         errors += fail_if( sval.compare( "" ) == 0, "did not get meeting day string" );
108         errors += fail_if( sval.compare( "Tuesday" ) != 0, "meeting day was not expected string" );
109
110         sval = jh->String( (char *) "meeting_place" );
111         fprintf( stderr, "<INFO> sval=(%s)\n", sval.c_str() );
112         errors += fail_if( sval.compare( "" ) == 0, "did not get meeting place" );
113         errors += fail_if( sval.compare( "16801 East Green Drive" ) != 0, "meeting place stirng was not correct" );
114
115         state = jh->Exists( (char *) "meeting_place" );
116         errors += fail_if( !state, "test for meeting place exists did not return true" );
117
118         state = jh->Exists( (char *) "no-name" );
119         errors += fail_if( state, "test for non-existant thing returned true" );
120
121         state = jh->Is_missing( (char *) "no-name" );
122         errors += fail_if( !state, "missing test for non-existant thing returned false" );
123
124         state = jh->Is_missing( (char *) "meeting_place" );
125         errors += fail_if( state, "missing test for existing thing returned true" );
126
127         val = jh->Value( (char *) "lodge_number" );
128         errors += fail_if( val != 41.0, "lodge number value was not correct" );
129
130         val = jh->Value( (char *) "monthly_dues" );
131         fprintf( stderr, "<INFO> got dues: %.2f\n", val );
132         errors += fail_if( val != (double) 43.5, "lodge dues value was not correct" );
133
134         len = jh->Array_len( (char *) "members" );
135         fprintf( stderr, "<INFO> got %d members\n", len );
136         errors += fail_if( len != 4, "array length was not correct" );
137         if( len > 0 ) {
138                 for( i = 0; i < len; i++ ) {
139                         if( ! jh->Set_blob_ele( (char *) "members", i ) ) {
140                                 errors++;
141                                 fprintf( stderr, (char *) "couldn't set blob for element %d\n", i );
142                         } else {
143                                 fprintf( stderr, (char *) "<INFO> testing element %d of %d\n", i, len );
144
145                                 state = jh->Is_value( (char *) "age" );
146                                 errors += fail_if( !state, "is value test for age returned false" );
147                                 state = jh->Is_value( (char *) "married" );
148                                 errors += fail_if( state, "is value test for married returned true" );
149
150                                 state = jh->Is_string( (char *) "occupation" );
151                                 errors += fail_if( !state, "is string test for spouse returned false" );
152                                 state = jh->Is_string( (char *) "married" );
153                                 errors += fail_if( state, "is string test for married returned true" );
154
155                                 state = jh->Is_bool( (char *) "married" );
156                                 errors += fail_if( !state, "is bool test for married returned false" );
157                                 state = jh->Is_bool( (char *) "occupation" );
158                                 errors += fail_if( state, "is bool test for spouse returned true" );
159
160                                 val = jh->Value( (char *) "age" );
161                                 fprintf( stderr, "<INFO> got age: %.2f\n", (double) val );
162                                 errors += fail_if( val < 0,  "age value wasn't positive" );
163
164                                 sval = jh->String( (char *) "name" );
165                                 fprintf( stderr, "<INFO> sval=(%s)\n", sval.c_str() );
166                                 errors += fail_if( sval.compare( "" ) == 0, "no name found in element" );
167
168                                 if( jh->Bool( (char *) "married" ) ) {
169                                         true_count++;
170                                 }
171                         }
172
173                         jh->Unset_blob();               // must return to root
174                 }
175
176                 fprintf( stderr, "<INFO> true count = %d\n", true_count );
177                 errors += fail_if( true_count != 3, "married == true count was not right" );
178         }
179
180         state = jh->Set_blob( (char *) "no-such-thing" );
181         errors += fail_if( state, "setting blob to non-existant blob returned true" );
182
183         state = jh->Set_blob( (char *) "grand_poobah" );
184         errors += fail_if( !state, "setting blob to existing blob failed" );
185         if( state ) {
186                 sval  = jh->String( (char *) "elected" );
187                 fprintf( stderr, "<INFO> sval=(%s)\n", sval.c_str() );
188                 errors += fail_if( sval != "February 2019", "blob 'elected' didn't return the expected string" );
189
190                 state = jh->Exists( (char *) "monthly_dues" );
191                 errors += fail_if( state, "blob that shouldn't have a field reports it does" );
192
193                 jh->Unset_blob( );                                                      // ensure that this is found once we unset to root
194                 state = jh->Exists( (char *) "monthly_dues" );
195                 errors += fail_if( !state, "after rest, root blob, that should have a field, reports it does not" );
196         }
197
198
199         // ---- test array element value type checks -------------------------------------------------
200         state = jh->Is_string_ele( (char *) "sponser", 1 );
201         errors += fail_if( !state, "string element check on sponser failed" );
202         state = jh->Is_string_ele( (char *) "current_on_dues", 1 );
203         errors += fail_if( state, "string element check on non-stirng element returned true" );
204
205         state = jh->Is_value_ele( (char *) "dues_assistance", 1 );
206         errors += fail_if( !state, "string element check on sponser failed" );
207         state = jh->Is_value_ele( (char *) "current_on_dues", 1 );
208         errors += fail_if( state, "string element check on non-stirng element returned true" );
209
210         state = jh->Is_bool_ele( (char *) "current_on_dues", 1 );
211         errors += fail_if( !state, "string element check on sponser failed" );
212         state = jh->Is_bool_ele( (char *) "sponser", 1 );
213         errors += fail_if( state, "string element check on non-stirng element returned true" );
214
215         state = jh->Is_null( (char *) "nvt" );
216         errors += fail_if( !state, "test for nil value returned false" );
217         state = jh->Is_null( (char *) "lodge_number" );
218         errors += fail_if( state, "nil test for non-nil value returned true" );
219
220         state = jh->Is_null_ele( (char *) "nvat", 0 );
221         errors += fail_if( !state, "test for nil array element value returned false" );
222
223
224         // ---- test sussing of elements from arrays -------------------------------------------------
225         sval = jh->String_ele( (char *) "sponser", 1 );
226         errors += fail_if( sval.compare( "" ) == 0, "get string element failed for sponser" );
227
228         val = jh->Value_ele( (char *) "dues_assistance", 1 );
229         errors += fail_if( val == 0.0, "get value element for dues_assistance was zero" );
230
231         state = jh->Bool_ele( (char *) "current_on_dues", 1 );
232         errors += fail_if( state, "bool ele test returned true for a false value" );
233         state = jh->Bool_ele( (char *) "current_on_dues", 0 );
234         errors += fail_if( !state, "bool ele test returned false for a true value" );
235
236
237         val = jh->Value( (char *) "timestamp" );
238         fprintf( stderr, "<INFO> timestamp: %.10f\n", val );
239
240
241
242
243
244         delete jh;
245
246         fprintf( stderr, "<INFO> testing for failures; jwrapper error and warning messages expected\n" );
247         // ---- these shouild all fail to parse, generate warnings to stderr, and drive error handling coverage ----
248     jh = new Jhash( (char *) "{ \"bad\": [ [ 1, 2, 3 ], [ 3, 4, 5]] }" );               // drive the exception process for bad json
249         delete jh;
250
251     jh = new Jhash( (char *) " \"bad\":  5 }" );                        // no opening brace
252         state = jh->Parse_errors();
253         errors += fail_if( !state, "parse errors check returned false when known errors exist" );
254         delete jh;
255
256     jh = new Jhash( (char *) "{ \"bad\":  fred }" );            // no quotes
257         delete jh;
258
259     jh = new Jhash( (char *) "{ \"bad:  456, \"good\": 100 }" );                        // missing quote; impossible to detect error
260         jh->Dump();                                                                                                                             // but dump should provide details
261         fprintf( stderr, "<INFO> good value=%d\n", (int) val );
262         delete jh;
263
264
265         // ---------------------------- end housekeeping ---------------------------
266         announce_results( errors );
267         return !!errors;
268 }