diff --git a/rjp++/CMakeLists.txt b/rjp++/CMakeLists.txt index e27d642..f2612e4 100644 --- a/rjp++/CMakeLists.txt +++ b/rjp++/CMakeLists.txt @@ -9,6 +9,7 @@ set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include) include_directories("${INCLUDE_PATH}") option(ENABLE_SHARED "Build shared library" OFF) +option(BUILD_TESTS "Build test programs" OFF) option(ENABLE_PROFILING "Enable asan" OFF) mark_as_advanced(ENABLE_PROFILING) @@ -38,6 +39,11 @@ if(ENABLE_PROFILING) target_compile_options(rjp++ PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) target_link_options(rjp++ PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) endif() +if(BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() + set_target_properties(rjp++ PROPERTIES COMPILE_FLAGS -std=c++17) target_compile_options(rjp++ PRIVATE -Wall -Wextra -pedantic) diff --git a/rjp++/tests/CMakeLists.txt b/rjp++/tests/CMakeLists.txt new file mode 100644 index 0000000..e82d543 --- /dev/null +++ b/rjp++/tests/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.0.2) +project(rjp++_tests) +set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include) +include_directories("${INCLUDE_PATH}") +add_compile_options(-Wall -Wextra -pedantic -std=c++17) +link_libraries(rjp++ rjp) + +if(ENABLE_PROFILING) + add_compile_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) + add_link_options(-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls) +endif() + +add_executable(parseplusplus "parse.cpp") +set_target_properties(parseplusplus PROPERTIES OUTPUT_NAME parse) +add_executable(outputplusplus "output.cpp") +set_target_properties(outputplusplus PROPERTIES OUTPUT_NAME output) + +add_test(NAME parse-test-plusplus COMMAND parseplusplus) +add_test(NAME output-test-plusplus COMMAND outputplusplus) diff --git a/rjp++/tests/output.cpp b/rjp++/tests/output.cpp new file mode 100644 index 0000000..6a6b253 --- /dev/null +++ b/rjp++/tests/output.cpp @@ -0,0 +1,285 @@ +#include "rjp.hpp" + +#include +#include + +typedef struct{ + rjp::value (*create)(void); + const char* res; + int fmt; +}test_pair; + +//Basic values excluding float because precision bs and stuff +rjp::value case_1(void){ + return rjp::null(); +} +rjp::value case_2(void){ + return rjp::integer(5); +} +rjp::value case_3(void){ + return rjp::boolean(true); +} +rjp::value case_4(void){ + return rjp::boolean(false); +} +rjp::value case_5(void){ + return rjp::object(); +} +rjp::value case_6(void){ + return rjp::array(); +} +/*RJP_value* case_7(void){ + return rjp_new_ordered_object(); +}*/ +//handle object with member +rjp::value case_8(void){ + rjp::object obj; + obj.add("key"_ss, 7); + return obj; +} +//handle object with subobject +rjp::value case_9(void){ + rjp::object obj; + rjp::object sub = obj.add("key"_ss); + sub.add("subkey"_ss, false); + return obj; +} +//handle object with multiple members +rjp::value case_10(void){ + rjp::object obj; + rjp::object sub = obj.add("key"_ss); + sub.add("subkey"_ss, false); + sub.add("subkey2"_ss, true); + return obj; +} +//handle object member ordering +rjp::value case_11(void){ + rjp::object obj; + rjp::object sub = obj.add("key"_ss); + sub.add("subkey2"_ss, true); + sub.add("subkey"_ss, false); + return obj; +} +//handle orderedobject member ordering +/*RJP_value* case_12(void){ + RJP_value* obj = rjp_new_object(); + RJP_value* sub = rjp_new_member(obj, "key", 0); + rjp_set_ordered_object(sub); + rjp_add_member(sub, "subkey", 0, rjp_new_bool(0)); + rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1)); + return obj; +} +//handle orderedobject member ordering pt2 +RJP_value* case_13(void){ + RJP_value* obj = rjp_new_object(); + RJP_value* sub = rjp_new_member(obj, "key", 0); + rjp_set_ordered_object(sub); + rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1)); + rjp_add_member(sub, "subkey", 0, rjp_new_bool(0)); + return obj; +}*/ +//handle array with element +rjp::value case_14(void){ + rjp::array arr; + arr.add(5); + return arr; +} +//handle array with subarray +rjp::value case_15(void){ + rjp::array arr; + arr.add().add(false); + return arr; +} +//handle array with multiple elements +rjp::value case_16(void){ + rjp::array arr; + rjp::array sub = arr.add(); + sub.add(false); + sub.add(true); + return arr; +} +//handle array with multiple elements and subarray +rjp::value case_17(void){ + rjp::array arr; + arr.add(5); + rjp::array sub = arr.add(); + sub.add(false); + sub.add(true); + return arr; +} +//handle array with subobject with subarray +rjp::value case_18(void){ + rjp::array arr; + arr.add(5); + rjp::object subobj = arr.add(); + rjp::array subarr = subobj.add("key"_ss); + subarr.add(false); + return arr; +} +//handle object with many members +rjp::value case_19(void){ + char c[] = "key0"; + rjp::array arr; + arr.add(5); + rjp::object subobj = arr.add(); + for(int i = 0;i < 10;++i){ + subobj.add(rexy::static_string(c), i % 2 == 0); + c[3] += 1; + } + return arr; +} +//handle orderedobject with many members as array element +/*RJP_value* case_20(void){ + char c[] = "key9"; + RJP_value* arr = rjp_new_array(); + rjp_add_element(arr, rjp_new_int(5)); + RJP_value* subobj = rjp_new_element(arr); + rjp_set_ordered_object(subobj); + for(int i = 0;i < 10;++i){ + RJP_value* newmem = rjp_new_member(subobj, c, 0); + c[3] -= 1; + if(i % 2 == 0) + rjp_set_bool(newmem, 1); + else + rjp_set_bool(newmem, 0); + } + return arr; +}*/ +//handle array with many element as object member +rjp::value case_21(void){ + rjp::object obj; + rjp::array arr = obj.add("arr"_ss); + for(int i = 0;i < 10;++i) + arr.add(i); + return obj; +} +/* +//handle unorderedobject conversion +RJP_value* case_22(void){ + RJP_value* obj = rjp_new_ordered_object(); + RJP_value* sub = rjp_new_member(obj, "key", 0); + rjp_set_ordered_object(sub); + rjp_add_member(sub, "subkey", 0, rjp_new_bool(0)); + rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1)); + rjp_object_to_unordered(sub); + return obj; +} +//handle unorderedobject conversion +RJP_value* case_23(void){ + RJP_value* obj = rjp_new_ordered_object(); + RJP_value* sub = rjp_new_member(obj, "key", 0); + rjp_set_ordered_object(sub); + rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1)); + rjp_add_member(sub, "subkey", 0, rjp_new_bool(0)); + rjp_object_to_unordered(sub); + return obj; +} +//handle orderedobject conversion +RJP_value* case_24(void){ + RJP_value* obj = rjp_new_ordered_object(); + RJP_value* sub = rjp_new_member(obj, "key", 0); + rjp_set_object(sub); + rjp_add_member(sub, "subkey", 0, rjp_new_bool(0)); + rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1)); + rjp_object_to_ordered(sub); + return obj; +} +//handle orderedobject conversion +RJP_value* case_25(void){ + RJP_value* obj = rjp_new_ordered_object(); + RJP_value* sub = rjp_new_member(obj, "key", 0); + rjp_set_object(sub); + rjp_add_member(sub, "subkey2", 0, rjp_new_bool(1)); + rjp_object_to_ordered(sub); + rjp_add_member(sub, "subkey", 0, rjp_new_bool(0)); + return obj; +} +*/ + +static test_pair tests[] = { + {case_1, "null", RJP_FORMAT_NONE}, + {case_2, "5", RJP_FORMAT_NONE}, + {case_3, "true", RJP_FORMAT_NONE}, + {case_4, "false", RJP_FORMAT_NONE}, + {case_5, "{}", RJP_FORMAT_NONE}, + {case_6, "[]", RJP_FORMAT_NONE}, +// {case_7, "{}", RJP_FORMAT_NONE}, + {case_8, "{\"key\":7}", RJP_FORMAT_NONE}, + {case_9, "{\"key\":{\"subkey\":false}}", RJP_FORMAT_NONE}, + {case_10, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE}, + {case_11, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE}, +// {case_12, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE}, +// {case_13, "{\"key\":{\"subkey2\":true,\"subkey\":false}}", RJP_FORMAT_NONE}, + {case_14, "[5]", RJP_FORMAT_NONE}, + {case_15, "[[false]]", RJP_FORMAT_NONE}, + {case_16, "[[false,true]]", RJP_FORMAT_NONE}, + {case_17, "[5,[false,true]]", RJP_FORMAT_NONE}, + {case_18, "[5,{\"key\":[false]}]", RJP_FORMAT_NONE}, + {case_19, "[5,{\"key0\":true,\"key1\":false,\"key2\":true,\"key3\":false,\"key4\":true,\"key5\":false,\"key6\":true,\"key7\":false,\"key8\":true,\"key9\":false}]", RJP_FORMAT_NONE}, +// {case_20, "[5,{\"key9\":true,\"key8\":false,\"key7\":true,\"key6\":false,\"key5\":true,\"key4\":false,\"key3\":true,\"key2\":false,\"key1\":true,\"key0\":false}]", RJP_FORMAT_NONE}, + {case_21, "{\"arr\":[0,1,2,3,4,5,6,7,8,9]}", RJP_FORMAT_NONE}, +/* {case_22, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE}, + {case_23, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE}, + {case_24, "{\"key\":{\"subkey\":false,\"subkey2\":true}}", RJP_FORMAT_NONE}, + {case_25, "{\"key\":{\"subkey2\":true,\"subkey\":false}}", RJP_FORMAT_NONE}, +*/ + {case_1, "null", RJP_FORMAT_PRETTY}, + {case_2, "5", RJP_FORMAT_PRETTY}, + {case_3, "true", RJP_FORMAT_PRETTY}, + {case_4, "false", RJP_FORMAT_PRETTY}, + {case_5, "{}", RJP_FORMAT_PRETTY}, + {case_6, "[]", RJP_FORMAT_PRETTY}, +// {case_7, "{}", RJP_FORMAT_PRETTY}, + {case_8, "{\n\t\"key\": 7\n}", RJP_FORMAT_PRETTY}, + {case_9, "{\n\t\"key\": {\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY}, + {case_10, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY}, + {case_11, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY}, +// {case_12, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY}, +// {case_13, "{\n\t\"key\": {\n\t\t\"subkey2\": true,\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY}, + {case_14, "[\n\t5\n]", RJP_FORMAT_PRETTY}, + {case_15, "[\n\t[\n\t\tfalse\n\t]\n]", RJP_FORMAT_PRETTY}, + {case_16, "[\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_PRETTY}, + {case_17, "[\n\t5,\n\t[\n\t\tfalse,\n\t\ttrue\n\t]\n]", RJP_FORMAT_PRETTY}, + {case_18, "[\n\t5,\n\t{\n\t\t\"key\": [\n\t\t\tfalse\n\t\t]\n\t}\n]", RJP_FORMAT_PRETTY}, + {case_19, "[\n\t5,\n\t{\n\t\t\"key0\": true,\n\t\t\"key1\": false,\n\t\t\"key2\": true,\n\t\t\"key3\": false,\n\t\t\"key4\": true,\n\t\t\"key5\": false,\n\t\t\"key6\": true,\n\t\t\"key7\": false,\n\t\t\"key8\": true,\n\t\t\"key9\": false\n\t}\n]", RJP_FORMAT_PRETTY}, +// {case_20, "[\n\t5,\n\t{\n\t\t\"key9\": true,\n\t\t\"key8\": false,\n\t\t\"key7\": true,\n\t\t\"key6\": false,\n\t\t\"key5\": true,\n\t\t\"key4\": false,\n\t\t\"key3\": true,\n\t\t\"key2\": false,\n\t\t\"key1\": true,\n\t\t\"key0\": false\n\t}\n]", RJP_FORMAT_PRETTY}, + {case_21, "{\n\t\"arr\": [\n\t\t0,\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4,\n\t\t5,\n\t\t6,\n\t\t7,\n\t\t8,\n\t\t9\n\t]\n}", RJP_FORMAT_PRETTY}, +/* {case_22, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY}, + {case_23, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY}, + {case_24, "{\n\t\"key\": {\n\t\t\"subkey\": false,\n\t\t\"subkey2\": true\n\t}\n}", RJP_FORMAT_PRETTY}, + {case_25, "{\n\t\"key\": {\n\t\t\"subkey2\": true,\n\t\t\"subkey\": false\n\t}\n}", RJP_FORMAT_PRETTY}, +*/}; + +int run_test(test_pair* p){ + rjp::value test = p->create(); + rjp::string buf = test.to_json(p->fmt); + if(!buf){ + if(!p->res) + return 1; + return 0; + } + int retval = !strcmp(buf, p->res); + if(retval){ + fprintf(stderr, "Success\n"); + }else{ + fprintf(stderr, "Failure\n"); + fprintf(stderr, "Expected: '%s'\nGot: '%s'\n", p->res, buf.get()); + } + return retval; +} + +int main(){ + const int num_tests = sizeof(tests) / sizeof(tests[0]); + int passed = 0; + fprintf(stderr, "Running %d tests that should succeed...\n", num_tests); + for(int i = 0;i < num_tests;++i){ + fprintf(stderr, "%8d) ", i+1); + if(run_test(&tests[i])){ + ++passed; + } + } + fprintf(stderr, "\nResults: %d/%d tests passed\n", passed, num_tests); + if(passed != num_tests) + return 1; + return 0; +} diff --git a/rjp++/tests/parse.cpp b/rjp++/tests/parse.cpp new file mode 100644 index 0000000..35df8f0 --- /dev/null +++ b/rjp++/tests/parse.cpp @@ -0,0 +1,184 @@ +#include "rjp.hpp" + +#include +#include + +#define MIN(x, y) ((x < y) ? (x) : (y)) + +int read_callback(char* c, int size, const char* data, int datalen, int& datapos){ + if(datapos >= datalen) + return 0; + int min = MIN(size, datalen - datapos); + int i; + for(i = 0;i < min;++i){ + c[i] = data[datapos++]; + } + return i; +} + +int handle_res(const rjp::value& res){ + if(res.valid()){ + fprintf(stderr, "Accepted\n"); + } + return !res.valid(); +} +int test_cbacks(const char* str, RJP_parse_flag flags){ + int pos = 0; + rjp::value res = rjp::parse_json(flags, read_callback, str, strlen(str), pos); + return handle_res(res); +} + +int test(const char* str, RJP_parse_flag flags){ + rjp::value res = rjp::parse_json(str, flags); + return handle_res(res); +} + +struct parse_pair{ + const char* str; + RJP_parse_flag flags; +}; + +struct parse_pair should_pass_strings[] = { + {"{}", RJP_PARSE_NONE}, + {"[]", RJP_PARSE_NONE}, + {"\"s\"", RJP_PARSE_NONE}, + {"\"\\n\"", RJP_PARSE_NONE}, + {"\"\\\"\"", RJP_PARSE_NONE}, + {"\"str\\nstr\"", RJP_PARSE_NONE}, + {"\"\\uD83D\\uDE10\"", RJP_PARSE_NONE}, + {"true", RJP_PARSE_NONE}, + {"false", RJP_PARSE_NONE}, + {"null", RJP_PARSE_NONE}, + {"5", RJP_PARSE_NONE}, + {"-5", RJP_PARSE_NONE}, + {"+5", RJP_PARSE_NONE}, + {"5.5", RJP_PARSE_NONE}, + {"-5.5", RJP_PARSE_NONE}, + {"+5.5", RJP_PARSE_NONE}, + {"5.5e6", RJP_PARSE_NONE}, + {"-5.5e6", RJP_PARSE_NONE}, + {"+5.5e6", RJP_PARSE_NONE}, + {"5.5e+6", RJP_PARSE_NONE}, + {"-5.5e+6", RJP_PARSE_NONE}, + {"+5.5e+6", RJP_PARSE_NONE}, + {"5.5e-6", RJP_PARSE_NONE}, + {"-5.5e-6", RJP_PARSE_NONE}, + {"+5.5e-6", RJP_PARSE_NONE}, + {" {}", RJP_PARSE_NONE}, + {"\n{}\n", RJP_PARSE_NONE}, + {" { \"key\" \t:\n\n\n5 \n\t\n } ", RJP_PARSE_NONE}, + {" {\t }\n", RJP_PARSE_NONE}, + {"5.5 ", RJP_PARSE_NONE}, + {"{\"key\":5}", RJP_PARSE_NONE}, + {"{\"key\":{}}", RJP_PARSE_NONE}, + {"{\"\\uD83D\\uDE10\":5}", RJP_PARSE_NONE}, + {"{\"😐\":5}", RJP_PARSE_NONE}, + {"{\"key\":{\"key\":5}}", RJP_PARSE_NONE}, + {"{\"key\":{\"key\":5,\"key2\":6}}", RJP_PARSE_NONE}, + {"{\"key\":{\"key\":5},\"key2\":6}", RJP_PARSE_NONE}, + {"[5, 6, 7, 8, 9, \"10\"]", RJP_PARSE_NONE}, + {"[[5,6],[7,8],[9,\"10\"]]", RJP_PARSE_NONE}, + {"{\"arr\":[5,6,6]}", RJP_PARSE_NONE}, + {"[{\"arr\":[5,6,6]}]", RJP_PARSE_NONE}, + {"[{\"arr\":[5,6,6]}, 6]", RJP_PARSE_NONE}, + {"[5,6,6,6,6.6]", RJP_PARSE_NONE}, + {"[6,7,]", RJP_PARSE_ALLOW_TRAILING_COMMA}, + {"{\"1\":1,\"2\":2,}", RJP_PARSE_ALLOW_TRAILING_COMMA}, + {"[6,]", RJP_PARSE_ALLOW_TRAILING_COMMA}, + {"{\"1\":1,}", RJP_PARSE_ALLOW_TRAILING_COMMA}, + {"//comment\n{}", RJP_PARSE_ALLOW_COMMENTS}, + {"{\"key\"://comment\n5}", RJP_PARSE_ALLOW_COMMENTS}, + {"{\"key\"//comment\n:5}", RJP_PARSE_ALLOW_COMMENTS}, + {"{}//comment", RJP_PARSE_ALLOW_COMMENTS}, + {"{//\"key\":5\n}", RJP_PARSE_ALLOW_COMMENTS}, + {"5 //comment*/", RJP_PARSE_ALLOW_COMMENTS}, + {"{/*\"key\":5*/\"key\":5}", RJP_PARSE_ALLOW_COMMENTS}, + {"[5, /*comment*/6]", RJP_PARSE_ALLOW_COMMENTS}, +}; +const int should_pass_cnt = sizeof(should_pass_strings)/sizeof(should_pass_strings[0]); +struct parse_pair should_fail_strings[] = { + {"//comment\n{}", RJP_PARSE_NONE}, + {"{", RJP_PARSE_NONE}, + {"}", RJP_PARSE_NONE}, + {"[", RJP_PARSE_NONE}, + {"]", RJP_PARSE_NONE}, + {"6.", RJP_PARSE_NONE}, + {"6.6e", RJP_PARSE_NONE}, + {"6.6e+", RJP_PARSE_NONE}, + {"5e", RJP_PARSE_NONE}, + {"{6}", RJP_PARSE_NONE}, + {"[\"key\":5]", RJP_PARSE_NONE}, + {"\"string\n\"", RJP_PARSE_NONE}, + {"[3 4]", RJP_PARSE_NONE}, + {"\"\\uD83D\\uDE1\"", RJP_PARSE_NONE}, + {"\"\\uD83D\\uDE1Q\"", RJP_PARSE_NONE}, + {"\"\\uD83\\uDE10\"", RJP_PARSE_NONE}, + {"\"\\uF83D\\uDE10\"", RJP_PARSE_NONE}, + {"\"\\uU83D\\uDE10\"", RJP_PARSE_NONE}, + {"{\"key\":1 \"key2\":2}", RJP_PARSE_NONE}, + {"{\"key\" 1}", RJP_PARSE_NONE}, + {"6, 7", RJP_PARSE_NONE}, + {"[,]", RJP_PARSE_NONE}, + {"{, RJP_PARSE_NONE}", RJP_PARSE_NONE}, + {"[1, 2],", RJP_PARSE_NONE}, + {"{\"key\nkey\":5}", RJP_PARSE_NONE}, + {"{\"key\":\"key\n\"}", RJP_PARSE_NONE}, + {"[6,7,]", RJP_PARSE_NONE}, + {"{\"1\":1,\"2\":2, RJP_PARSE_NONE}", RJP_PARSE_NONE}, + {"[6,]", RJP_PARSE_NONE}, + {"{\"1\":1, RJP_PARSE_NONE}", RJP_PARSE_NONE}, + {"{//comment\"key\":\n5}", RJP_PARSE_NONE}, + {"{/*\"key\":*/5}", RJP_PARSE_NONE}, + {"[5, /*6*/, 7]", RJP_PARSE_NONE}, + {"{/*comment}", RJP_PARSE_NONE}, + {"{//comment}", RJP_PARSE_NONE}, + {"{\"key\"://comment\n5}", RJP_PARSE_NONE}, + {"{\"key\"//comment\n:5}", RJP_PARSE_NONE}, + {"{}//comment", RJP_PARSE_NONE}, + {"{//\"key\":5\n}", RJP_PARSE_NONE}, + {"5 //comment*/", RJP_PARSE_NONE}, + {"{/*\"key\":5*/\"key\":5}", RJP_PARSE_NONE}, + {"[5, /*comment*/6]", RJP_PARSE_NONE}, + {"{\"key\"//:5}", RJP_PARSE_ALLOW_COMMENTS}, + {"{,}", RJP_PARSE_ALLOW_TRAILING_COMMA}, + {"[,]", RJP_PARSE_ALLOW_TRAILING_COMMA}, +}; +const int should_fail_cnt = sizeof(should_fail_strings)/sizeof(should_fail_strings[0]); +const int total_tests = should_pass_cnt + should_fail_cnt; + +int run_test(int (*fun)(const char*,RJP_parse_flag)){ + int passed = 0; + fprintf(stderr, "Running %d tests that should pass...\n", should_pass_cnt); + for(unsigned i = 0;i < sizeof(should_pass_strings)/sizeof(should_pass_strings[0]);++i){ + fprintf(stderr, "%8d) ", i+1); + if(!fun(should_pass_strings[i].str, should_pass_strings[i].flags)){ + ++passed; + }else{ + fprintf(stderr, "%13s%s\n", "", should_pass_strings[i].str); + } + } + fprintf(stderr, "\n"); + printf("Running %d tests that should fail...\n", should_fail_cnt); + for(unsigned i = 0;i < sizeof(should_fail_strings)/sizeof(should_fail_strings[0]);++i){ + fprintf(stderr, "%8d) ", i+1); + if(fun(should_fail_strings[i].str, should_fail_strings[i].flags)){ + ++passed; + }else{ + fprintf(stderr, "%13s%s\n", "", should_fail_strings[i].str); + } + } + return passed; +} +int main(){ + + int normal_passed = run_test(test); + int cback_passed = run_test(test_cbacks); + int total_passed = normal_passed + cback_passed; + + fprintf(stderr, "\nResults: %d/%d normal tests passed\n", normal_passed, total_tests); + fprintf(stderr, "Results: %d/%d callback tests passed\n", cback_passed, total_tests); + fprintf(stderr, "Results: %d/%d tests passed\n", total_passed, total_tests*2); + if(total_passed != (total_tests*2)) + return 1; + return 0; +}