// vim: ts=4 sw=4 noet : /* ================================================================================== Copyright (c) 2020 Nokia Copyright (c) 2020 AT&T Intellectual Property. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================================================== */ /* Mnemonic: json_test.cpp Abstract: Unit test for the json module. This expects that a static json file exist in the current directory with a known set of fields, arrays and objects that can be sussed out after parsing. The expected file is test.json. Date: 26 June 2020 Author: E. Scott Daniels */ #include #include #include #include #include #include #include #include /* Very simple file reader. Reads up to 8k into a single buffer and returns the buffer as char*. Easier to put json test things in a file than strings. */ static char* read_jstring( char* fname ) { char* rbuf; int fd; int len; rbuf = (char *) malloc( sizeof( char ) * 8192 ); fd = open( fname, O_RDONLY, 0 ); if( fd < 0 ) { fprintf( stderr, " can't open test file: %s: %s\n", fname, strerror( errno ) ); exit( 1 ); } len = read( fd, rbuf, 8190 ); if( len < 0 ) { close( fd ); fprintf( stderr, " read from file failed: %s: %s\n", fname, strerror( errno ) ); exit( 1 ); } rbuf[len] = 0; close( fd ); return rbuf; } // this also tests jwrapper.c but that is built as a special object to link in // rather than including here. // #include "../src/json/jhash.hpp" #include "../src/json/jhash.cpp" #include "ut_support.cpp" int main( int argc, char** argv ) { int errors = 0; xapp::Jhash* jh; char* jstr; std::string sval; double val; bool state; int i; int len; int true_count = 0; set_test_name( "jhash_test" ); jstr = read_jstring( (char *) "test.json" ); // read and parse the json fprintf( stderr, "read: (%s)\n", jstr ); jh = new xapp::Jhash( jstr ); free( jstr ); if( jh == NULL ) { fprintf( stderr, " could not parse json string from: test.json\n" ); exit( 1 ); } sval = jh->String( (char *) "meeting_day" ); fprintf( stderr, " sval=(%s)\n", sval.c_str() ); errors += fail_if( sval.compare( "" ) == 0, "did not get meeting day string" ); errors += fail_if( sval.compare( "Tuesday" ) != 0, "meeting day was not expected string" ); sval = jh->String( (char *) "meeting_place" ); fprintf( stderr, " sval=(%s)\n", sval.c_str() ); errors += fail_if( sval.compare( "" ) == 0, "did not get meeting place" ); errors += fail_if( sval.compare( "16801 East Green Drive" ) != 0, "meeting place stirng was not correct" ); state = jh->Exists( (char *) "meeting_place" ); errors += fail_if( !state, "test for meeting place exists did not return true" ); state = jh->Exists( (char *) "no-name" ); errors += fail_if( state, "test for non-existant thing returned true" ); state = jh->Is_missing( (char *) "no-name" ); errors += fail_if( !state, "missing test for non-existant thing returned false" ); state = jh->Is_missing( (char *) "meeting_place" ); errors += fail_if( state, "missing test for existing thing returned true" ); val = jh->Value( (char *) "lodge_number" ); errors += fail_if( val != 41.0, "lodge number value was not correct" ); val = jh->Value( (char *) "monthly_dues" ); fprintf( stderr, " got dues: %.2f\n", val ); errors += fail_if( val != (double) 43.5, "lodge dues value was not correct" ); len = jh->Array_len( (char *) "members" ); fprintf( stderr, " got %d members\n", len ); errors += fail_if( len != 4, "array length was not correct" ); if( len > 0 ) { for( i = 0; i < len; i++ ) { if( ! jh->Set_blob_ele( (char *) "members", i ) ) { errors++; fprintf( stderr, (char *) "couldn't set blob for element %d\n", i ); } else { fprintf( stderr, (char *) " testing element %d of %d\n", i, len ); state = jh->Is_value( (char *) "age" ); errors += fail_if( !state, "is value test for age returned false" ); state = jh->Is_value( (char *) "married" ); errors += fail_if( state, "is value test for married returned true" ); state = jh->Is_string( (char *) "occupation" ); errors += fail_if( !state, "is string test for spouse returned false" ); state = jh->Is_string( (char *) "married" ); errors += fail_if( state, "is string test for married returned true" ); state = jh->Is_bool( (char *) "married" ); errors += fail_if( !state, "is bool test for married returned false" ); state = jh->Is_bool( (char *) "occupation" ); errors += fail_if( state, "is bool test for spouse returned true" ); val = jh->Value( (char *) "age" ); fprintf( stderr, " got age: %.2f\n", (double) val ); errors += fail_if( val < 0, "age value wasn't positive" ); sval = jh->String( (char *) "name" ); fprintf( stderr, " sval=(%s)\n", sval.c_str() ); errors += fail_if( sval.compare( "" ) == 0, "no name found in element" ); if( jh->Bool( (char *) "married" ) ) { true_count++; } } jh->Unset_blob(); // must return to root } fprintf( stderr, " true count = %d\n", true_count ); errors += fail_if( true_count != 3, "married == true count was not right" ); } state = jh->Set_blob( (char *) "no-such-thing" ); errors += fail_if( state, "setting blob to non-existant blob returned true" ); state = jh->Set_blob( (char *) "grand_poobah" ); errors += fail_if( !state, "setting blob to existing blob failed" ); if( state ) { sval = jh->String( (char *) "elected" ); fprintf( stderr, " sval=(%s)\n", sval.c_str() ); errors += fail_if( sval != "February 2019", "blob 'elected' didn't return the expected string" ); state = jh->Exists( (char *) "monthly_dues" ); errors += fail_if( state, "blob that shouldn't have a field reports it does" ); jh->Unset_blob( ); // ensure that this is found once we unset to root state = jh->Exists( (char *) "monthly_dues" ); errors += fail_if( !state, "after rest, root blob, that should have a field, reports it does not" ); } // ---- test array element value type checks ------------------------------------------------- state = jh->Is_string_ele( (char *) "sponser", 1 ); errors += fail_if( !state, "string element check on sponser failed" ); state = jh->Is_string_ele( (char *) "current_on_dues", 1 ); errors += fail_if( state, "string element check on non-stirng element returned true" ); state = jh->Is_value_ele( (char *) "dues_assistance", 1 ); errors += fail_if( !state, "value element type check on value element reported false" ); state = jh->Is_value_ele( (char *) "current_on_dues", 1 ); errors += fail_if( state, "value element type check on non-value element returned true" ); state = jh->Is_bool_ele( (char *) "current_on_dues", 1 ); errors += fail_if( !state, "string element check on sponser failed" ); state = jh->Is_bool_ele( (char *) "sponser", 1 ); errors += fail_if( state, "string element check on non-stirng element returned true" ); state = jh->Is_null( (char *) "nvt" ); errors += fail_if( !state, "test for nil value returned false" ); state = jh->Is_null( (char *) "lodge_number" ); errors += fail_if( state, "nil test for non-nil value returned true" ); state = jh->Is_null_ele( (char *) "nvat", 0 ); errors += fail_if( !state, "test for nil array element value returned false" ); // ---- test sussing of elements from arrays ------------------------------------------------- sval = jh->String_ele( (char *) "sponser", 1 ); errors += fail_if( sval.compare( "" ) == 0, "get string element failed for sponser (empty string)" ); errors += fail_if( sval.compare( "slate" ) != 0, "get string element failed for sponser (wrong value for[1])" ); sval = jh->String_ele( (char *) "sponser", 0 ); errors += fail_if( sval.compare( "slate" ) != 0, "get string element failed for sponser (wrong value for [0])" ); sval = jh->String_ele( (char *) "sponser", 3 ); errors += fail_if( sval.compare( "brick" ) != 0, "get string element failed for sponser (wrong value for [3])" ); val = jh->Value_ele( (char *) "dues_assistance", 1 ); errors += fail_if( val == 0.0, "get value element for dues_assistance was zero" ); state = jh->Bool_ele( (char *) "current_on_dues", 1 ); errors += fail_if( state, "bool ele test returned true for a false value" ); state = jh->Bool_ele( (char *) "current_on_dues", 0 ); errors += fail_if( !state, "bool ele test returned false for a true value" ); val = jh->Value( (char *) "timestamp" ); fprintf( stderr, " timestamp: %.10f\n", val ); jh->Dump(); // for coverage of debug things // ----- jhashes can be moved, drive that logic for coverage xapp::Jhash j2( "{}" ); xapp::Jhash j1 = std::move( *jh ); // drives move constructor function j2 = std::move( j1 ); // drives move operator function delete jh; fprintf( stderr, " testing for failures; jwrapper error and warning messages expected\n" ); // ---- these shouild all fail to parse, generate warnings to stderr, and drive error handling coverage ---- jh = new xapp::Jhash( (char *) "{ \"bad\": [ [ 1, 2, 3 ], [ 3, 4, 5]] }" ); // drive the exception process for bad json delete jh; jh = new xapp::Jhash( (char *) " \"bad\": 5 }" ); // no opening brace state = jh->Parse_errors(); errors += fail_if( !state, "parse errors check returned false when known errors exist" ); delete jh; jh = new xapp::Jhash( (char *) "{ \"bad\": fred }" ); // no quotes delete jh; jh = new xapp::Jhash( (char *) "{ \"bad: 456, \"good\": 100 }" ); // missing quote; impossible to detect error jh->Dump(); // but dump should provide details fprintf( stderr, " good value=%d\n", (int) val ); delete jh; // ---------------------------- end housekeeping --------------------------- announce_results( errors ); return !!errors; }