diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b62ba7..e40c750 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,8 @@ install(FILES ${PROJECT_BINARY_DIR}/cJSONConfig.cmake option(ENABLE_CJSON_TEST "Enable building cJSON test" ON) if(ENABLE_CJSON_TEST) + enable_testing() + set(TEST_CJSON cJSON_test) add_executable("${TEST_CJSON}" test.c) target_link_libraries("${TEST_CJSON}" "${CJSON_LIB}") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a7cf61f..e6d0445 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1 +1,18 @@ add_library(unity unity/src/unity.c) + +if(ENABLE_CJSON_TEST) + #copy test files + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs") + file(GLOB test_files "inputs/*") + file(COPY ${test_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/inputs/") + + set(unity_tests + parse_examples + ) + + foreach(unity_test ${unity_tests}) + add_executable("${unity_test}" "${unity_test}.c") + target_link_libraries("${unity_test}" "${CJSON_LIB}" unity) + add_test("${unity_test}" "${unity_test}") + endforeach() +endif() diff --git a/tests/test1 b/tests/inputs/test1 similarity index 100% rename from tests/test1 rename to tests/inputs/test1 diff --git a/tests/inputs/test1.expected b/tests/inputs/test1.expected new file mode 100644 index 0000000..a5839e8 --- /dev/null +++ b/tests/inputs/test1.expected @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} \ No newline at end of file diff --git a/tests/test2 b/tests/inputs/test2 similarity index 100% rename from tests/test2 rename to tests/inputs/test2 diff --git a/tests/inputs/test2.expected b/tests/inputs/test2.expected new file mode 100644 index 0000000..d42a017 --- /dev/null +++ b/tests/inputs/test2.expected @@ -0,0 +1,18 @@ +{ + "menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [{ + "value": "New", + "onclick": "CreateNewDoc()" + }, { + "value": "Open", + "onclick": "OpenDoc()" + }, { + "value": "Close", + "onclick": "CloseDoc()" + }] + } + } +} \ No newline at end of file diff --git a/tests/test3 b/tests/inputs/test3 similarity index 100% rename from tests/test3 rename to tests/inputs/test3 diff --git a/tests/inputs/test3.expected b/tests/inputs/test3.expected new file mode 100644 index 0000000..a9b9c55 --- /dev/null +++ b/tests/inputs/test3.expected @@ -0,0 +1,28 @@ +{ + "widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } + } +} \ No newline at end of file diff --git a/tests/test4 b/tests/inputs/test4 similarity index 100% rename from tests/test4 rename to tests/inputs/test4 diff --git a/tests/inputs/test4.expected b/tests/inputs/test4.expected new file mode 100644 index 0000000..3648a95 --- /dev/null +++ b/tests/inputs/test4.expected @@ -0,0 +1,94 @@ +{ + "web-app": { + "servlet": [{ + "servlet-name": "cofaxCDS", + "servlet-class": "org.cofax.cds.CDSServlet", + "init-param": { + "configGlossary:installationAt": "Philadelphia, PA", + "configGlossary:adminEmail": "ksm@pobox.com", + "configGlossary:poweredBy": "Cofax", + "configGlossary:poweredByIcon": "/images/cofax.gif", + "configGlossary:staticPath": "/content/static", + "templateProcessorClass": "org.cofax.WysiwygTemplate", + "templateLoaderClass": "org.cofax.FilesTemplateLoader", + "templatePath": "templates", + "templateOverridePath": "", + "defaultListTemplate": "listTemplate.htm", + "defaultFileTemplate": "articleTemplate.htm", + "useJSP": false, + "jspListTemplate": "listTemplate.jsp", + "jspFileTemplate": "articleTemplate.jsp", + "cachePackageTagsTrack": 200, + "cachePackageTagsStore": 200, + "cachePackageTagsRefresh": 60, + "cacheTemplatesTrack": 100, + "cacheTemplatesStore": 50, + "cacheTemplatesRefresh": 15, + "cachePagesTrack": 200, + "cachePagesStore": 100, + "cachePagesRefresh": 10, + "cachePagesDirtyRead": 10, + "searchEngineListTemplate": "forSearchEnginesList.htm", + "searchEngineFileTemplate": "forSearchEngines.htm", + "searchEngineRobotsDb": "WEB-INF/robots.db", + "useDataStore": true, + "dataStoreClass": "org.cofax.SqlDataStore", + "redirectionClass": "org.cofax.SqlRedirection", + "dataStoreName": "cofax", + "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", + "dataStoreUser": "sa", + "dataStorePassword": "dataStoreTestQuery", + "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", + "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", + "dataStoreInitConns": 10, + "dataStoreMaxConns": 100, + "dataStoreConnUsageLimit": 100, + "dataStoreLogLevel": "debug", + "maxUrlLength": 500 + } + }, { + "servlet-name": "cofaxEmail", + "servlet-class": "org.cofax.cds.EmailServlet", + "init-param": { + "mailHost": "mail1", + "mailHostOverride": "mail2" + } + }, { + "servlet-name": "cofaxAdmin", + "servlet-class": "org.cofax.cds.AdminServlet" + }, { + "servlet-name": "fileServlet", + "servlet-class": "org.cofax.cds.FileServlet" + }, { + "servlet-name": "cofaxTools", + "servlet-class": "org.cofax.cms.CofaxToolsServlet", + "init-param": { + "templatePath": "toolstemplates/", + "log": 1, + "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", + "logMaxSize": "", + "dataLog": 1, + "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", + "dataLogMaxSize": "", + "removePageCache": "/content/admin/remove?cache=pages&id=", + "removeTemplateCache": "/content/admin/remove?cache=templates&id=", + "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", + "lookInContext": 1, + "adminGroupID": 4, + "betaServer": true + } + }], + "servlet-mapping": { + "cofaxCDS": "/", + "cofaxEmail": "/cofaxutil/aemail/*", + "cofaxAdmin": "/admin/*", + "fileServlet": "/static/*", + "cofaxTools": "/tools/*" + }, + "taglib": { + "taglib-uri": "cofax.tld", + "taglib-location": "/WEB-INF/tlds/cofax.tld" + } + } +} \ No newline at end of file diff --git a/tests/test5 b/tests/inputs/test5 similarity index 100% rename from tests/test5 rename to tests/inputs/test5 diff --git a/tests/inputs/test5.expected b/tests/inputs/test5.expected new file mode 100644 index 0000000..886ed58 --- /dev/null +++ b/tests/inputs/test5.expected @@ -0,0 +1,54 @@ +{ + "menu": { + "header": "SVG Viewer", + "items": [{ + "id": "Open" + }, { + "id": "OpenNew", + "label": "Open New" + }, null, { + "id": "ZoomIn", + "label": "Zoom In" + }, { + "id": "ZoomOut", + "label": "Zoom Out" + }, { + "id": "OriginalView", + "label": "Original View" + }, null, { + "id": "Quality" + }, { + "id": "Pause" + }, { + "id": "Mute" + }, null, { + "id": "Find", + "label": "Find..." + }, { + "id": "FindAgain", + "label": "Find Again" + }, { + "id": "Copy" + }, { + "id": "CopyAgain", + "label": "Copy Again" + }, { + "id": "CopySVG", + "label": "Copy SVG" + }, { + "id": "ViewSVG", + "label": "View SVG" + }, { + "id": "ViewSource", + "label": "View Source" + }, { + "id": "SaveAs", + "label": "Save As" + }, null, { + "id": "Help" + }, { + "id": "About", + "label": "About Adobe CVG Viewer..." + }] + } +} \ No newline at end of file diff --git a/tests/test6 b/tests/inputs/test6 similarity index 100% rename from tests/test6 rename to tests/inputs/test6 diff --git a/tests/parse_examples.c b/tests/parse_examples.c new file mode 100644 index 0000000..6ea45d8 --- /dev/null +++ b/tests/parse_examples.c @@ -0,0 +1,223 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "../cJSON.h" + +static char *read_file(const char *filename) +{ + FILE *file = NULL; + long length = 0; + char *content = NULL; + size_t read_chars = 0; + + /* open in read binary mode */ + file = fopen(filename, "rb"); + if (file == NULL) + { + goto cleanup; + } + + /* get the length */ + if (fseek(file, 0, SEEK_END) != 0) + { + goto cleanup; + } + length = ftell(file); + if (length < 0) + { + goto cleanup; + } + if (fseek(file, 0, SEEK_SET) != 0) + { + goto cleanup; + } + + /* allocate content buffer */ + content = (char*)malloc((size_t)length + sizeof('\0')); + if (content == NULL) + { + goto cleanup; + } + + /* read the file into memory */ + read_chars = fread(content, sizeof(char), (size_t)length, file); + if ((long)read_chars != length) + { + free(content); + content = NULL; + goto cleanup; + } + content[read_chars] = '\0'; + + +cleanup: + if (file != NULL) + { + fclose(file); + } + + return content; +} + +static cJSON *parse_file(const char *filename) +{ + cJSON *parsed = NULL; + char *content = read_file(filename); + + parsed = cJSON_Parse(content); + + if (content != NULL) + { + free(content); + } + + return parsed; +} + +static void do_test(const char *test_name) +{ + char *expected = NULL; + char *actual = NULL; + cJSON *tree = NULL; + + size_t test_name_length = 0; + /* path of the test input */ + char *test_path = NULL; + /* path of the expected output */ + char *expected_path = NULL; + + test_name_length = strlen(test_name); + + /* allocate file paths */ +#define TEST_DIR_PATH "inputs/" + test_path = (char*)malloc(sizeof(TEST_DIR_PATH) + test_name_length); + TEST_ASSERT_NOT_NULL_MESSAGE(test_path, "Failed to allocate test_path buffer."); + expected_path = (char*)malloc(sizeof(TEST_DIR_PATH) + test_name_length + sizeof(".expected")); + TEST_ASSERT_NOT_NULL_MESSAGE(expected_path, "Failed to allocate expected_path buffer."); + + /* create file paths */ + sprintf(test_path, TEST_DIR_PATH"%s", test_name); + sprintf(expected_path, TEST_DIR_PATH"%s.expected", test_name); + + /* read expected output */ + expected = read_file(expected_path); + TEST_ASSERT_NOT_NULL_MESSAGE(expected, "Failed to read expected output."); + + /* read and parse test */ + tree = parse_file(test_path); + TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to read of parse test."); + + /* print the parsed tree */ + actual = cJSON_Print(tree); + TEST_ASSERT_NOT_NULL_MESSAGE(actual, "Failed to print tree back to JSON."); + + + TEST_ASSERT_EQUAL_STRING(expected, actual); + + /* cleanup resources */ + if (expected != NULL) + { + free(expected); + } + if (tree != NULL) + { + cJSON_Delete(tree); + } + if (actual != NULL) + { + free(actual); + } + if (test_path != NULL) + { + free(test_path); + } + if (expected_path != NULL) + { + free(expected_path); + } +} + +static void file_test1_should_be_parsed_and_printed(void) +{ + do_test("test1"); +} + +static void file_test2_should_be_parsed_and_printed(void) +{ + do_test("test2"); +} + +static void file_test3_should_be_parsed_and_printed(void) +{ + do_test("test3"); +} + +static void file_test4_should_be_parsed_and_printed(void) +{ + do_test("test4"); +} + +static void file_test5_should_be_parsed_and_printed(void) +{ + do_test("test5"); +} + +static void file_test6_should_not_be_parsed(void) +{ + char *test6 = NULL; + cJSON *tree = NULL; + + test6 = read_file("inputs/test6"); + TEST_ASSERT_NOT_NULL_MESSAGE(test6, "Failed to read test6 data."); + + tree = cJSON_Parse(test6); + TEST_ASSERT_NULL_MESSAGE(tree, "Should fail to parse what is not JSON."); + + TEST_ASSERT_EQUAL_STRING_MESSAGE(test6, cJSON_GetErrorPtr(), "Error pointer is incorrect."); + + if (test6 != NULL) + { + free(test6); + } + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(file_test1_should_be_parsed_and_printed); + RUN_TEST(file_test2_should_be_parsed_and_printed); + RUN_TEST(file_test3_should_be_parsed_and_printed); + RUN_TEST(file_test4_should_be_parsed_and_printed); + RUN_TEST(file_test5_should_be_parsed_and_printed); + RUN_TEST(file_test6_should_not_be_parsed); + return UNITY_END(); +}