From a43236b7772d68e6c305dfe73e871ff1bc69911e Mon Sep 17 00:00:00 2001 From: MooZ Date: Fri, 16 Mar 2018 22:09:23 +0100 Subject: [PATCH] V1.1 (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switched to [µnit](https://nemequ.github.io/munit/) for unit tests. --- CMakeLists.txt | 61 +- README.md | 2 +- examples/rosetta/CMakeLists.txt | 1 + test/PDS_DOM_find.c | 218 +-- test/PDS_DOM_parse.c | 130 +- test/PDS_find_first.c | 33 - test/PDS_parse.c | 68 +- test/PDS_parse_datetime.c | 81 - test/PDS_parse_identifier.c | 32 - test/PDS_parse_int.c | 42 - test/PDS_parse_int_base.c | 39 - test/PDS_parse_real.c | 50 - test/PDS_parse_sequence.c | 185 -- test/PDS_parse_set.c | 103 -- test/PDS_parse_statement.c | 129 -- test/PDS_parse_string.c | 44 - test/PDS_parse_symbol.c | 47 - test/PDS_parse_tests.c | 1034 +++++++++++ test/PDS_parse_unit.c | 47 - test/PDS_skip_whitespaces.c | 52 - test/PDS_string_case_compare.c | 31 - test/PDS_string_compare.c | 30 - test/PDS_string_tests.c | 202 +++ test/PDS_trim.c | 38 - test/data/AMI_LR3_R00976_00031_00040.IMG | 263 +++ test/munit/.appveyor.yml | 34 + test/munit/.dir-locals.el | 2 + test/munit/.gitignore | 6 + test/munit/.travis.yml | 142 ++ test/munit/COPYING | 21 + test/munit/Makefile | 55 + test/munit/README.md | 40 + test/munit/example.c | 351 ++++ test/munit/munit.c | 2047 ++++++++++++++++++++++ test/munit/munit.h | 533 ++++++ test/test.c | 7 - test/test.h | 35 - test/utils.c | 306 +--- test/utils.h | 38 +- tinypds.h | 3 +- tinypds_dom.h | 288 ++- 41 files changed, 5071 insertions(+), 1799 deletions(-) delete mode 100644 test/PDS_find_first.c delete mode 100644 test/PDS_parse_datetime.c delete mode 100644 test/PDS_parse_identifier.c delete mode 100644 test/PDS_parse_int.c delete mode 100644 test/PDS_parse_int_base.c delete mode 100644 test/PDS_parse_real.c delete mode 100644 test/PDS_parse_sequence.c delete mode 100644 test/PDS_parse_set.c delete mode 100644 test/PDS_parse_statement.c delete mode 100644 test/PDS_parse_string.c delete mode 100644 test/PDS_parse_symbol.c create mode 100644 test/PDS_parse_tests.c delete mode 100644 test/PDS_parse_unit.c delete mode 100644 test/PDS_skip_whitespaces.c delete mode 100644 test/PDS_string_case_compare.c delete mode 100644 test/PDS_string_compare.c create mode 100644 test/PDS_string_tests.c delete mode 100644 test/PDS_trim.c create mode 100644 test/data/AMI_LR3_R00976_00031_00040.IMG create mode 100644 test/munit/.appveyor.yml create mode 100644 test/munit/.dir-locals.el create mode 100644 test/munit/.gitignore create mode 100644 test/munit/.travis.yml create mode 100644 test/munit/COPYING create mode 100644 test/munit/Makefile create mode 100644 test/munit/README.md create mode 100644 test/munit/example.c create mode 100644 test/munit/munit.c create mode 100644 test/munit/munit.h delete mode 100644 test/test.c delete mode 100644 test/test.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fcdbd75..365f5fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,54 +31,35 @@ add_custom_target(doc COMMENT "Generating API documentation with Doxygen" VERBATIM ) -set(TINY_PDS_TEST_SOURCE_FILES test/utils.c test/test.c) - enable_testing() +add_library(munit STATIC ${CMAKE_CURRENT_SOURCE_DIR}/test/munit/munit.c) +target_include_directories(munit PUBLIC $) +set_property(TARGET munit PROPERTY INTERFACE_INCLUDE_DIRECTORIES $) + include_directories(.) -add_executable(PDS_find_first test/PDS_find_first.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_datetime test/PDS_parse_datetime.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_identifier test/PDS_parse_identifier.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_int_base test/PDS_parse_int_base.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_int test/PDS_parse_int.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_real test/PDS_parse_real.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_sequence test/PDS_parse_sequence.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_set test/PDS_parse_set.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_statement test/PDS_parse_statement.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_string test/PDS_parse_string.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_symbol test/PDS_parse_symbol.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse_unit test/PDS_parse_unit.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_skip_whitespaces test/PDS_skip_whitespaces.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_string_case_compare test/PDS_string_case_compare.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_string_compare test/PDS_string_compare.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_trim test/PDS_trim.c ${TINY_PDS_TEST_SOURCE_FILES}) -add_executable(PDS_parse test/PDS_parse.c ${TINY_PDS_TEST_SOURCE_FILES}) - -add_executable(PDS_DOM_parse test/PDS_DOM_parse.c ${TINY_PDS_TEST_SOURCE_FILES}) + +add_executable(PDS_string_tests test/PDS_string_tests.c) +target_link_libraries(PDS_string_tests munit) + +add_executable(PDS_parse_tests test/PDS_parse_tests.c) +target_link_libraries(PDS_parse_tests munit) + +add_executable(PDS_parse test/PDS_parse.c test/utils.c) + +add_executable(PDS_DOM_parse test/PDS_DOM_parse.c test/utils.c) target_link_libraries(PDS_DOM_parse tinypds) -add_executable(PDS_DOM_find test/PDS_DOM_find.c ${TINY_PDS_TEST_SOURCE_FILES}) -target_link_libraries(PDS_DOM_find tinypds tinypds_dom) - -add_test(PDS_find_first PDS_find_first ) -add_test(PDS_parse_datetime PDS_parse_datetime) -add_test(PDS_parse_identifier PDS_parse_identifier) -add_test(PDS_parse_int_base PDS_parse_int_base) -add_test(PDS_parse_int PDS_parse_int) -add_test(PDS_parse_real PDS_parse_real) -add_test(PDS_parse_sequence PDS_parse_sequence) -add_test(PDS_parse_set PDS_parse_set) -add_test(PDS_parse_statement PDS_parse_statement) -add_test(PDS_parse_string PDS_parse_string) -add_test(PDS_parse_symbol PDS_parse_symbol) -add_test(PDS_parse_unit PDS_parse_unit) -add_test(PDS_skip_whitespaces PDS_skip_whitespaces) -add_test(PDS_string_case_compare PDS_string_case_compare) -add_test(PDS_string_compare PDS_string_compare) -add_test(PDS_trim PDS_trim) +add_executable(PDS_DOM_find test/PDS_DOM_find.c) +target_link_libraries(PDS_DOM_find tinypds_dom tinypds munit) + +add_test(PDS_string_tests PDS_string_tests) +add_test(PDS_parse_tests PDS_parse_tests) add_test(PDS_parse_GASPRA_SUMM_RTN.LBL PDS_parse ${CMAKE_CURRENT_SOURCE_DIR}/test/data/GASPRA_SUMM_RTN.LBL) add_test(PDS_parse_LOR_0034851929_0X630_ENG_1.LBL PDS_parse ${CMAKE_CURRENT_SOURCE_DIR}/test/data/LOR_0034851929_0X630_ENG_1.LBL) add_test(PDS_parse_W20140403T020157750ID20F12.IMG PDS_parse ${CMAKE_CURRENT_SOURCE_DIR}/test/data/W20140403T020157750ID20F12.IMG) +add_test(PDS_parse_AMI_LR3_R00976_00031_00040.IMG PDS_parse ${CMAKE_CURRENT_SOURCE_DIR}/test/data/AMI_LR3_R00976_00031_00040.IMG) + add_test(PDS_DOM_parse_W20140403T020157750ID20F12.IMG PDS_DOM_parse ${CMAKE_CURRENT_SOURCE_DIR}/test/data/W20140403T020157750ID20F12.IMG) add_test(PDS_DOM_parse_0844636876_lvl1_0.lbl PDS_DOM_parse ${CMAKE_CURRENT_SOURCE_DIR}/test/data/0844636876_lvl1_0.lbl) add_test(PDS_DOM_parse_hayabusa_20050907_20050908_v01.lbl PDS_DOM_parse ${CMAKE_CURRENT_SOURCE_DIR}/test/data/hayabusa_20050907_20050908_v01.lbl) diff --git a/README.md b/README.md index f063a34..98c9808 100644 --- a/README.md +++ b/README.md @@ -301,4 +301,4 @@ Finally the parsing is started by calling **PDS_parse**. An implementation examp ## License ## The MIT License -Copyright (c) 2016 Vincent Cruz +Copyright (c) 2018 Vincent Cruz diff --git a/examples/rosetta/CMakeLists.txt b/examples/rosetta/CMakeLists.txt index b9e6159..77a877d 100644 --- a/examples/rosetta/CMakeLists.txt +++ b/examples/rosetta/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") find_package(CFITSIO) include_directories(${CFITSIO_INCLUDE_DIR} ../..) +include_directories(${CFITSIO_INCLUDE_DIR} ../../dom) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wshadow -Wextra") diff --git a/test/PDS_DOM_find.c b/test/PDS_DOM_find.c index 3840812..7a94afb 100644 --- a/test/PDS_DOM_find.c +++ b/test/PDS_DOM_find.c @@ -1,12 +1,8 @@ -#include -#include -#include +#include #include #include -#include "test.h" - static const char *pds_string = "PDS_VERSION_ID = PDS3\r\n" "OBJECT = object_0\r\n" @@ -45,104 +41,122 @@ static const char *pds_string = "ATTRIBUTE_8 = 14\r\n" "END\r\n"; -PDS_item *item = NULL; +static void* pds_dom_setup(const MunitParameter params[], void* user_data) { + (void)params; + (void)user_data; + return malloc(sizeof(PDS_item**)); +} -void cleanup() -{ - if(item) - { - PDS_DOM_delete(item); - } +static void pds_dom_tear_down(void* fixture) { + if(NULL == fixture) { + return; + } + PDS_item **item = (PDS_item**)fixture; + if(*item) { + PDS_DOM_delete(*item); + } + free(item); } -int main() -{ - PDS_item *found; - PDS_scalar scalar; - PDS_error_description err; - int ret; - - atexit(cleanup); - - ret = PDS_DOM_parse(pds_string, strlen(pds_string), &item, &err); - if(!ret) - { - fprintf(stderr, "parse error line %d:%d: %s\n", err.number, err.position, err.msg); - return EXIT_FAILURE; - } - - found = PDS_DOM_find("ATTRIBUTE_0", item, PDS_ONLY_SIBLINGS, 0); - check(NULL != found); - check(PDS_DOM_is_attribute(found)); - check(PDS_SINGLE == PDS_DOM_scalar_typeof(found)); - - ret = PDS_DOM_scalar_get(found, &scalar); - check(ret); - check(PDS_INTEGER_VALUE == scalar.type); - check(13 == scalar.integer.value); - - found = PDS_DOM_find("ATTRIBUTE_8", found, PDS_ONLY_SIBLINGS, 0); - check(NULL != found); - check(PDS_DOM_is_attribute(found)); - check(PDS_SINGLE == PDS_DOM_scalar_typeof(found)); - - ret = PDS_DOM_scalar_get(found, &scalar); - check(ret); - check(PDS_INTEGER_VALUE == scalar.type); - check(14 == scalar.integer.value); - - found = PDS_DOM_find("ATTRIBUTE_1", item, PDS_ONLY_SIBLINGS, 1); - check(NULL == found); - - found = PDS_DOM_find("PDS_VERSION_ID", item, PDS_ONLY_SIBLINGS, 1); - check(NULL != found); - - found = PDS_DOM_find("ATTRIBUTE_8", item, PDS_ONLY_CHILDREN, 0); - check(NULL == found); - - found = PDS_DOM_find("object_0", item, PDS_ONLY_SIBLINGS, 0); - check(NULL != found); - check(PDS_DOM_is_object(found)); - - check(NULL == PDS_DOM_find("ATTRIBUTE_8", found, PDS_ONLY_CHILDREN, 0)); - - found = PDS_DOM_find("object_1", found, PDS_ONLY_CHILDREN, 0); - check(NULL != found); - check(PDS_DOM_is_object(found)); - - found = PDS_DOM_find("ATTRIBUTE_8", found, PDS_CHILDREN_RECURSIVE, 0); - check(NULL != found); - check(PDS_DOM_is_attribute(found)); - check(PDS_SINGLE == PDS_DOM_scalar_typeof(found)); - - ret = PDS_DOM_scalar_get(found, &scalar); - check(ret); - check(PDS_INTEGER_VALUE == scalar.type); - check(8 == scalar.integer.value); - - found = PDS_DOM_find("ATTRIBUTE_E", item, PDS_SIBLINGS_RECURSIVE, 1); - check(NULL != found); - check(PDS_DOM_is_attribute(found)); - check(PDS_SINGLE == PDS_DOM_scalar_typeof(found)); - - ret = PDS_DOM_scalar_get(found, &scalar); - check(ret); - check(PDS_INTEGER_VALUE == scalar.type); - check(16 == scalar.integer.value); - - found = PDS_DOM_find("ATTRIBUTE_2", item, PDS_SIBLINGS_RECURSIVE, 0); - check(NULL != found); - check(PDS_DOM_is_attribute(found)); - check(PDS_SINGLE == PDS_DOM_scalar_typeof(found)); - - ret = PDS_DOM_scalar_get(found, &scalar); - check(ret); - check(PDS_INTEGER_VALUE == scalar.type); - check(2 == scalar.integer.value); - - found = PDS_DOM_find("object_3", found, PDS_SIBLINGS_RECURSIVE, 0); - check(NULL != found); - check(PDS_DOM_is_object(found)); - - return EXIT_SUCCESS; +MunitResult pds_dom_find_test(const MunitParameter params[], void* fixture) { + (void)params; + PDS_item **item = (PDS_item**)fixture; + PDS_item *found; + PDS_scalar scalar; + PDS_error_description err; + int ret; + + ret = PDS_DOM_parse(pds_string, strlen(pds_string), item, &err); + if(!ret) { + munit_errorf("parse error line %d:%d: %s\n", err.number, err.position, err.msg); + return MUNIT_FAIL; + } + + found = PDS_DOM_find("ATTRIBUTE_0", *item, PDS_ONLY_SIBLINGS, 0); + munit_assert_ptr_not_equal(NULL, found); + munit_assert_true(PDS_DOM_is_attribute(found)); + munit_assert_int(PDS_SINGLE, ==, PDS_DOM_scalar_typeof(found)); + + ret = PDS_DOM_scalar_get(found, &scalar); + munit_assert_true(ret); + munit_assert_int(PDS_INTEGER_VALUE, ==, scalar.type); + munit_assert_int(13, ==, scalar.integer.value); + + found = PDS_DOM_find("ATTRIBUTE_8", found, PDS_ONLY_SIBLINGS, 0); + munit_assert_ptr_not_equal(NULL, found); + munit_assert_true(PDS_DOM_is_attribute(found)); + munit_assert_int(PDS_SINGLE, ==, PDS_DOM_scalar_typeof(found)); + + ret = PDS_DOM_scalar_get(found, &scalar); + munit_assert_true(ret); + munit_assert_int(PDS_INTEGER_VALUE, ==, scalar.type); + munit_assert_int(14, ==, scalar.integer.value); + + found = PDS_DOM_find("ATTRIBUTE_1", *item, PDS_ONLY_SIBLINGS, 1); + munit_assert_ptr_equal(NULL, found); + + found = PDS_DOM_find("PDS_VERSION_ID", *item, PDS_ONLY_SIBLINGS, 1); + munit_assert_ptr_not_equal(NULL, found); + + found = PDS_DOM_find("ATTRIBUTE_8", *item, PDS_ONLY_CHILDREN, 0); + munit_assert_ptr_equal(NULL, found); + + found = PDS_DOM_find("object_0", *item, PDS_ONLY_SIBLINGS, 0); + munit_assert_ptr_not_equal(NULL, found); + munit_assert_true(PDS_DOM_is_object(found)); + + munit_assert_ptr_equal(NULL, PDS_DOM_find("ATTRIBUTE_8", found, PDS_ONLY_CHILDREN, 0)); + + found = PDS_DOM_find("object_1", found, PDS_ONLY_CHILDREN, 0); + munit_assert_ptr_not_equal(NULL, found); + munit_assert_true(PDS_DOM_is_object(found)); + + found = PDS_DOM_find("ATTRIBUTE_8", found, PDS_CHILDREN_RECURSIVE, 0); + munit_assert_ptr_not_equal(NULL, found); + munit_assert_true(PDS_DOM_is_attribute(found)); + munit_assert_int(PDS_SINGLE, ==, PDS_DOM_scalar_typeof(found)); + + ret = PDS_DOM_scalar_get(found, &scalar); + munit_assert_true(ret); + munit_assert_int(PDS_INTEGER_VALUE, ==, scalar.type); + munit_assert_int(8, ==, scalar.integer.value); + + found = PDS_DOM_find("ATTRIBUTE_E", *item, PDS_SIBLINGS_RECURSIVE, 1); + munit_assert_ptr_not_equal(NULL, found); + munit_assert_true(PDS_DOM_is_attribute(found)); + munit_assert_int(PDS_SINGLE, ==, PDS_DOM_scalar_typeof(found)); + + ret = PDS_DOM_scalar_get(found, &scalar); + munit_assert_true(ret); + munit_assert_int(PDS_INTEGER_VALUE, ==, scalar.type); + munit_assert_int(16, ==, scalar.integer.value); + + found = PDS_DOM_find("ATTRIBUTE_2", *item, PDS_SIBLINGS_RECURSIVE, 0); + munit_assert_ptr_not_equal(NULL, found); + munit_assert_true(PDS_DOM_is_attribute(found)); + munit_assert_int(PDS_SINGLE, ==, PDS_DOM_scalar_typeof(found)); + + ret = PDS_DOM_scalar_get(found, &scalar); + munit_assert_true(ret); + munit_assert_int(PDS_INTEGER_VALUE, ==, scalar.type); + munit_assert_int(2, ==, scalar.integer.value); + + found = PDS_DOM_find("object_3", found, PDS_SIBLINGS_RECURSIVE, 0); + munit_assert_ptr_not_equal(NULL, found); + munit_assert_true(PDS_DOM_is_object(found)); + + return MUNIT_OK; } + +static MunitTest pds_dom_tests[] = { + { "find", pds_dom_find_test, pds_dom_setup, pds_dom_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite pds_dom_suite = { + "PDS DOM test suite", pds_dom_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main (int argc, char* const* argv) { + return munit_suite_main(&pds_dom_suite, NULL, argc, argv); +} \ No newline at end of file diff --git a/test/PDS_DOM_parse.c b/test/PDS_DOM_parse.c index 4e8fecd..e0f8735 100644 --- a/test/PDS_DOM_parse.c +++ b/test/PDS_DOM_parse.c @@ -1,8 +1,7 @@ #include -#include -#include +#include +#include -#include "test.h" #include "utils.h" #include @@ -10,115 +9,92 @@ #define TINY_PDS_DOM_IMPL #include -void tabs(int depth) -{ - int i; - for(i=0; itype) - { +void print(PDS_item *item, int depth) { + for( ; NULL != item; item=PDS_DOM_sibling(item)) { + print_tab(depth); + switch(item->type) { case PDS_ATTRIBUTE: printf("[attribute] "); print_string(item->name.first, item->name.last); printf(" = "); print_DOM_scalar(item, depth+1); - break; + break; case PDS_POINTER: printf("[pointer ] "); print_string(item->name.first, item->name.last); printf(" = "); print_DOM_scalar(item, depth+1); - break; + break; case PDS_GROUP: printf("[group ] "); print_string(item->name.first, item->name.last); printf("\n"); print(PDS_DOM_group_begin(item), depth+1); - break; + break; case PDS_OBJECT: printf("[object ] "); print_string(item->name.first, item->name.last); printf("\n"); print(PDS_DOM_object_begin(item), depth+1); - break; + break; } } } -int main(int argc, const char* argv[]) -{ +int main(int argc, const char* argv[]) { FILE *in; size_t len; char *buffer; @@ -128,14 +104,12 @@ int main(int argc, const char* argv[]) int ret; const char *filename = argv[1]; - if(argc != 2) - { + if(argc != 2) { fprintf(stderr, "missing PDS file\n"); return EXIT_FAILURE; } in = fopen(filename, "rb"); - if(NULL == in) - { + if(NULL == in) { fprintf(stderr, "failed to read %s : %s\n", filename, strerror(errno)); return EXIT_FAILURE; } @@ -145,14 +119,12 @@ int main(int argc, const char* argv[]) len -= ftell(in); buffer = (char*)malloc(len); - if(NULL == buffer) - { + if(NULL == buffer) { fprintf(stderr, "unable to allocate buffer : %s\n", strerror(errno)); return EXIT_FAILURE; } - if(fread(buffer, 1, len, in) != len) - { + if(fread(buffer, 1, len, in) != len) { fprintf(stderr, "failed to read buffer %s : %s\n", filename, strerror(errno)); return EXIT_FAILURE; } diff --git a/test/PDS_find_first.c b/test/PDS_find_first.c deleted file mode 100644 index 0ef07f1..0000000 --- a/test/PDS_find_first.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "test.h" - -#include -#define TINY_PDS_IMPL -#include - -int main() -{ - begin_test_data(char) - test_data( " b1234\t`#_Q3#[S}", 9, '#', PDS_OK ), - test_data( "namespace:VALUE_1 = 'literal' \r\n", 21, '=', PDS_OK ), - test_data( "I hate clowns!", 0, 'I', PDS_OK ), - test_data( "Klatu verata ni-mumble mumble", -1, '*', PDS_INVALID_VALUE ), - end_test_data() - - size_t i; - test_foreach(i) - { - const char *first = test_str(i); - const char *last = first + strlen(first) - 1; - - const char *found = PDS_find_first(first, last, test_expected(i)); - if(PDS_OK != test_status(i)) - { - check(0 == found); - } - else - { - check(test_end(i) == found); - } - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse.c b/test/PDS_parse.c index e13002f..3fa0830 100644 --- a/test/PDS_parse.c +++ b/test/PDS_parse.c @@ -1,34 +1,29 @@ +#include +#include +#include #include -#include -#include "test.h" #include "utils.h" #define TINY_PDS_IMPL #include -typedef struct -{ +typedef struct { int depth; } user_data_t; -void print_tab(int depth) -{ - int i; - for(i=0; inumber, desc->position, desc->msg); } -int dummy_scalar(const PDS_scalar *scalar, void *user_data) -{ + +int dummy_scalar(const PDS_scalar *scalar, void *user_data) { (void)user_data; print_scalar(scalar); return 1; } -int dummy_attribute_begin(const char *first, const char *last, void *user_data) -{ +int dummy_attribute_begin(const char *first, const char *last, void *user_data) { user_data_t *data = (user_data_t*)user_data; print_tab(data->depth); printf("[attribute] "); @@ -37,16 +32,14 @@ int dummy_attribute_begin(const char *first, const char *last, void *user_data) return 1; } -int dummy_attribute_end(const char *first, const char *last, void *user_data) -{ +int dummy_attribute_end(const char *first, const char *last, void *user_data) { (void)first; (void)last; (void)user_data; return 1; } -int dummy_pointer_begin(const char *first, const char *last, void *user_data) -{ +int dummy_pointer_begin(const char *first, const char *last, void *user_data) { user_data_t *data = (user_data_t*)user_data; print_tab(data->depth); printf("[pointer ] "); @@ -55,46 +48,44 @@ int dummy_pointer_begin(const char *first, const char *last, void *user_data) return 1; } -int dummy_pointer_end(const char *first, const char *last, void *user_data) -{ +int dummy_pointer_end(const char *first, const char *last, void *user_data) { (void)first; (void)last; (void)user_data; return 1; } -int dummy_set_begin(void *user_data) -{ +int dummy_set_begin(void *user_data) { user_data_t *data = (user_data_t*)user_data; data->depth++; printf(" {\n"); return 1; } -int dummy_set_end(void *user_data) -{ + +int dummy_set_end(void *user_data) { user_data_t *data = (user_data_t*)user_data; data->depth--; print_tab(data->depth); printf("}\n"); return 1; } -int dummy_sequence_begin(void *user_data) -{ + +int dummy_sequence_begin(void *user_data) { user_data_t *data = (user_data_t*)user_data; data->depth++; printf("(\n"); return 1; } -int dummy_sequence_end(void *user_data) -{ + +int dummy_sequence_end(void *user_data) { user_data_t *data = (user_data_t*)user_data; data->depth--; print_tab(data->depth); printf(")\n"); return 1; } -int dummy_object_begin(const char *first, const char *last, void *user_data) -{ + +int dummy_object_begin(const char *first, const char *last, void *user_data) { user_data_t *data = (user_data_t*)user_data; print_tab(data->depth); printf("[object ] "); @@ -103,8 +94,8 @@ int dummy_object_begin(const char *first, const char *last, void *user_data) data->depth++; return 1; } -int dummy_object_end(const char *first, const char *last, void *user_data) -{ + +int dummy_object_end(const char *first, const char *last, void *user_data) { (void)first; (void)last; user_data_t *data = (user_data_t*)user_data; @@ -113,8 +104,8 @@ int dummy_object_end(const char *first, const char *last, void *user_data) printf("]\n"); return 1; } -int dummy_group_begin(const char *first, const char *last, void *user_data) -{ + +int dummy_group_begin(const char *first, const char *last, void *user_data) { user_data_t *data = (user_data_t*)user_data; print_tab(data->depth); printf("[group ] "); @@ -123,8 +114,8 @@ int dummy_group_begin(const char *first, const char *last, void *user_data) data->depth++; return 1; } -int dummy_group_end(const char *first, const char *last, void *user_data) -{ + +int dummy_group_end(const char *first, const char *last, void *user_data) { (void)first; (void)last; user_data_t *data = (user_data_t*)user_data; @@ -134,8 +125,7 @@ int dummy_group_end(const char *first, const char *last, void *user_data) return 1; } -int main(int argc, const char* argv[]) -{ +int main(int argc, const char* argv[]) { PDS_callbacks callbacks; FILE *in; diff --git a/test/PDS_parse_datetime.c b/test/PDS_parse_datetime.c deleted file mode 100644 index 3003b75..0000000 --- a/test/PDS_parse_datetime.c +++ /dev/null @@ -1,81 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - -typedef struct -{ - int ret; - PDS_datetime t; -} expected_t; - -int main() -{ - static const expected_t values[] = - { - { 1, {0,1977, 24, 7, 0, 0, 0, 0, 0, 0, PDS_LOCAL_TIME } }, - { 1, {0,1985,320, 0, 0, 0, 0, 0, 0, 0, PDS_LOCAL_TIME } }, - { 1, {0, 0, 0, 0,12, 7,31, 0, 0, 0, PDS_LOCAL_TIME } }, - { 1, {0, 0, 0, 0, 8,15, 1, 2450,-8,-15, PDS_ZONED_TIME } }, - { 1, {0,2015, 14, 7,11,49,57, 0, 0, 0, PDS_LOCAL_TIME } }, - { 1, {0,2016, 3, 1,18,21,49,129900, 2, 10, PDS_ZONED_TIME } }, - { 1, {0,1977, 5, 9,12,56, 0, 0, 0, 0, PDS_UTC_TIME } }, - { 1, {0,1997, 15,10, 8,43, 0, 0, 1, 0, PDS_ZONED_TIME } }, - { 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, PDS_LOCAL_TIME } }, - }; - - begin_test_data(expected_t) - test_data( "1977-07-24 /* single date */", 10, values[0], PDS_OK ), - test_data( "1985-320 /* day of year */", 8, values[1], PDS_OK ), - test_data( "12:07:31 ", 8, values[2], PDS_OK ), - test_data( "08:15:1.00245-08:15 ", 19, values[3], PDS_OK ), - test_data( "2015-07-14T11:49:57\r\n ", 19, values[4], PDS_OK ), - test_data( "2016-01-3T18:21:49.1299+2:10", 28, values[5], PDS_OK ), - test_data( "1977-09-05T12:56:000.000Z\t ", 25, values[6], PDS_OK ), - test_data( "1997-10-15T08:43:00+1 ", 21, values[7], PDS_OK ), - test_data( "11-11-11 /* year */", 0, values[8], PDS_INVALID_RANGE ), - test_data( "1989-53-11 /* month */", 0, values[8], PDS_INVALID_RANGE ), - test_data( "1975-04-34 /* day */", 0, values[8], PDS_INVALID_RANGE ), - test_data( "1976-11-11T /* empty time */", 0, values[8], PDS_INVALID_VALUE ), - test_data( "1977-12-24T07 /*incomplete*/", 0, values[8], PDS_INVALID_VALUE ), - test_data( "1977-12-24T07:15:00-44 ", 0, values[8], PDS_INVALID_RANGE ), - test_data( "1977-12-24T07:15:00+12:75 ", 0, values[8], PDS_INVALID_RANGE ), - end_test_data() - - PDS_parser parser; - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - - size_t i; - test_foreach(i) - { - parser.line_num = 1; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.status = PDS_OK; - - int ret = PDS_parse_datetime(&parser); - - check(test_expected(i).ret == ret); - check(test_end(i) == parser.current); - check(test_status(i) == parser.status); - if(ret) - { - PDS_datetime *date_time = &parser.scalar.date_time; - check(PDS_DATE_TIME_VALUE == parser.scalar.type); - check(test_expected(i).t.year == date_time->year); - check(test_expected(i).t.day == date_time->day); - check(test_expected(i).t.month == date_time->month); - check(test_expected(i).t.hour == date_time->hour); - check(test_expected(i).t.minute == date_time->minute); - check(test_expected(i).t.second == date_time->second); - check(test_expected(i).t.microsecond == date_time->microsecond); - check(test_expected(i).t.hour_offset == date_time->hour_offset); - check(test_expected(i).t.minute_offset == date_time->minute_offset); - check(test_expected(i).t.time_type == date_time->time_type); - } - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_identifier.c b/test/PDS_parse_identifier.c deleted file mode 100644 index 51c23ba..0000000 --- a/test/PDS_parse_identifier.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - begin_test_data(int) - test_data( "ROSETTA:", 7, 1, PDS_OK ), - test_data( "SPACECRAFT_CLOCK_STOP_COUNT", 27, 1, PDS_OK ), - test_data( "INVALID_ENDING_", 0, 0, PDS_INVALID_VALUE ), - test_data( "DOUBLE__SEPARATOR", 0, 0, PDS_INVALID_VALUE ), - test_data( "INVALID_@ID", 0, 0, PDS_INVALID_VALUE ), - test_data( "_INVALID_ID", 0, 0, PDS_INVALID_VALUE ), - end_test_data() - - size_t i; - test_foreach(i) - { - const char *first = test_str(i); - const char *last = first + strlen(first) - 1; - const char *end = 0; - int status = PDS_OK; - - const char *ptr = PDS_parse_identifier(first, last, &end, &status); - check(test_expected(i) == (ptr!=0)); - check(test_end(i) == end); - check(test_status(i) == status); - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_int.c b/test/PDS_parse_int.c deleted file mode 100644 index c562219..0000000 --- a/test/PDS_parse_int.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - - -int main() -{ - begin_test_data(int32_t) - test_data( " 511-", 6, 511, PDS_OK ), - test_data("2#+0101#@ ", 8, 5, PDS_OK ), - test_data( "8#foo#", 2, 0, PDS_INVALID_VALUE ), - test_data( "10#-9a#", 5, INT32_MAX, PDS_INVALID_VALUE ), - test_data( "16#ffc", 6, INT32_MAX, PDS_INVALID_VALUE ), - test_data( "4##", 2, 0, PDS_INVALID_VALUE ), - end_test_data() - - PDS_parser parser; - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - - size_t i; - test_foreach(i) - { - parser.line_num = 1; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.status = PDS_OK; - - int ret = PDS_parse_int(&parser); - check(test_status(i) == parser.status); - if(ret) - { - check(test_end(i) == parser.current); - check(PDS_INTEGER_VALUE == parser.scalar.type); - check(test_expected(i) == parser.scalar.integer.value); - } - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_int_base.c b/test/PDS_parse_int_base.c deleted file mode 100644 index ff97782..0000000 --- a/test/PDS_parse_int_base.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - -typedef struct -{ - int base; - int64_t value; -} integer_t; - -int main() -{ - begin_test_data(integer_t) - test_data( " \t-714@_", 6, test_pair(10, -714), PDS_OK ), - test_data( "bEeF", 4, test_pair(16, 0xbeef), PDS_OK ), - test_data( "10001111", 8, test_pair( 2, 0x8f), PDS_OK ), - test_data( "+1339/6 ", 4, test_pair( 8, 91), PDS_OK ), - test_data( "dummy", 0, test_pair( 7, 0), PDS_INVALID_VALUE ), - test_data( "21874836501234567890", 20, test_pair(10, INT64_MAX), PDS_INVALID_RANGE ), - test_data( "-30000000000000000000", 21, test_pair(10, INT64_MIN), PDS_INVALID_RANGE ), - end_test_data() - - size_t i; - test_foreach(i) - { - const char *first = test_str(i); - const char *last = first + strlen(first) - 1; - const char *end = 0; - int status = PDS_OK; - - int64_t value = PDS_parse_int_base(first, last, &end, test_expected(i).base, &status); - check(test_expected(i).value == value); - check(test_end(i) == end); - check(test_status(i) == status); - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_real.c b/test/PDS_parse_real.c deleted file mode 100644 index 565a0ae..0000000 --- a/test/PDS_parse_real.c +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - begin_test_data(double) - test_data( "0.6170492162", 12, 0.6170492162, PDS_OK), - test_data("-0.3187327298", 13, -0.3187327298, PDS_OK), - test_data("-0.5045034227", 13, -0.5045034227, PDS_OK), - test_data("-0.5129678433", 13, -0.5129678433, PDS_OK), - test_data( " 511-", 6, 511.0, PDS_OK ), - test_data("-71.600 ", 7, -71.6, PDS_OK ), - test_data( "+0.0004", 7, 0.0004, PDS_OK ), - test_data( "1.e3@", 4, 1000.0, PDS_OK ), - test_data("211.32e4", 8, 2113200.0, PDS_OK ), - test_data( "-1.e-5", 6, -1.e-5, PDS_OK ), - test_data( "-.2e-1", 6, -0.02, PDS_OK ), - test_data( "-.01e3", 6, -10.0, PDS_OK ), - test_data( "af.01", 0, 0.0, PDS_OK ), - test_data( "-.e", 0, 0.0, PDS_INVALID_VALUE ), - end_test_data() - - PDS_parser parser; - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - - size_t i; - test_foreach(i) - { - parser.line_num = 1; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.status = PDS_OK; - - int ret = PDS_parse_real(&parser); - check(test_status(i) == parser.status); - check(test_end(i) == parser.current); - if(ret) - { - check(PDS_REAL_VALUE == parser.scalar.type); - check(fabs(test_expected(i) - parser.scalar.real.value) < 1.e-6); - } - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_sequence.c b/test/PDS_parse_sequence.c deleted file mode 100644 index 934e179..0000000 --- a/test/PDS_parse_sequence.c +++ /dev/null @@ -1,185 +0,0 @@ -#include - -#include "test.h" -#include "utils.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - const char *unit_first = "Km"; - const char *unit_last = unit_first + strlen(unit_first) - 1; - - const PDS_scalar sequence_1D[] = - { - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 51, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 8, - .integer.unit.first = unit_first, - .integer.unit.last = unit_last - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 117, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .real.type = PDS_REAL_VALUE, - .real.value = 9.2, - .real.unit.first = unit_first, - .real.unit.last = unit_last - }, - }; - - const PDS_scalar sequence_2D[] = - { - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 4, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { - .integer.type = PDS_INTEGER_VALUE, - .integer.value = 2048, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .real.type = PDS_REAL_VALUE, - .real.value = 0.6, - .real.unit.first = 0, - .real.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 14, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { - .integer.type = PDS_INTEGER_VALUE, - .integer.value = 1024, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .real.type = PDS_REAL_VALUE, - .real.value = 1.2, - .real.unit.first = 0, - .real.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 24, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { - .integer.type = PDS_INTEGER_VALUE, - .integer.value = 256, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .real.type = PDS_REAL_VALUE, - .real.value = 2.4, - .real.unit.first = 0, - .real.unit.last = 0 - } - }; - - const PDS_scalar failure[] = - { - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 1, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 2, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 3, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 4, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 5, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 6, - .integer.unit.first = 0, - .integer.unit.last = 0 - } - }; - - const expected_t sequences[] = - { - { .depth = 1, - .count = 4, - .name = "dummy", - .scalar = sequence_1D - }, - { - .depth = 2, - .count = 9, - .name = "matrix", - .scalar = sequence_2D - }, - { - .depth = 2, - .count = 6, - .name = "failure", - .scalar = failure - } - }; - - begin_test_data(const expected_t*) - test_data(" dummy = \t( 51, 8 , \t117, 9.2 )\r\n", 44, &sequences[0], PDS_OK), - test_data("matrix = (\r\n\t(4,2048,.6),\r\n\t(14,1024,1.2),\r\n\t(24,256,2.4) )\r\n", 61, &sequences[1], PDS_OK), - test_data("failure = (\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - test_data("failure = ()\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - test_data("failure = (())\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - test_data("failure = ((1,2)\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - test_data("failure = (1,,3),\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - test_data("failure = ((1,2,3,),(4,5,6))\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - test_data("failure = ((1,2,3),(4,5,6)\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - test_data("failure = (1,2,3,(4,5,6))\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - test_data("failure = ((1,2),3,4,5,6)\r\n", 0, &sequences[2], PDS_INVALID_VALUE), - end_test_data() - - PDS_parser parser; - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - PDS_set_sequence_callbacks(&parser.callbacks, sequence_begin_callback, sequence_end_callback); - PDS_set_scalar_callback(&parser.callbacks, sequence_element_callback); - - size_t i; - test_foreach(i) - { - state_t state = { .depth = 0, .index = 0, .expected = test_expected(i) }; - - parser.line_num = 1; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.status = PDS_OK; - parser.user_data = &state; - - int ret = PDS_parse_statement(&parser); - check(test_status(i) == parser.status); - if(ret) - { - check(test_end(i) == parser.current); - } - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_set.c b/test/PDS_parse_set.c deleted file mode 100644 index c26bf74..0000000 --- a/test/PDS_parse_set.c +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include "test.h" -#include "utils.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - const PDS_scalar data[] = - { - { .real.type = PDS_REAL_VALUE, - .real.value = 123.8, - .real.unit.first = 0, - .real.unit.last = 0 - }, - { .real.type = PDS_REAL_VALUE, - .real.value = 90.0, - .real.unit.first = 0, - .real.unit.last = 0 - }, - { .real.type = PDS_REAL_VALUE, - .real.value = 0.125, - .real.unit.first = 0, - .real.unit.last = 0 - }, - { .real.type = PDS_REAL_VALUE, - .real.value = 0.02, - .real.unit.first = 0, - .real.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 1, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 2, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 3, - .integer.unit.first = 0, - .integer.unit.last = 0 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.value = 4, - .integer.unit.first = 0, - .integer.unit.last = 0 - } - }; - - - const expected_t set[] = - { - { .count = 4, - .name = "dummy", - .scalar = &data[0] - }, - { .count = 6, - .name = "failure", - .scalar = &data[4] - } - }; - - begin_test_data(const expected_t*) - test_data("dummy = { 123.8, 90.0, 0.125, 0.02 }\r\n", 38, &set[0], PDS_OK), - test_data("failure = {\r\n", 0, &set[1], PDS_INVALID_VALUE), - test_data("failure = {}\r\n", 0, &set[1], PDS_INVALID_VALUE), - test_data("failure = { \t, }\r\n", 0, &set[1], PDS_INVALID_VALUE), - test_data("failure = {{}}\r\n", 0, &set[1], PDS_INVALID_VALUE), - test_data("failure = {1,,3}\r\n", 0, &set[1], PDS_INVALID_VALUE), - test_data("failure = {{1,2,3,4,5,6}}\r\n", 0, &set[1], PDS_INVALID_VALUE), - end_test_data() - - PDS_parser parser; - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - PDS_set_set_callbacks(&parser.callbacks, set_begin_callback, set_end_callback); - PDS_set_scalar_callback(&parser.callbacks, set_element_callback); - - size_t i; - test_foreach(i) - { - state_t state = { .index = 0, .expected = test_expected(i) }; - - parser.line_num = 1; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.status = PDS_OK; - parser.user_data = &state; - - int ret = PDS_parse_statement(&parser); - check(test_status(i) == parser.status); - if(ret) - { - check(test_end(i) == parser.current); - } - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_statement.c b/test/PDS_parse_statement.c deleted file mode 100644 index 6eba506..0000000 --- a/test/PDS_parse_statement.c +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include "test.h" -#include "utils.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - const char *degrees = "DEGREES"; - const char *symbolic = "dummy"; - const char *group = "dummy_group_0"; - const char *object = "IMAGE"; - - const PDS_scalar data[] = - { - { .date_time.type = PDS_DATE_TIME_VALUE, - .date_time.year = 2016, - .date_time.day = 3, - .date_time.month = 1, - .date_time.hour = 18, - .date_time.minute = 21, - .date_time.second = 49, - .date_time.microsecond = 129900, - .date_time.hour_offset = 2, - .date_time.minute_offset = 10, - .date_time.time_type = PDS_ZONED_TIME - }, - { .real.type = PDS_REAL_VALUE, - .real.unit.first = degrees, - .real.unit.last = degrees + strlen(degrees)-1, - .real.value = 12.0 - }, - { .symbolic.type = PDS_SYMBOLIC_VALUE, - .symbolic.first = symbolic, - .symbolic.last = symbolic + strlen(symbolic)-1 - }, - { .integer.type = PDS_INTEGER_VALUE, - .integer.unit.first = 0, - .integer.unit.last = 0, - .integer.value = 327128 - }, - { - .identifier.type = PDS_IDENTIFIER_VALUE, - .identifier.first = group, - .identifier.last = group + strlen(group)-1 - }, - { - .identifier.type = PDS_IDENTIFIER_VALUE, - .identifier.first = object, - .identifier.last = object + strlen(object)-1 - } - }; - const expected_t expected[] = - { - { .name = "LOCAL_NAMESPACE:DATE_TIME_ATTR", - .scalar = &data[0], - .count = 1 - }, - { .name = "ELEVATION_FOV", - .scalar = &data[1], - .count = 1 - }, - { .name = "DUMMY:SYMBOLIC_LITERAL", - .scalar = &data[2], - .count = 1 - }, - { .name = "IMAGE", - .scalar = &data[3], - .count = 1 - }, - { - .name = "group", - .scalar = &data[4], - .count = 1 - }, - { - .name = "object", - .scalar = &data[5], - .count = 1 - } - }; - - begin_test_data(const expected_t*) - test_data("LOCAL_NAMESPACE:DATE_TIME_ATTR = 2016-01-3T18:21:49.1299+2:10 \r\n", 64, &expected[0], PDS_OK ), - test_data("ELEVATION_FOV = 12.000 \r\n", 51, &expected[1], PDS_OK ), - test_data("DUMMY:SYMBOLIC_LITERAL = 'dummy' \r\n", 35, &expected[2], PDS_OK ), - test_data("/* comment line #0 */\r\n/* comment line #1 */\r\n^IMAGE = \t 327128 \r\n", 69, &expected[3], PDS_OK ), - test_data(" \r\n group\t = dummy_group_0\r\n", 35, &expected[4], PDS_OK ), - test_data("end_group=dummy_group_0\t \r\n", 30, &expected[4], PDS_OK ), - test_data("OBJECT = IMAGE\r\n", 16, &expected[5], PDS_OK ), - test_data("END_OBJECT = IMAGE\r\n", 20, &expected[5], PDS_OK ), - end_test_data() - - PDS_parser parser; - state_t state; - - memset(&state, 0, sizeof(state_t)); - - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - PDS_set_scalar_callback(&parser.callbacks, scalar_callback); - PDS_set_attribute_callbacks(&parser.callbacks, attribute_begin_callback, attribute_end_callback); - PDS_set_pointer_callbacks(&parser.callbacks, pointer_begin_callback, pointer_end_callback); - PDS_set_group_callbacks(&parser.callbacks, group_begin_callback, group_end_callback); - PDS_set_object_callbacks(&parser.callbacks, object_begin_callback, object_end_callback); - - size_t i; - test_foreach(i) - { - state.index = 0; - state.expected = test_expected(i); - - parser.line_num = 1; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.status = PDS_OK; - parser.user_data = &state; - - int ret = PDS_parse_statement(&parser); - check(test_status(i) == parser.status); - if(ret) - { - check(test_end(i) == parser.current); - } - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_string.c b/test/PDS_parse_string.c deleted file mode 100644 index 64b207f..0000000 --- a/test/PDS_parse_string.c +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - PDS_parser parser; - begin_test_data(int) - test_data( "\"\" /*empty string*/", 2, 1, PDS_OK ), - test_data( "\"simple string\" ", 15, 1, PDS_OK ), - test_data("\"multi\r\nline\r\n\tstring\" ", 22, 1, PDS_OK ), - test_data( "\"Some\\\\escaped\\nchars\"", 22, 1, PDS_OK ), - test_data( "\" missing delimiter ", 0, 0, PDS_INVALID_VALUE ), - end_test_data() - - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - - size_t i; - test_foreach(i) - { - parser.status = PDS_OK; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.line_num = 1; - - int ret = PDS_parse_string(&parser); - - check(test_expected(i) == ret); - check(test_end(i) == parser.current); - check(test_status(i) == parser.status); - if(ret) - { - check(PDS_TEXT_STRING_VALUE == parser.scalar.type); - check((parser.first+1) == parser.scalar.text.first); - check((parser.current-2) == parser.scalar.text.last); - } - } - - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_symbol.c b/test/PDS_parse_symbol.c deleted file mode 100644 index 777a6ce..0000000 --- a/test/PDS_parse_symbol.c +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - - -int main() -{ - begin_test_data(int) - test_data( "' \t _/<@>)?!#$.;'", 17, 1, PDS_OK ), - test_data( "'Itchy&Scratchy'", 16, 1, PDS_OK ), - test_data("'New York 1997' ", 15, 1, PDS_OK ), - test_data( "'%' ", 3, 1, PDS_OK ), - test_data( "'ANEW-LINE\r\n'", 0, 0, PDS_INVALID_VALUE ), - test_data("'' /*empty string*/", 0, 0, PDS_INVALID_VALUE ), - end_test_data() - - PDS_parser parser; - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - - size_t i; - test_foreach(i) - { - parser.scalar.type = PDS_UNKNOWN_VALUE; - - parser.line_num = 1; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.status = PDS_OK; - - int ret = PDS_parse_symbol(&parser); - - check(test_expected(i) == ret); - check(test_end(i) == parser.current); - check(test_status(i) == parser.status); - if(ret) - { - check(PDS_SYMBOLIC_VALUE == parser.scalar.type); - check((test_str(i)+1) == parser.scalar.symbolic.first); - check((test_end(i)-2) == parser.scalar.symbolic.last); - } - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_parse_tests.c b/test/PDS_parse_tests.c new file mode 100644 index 0000000..052ead9 --- /dev/null +++ b/test/PDS_parse_tests.c @@ -0,0 +1,1034 @@ +#include + +#define TINY_PDS_IMPL +#include + +void dummy_error(const PDS_error_description *desc, void *unused) { + (void)unused; + munit_logf(MUNIT_LOG_INFO, "line %d:%d: %s", desc->number, desc->position, desc->msg); +} + +int compare_scalar(const PDS_scalar *s0, const PDS_scalar *s1) { + if(s0->type != s1->type) { + return 0; + } + switch(s0->type) { + case PDS_INTEGER_VALUE: + if(s0->integer.value == s1->integer.value) { + if( (s0->integer.unit.first && s1->integer.unit.first) + && (s0->integer.unit.last && s1->integer.unit.last ) ) { + return PDS_string_compare(s0->integer.unit.first, s0->integer.unit.last, + s1->integer.unit.first, s1->integer.unit.last); + } + else if( (0 == s0->integer.unit.first) && (0 == s1->integer.unit.first) + && (0 == s1->integer.unit.first) && (0 == s1->integer.unit.last) ) { + return 1; + } + } + return 0; + case PDS_REAL_VALUE: + if(s0->real.value == s1->real.value) { + if( (s0->real.unit.first && s1->real.unit.first) + && (s0->real.unit.last && s1->real.unit.last ) ) { + return PDS_string_compare(s0->real.unit.first, s0->real.unit.last, + s1->real.unit.first, s1->real.unit.last); + } + else if( (0 == s0->real.unit.first) && (0 == s1->real.unit.first) + && (0 == s1->real.unit.first) && (0 == s1->real.unit.last) ) { + return 1; + } + } + return 0; + case PDS_DATE_TIME_VALUE: + return ( (s0->date_time.time_type == s1->date_time.time_type) + && (s0->date_time.year == s1->date_time.year) + && (s0->date_time.day == s1->date_time.day) + && (s0->date_time.month == s1->date_time.month) + && (s0->date_time.hour == s1->date_time.hour) + && (s0->date_time.minute == s1->date_time.minute) + && (s0->date_time.second == s1->date_time.second) + && (s0->date_time.microsecond == s1->date_time.microsecond) + && (s0->date_time.hour_offset == s1->date_time.hour_offset) + && (s0->date_time.minute_offset == s1->date_time.minute_offset) ); + case PDS_SYMBOLIC_VALUE: + return PDS_string_compare(s0->symbolic.first, s0->symbolic.last, s1->symbolic.first, s1->symbolic.last); + case PDS_TEXT_STRING_VALUE: + return PDS_string_compare(s0->text.first, s0->text.last, s1->text.first, s1->text.last); + case PDS_IDENTIFIER_VALUE: + return PDS_string_compare(s0->identifier.first, s0->identifier.last, s1->identifier.first, s1->identifier.last); + default: + return 0; + } +} + +typedef struct { + int depth; + int count; + const char *name; + const PDS_scalar *scalar; +} expected_t; + +typedef struct { + int depth; + int index; + const expected_t *expected; +} state_t; + +int sequence_begin_callback(void *user_data) +{ + state_t *state = (state_t*)user_data; + const expected_t *expected = state->expected; + state->depth++; + if(state->depth > expected->depth) { + munit_log(MUNIT_LOG_INFO, "invalid sequence dimension"); + return 0; + } + return 1; +} + +int sequence_element_callback(const PDS_scalar *scalar, void *user_data) +{ + state_t *state = (state_t*)user_data; + const expected_t *expected = state->expected; + if(state->index >= expected->count) { + munit_log(MUNIT_LOG_INFO, "too many scalars"); + return 0; + } + if(!compare_scalar(scalar, &(expected->scalar[state->index]))) { + munit_log(MUNIT_LOG_INFO, "scalar value mismatch"); + return 0; + } + state->index++; + return 1; +} + +int sequence_end_callback(void *user_data) { + state_t *state = (state_t*)user_data; + state->depth--; + if(state->depth < 0) { + munit_log(MUNIT_LOG_INFO, "invalid sequence dimension"); + return 0; + } + return 1; +} + +int set_begin_callback(void *user_data) { + (void)user_data; + return 1; +} + +int set_element_callback(const PDS_scalar *scalar, void *user_data) { + state_t *state = (state_t*)user_data; + const expected_t *expected = state->expected; + if(state->index >= expected->count) { + munit_log(MUNIT_LOG_INFO, "too many scalars"); + return 0; + } + if(!compare_scalar(scalar, &(expected->scalar[state->index]))) { + munit_log(MUNIT_LOG_INFO, "scalar value mismatch"); + return 0; + } + state->index++; + return 1; +} + +int set_end_callback(void *user_data) { + (void)user_data; + return 1; +} + +int attribute_begin_callback(const char *first, const char *last, void *user_data) { + state_t *state = (state_t*)user_data; + const expected_t *expected = state->expected; + if(!PDS_string_compare(first, last, expected->name, expected->name+strlen(expected->name)-1)) { + munit_log(MUNIT_LOG_INFO, "attribute name mismatch"); + return 0; + } + return 1; +} + +int attribute_end_callback(const char *first, const char *last, void *user_data) { + (void)first; + (void)last; + (void)user_data; + return 1; +} + +int pointer_begin_callback(const char *first, const char *last, void *user_data) { + state_t *state = (state_t*)user_data; + const expected_t *expected = state->expected; + if(!PDS_string_compare(first, last, expected->name, expected->name+strlen(expected->name)-1)) + { + munit_log(MUNIT_LOG_INFO, "pointer name mismatch"); + return 0; + } + return 1; +} + +int pointer_end_callback(const char *first, const char *last, void *user_data) { + (void)first; + (void)last; + (void)user_data; + return 1; +} + +int scalar_callback(const PDS_scalar *scalar, void *user_data) { + state_t *state = (state_t*)user_data; + const expected_t *expected = state->expected; + if(!compare_scalar(scalar, expected->scalar)) { + munit_log(MUNIT_LOG_INFO, "scalar value mismatch"); + return 0; + } + return 1; +} + +int declaration_check_name(const char *what, const char *first, const char *last, void *user_data) { + state_t *state = (state_t*)user_data; + const expected_t *expected = state->expected; + const PDS_scalar *scalar; + int i = state->index++; + if(i >= expected->count) + { + munit_log(MUNIT_LOG_INFO, "too many elements"); + return 0; + } + scalar = &expected->scalar[i]; + if(!PDS_string_compare(scalar->identifier.first, scalar->identifier.last, first, last)) + { + munit_logf(MUNIT_LOG_INFO, "%s name mismatch", what); + return 0; + } + return 1; +} + +static int declaration_begin(const char *what, const char *first, const char *last, void *user_data) { + if(!declaration_check_name(what, first, last, user_data)) { + return 0; + } + return 1; +} + +static int declaration_end(const char *what, const char *first, const char *last, void *user_data) { + if(!declaration_check_name(what, first, last, user_data)) { + return 0; + } + return 1; +} + +int group_begin_callback(const char *first, const char *last, void *user_data) { + return declaration_begin("group", first, last, user_data); +} + +int group_end_callback(const char *first, const char *last, void *user_data) { + return declaration_end("group", first, last, user_data); +} + +int object_begin_callback(const char *first, const char *last, void *user_data) { + return declaration_begin("object", first, last, user_data); +} + +int object_end_callback(const char *first, const char *last, void *user_data) { + return declaration_end("object", first, last, user_data); +} + +MunitResult parse_statement_test(const MunitParameter params[], void* fixture) { + const char *degrees = "DEGREES"; + const char *symbolic = "dummy"; + const char *group = "dummy_group_0"; + const char *object = "IMAGE"; + + const PDS_scalar data[] = { + { .date_time.type = PDS_DATE_TIME_VALUE, + .date_time.year = 2016, + .date_time.day = 3, + .date_time.month = 1, + .date_time.hour = 18, + .date_time.minute = 21, + .date_time.second = 49, + .date_time.microsecond = 129900, + .date_time.hour_offset = 2, + .date_time.minute_offset = 10, + .date_time.time_type = PDS_ZONED_TIME + }, + { .real.type = PDS_REAL_VALUE, + .real.unit.first = degrees, + .real.unit.last = degrees + strlen(degrees)-1, + .real.value = 12.0 + }, + { .symbolic.type = PDS_SYMBOLIC_VALUE, + .symbolic.first = symbolic, + .symbolic.last = symbolic + strlen(symbolic)-1 + }, + { .integer.type = PDS_INTEGER_VALUE, + .integer.unit.first = 0, + .integer.unit.last = 0, + .integer.value = 327128 + }, + { .identifier.type = PDS_IDENTIFIER_VALUE, + .identifier.first = group, + .identifier.last = group + strlen(group)-1 + }, + { .identifier.type = PDS_IDENTIFIER_VALUE, + .identifier.first = object, + .identifier.last = object + strlen(object)-1 + } + }; + const expected_t expected[] = { + { .name = "LOCAL_NAMESPACE:DATE_TIME_ATTR", + .scalar = &data[0], + .count = 1 + }, + { .name = "ELEVATION_FOV", + .scalar = &data[1], + .count = 1 + }, + { .name = "DUMMY:SYMBOLIC_LITERAL", + .scalar = &data[2], + .count = 1 + }, + { .name = "IMAGE", + .scalar = &data[3], + .count = 1 + }, + { + .name = "group", + .scalar = &data[4], + .count = 1 + }, + { + .name = "object", + .scalar = &data[5], + .count = 1 + } + }; + + struct { + const char *str; + int end; + const expected_t *expected; + int status; + } tv[] = { + { "LOCAL_NAMESPACE:DATE_TIME_ATTR = 2016-01-3T18:21:49.1299+2:10 \r\n", 64, &expected[0], PDS_OK }, + { "ELEVATION_FOV = 12.000 \r\n", 51, &expected[1], PDS_OK }, + { "DUMMY:SYMBOLIC_LITERAL = 'dummy' \r\n", 35, &expected[2], PDS_OK }, + { "/* comment line #0 */\r\n/* comment line #1 */\r\n^IMAGE = \t 327128 \r\n", 69, &expected[3], PDS_OK }, + { " \r\n group\t = dummy_group_0\r\n", 35, &expected[4], PDS_OK }, + { "end_group=dummy_group_0\t \r\n", 30, &expected[4], PDS_OK }, + { "OBJECT = IMAGE\r\n", 16, &expected[5], PDS_OK }, + { "END_OBJECT = IMAGE\r\n", 20, &expected[5], PDS_OK } + }; + + PDS_parser parser; + state_t state; + + (void)params; + (void)fixture; + + memset(&state, 0, sizeof(state_t)); + + memset(&parser, 0, sizeof(PDS_parser)); + PDS_set_error_callback(&parser.callbacks, dummy_error); + PDS_set_scalar_callback(&parser.callbacks, scalar_callback); + PDS_set_attribute_callbacks(&parser.callbacks, attribute_begin_callback, attribute_end_callback); + PDS_set_pointer_callbacks(&parser.callbacks, pointer_begin_callback, pointer_end_callback); + PDS_set_group_callbacks(&parser.callbacks, group_begin_callback, group_end_callback); + PDS_set_object_callbacks(&parser.callbacks, object_begin_callback, object_end_callback); + + size_t i; + for(i=0; i, \t117, 9.2 )\r\n", 44, &sequences[0], PDS_OK }, + { "matrix = (\r\n\t(4,2048,.6),\r\n\t(14,1024,1.2),\r\n\t(24,256,2.4) )\r\n", 61, &sequences[1], PDS_OK }, + { "failure = (\r\n", 0, &sequences[2], PDS_INVALID_VALUE }, + { "failure = ()\r\n", 0, &sequences[2], PDS_INVALID_VALUE }, + { "failure = (())\r\n", 0, &sequences[2], PDS_INVALID_VALUE }, + { "failure = ((1,2)\r\n", 0, &sequences[2], PDS_INVALID_VALUE }, + { "failure = (1,,3),\r\n", 0, &sequences[2], PDS_INVALID_VALUE }, + { "failure = ((1,2,3,),(4,5,6))\r\n", 0, &sequences[2], PDS_INVALID_VALUE }, + { "failure = ((1,2,3),(4,5,6)\r\n", 0, &sequences[2], PDS_INVALID_VALUE }, + { "failure = (1,2,3,(4,5,6))\r\n", 0, &sequences[2], PDS_INVALID_VALUE }, + { "failure = ((1,2),3,4,5,6)\r\n", 0, &sequences[2], PDS_INVALID_VALUE } + }; + + (void)params; + (void)fixture; + + PDS_parser parser; + memset(&parser, 0, sizeof(PDS_parser)); + PDS_set_error_callback(&parser.callbacks, dummy_error); + PDS_set_sequence_callbacks(&parser.callbacks, sequence_begin_callback, sequence_end_callback); + PDS_set_scalar_callback(&parser.callbacks, sequence_element_callback); + + size_t i; + for(i=0; iyear); + munit_assert_int(tv[i].expected.t.day, ==, date_time->day); + munit_assert_int(tv[i].expected.t.month, ==, date_time->month); + munit_assert_int(tv[i].expected.t.hour, ==, date_time->hour); + munit_assert_int(tv[i].expected.t.minute, ==, date_time->minute); + munit_assert_int(tv[i].expected.t.second, ==, date_time->second); + munit_assert_int(tv[i].expected.t.microsecond, ==, date_time->microsecond); + munit_assert_int(tv[i].expected.t.hour_offset, ==, date_time->hour_offset); + munit_assert_int(tv[i].expected.t.minute_offset, ==, date_time->minute_offset); + munit_assert_int(tv[i].expected.t.time_type, ==, date_time->time_type); + } + } + return MUNIT_OK; +} + +MunitResult parse_symbol_test(const MunitParameter params[], void* fixture) { + struct { + const char *str; + int end; + int expected; + int status; + } tv[] = { + { "' \t _/<@>)?!#$.;'", 17, 1, PDS_OK }, + { "'Itchy&Scratchy'", 16, 1, PDS_OK }, + {"'New York 1997' ", 15, 1, PDS_OK }, + { "'%' ", 3, 1, PDS_OK }, + { "'ANEW-LINE\r\n'", 0, 0, PDS_INVALID_VALUE }, + {"'' /*empty string*/", 0, 0, PDS_INVALID_VALUE } + }; + + PDS_parser parser; + memset(&parser, 0, sizeof(PDS_parser)); + PDS_set_error_callback(&parser.callbacks, dummy_error); + + (void)params; + (void)fixture; + + size_t i; + for(i=0; i", 18, 1, PDS_OK }, + { "\r\n", 6, 1, PDS_OK }, + { " ", 9, 1, PDS_OK }, + { "", 15, 1, PDS_OK }, + { "", 0, 0, PDS_INVALID_VALUE }, + { "", 0, 0, PDS_INVALID_VALUE }, + { "", 0, 0, PDS_INVALID_VALUE }, + { "", 0, 0, PDS_INVALID_VALUE }, + { "", 0, 0, PDS_INVALID_VALUE }, + { "", 0, 0, PDS_INVALID_VALUE } + }; + + PDS_parser parser; + memset(&parser, 0, sizeof(PDS_parser)); + PDS_set_error_callback(&parser.callbacks, dummy_error); + + (void)params; + (void)fixture; + + size_t i; + for(i=0; i -#include "test.h" - -#define TINY_PDS_IMPL -#include - - -int main() -{ - begin_test_data(int) - test_data( "", 18, 1, PDS_OK ), - test_data( "\r\n", 6, 1, PDS_OK ), - test_data( " ", 9, 1, PDS_OK ), - test_data( "", 15, 1, PDS_OK ), - test_data( "", 0, 0, PDS_INVALID_VALUE ), - test_data( "", 0, 0, PDS_INVALID_VALUE ), - test_data( "", 0, 0, PDS_INVALID_VALUE ), - test_data( "", 0, 0, PDS_INVALID_VALUE ), - test_data( "", 0, 0, PDS_INVALID_VALUE ), - test_data( "", 0, 0, PDS_INVALID_VALUE ), - end_test_data() - - PDS_parser parser; - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - - size_t i; - test_foreach(i) - { - parser.line_num = 1; - parser.first = test_str(i); - parser.last = parser.first + strlen(parser.first) - 1; - parser.current = parser.first; - parser.status = PDS_OK; - - parser.scalar.type = PDS_INTEGER_VALUE; - parser.scalar.integer.unit.first = 0; - parser.scalar.integer.unit.last = 0; - - int ret = PDS_parse_unit(&parser); - - check(test_expected(i) == ret); - check(test_end(i) == parser.current); - check(test_status(i) == parser.status); - } - return EXIT_SUCCESS; -} diff --git a/test/PDS_skip_whitespaces.c b/test/PDS_skip_whitespaces.c deleted file mode 100644 index 4514244..0000000 --- a/test/PDS_skip_whitespaces.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - -typedef struct -{ - int ret; - int line; -} expected_t; - -int main() -{ - begin_test_data(expected_t) - test_data( "nothing to skip!", 0, test_pair(1,1), PDS_OK ), - test_data( "\t \r\n end", 8, test_pair(1,2), PDS_OK ), - test_data( " /***/end", 6, test_pair(1,1), PDS_OK ), - test_data( "/**/ end", 5, test_pair(1,1), PDS_OK ), - test_data( "/* a\tlonger\tcomment */\t\r\n\r\nend", 27, test_pair(1,3), PDS_OK ), - test_data(" \t/*line 1 */\n \t\t/*line 2*/\n \t\t\t/*line 3*/\nend", 44, test_pair(1,4), PDS_OK ), - test_data( " /* /* nested comment */", 6, test_pair(0,1), PDS_INVALID_VALUE ), - test_data( " /* muti-line \n comment */", 15, test_pair(0,1), PDS_INVALID_VALUE ), - test_data( " /*/ ", 6, test_pair(0,1), PDS_INCOMPLETE ), - test_data( " /* incomplete comment", 23, test_pair(0,1), PDS_INCOMPLETE ), - test_data( "/", 1, test_pair(0,1), PDS_INCOMPLETE ), - test_data( " /\t*", 2, test_pair(0,1), PDS_INVALID_VALUE ), - test_data( "\t\t/*", 4, test_pair(0,1), PDS_INCOMPLETE ), - end_test_data() - - PDS_parser parser; - memset(&parser, 0, sizeof(PDS_parser)); - PDS_set_error_callback(&parser.callbacks, dummy_error); - - size_t i; - test_foreach(i) - { - parser.line_num = 1; - parser.first = test_str(i); - parser.current = parser.first; - parser.last = parser.first + strlen(parser.first) - 1; - parser.status = PDS_OK; - int ret; - ret = PDS_skip_whitespaces(&parser); - check(test_status(i) == parser.status); - check(test_end(i) == parser.current); - check(test_expected(i).ret == ret); - check(test_expected(i).line == parser.line_num); - } - - return EXIT_SUCCESS; -} diff --git a/test/PDS_string_case_compare.c b/test/PDS_string_case_compare.c deleted file mode 100644 index cb86232..0000000 --- a/test/PDS_string_case_compare.c +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - const char *buffer = "test string with lowercase test string, uppercase TEST STRING and mixed-case test sTrIng."; - - struct - { - const char *first[2]; - const char *second[2]; - int expected; - } tv[] = { - { { buffer , buffer+10 }, { buffer+27, buffer+37 }, 1 }, - { { buffer , buffer+10 }, { buffer+50, buffer+60 }, 1 }, - { { buffer+50, buffer+60 }, { buffer+77, buffer+87 }, 1 }, - { { buffer , buffer+25 }, { buffer+27, buffer+42 }, 0 }, - { { buffer , buffer+10 }, { buffer+27, buffer+50 }, 0 }, - { { buffer+27, buffer+47 }, { buffer , buffer+10 }, 0 }, - }; - - size_t i; - for(i=0; i -#include "test.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - const char *buffer = "test string with lowercase test string, uppercase TEST STRING and mixed-case test sTrIng."; - - struct - { - const char *first[2]; - const char *second[2]; - int expected; - } tv[] = { - { { buffer , buffer+10 }, { buffer+27, buffer+37 }, 1 }, - { { buffer+50, buffer+60 }, { buffer+77, buffer+87 }, 0 }, - { { buffer , buffer+25 }, { buffer+27, buffer+42 }, 0 }, - { { buffer , buffer+10 }, { buffer+27, buffer+50 }, 0 }, - { { buffer+27, buffer+47 }, { buffer , buffer+10 }, 0 }, - }; - - size_t i; - for(i=0; i + +#define TINY_PDS_IMPL +#include + +void dummy_error(const PDS_error_description *desc, void *unused) +{ + (void)unused; + munit_logf(MUNIT_LOG_INFO, "line %d:%d: %s\n", desc->number, desc->position, desc->msg); +} + +MunitResult find_first_test(const MunitParameter params[], void* fixture) { + struct { + const char *str; + int end; + char expected; + int status; + } tv[] = { + { " b1234\t`#_Q3#[S}", 9, '#', PDS_OK }, + { "namespace:VALUE_1 = 'literal' \r\n", 21, '=', PDS_OK }, + { "I hate clowns!", 0, 'I', PDS_OK }, + { "Klatu verata ni-mumble mumble", -1, '*', PDS_INVALID_VALUE } + }; + + (void)params; + (void)fixture; + + size_t i; + for(i=0; i= 0) + munit_assert_ptr_equal(first + tv[i].expected, begin); + else + munit_assert_ptr_equal(0, begin); + + if(tv[i].end >= 0) + munit_assert_ptr_equal(first + tv[i].end, end); + else + munit_assert_ptr_equal(0, begin); + } + + return MUNIT_OK; +} + +static MunitTest string_tests[] = { + { "find first", find_first_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, + { "skip whitespaces", skip_whitespaces_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, + { "case compare", string_case_compare_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, + { "compare", string_compare_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, + { "trim", trim_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +static const MunitSuite string_suite = { + "string test suite/", string_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE +}; + +int main (int argc, char* const* argv) { + return munit_suite_main(&string_suite, NULL, argc, argv); +} \ No newline at end of file diff --git a/test/PDS_trim.c b/test/PDS_trim.c deleted file mode 100644 index a8d1f0f..0000000 --- a/test/PDS_trim.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include "test.h" - -#define TINY_PDS_IMPL -#include - -int main() -{ - begin_test_data(off_t) - test_data( "\t\n \r test_\t fin", 17, 7, PDS_OK ), - test_data( "\t [ string ]\r\n", 12, 3, PDS_OK ), - test_data( "another test", 11, 0, PDS_OK ), - test_data( "_+=*\t\t\t\n", 3, 0, PDS_OK ), - test_data( " \r\v\t \n ", -1,-1, PDS_INVALID_VALUE ), - end_test_data() - - size_t i; - test_foreach(i) - { - const char *first = test_str(i); - const char *last = first + strlen(first) - 1; - const char *begin = 0; - const char *end = 0; - - PDS_trim(first, last, &begin, &end); - if(PDS_OK != test_status(i)) - { - check(0 == begin); - check(0 == end); - } - else - { - check(first+test_expected(i) == begin); - check(test_end(i) == end); - } - } - return EXIT_SUCCESS; -} diff --git a/test/data/AMI_LR3_R00976_00031_00040.IMG b/test/data/AMI_LR3_R00976_00031_00040.IMG new file mode 100644 index 0000000..550ad1d --- /dev/null +++ b/test/data/AMI_LR3_R00976_00031_00040.IMG @@ -0,0 +1,263 @@ +PDS_VERSION_ID = PDS3 + +/*** FILE CHARACTERISTICS ***/ +FILE_NAME = "AMI_LR3_R00976_00031_00040.IMG" +RECORD_TYPE = FIXED_LENGTH +RECORD_BYTES = 2048 +FILE_RECORDS = 530 +LABEL_RECORDS = 10 +INTERCHANGE_FORMAT = BINARY + +/*** POINTERS TO DATA OBJECTS ***/ +^BROWSE_IMAGE = 20481 +^IMAGE = 36865 + +/*** IDENTIFICATION DATA ELEMENTS ***/ +RELEASE_ID = 0001 +REVISION_ID = 0000 +DATA_SET_ID = "S1-L/X-AMIE-3-RDR-LP-V1.0" +DATA_SET_NAME = " + SMART-1 LUNAR/OTHER AMIE 3 RDR CAL DATA LUNAR PHASE V1.0" +PRODUCT_ID = AMI_LR3_R00976_00031_00040 +PROCESSING_LEVEL_ID = 3 +PROCESSING_LEVEL_DESC = "CALIBRATED DATA. + SOMETIMES CALLED REDUCED DATA RECORD (RDR)" +PRODUCT_CREATION_TIME = 2010-04-08T20:32:17 +PRODUCER_INSTITUTION_NAME = "SPACE-X" +MISSION_ID = "SMART1" +MISSION_NAME = " + SMALL MISSIONS FOR ADVANCED RESEARCH AND TECHNOLOGY" +INSTRUMENT_HOST_ID = "S1" +INSTRUMENT_HOST_NAME = " + SMALL MISSIONS FOR ADVANCED RESEARCH AND TECHNOLOGY" +TARGET_NAME = "DARK SKY" +TARGET_TYPE = "CALIBRATION" +MISSION_PHASE_NAME = "LUNAR PHASE" +IMAGE_OBSERVATION_TYPE = "DARK_CURRENT" +PRODUCT_TYPE = RDR +UNCORRECTED_START_TIME = 2005-07-31T20:25:20.923 +START_TIME = 2005-07-31T20:25:22.059 +STOP_TIME = 2005-07-31T20:25:22.099 +SPACECRAFT_CLOCK_START_COUNT = "8/0045126240.11497" +SPACECRAFT_CLOCK_STOP_COUNT = "8/0045126240.14118" +ORBIT_NUMBER = 0976 +PRODUCER_ID = SMART1_AMIE_TEAM +PRODUCER_FULL_NAME = "STEPHANE BEAUVIVRE" + +/*** INSTRUMENT RELATED PARAMETERS ***/ +INSTRUMENT_ID = AMIE +INSTRUMENT_NAME = "ADVANCED MOON MICROIMAGING EXPERIMENT" +INST_CMPRS_NAME = "WAVELET" +INST_CMPRS_RATE = 1 +FILTER_NAME = "NONE" +CENTER_FILTER_WAVELENGTH = 800 +BANDWIDTH = 700 +EXPOSURE_DURATION = 40 +FOCAL_PLANE_TEMPERATURE = 289.96 +GAIN_NUMBER = 280 +DATA_QUALITY_ID = 4 +DATA_QUALITY_DESC = "THE DATA QUALITY IS EVALUATED ACCORDING TO + THE RATIO OF SATURATED TO NON-SATURATED + PIXELS IN THE IMAGE. + 4 IS HIGHEST QUALILTY, RATIO IS [0, 0.02] + 3 IS HIGH QUALITY, RATIO IS ]0.02,0.34] + 2 IS MEDIUM QUALITY, RATIO IS ]0.34,0.66] + 1 IS LOW QUALITY, RATIO IS ]0.66,0.98] + 0 IS LOWEST QUALITY, RATIO IS ]0.98,1] + -1 IS NON EXISTENT" +INSTRUMENT_MODE_ID = "ALL" +INSTRUMENT_MODE_DESC = "ALL - Read out complete detector + filter name - read out only one filter + COLOR_X - Read out VIS_X, FeL_X, FeH_X + COLOR_Y - Read out VIS_Y, FeL_Y, FeH_Y" +INSTRUMENT_TYPE = "IMAGING CAMERA" + +/*** IMAGE CALIBRATION RELATED PARAMETERS ***/ +DARK_CURRENT_CORRECTION_FLAG = "TRUE" +DARK_CURRENT_FILE_NAME = (AMI_CMA_071101_00001_00000.IMG, + AMI_CMA_071101_00002_00001.IMG) +FLAT_FIELD_CORRECTION_FLAG = "TRUE" +FLAT_FIELD_FILE_NAME = "AMI_CMA_080319_00001_XXXXX.IMG" + +/*** POSITIONAL INFORMATION ***/ + + /*** TARGET PARAMETERS: POSITION , VELOCITY ***/ +SC_TARGET_POSITION_VECTOR = (1691.6, 3567.0, -2259.5) +SC_TARGET_VELOCITY_VECTOR = (-0.3014, -0.1916, -0.7663) +TARGET_CENTER_DISTANCE = 4548.7 +SOLAR_DISTANCE = 151571944.3 +CENTER_LONGITUDE = "N/A" +WESTERNMOST_LONGITUDE = "N/A" +EASTERNMOST_LONGITUDE = "N/A" +RETICLE_POINT_LONGITUDE = "N/A" +CENTER_LATITUDE = "N/A" +MINIMUM_LATITUDE = "N/A" +MAXIMUM_LATITUDE = "N/A" +RETICLE_POINT_LATITUDE = "N/A" + + /*** SPACECRAFT POSITION ***/ +SPACECRAFT_ALTITUDE = 2811.3 + +/*** GEOMETRICAL INFORMATION ***/ + + /*** TARGET WITHIN SENSOR FOV: ANGLES IN ***/ +SLANT_DISTANCE = "N/A" +RIGHT_ASCENSION = 279.68 +RETICLE_POINT_RA = (279.21, 282.05, 280.17, 277.28) +DECLINATION = 39.76 +RETICLE_POINT_DECLINATION = (37.92, 39.37, 41.60, 40.11) +HORIZONTAL_PIXEL_SCALE = "N/A" +VERTICAL_PIXEL_SCALE = "N/A" + + /*** VIEWING AND LIGHTING GEOMETRY (SUN ON TARGET) ***/ +INCIDENCE_ANGLE = "N/A" +PHASE_ANGLE = "N/A" +EMISSION_ANGLE = "N/A" +LOCAL_HOUR_ANGLE = "N/A" +SUB_SOLAR_LATITUDE = "N/A" +SUB_SOLAR_LONGITUDE = "N/A" +SOLAR_ELONGATION = 115.15 + +/*** SMART-1 SPECIFIC DICTIONARY ***/ +SMART1:AMIE_SC_EFRF_VECTOR = (-349434.7783, -30198.6358, 188844.1810) +SMART1:AMIE_SC_ATTITUDE_VECTOR = (34.87, 64.42, 13.58) +SMART1:AMIE_SC_ROT_RATE_VECTOR = (0.00, 0.00, 0.00) + +/*** OBJECT DESCRIPTION ***/ +OBJECT = BROWSE_IMAGE + LINES = 128 + LINE_SAMPLES = 128 + SAMPLE_TYPE = MSB_UNSIGNED_INTEGER + SAMPLE_BITS = 8 + SCALING_FACTOR = 1 +END_OBJECT = BROWSE_IMAGE + +OBJECT = IMAGE + DERIVED_MINIMUM = 0.000000 + DERIVED_MAXIMUM = 0.049589 + FIRST_LINE = 1 + LINE_PREFIX_BYTES = 0 + LINE_SUFFIX_BYTES = 0 + LINES = 512 + LINE_SAMPLES = 512 + SAMPLE_TYPE = PC_REAL + SAMPLE_BITS = 32 + SCALING_FACTOR = 1 +END_OBJECT = IMAGE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +END + J::N$;Z<T<<N;X;pJk;#8l.:B$;G:wˆ:Z+:;%<::e'9:Gb:M95X::!:;vd:99":4-<Ie;:߅:w;I;&*9j:1:*Iz97;p;:Z8v :::69'<r&:#:V;c:(&;9:9nw;c:w;qV;5:ꙻ::;;l;2B:9F=;] :19L;9Y:N;iA:57<< 9j*<Z5:f";:;};;E`P<=98f;[VS:Zr9:nم:sM;D::;z9C0:n:2H ;J:q9z;=t:-9F7;6z:; +:2F;89;:: 8M6<L9$;;:9F2:~M9?9늆:N:d<I:9]';Z;9u +:.;:׻99J9:r;j,]:x$;%s::XL<$<c~d:;:^S: 7;n[;]9;X;:mM:.::o$9Ma<t7%;cM;9v:Q:^:UG;(;;ϳ;I|A8 9U;^:9"9Զ;n[;m:$,=;;7 +;Ҁ[<0 ;S9&O:; H9@::K,; :!;K:0;=;F:;:+C{;6:;^;[n;W;1::n;`T: ;ٍ;:US9/y5:*;o;F܎::E :'8w:6:;2^$;z!:FS:P;!<I8C8;D:: +={;0F:B:Rr< +:B;39[::f;r;H7;$@<W<":<5:U;};v'9^;J98dh:9BO;Ļ:d9:L:mW; ;2T9'&<p;:;N;~q::P7;o;wx9Vj:;`:Uu%:89:;L ;::ƾ<g0<-<S';L8*;'59vS9 +Y9 \ No newline at end of file diff --git a/test/munit/.appveyor.yml b/test/munit/.appveyor.yml new file mode 100644 index 0000000..5572092 --- /dev/null +++ b/test/munit/.appveyor.yml @@ -0,0 +1,34 @@ +version: "{build}" + +environment: + matrix: + - ARCHITECTURE: x64 + MSVC_VER: 14 + - ARCHITECTURE: x86 + MSVC_VER: 14 + - ARCHITECTURE: x64 + MSVC_VER: 12 + - ARCHITECTURE: x86 + MSVC_VER: 12 + - ARCHITECTURE: x86 + MSVC_VER: 11 + - ARCHITECTURE: x86 + MSVC_VER: 10 + - ARCHITECTURE: x86 + MSVC_VER: 9 + +branches: + except: + - master + - /^(wip\/)?(travis|osx|mingw|ipp)(\-.+)?$/ + +configuration: Debug + +install: + +before_build: + - call "C:\Program Files (x86)\Microsoft Visual Studio %MSVC_VER%.0\VC\vcvarsall.bat" %ARCHITECTURE% + +build_script: cl.exe /W4 /WX /Feexample munit.c example.c + +test_script: example.exe --color always diff --git a/test/munit/.dir-locals.el b/test/munit/.dir-locals.el new file mode 100644 index 0000000..4cc5d84 --- /dev/null +++ b/test/munit/.dir-locals.el @@ -0,0 +1,2 @@ +((nil . ((indent-tabs-mode . nil) + (c-basic-offset . 2)))) diff --git a/test/munit/.gitignore b/test/munit/.gitignore new file mode 100644 index 0000000..115d637 --- /dev/null +++ b/test/munit/.gitignore @@ -0,0 +1,6 @@ +*~ +.#* +/*.o +/example +/*.exe +/*.dll diff --git a/test/munit/.travis.yml b/test/munit/.travis.yml new file mode 100644 index 0000000..d6ed0de --- /dev/null +++ b/test/munit/.travis.yml @@ -0,0 +1,142 @@ +language: c +sudo: false +dist: trusty +branches: + except: + - /^(wip\/)?(appveyor|msvc|mingw|windows)(\-.+)?$/ + - /^master$/ +matrix: + include: + ### + ## Linux builds using various versions of GCC. + ### + - env: C_COMPILER=gcc-6 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-6 + - g++-6 + - env: C_COMPILER=gcc-5 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-5 + - g++-5 + # - env: C_COMPILER=gcc-4.9 + # addons: + # apt: + # sources: + # - ubuntu-toolchain-r-test + # packages: + # - gcc-4.9 + # - g++-4.9 + - env: C_COMPILER=gcc-4.8 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.8 + - g++-4.8 + # - env: C_COMPILER=gcc-4.7 + # addons: + # apt: + # sources: + # - ubuntu-toolchain-r-test + # packages: + # - gcc-4.7 + # - g++-4.7 + - env: C_COMPILER=gcc-4.6 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.6 + - g++-4.6 + # - os: linux + # env: C_COMPILER=gcc-4.5 + # addons: + # apt: + # sources: + # - ubuntu-toolchain-r-test + # packages: + # - gcc-4.5 + # - g++-4.5 + - env: C_COMPILER=gcc-4.4 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.4 + - g++-4.4 + + ### + ## clang on Linux + ### + - env: C_COMPILER=clang-3.9 + addons: + apt: + sources: + - llvm-toolchain-precise-3.9 + - ubuntu-toolchain-r-test + packages: + - clang-3.9 + # - env: C_COMPILER=clang-3.8 + # addons: + # apt: + # sources: + # - llvm-toolchain-precise-3.8 + # - ubuntu-toolchain-r-test + # packages: + # - clang-3.8 + - env: C_COMPILER=clang-3.7 + addons: + apt: + sources: + - llvm-toolchain-precise-3.7 + - ubuntu-toolchain-r-test + packages: + - clang-3.7 + # - env: C_COMPILER=clang-3.6 + # addons: + # apt: + # sources: + # - llvm-toolchain-precise-3.6 + # - ubuntu-toolchain-r-test + # packages: + # - clang-3.6 + - env: C_COMPILER=clang-3.5 + addons: + apt: + sources: + - llvm-toolchain-precise-3.5 + - ubuntu-toolchain-r-test + packages: + - clang-3.5 + + ### + ## PGI + ### + - env: C_COMPILER=pgcc OPENMP=y + + ### + ## OS X + ### + - os: osx + +before_install: +- if [ -n "${C_COMPILER}" ]; then export CC="${C_COMPILER}"; fi +- if [ "${C_COMPILER}" = "pgcc" ]; then wget -q -O /dev/stdout 'https://raw.githubusercontent.com/nemequ/pgi-travis/master/install-pgi.sh' | /bin/sh; fi + +script: + - make CC="${CC}" AGGRESSIVE_WARNINGS=y EXTENSION="${EXTENSION}" OPENMP="${OPENMP}" ASAN="${ASAN}" UBSAN="${UBSAN}" + - make test + +notifications: + email: false diff --git a/test/munit/COPYING b/test/munit/COPYING new file mode 100644 index 0000000..2991ac7 --- /dev/null +++ b/test/munit/COPYING @@ -0,0 +1,21 @@ +µnit Testing Framework +Copyright (c) 2013-2016 Evan Nemerson + +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. diff --git a/test/munit/Makefile b/test/munit/Makefile new file mode 100644 index 0000000..65d33fc --- /dev/null +++ b/test/munit/Makefile @@ -0,0 +1,55 @@ +# Using µnit is very simple; just include the header and add the C +# file to your sources. That said, here is a simple Makefile to build +# the example. + +CSTD:=99 +OPENMP:=n +ASAN:=n +UBSAN:=n +EXTENSION:= +TEST_ENV:= +CFLAGS:= +AGGRESSIVE_WARNINGS=n + +ifeq ($(CC),pgcc) + CFLAGS+=-c$(CSTD) +else + CFLAGS+=-std=c$(CSTD) +endif + +ifeq ($(OPENMP),y) + ifeq ($(CC),pgcc) + CFLAGS+=-mp + else + CFLAGS+=-fopenmp + endif +endif + +ifneq ($(SANITIZER),) + CFLAGS+=-fsanitize=$(SANITIZER) +endif + +ifneq ($(CC),pgcc) + ifeq ($(EXTRA_WARNINGS),y) + CFLAGS+=-Wall -Wextra -Werror + endif + + ifeq ($(ASAN),y) + CFLAGS+=-fsanitize=address + endif + + ifeq ($(UBSAN),y) + CFLAGS+=-fsanitize=undefined + endif +endif + +example$(EXTENSION): munit.h munit.c example.c + $(CC) $(CFLAGS) -o $@ munit.c example.c + +test: + $(TEST_ENV) ./example$(EXTENSION) + +clean: + rm -f example$(EXTENSION) + +all: example$(EXTENSION) diff --git a/test/munit/README.md b/test/munit/README.md new file mode 100644 index 0000000..e3649b5 --- /dev/null +++ b/test/munit/README.md @@ -0,0 +1,40 @@ +# µnit + +µnit is a small but full-featured unit testing framework for C. It has +no dependencies (beyond libc), is permissively licensed (MIT), and is +easy to include into any project. + +For more information, see +[the µnit web site](https://nemequ.github.io/munit). + +[![Build status](https://travis-ci.org/nemequ/munit.svg?branch=master)](https://travis-ci.org/nemequ/munit) +[![Windows build status](https://ci.appveyor.com/api/projects/status/db515g5ifcwjohq7/branch/master?svg=true)](https://ci.appveyor.com/project/quixdb/munit/branch/master) + +## Features + +Features µnit currently includes include: + + * Handy assertion macros which make for nice error messages. + * Reproducible cross-platform random number generation, including + support for supplying a seed via CLI. + * Timing of both wall-clock and CPU time. + * Parameterized tests. + * Nested test suites. + * Flexible CLI. + * Forking + ([except on Windows](https://github.com/nemequ/munit/issues/2)). + * Hiding output of successful tests. + +Features µnit does not currently include, but some day may include +(a.k.a., if you file a PR…), include: + + * [TAP](http://testanything.org/) support; feel free to discuss in + [issue #1](https://github.com/nemequ/munit/issues/1) + +## Documentation + +See [the µnit web site](https://nemequ.github.io/munit). + +Additionally, there is a heavily-commented +[example.c](https://github.com/nemequ/munit/blob/master/example.c) in +the repository. diff --git a/test/munit/example.c b/test/munit/example.c new file mode 100644 index 0000000..d238d09 --- /dev/null +++ b/test/munit/example.c @@ -0,0 +1,351 @@ +/* Example file for using µnit. + * + * µnit is MIT-licensed, but for this file and this file alone: + * + * To the extent possible under law, the author(s) of this file have + * waived all copyright and related or neighboring rights to this + * work. See for + * details. + *********************************************************************/ + +#include "munit.h" + +/* This is just to disable an MSVC warning about conditional + * expressions being constant, which you shouldn't have to do for your + * code. It's only here because we want to be able to do silly things + * like assert that 0 != 1 for our demo. */ +#if defined(_MSC_VER) +#pragma warning(disable: 4127) +#endif + +/* Tests are functions that return void, and take a single void* + * parameter. We'll get to what that parameter is later. */ +static MunitResult +test_compare(const MunitParameter params[], void* data) { + /* We'll use these later */ + const unsigned char val_uchar = 'b'; + const short val_short = 1729; + double pi = 3.141592654; + char* stewardesses = "stewardesses"; + char* most_fun_word_to_type; + + /* These are just to silence compiler warnings about the parameters + * being unused. */ + (void) params; + (void) data; + + /* Let's start with the basics. */ + munit_assert(0 != 1); + + /* There is also the more verbose, though slightly more descriptive + munit_assert_true/false: */ + munit_assert_false(0); + + /* You can also call munit_error and munit_errorf yourself. We + * won't do it is used to indicate a failure, but here is what it + * would look like: */ + /* munit_error("FAIL"); */ + /* munit_errorf("Goodbye, cruel %s", "world"); */ + + /* There are macros for comparing lots of types. */ + munit_assert_char('a', ==, 'a'); + + /* Sure, you could just assert('a' == 'a'), but if you did that, a + * failed assertion would just say something like "assertion failed: + * val_uchar == 'b'". µnit will tell you the actual values, so a + * failure here would result in something like "assertion failed: + * val_uchar == 'b' ('X' == 'b')." */ + munit_assert_uchar(val_uchar, ==, 'b'); + + /* Obviously we can handle values larger than 'char' and 'uchar'. + * There are versions for char, short, int, long, long long, + * int8/16/32/64_t, as well as the unsigned versions of them all. */ + munit_assert_short(42, <, val_short); + + /* There is also support for size_t. + * + * The longest word in English without repeating any letters is + * "uncopyrightables", which has uncopyrightable (and + * dermatoglyphics, which is the study of fingerprints) beat by a + * character */ + munit_assert_size(strlen("uncopyrightables"), >, strlen("dermatoglyphics")); + + /* Of course there is also support for doubles and floats. */ + munit_assert_double(pi, ==, 3.141592654); + + /* If you want to compare two doubles for equality, you might want + * to consider using munit_assert_double_equal. It compares two + * doubles for equality within a precison of 1.0 x 10^-(precision). + * Note that precision (the third argument to the macro) needs to be + * fully evaluated to an integer by the preprocessor so µnit doesn't + * have to depend pow, which is often in libm not libc. */ + munit_assert_double_equal(3.141592654, 3.141592653589793, 9); + + /* And if you want to check strings for equality (or inequality), + * there is munit_assert_string_equal/not_equal. + * + * "stewardesses" is the longest word you can type on a QWERTY + * keyboard with only one hand, which makes it loads of fun to type. + * If I'm going to have to type a string repeatedly, let's make it a + * good one! */ + munit_assert_string_equal(stewardesses, "stewardesses"); + + /* A personal favorite macro which is fantastic if you're working + * with binary data, is the one which naïvely checks two blobs of + * memory for equality. If this fails it will tell you the offset + * of the first differing byte. */ + munit_assert_memory_equal(7, stewardesses, "steward"); + + /* You can also make sure that two blobs differ *somewhere*: */ + munit_assert_memory_not_equal(8, stewardesses, "steward"); + + /* There are equal/not_equal macros for pointers, too: */ + most_fun_word_to_type = stewardesses; + munit_assert_ptr_equal(most_fun_word_to_type, stewardesses); + + /* And null/not_null */ + munit_assert_null(NULL); + munit_assert_not_null(most_fun_word_to_type); + + /* Lets verify that the data parameter is what we expected. We'll + * see where this comes from in a bit. + * + * Note that the casting isn't usually required; if you give this + * function a real pointer (instead of a number like 0xdeadbeef) it + * would work as expected. */ + munit_assert_ptr_equal(data, (void*)(uintptr_t)0xdeadbeef); + + return MUNIT_OK; +} + +static MunitResult +test_rand(const MunitParameter params[], void* user_data) { + int random_int; + double random_dbl; + munit_uint8_t data[5]; + + (void) params; + (void) user_data; + + /* One thing missing from a lot of unit testing frameworks is a + * random number generator. You can't just use srand/rand because + * the implementation varies across different platforms, and it's + * important to be able to look at the seed used in a failing test + * to see if you can reproduce it. Some randomness is a fantastic + * thing to have in your tests, I don't know why more people don't + * do it... + * + * µnit's PRNG is re-seeded with the same value for each iteration + * of each test. The seed is retrieved from the MUNIT_SEED + * envirnment variable or, if none is provided, one will be + * (pseudo-)randomly generated. */ + + /* If you need an integer in a given range */ + random_int = munit_rand_int_range(128, 4096); + munit_assert_int(random_int, >=, 128); + munit_assert_int(random_int, <=, 4096); + + /* Or maybe you want a double, between 0 and 1: */ + random_dbl = munit_rand_double(); + munit_assert_double(random_dbl, >=, 0.0); + munit_assert_double(random_dbl, <=, 1.0); + + /* Of course, you want to be able to reproduce bugs discovered + * during testing, so every time the tests are run they print the + * random seed used. When you want to reproduce a result, just put + * that random seed in the MUNIT_SEED environment variable; it even + * works on different platforms. + * + * If you want this to pass, use 0xdeadbeef as the random seed and + * uncomment the next line of code. Note that the PRNG is not + * re-seeded between iterations of the same test, so this will only + * work on the first iteration. */ + /* munit_assert_uint32(munit_rand_uint32(), ==, 1306447409); */ + + /* You can also get blobs of random memory: */ + munit_rand_memory(sizeof(data), data); + + return MUNIT_OK; +} + +/* This test case shows how to accept parameters. We'll see how to + * specify them soon. + * + * By default, every possible variation of a parameterized test is + * run, but you can specify parameters manually if you want to only + * run specific test(s), or you can pass the --single argument to the + * CLI to have the harness simply choose one variation at random + * instead of running them all. */ +static MunitResult +test_parameters(const MunitParameter params[], void* user_data) { + const char* foo; + const char* bar; + + (void) user_data; + + /* The "foo" parameter is specified as one of the following values: + * "one", "two", or "three". */ + foo = munit_parameters_get(params, "foo"); + /* Similarly, "bar" is one of "four", "five", or "six". */ + bar = munit_parameters_get(params, "bar"); + /* "baz" is a bit more complicated. We don't actually specify a + * list of valid values, so by default NULL is passed. However, the + * CLI will accept any value. This is a good way to have a value + * that is usually selected randomly by the test, but can be + * overridden on the command line if desired. */ + /* const char* baz = munit_parameters_get(params, "baz"); */ + + /* Notice that we're returning MUNIT_FAIL instead of writing an + * error message. Error messages are generally preferable, since + * they make it easier to diagnose the issue, but this is an + * option. + * + * Possible values are: + * - MUNIT_OK: Sucess + * - MUNIT_FAIL: Failure + * - MUNIT_SKIP: The test was skipped; usually this happens when a + * particular feature isn't in use. For example, if you're + * writing a test which uses a Wayland-only feature, but your + * application is running on X11. + * - MUNIT_ERROR: The test failed, but not because of anything you + * wanted to test. For example, maybe your test downloads a + * remote resource and tries to parse it, but the network was + * down. + */ + + if (strcmp(foo, "one") != 0 && + strcmp(foo, "two") != 0 && + strcmp(foo, "three") != 0) + return MUNIT_FAIL; + + if (strcmp(bar, "red") != 0 && + strcmp(bar, "green") != 0 && + strcmp(bar, "blue") != 0) + return MUNIT_FAIL; + + return MUNIT_OK; +} + +/* The setup function, if you provide one, for a test will be run + * before the test, and the return value will be passed as the sole + * parameter to the test function. */ +static void* +test_compare_setup(const MunitParameter params[], void* user_data) { + (void) params; + + munit_assert_string_equal(user_data, "µnit"); + return (void*) (uintptr_t) 0xdeadbeef; +} + +/* To clean up after a test, you can use a tear down function. The + * fixture argument is the value returned by the setup function + * above. */ +static void +test_compare_tear_down(void* fixture) { + munit_assert_ptr_equal(fixture, (void*)(uintptr_t)0xdeadbeef); +} + +static char* foo_params[] = { + (char*) "one", (char*) "two", (char*) "three", NULL +}; + +static char* bar_params[] = { + (char*) "red", (char*) "green", (char*) "blue", NULL +}; + +static MunitParameterEnum test_params[] = { + { (char*) "foo", foo_params }, + { (char*) "bar", bar_params }, + { (char*) "baz", NULL }, + { NULL, NULL }, +}; + +/* Creating a test suite is pretty simple. First, you'll need an + * array of tests: */ +static MunitTest test_suite_tests[] = { + { + /* The name is just a unique human-readable way to identify the + * test. You can use it to run a specific test if you want, but + * usually it's mostly decorative. */ + (char*) "/example/compare", + /* You probably won't be surprised to learn that the tests are + * functions. */ + test_compare, + /* If you want, you can supply a function to set up a fixture. If + * you supply NULL, the user_data parameter from munit_suite_main + * will be used directly. If, however, you provide a callback + * here the user_data parameter will be passed to this callback, + * and the return value from this callback will be passed to the + * test function. + * + * For our example we don't really need a fixture, but lets + * provide one anyways. */ + test_compare_setup, + /* If you passed a callback for the fixture setup function, you + * may want to pass a corresponding callback here to reverse the + * operation. */ + test_compare_tear_down, + /* Finally, there is a bitmask for options you can pass here. You + * can provide either MUNIT_TEST_OPTION_NONE or 0 here to use the + * defaults. */ + MUNIT_TEST_OPTION_NONE, + NULL + }, + /* Usually this is written in a much more compact format; all these + * comments kind of ruin that, though. Here is how you'll usually + * see entries written: */ + { (char*) "/example/rand", test_rand, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }, + /* To tell the test runner when the array is over, just add a NULL + * entry at the end. */ + { (char*) "/example/parameters", test_parameters, NULL, NULL, MUNIT_TEST_OPTION_NONE, test_params }, + { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } +}; + +/* If you wanted to have your test suite run other test suites you + * could declare an array of them. Of course each sub-suite can + * contain more suites, etc. */ +/* static const MunitSuite other_suites[] = { */ +/* { "/second", test_suite_tests, NULL, 1, MUNIT_SUITE_OPTION_NONE }, */ +/* { NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } */ +/* }; */ + +/* Now we'll actually declare the test suite. You could do this in + * the main function, or on the heap, or whatever you want. */ +static const MunitSuite test_suite = { + /* This string will be prepended to all test names in this suite; + * for example, "/example/rand" will become "/µnit/example/rand". + * Note that, while it doesn't really matter for the top-level + * suite, NULL signal the end of an array of tests; you should use + * an empty string ("") instead. */ + (char*) "", + /* The first parameter is the array of test suites. */ + test_suite_tests, + /* In addition to containing test cases, suites can contain other + * test suites. This isn't necessary in this example, but it can be + * a great help to projects with lots of tests by making it easier + * to spread the tests across many files. This is where you would + * put "other_suites" (which is commented out above). */ + NULL, + /* An interesting feature of µnit is that it supports automatically + * running multiple iterations of the tests. This is usually only + * interesting if you make use of the PRNG to randomize your tests + * cases a bit, or if you are doing performance testing and want to + * average multiple runs. 0 is an alias for 1. */ + 1, + /* Just like MUNIT_TEST_OPTION_NONE, you can provide + * MUNIT_SUITE_OPTION_NONE or 0 to use the default settings. */ + MUNIT_SUITE_OPTION_NONE +}; + +/* This is only necessary for EXIT_SUCCESS and EXIT_FAILURE, which you + * *should* be using but probably aren't (no, zero and non-zero don't + * always mean success and failure). I guess my point is that nothing + * about µnit requires it. */ +#include + +int main(int argc, char* argv[MUNIT_ARRAY_PARAM(argc + 1)]) { + /* Finally, we'll actually run our test suite! That second argument + * is the user_data parameter which will be passed either to the + * test or (if provided) the fixture setup function. */ + return munit_suite_main(&test_suite, (void*) "µnit", argc, argv); +} diff --git a/test/munit/munit.c b/test/munit/munit.c new file mode 100644 index 0000000..c2489d4 --- /dev/null +++ b/test/munit/munit.c @@ -0,0 +1,2047 @@ +/* Copyright (c) 2013-2017 Evan Nemerson + * + * 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. + */ + +/*** Configuration ***/ + +/* This is just where the output from the test goes. It's really just + * meant to let you choose stdout or stderr, but if anyone really want + * to direct it to a file let me know, it would be fairly easy to + * support. */ +#if !defined(MUNIT_OUTPUT_FILE) +# define MUNIT_OUTPUT_FILE stdout +#endif + +/* This is a bit more useful; it tells µnit how to format the seconds in + * timed tests. If your tests run for longer you might want to reduce + * it, and if your computer is really fast and your tests are tiny you + * can increase it. */ +#if !defined(MUNIT_TEST_TIME_FORMAT) +# define MUNIT_TEST_TIME_FORMAT "0.8f" +#endif + +/* If you have long test names you might want to consider bumping + * this. The result information takes 43 characters. */ +#if !defined(MUNIT_TEST_NAME_LEN) +# define MUNIT_TEST_NAME_LEN 37 +#endif + +/* If you don't like the timing information, you can disable it by + * defining MUNIT_DISABLE_TIMING. */ +#if !defined(MUNIT_DISABLE_TIMING) +# define MUNIT_ENABLE_TIMING +#endif + +/*** End configuration ***/ + +#if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE < 200809L) +# undef _POSIX_C_SOURCE +#endif +#if !defined(_POSIX_C_SOURCE) +# define _POSIX_C_SOURCE 200809L +#endif + +/* Solaris freaks out if you try to use a POSIX or SUS standard without + * the "right" C standard. */ +#if defined(_XOPEN_SOURCE) +# undef _XOPEN_SOURCE +#endif + +#if defined(__STDC_VERSION__) +# if __STDC_VERSION__ >= 201112L +# define _XOPEN_SOURCE 700 +# elif __STDC_VERSION__ >= 199901L +# define _XOPEN_SOURCE 600 +# endif +#endif + +/* Because, according to Microsoft, POSIX is deprecated. You've got + * to appreciate the chutzpah. */ +#if defined(_MSC_VER) && !defined(_CRT_NONSTDC_NO_DEPRECATE) +# define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +# include +#elif defined(_WIN32) +/* https://msdn.microsoft.com/en-us/library/tf4dy80a.aspx */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(MUNIT_NO_NL_LANGINFO) && !defined(_WIN32) +#define MUNIT_NL_LANGINFO +#include +#include +#include +#endif + +#if !defined(_WIN32) +# include +# include +# include +#else +# include +# include +# include +# if !defined(STDERR_FILENO) +# define STDERR_FILENO _fileno(stderr) +# endif +#endif + +#include "munit.h" + +#define MUNIT_STRINGIFY(x) #x +#define MUNIT_XSTRINGIFY(x) MUNIT_STRINGIFY(x) + +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) || defined(_Thread_local) +# define MUNIT_THREAD_LOCAL _Thread_local +#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) +# define MUNIT_THREAD_LOCAL __thread +#elif defined(_WIN32) +# define MUNIT_THREAD_LOCAL __declspec(thread) +#endif + +/* MSVC 12.0 will emit a warning at /W4 for code like 'do { ... } + * while (0)', or 'do { ... } while (true)'. I'm pretty sure nobody + * at Microsoft compiles with /W4. */ +#if defined(_MSC_VER) && (_MSC_VER <= 1800) +#pragma warning(disable: 4127) +#endif + +#if defined(_WIN32) || defined(__EMSCRIPTEN__) +# define MUNIT_NO_FORK +#endif + +#if defined(__EMSCRIPTEN__) +# define MUNIT_NO_BUFFER +#endif + +/*** Logging ***/ + +static MunitLogLevel munit_log_level_visible = MUNIT_LOG_INFO; +static MunitLogLevel munit_log_level_fatal = MUNIT_LOG_ERROR; + +#if defined(MUNIT_THREAD_LOCAL) +static MUNIT_THREAD_LOCAL bool munit_error_jmp_buf_valid = false; +static MUNIT_THREAD_LOCAL jmp_buf munit_error_jmp_buf; +#endif + +/* At certain warning levels, mingw will trigger warnings about + * suggesting the format attribute, which we've explicity *not* set + * because it will then choke on our attempts to use the MS-specific + * I64 modifier for size_t (which we have to use since MSVC doesn't + * support the C99 z modifier). */ + +#if defined(__MINGW32__) || defined(__MINGW64__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsuggest-attribute=format" +#endif + +MUNIT_PRINTF(5,0) +static void +munit_logf_exv(MunitLogLevel level, FILE* fp, const char* filename, int line, const char* format, va_list ap) { + if (level < munit_log_level_visible) + return; + + switch (level) { + case MUNIT_LOG_DEBUG: + fputs("Debug", fp); + break; + case MUNIT_LOG_INFO: + fputs("Info", fp); + break; + case MUNIT_LOG_WARNING: + fputs("Warning", fp); + break; + case MUNIT_LOG_ERROR: + fputs("Error", fp); + break; + default: + munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Invalid log level (%d)", level); + return; + } + + fputs(": ", fp); + if (filename != NULL) + fprintf(fp, "%s:%d: ", filename, line); + vfprintf(fp, format, ap); + fputc('\n', fp); +} + +MUNIT_PRINTF(3,4) +static void +munit_logf_internal(MunitLogLevel level, FILE* fp, const char* format, ...) { + va_list ap; + + va_start(ap, format); + munit_logf_exv(level, fp, NULL, 0, format, ap); + va_end(ap); +} + +static void +munit_log_internal(MunitLogLevel level, FILE* fp, const char* message) { + munit_logf_internal(level, fp, "%s", message); +} + +void +munit_logf_ex(MunitLogLevel level, const char* filename, int line, const char* format, ...) { + va_list ap; + + va_start(ap, format); + munit_logf_exv(level, stderr, filename, line, format, ap); + va_end(ap); + + if (level >= munit_log_level_fatal) { +#if defined(MUNIT_THREAD_LOCAL) + if (munit_error_jmp_buf_valid) + longjmp(munit_error_jmp_buf, 1); +#endif + abort(); + } +} + +void +munit_errorf_ex(const char* filename, int line, const char* format, ...) { + va_list ap; + + va_start(ap, format); + munit_logf_exv(MUNIT_LOG_ERROR, stderr, filename, line, format, ap); + va_end(ap); + +#if defined(MUNIT_THREAD_LOCAL) + if (munit_error_jmp_buf_valid) + longjmp(munit_error_jmp_buf, 1); +#endif + abort(); +} + +#if defined(__MINGW32__) || defined(__MINGW64__) +#pragma GCC diagnostic pop +#endif + +#if !defined(MUNIT_STRERROR_LEN) +# define MUNIT_STRERROR_LEN 80 +#endif + +static void +munit_log_errno(MunitLogLevel level, FILE* fp, const char* msg) { +#if defined(MUNIT_NO_STRERROR_R) || (defined(__MINGW32__) && !defined(MINGW_HAS_SECURE_API)) + munit_logf_internal(level, fp, "%s: %s (%d)", msg, strerror(errno), errno); +#else + char munit_error_str[MUNIT_STRERROR_LEN]; + munit_error_str[0] = '\0'; + +#if !defined(_WIN32) + strerror_r(errno, munit_error_str, MUNIT_STRERROR_LEN); +#else + strerror_s(munit_error_str, MUNIT_STRERROR_LEN, errno); +#endif + + munit_logf_internal(level, fp, "%s: %s (%d)", msg, munit_error_str, errno); +#endif +} + +/*** Memory allocation ***/ + +void* +munit_malloc_ex(const char* filename, int line, size_t size) { + void* ptr; + + if (size == 0) + return NULL; + + ptr = calloc(1, size); + if (MUNIT_UNLIKELY(ptr == NULL)) { + munit_logf_ex(MUNIT_LOG_ERROR, filename, line, "Failed to allocate %" MUNIT_SIZE_MODIFIER "u bytes.", size); + } + + return ptr; +} + +/*** Timer code ***/ + +#if defined(MUNIT_ENABLE_TIMING) + +#define psnip_uint64_t munit_uint64_t +#define psnip_uint32_t munit_uint32_t + +/* Code copied from portable-snippets + * . If you need to + * change something, please do it there so we can keep the code in + * sync. */ + +/* Clocks (v1) + * Portable Snippets - https://gitub.com/nemequ/portable-snippets + * Created by Evan Nemerson + * + * To the extent possible under law, the authors have waived all + * copyright and related or neighboring rights to this code. For + * details, see the Creative Commons Zero 1.0 Universal license at + * https://creativecommons.org/publicdomain/zero/1.0/ + */ + +#if !defined(PSNIP_CLOCK_H) +#define PSNIP_CLOCK_H + +#if !defined(psnip_uint64_t) +# include "../exact-int/exact-int.h" +#endif + +#if !defined(PSNIP_CLOCK_STATIC_INLINE) +# if defined(__GNUC__) +# define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__)) +# else +# define PSNIP_CLOCK__COMPILER_ATTRIBUTES +# endif + +# define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static +#endif + +enum PsnipClockType { + /* This clock provides the current time, in units since 1970-01-01 + * 00:00:00 UTC not including leap seconds. In other words, UNIX + * time. Keep in mind that this clock doesn't account for leap + * seconds, and can go backwards (think NTP adjustments). */ + PSNIP_CLOCK_TYPE_WALL = 1, + /* The CPU time is a clock which increases only when the current + * process is active (i.e., it doesn't increment while blocking on + * I/O). */ + PSNIP_CLOCK_TYPE_CPU = 2, + /* Monotonic time is always running (unlike CPU time), but it only + ever moves forward unless you reboot the system. Things like NTP + adjustments have no effect on this clock. */ + PSNIP_CLOCK_TYPE_MONOTONIC = 3 +}; + +struct PsnipClockTimespec { + psnip_uint64_t seconds; + psnip_uint64_t nanoseconds; +}; + +/* Methods we support: */ + +#define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1 +#define PSNIP_CLOCK_METHOD_TIME 2 +#define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3 +#define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4 +#define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5 +#define PSNIP_CLOCK_METHOD_CLOCK 6 +#define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7 +#define PSNIP_CLOCK_METHOD_GETRUSAGE 8 +#define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9 +#define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10 + +#include + +#if defined(HEDLEY_UNREACHABLE) +# define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE() +#else +# define PSNIP_CLOCK_UNREACHABLE() assert(0) +#endif + +/* Choose an implementation */ + +/* #undef PSNIP_CLOCK_WALL_METHOD */ +/* #undef PSNIP_CLOCK_CPU_METHOD */ +/* #undef PSNIP_CLOCK_MONOTONIC_METHOD */ + +/* We want to be able to detect the libc implementation, so we include + ( isn't available everywhere). */ + +#if defined(__unix__) || defined(__unix) || defined(__linux__) +# include +# include +#endif + +#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) +/* These are known to work without librt. If you know of others + * please let us know so we can add them. */ +# if \ + (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \ + (defined(__FreeBSD__)) +# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME +# elif !defined(PSNIP_CLOCK_NO_LIBRT) +# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME +# endif +#endif + +#if defined(_WIN32) +# if !defined(PSNIP_CLOCK_CPU_METHOD) +# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES +# endif +# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) +# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER +# endif +#endif + +#if defined(__MACH__) && !defined(__gnu_hurd__) +# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) +# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME +# endif +#endif + +#if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME) +# include +# if !defined(PSNIP_CLOCK_WALL_METHOD) +# if defined(CLOCK_REALTIME_PRECISE) +# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE +# elif !defined(__sun) +# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME +# endif +# endif +# if !defined(PSNIP_CLOCK_CPU_METHOD) +# if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID) +# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID +# elif defined(CLOCK_VIRTUAL) +# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL +# endif +# endif +# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) +# if defined(CLOCK_MONOTONIC_RAW) +# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC +# elif defined(CLOCK_MONOTONIC_PRECISE) +# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_PRECISE +# elif defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC) +# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME +# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC +# endif +# endif +#endif + +#if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L) +# if !defined(PSNIP_CLOCK_WALL_METHOD) +# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY +# endif +#endif + +#if !defined(PSNIP_CLOCK_WALL_METHOD) +# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME +#endif + +#if !defined(PSNIP_CLOCK_CPU_METHOD) +# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK +#endif + +/* Primarily here for testing. */ +#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && defined(PSNIP_CLOCK_REQUIRE_MONOTONIC) +# error No monotonic clock found. +#endif + +/* Implementations */ + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME)) +# include +#endif + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) +# include +#endif + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) +# include +#endif + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) +# include +# include +#endif + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) +# include +# include +# include +#endif + +/*** Implementations ***/ + +#define PSNIP_CLOCK_NSEC_PER_SEC ((psnip_uint32_t) (1000000000ULL)) + +#if \ + (defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \ + (defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock__clock_getres (clockid_t clk_id) { + struct timespec res; + int r; + + r = clock_getres(clk_id, &res); + if (r != 0) + return 0; + + return (psnip_uint32_t) (PSNIP_CLOCK_NSEC_PER_SEC / res.tv_nsec); +} + +PSNIP_CLOCK__FUNCTION int +psnip_clock__clock_gettime (clockid_t clk_id, struct PsnipClockTimespec* res) { + struct timespec ts; + + if (clock_gettime(clk_id, &ts) != 0) + return -10; + + res->seconds = (psnip_uint64_t) (ts.tv_sec); + res->nanoseconds = (psnip_uint64_t) (ts.tv_nsec); + + return 0; +} +#endif + +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock_wall_get_precision (void) { +#if !defined(PSNIP_CLOCK_WALL_METHOD) + return 0; +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL); +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY + return 1000000; +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME + return 1; +#else + return 0; +#endif +} + +PSNIP_CLOCK__FUNCTION int +psnip_clock_wall_get_time (struct PsnipClockTimespec* res) { + (void) res; + +#if !defined(PSNIP_CLOCK_WALL_METHOD) + return -2; +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res); +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME + res->seconds = time(NULL); + res->nanoseconds = 0; +#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) + return -6; + + res->seconds = tv.tv_sec; + res->nanoseconds = tv.tv_usec * 1000; +#else + return -2; +#endif + + return 0; +} + +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock_cpu_get_precision (void) { +#if !defined(PSNIP_CLOCK_CPU_METHOD) + return 0; +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU); +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK + return CLOCKS_PER_SEC; +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES + return PSNIP_CLOCK_NSEC_PER_SEC / 100; +#else + return 0; +#endif +} + +PSNIP_CLOCK__FUNCTION int +psnip_clock_cpu_get_time (struct PsnipClockTimespec* res) { +#if !defined(PSNIP_CLOCK_CPU_METHOD) + (void) res; + return -2; +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res); +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK + clock_t t = clock(); + if (t == ((clock_t) -1)) + return -5; + res->seconds = t / CLOCKS_PER_SEC; + res->nanoseconds = (t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC); +#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES + FILETIME CreationTime, ExitTime, KernelTime, UserTime; + LARGE_INTEGER date, adjust; + + if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime)) + return -7; + + /* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */ + date.HighPart = UserTime.dwHighDateTime; + date.LowPart = UserTime.dwLowDateTime; + adjust.QuadPart = 11644473600000 * 10000; + date.QuadPart -= adjust.QuadPart; + + res->seconds = date.QuadPart / 10000000; + res->nanoseconds = (date.QuadPart % 10000000) * (PSNIP_CLOCK_NSEC_PER_SEC / 100); +#elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) != 0) + return -8; + + res->seconds = usage.ru_utime.tv_sec; + res->nanoseconds = tv.tv_usec * 1000; +#else + (void) res; + return -2; +#endif + + return 0; +} + +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock_monotonic_get_precision (void) { +#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) + return 0; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC); +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME + static mach_timebase_info_data_t tbi = { 0, }; + if (tbi.denom == 0) + mach_timebase_info(&tbi); + return (psnip_uint32_t) (tbi.numer / tbi.denom); +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 + return 1000; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER + LARGE_INTEGER Frequency; + QueryPerformanceFrequency(&Frequency); + return (psnip_uint32_t) ((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) ? PSNIP_CLOCK_NSEC_PER_SEC : Frequency.QuadPart); +#else + return 0; +#endif +} + +PSNIP_CLOCK__FUNCTION int +psnip_clock_monotonic_get_time (struct PsnipClockTimespec* res) { +#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) + (void) res; + return -2; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME + return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res); +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME + psnip_uint64_t nsec = mach_absolute_time(); + static mach_timebase_info_data_t tbi = { 0, }; + if (tbi.denom == 0) + mach_timebase_info(&tbi); + nsec *= ((psnip_uint64_t) tbi.numer) / ((psnip_uint64_t) tbi.denom); + res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC; + res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER + LARGE_INTEGER t, f; + if (QueryPerformanceCounter(&t) == 0) + return -12; + + QueryPerformanceFrequency(&f); + res->seconds = t.QuadPart / f.QuadPart; + res->nanoseconds = t.QuadPart % f.QuadPart; + if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) + res->nanoseconds /= f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC; + else + res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / f.QuadPart; +#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64 + const ULONGLONG msec = GetTickCount64(); + res->seconds = msec / 1000; + res->nanoseconds = sec % 1000; +#else + return -2; +#endif + + return 0; +} + +/* Returns the number of ticks per second for the specified clock. + * For example, a clock with millisecond precision would return 1000, + * and a clock with 1 second (such as the time() function) would + * return 1. + * + * If the requested clock isn't available, it will return 0. + * Hopefully this will be rare, but if it happens to you please let us + * know so we can work on finding a way to support your system. + * + * Note that different clocks on the same system often have a + * different precisions. + */ +PSNIP_CLOCK__FUNCTION psnip_uint32_t +psnip_clock_get_precision (enum PsnipClockType clock_type) { + switch (clock_type) { + case PSNIP_CLOCK_TYPE_MONOTONIC: + return psnip_clock_monotonic_get_precision (); + case PSNIP_CLOCK_TYPE_CPU: + return psnip_clock_cpu_get_precision (); + case PSNIP_CLOCK_TYPE_WALL: + return psnip_clock_wall_get_precision (); + } + + PSNIP_CLOCK_UNREACHABLE(); + return 0; +} + +/* Set the provided timespec to the requested time. Returns 0 on + * success, or a negative value on failure. */ +PSNIP_CLOCK__FUNCTION int +psnip_clock_get_time (enum PsnipClockType clock_type, struct PsnipClockTimespec* res) { + assert(res != NULL); + + switch (clock_type) { + case PSNIP_CLOCK_TYPE_MONOTONIC: + return psnip_clock_monotonic_get_time (res); + case PSNIP_CLOCK_TYPE_CPU: + return psnip_clock_cpu_get_time (res); + case PSNIP_CLOCK_TYPE_WALL: + return psnip_clock_wall_get_time (res); + } + + return -1; +} + +#endif /* !defined(PSNIP_CLOCK_H) */ + +static psnip_uint64_t +munit_clock_get_elapsed(struct PsnipClockTimespec* start, struct PsnipClockTimespec* end) { + psnip_uint64_t r = (end->seconds - start->seconds) * PSNIP_CLOCK_NSEC_PER_SEC; + if (end->nanoseconds < start->nanoseconds) { + r -= (start->nanoseconds - end->nanoseconds); + } else { + r += (end->nanoseconds - start->nanoseconds); + } + return r; +} + +#endif /* defined(MUNIT_ENABLE_TIMING) */ + +/*** PRNG stuff ***/ + +/* This is (unless I screwed up, which is entirely possible) the + * version of PCG with 32-bit state. It was chosen because it has a + * small enough state that we should reliably be able to use CAS + * instead of requiring a lock for thread-safety. + * + * If I did screw up, I probably will not bother changing it unless + * there is a significant bias. It's really not important this be + * particularly strong, as long as it is fairly random it's much more + * important that it be reproducible, so bug reports have a better + * chance of being reproducible. */ + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__) && !defined(__EMSCRIPTEN__) +# define HAVE_STDATOMIC +#elif defined(__clang__) +# if __has_extension(c_atomic) +# define HAVE_CLANG_ATOMICS +# endif +#endif + +/* Workaround for http://llvm.org/bugs/show_bug.cgi?id=26911 */ +#if defined(__clang__) && defined(_WIN32) +# undef HAVE_STDATOMIC +# if defined(__c2__) +# undef HAVE_CLANG_ATOMICS +# endif +#endif + +#if defined(_OPENMP) +# define ATOMIC_UINT32_T uint32_t +# define ATOMIC_UINT32_INIT(x) (x) +#elif defined(HAVE_STDATOMIC) +# include +# define ATOMIC_UINT32_T _Atomic uint32_t +# define ATOMIC_UINT32_INIT(x) ATOMIC_VAR_INIT(x) +#elif defined(HAVE_CLANG_ATOMICS) +# define ATOMIC_UINT32_T _Atomic uint32_t +# define ATOMIC_UINT32_INIT(x) (x) +#elif defined(_WIN32) +# define ATOMIC_UINT32_T volatile LONG +# define ATOMIC_UINT32_INIT(x) (x) +#else +# define ATOMIC_UINT32_T volatile uint32_t +# define ATOMIC_UINT32_INIT(x) (x) +#endif + +static ATOMIC_UINT32_T munit_rand_state = ATOMIC_UINT32_INIT(42); + +#if defined(_OPENMP) +static inline void +munit_atomic_store(ATOMIC_UINT32_T* dest, ATOMIC_UINT32_T value) { +#pragma omp critical (munit_atomics) + *dest = value; +} + +static inline uint32_t +munit_atomic_load(ATOMIC_UINT32_T* src) { + int ret; +#pragma omp critical (munit_atomics) + ret = *src; + return ret; +} + +static inline uint32_t +munit_atomic_cas(ATOMIC_UINT32_T* dest, ATOMIC_UINT32_T* expected, ATOMIC_UINT32_T desired) { + bool ret; + +#pragma omp critical (munit_atomics) + { + if (*dest == *expected) { + *dest = desired; + ret = true; + } else { + ret = false; + } + } + + return ret; +} +#elif defined(HAVE_STDATOMIC) +# define munit_atomic_store(dest, value) atomic_store(dest, value) +# define munit_atomic_load(src) atomic_load(src) +# define munit_atomic_cas(dest, expected, value) atomic_compare_exchange_weak(dest, expected, value) +#elif defined(HAVE_CLANG_ATOMICS) +# define munit_atomic_store(dest, value) __c11_atomic_store(dest, value, __ATOMIC_SEQ_CST) +# define munit_atomic_load(src) __c11_atomic_load(src, __ATOMIC_SEQ_CST) +# define munit_atomic_cas(dest, expected, value) __c11_atomic_compare_exchange_weak(dest, expected, value, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#elif defined(__GNUC__) && (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) +# define munit_atomic_store(dest, value) __atomic_store_n(dest, value, __ATOMIC_SEQ_CST) +# define munit_atomic_load(src) __atomic_load_n(src, __ATOMIC_SEQ_CST) +# define munit_atomic_cas(dest, expected, value) __atomic_compare_exchange_n(dest, expected, value, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#elif defined(__GNUC__) && (__GNUC__ >= 4) +# define munit_atomic_store(dest,value) do { *(dest) = (value); } while (0) +# define munit_atomic_load(src) (*(src)) +# define munit_atomic_cas(dest, expected, value) __sync_bool_compare_and_swap(dest, *expected, value) +#elif defined(_WIN32) /* Untested */ +# define munit_atomic_store(dest,value) do { *(dest) = (value); } while (0) +# define munit_atomic_load(src) (*(src)) +# define munit_atomic_cas(dest, expected, value) InterlockedCompareExchange((dest), (value), *(expected)) +#else +# warning No atomic implementation, PRNG will not be thread-safe +# define munit_atomic_store(dest, value) do { *(dest) = (value); } while (0) +# define munit_atomic_load(src) (*(src)) +static inline bool +munit_atomic_cas(ATOMIC_UINT32_T* dest, ATOMIC_UINT32_T* expected, ATOMIC_UINT32_T desired) { + if (*dest == *expected) { + *dest = desired; + return true; + } else { + return false; + } +} +#endif + +#define MUNIT_PRNG_MULTIPLIER (747796405U) +#define MUNIT_PRNG_INCREMENT (1729U) + +static munit_uint32_t +munit_rand_next_state(munit_uint32_t state) { + return state * MUNIT_PRNG_MULTIPLIER + MUNIT_PRNG_INCREMENT; +} + +static munit_uint32_t +munit_rand_from_state(munit_uint32_t state) { + munit_uint32_t res = ((state >> ((state >> 28) + 4)) ^ state) * (277803737U); + res ^= res >> 22; + return res; +} + +void +munit_rand_seed(munit_uint32_t seed) { + munit_uint32_t state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT); + munit_atomic_store(&munit_rand_state, state); +} + +static munit_uint32_t +munit_rand_generate_seed(void) { + struct PsnipClockTimespec wc = { 0, }; + munit_uint32_t seed, state; + + psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wc); + seed = (munit_uint32_t) wc.nanoseconds; + + state = munit_rand_next_state(seed + MUNIT_PRNG_INCREMENT); + return munit_rand_from_state(state); +} + +static munit_uint32_t +munit_rand_state_uint32(munit_uint32_t* state) { + const munit_uint32_t old = *state; + *state = munit_rand_next_state(old); + return munit_rand_from_state(old); +} + +munit_uint32_t +munit_rand_uint32(void) { + munit_uint32_t old, state; + + do { + old = munit_atomic_load(&munit_rand_state); + state = munit_rand_next_state(old); + } while (!munit_atomic_cas(&munit_rand_state, &old, state)); + + return munit_rand_from_state(old); +} + +static void +munit_rand_state_memory(munit_uint32_t* state, size_t size, munit_uint8_t data[MUNIT_ARRAY_PARAM(size)]) { + size_t members_remaining = size / sizeof(munit_uint32_t); + size_t bytes_remaining = size % sizeof(munit_uint32_t); + munit_uint8_t* b = data; + munit_uint32_t rv; + while (members_remaining-- > 0) { + rv = munit_rand_state_uint32(state); + memcpy(b, &rv, sizeof(munit_uint32_t)); + b += sizeof(munit_uint32_t); + } + if (bytes_remaining != 0) { + rv = munit_rand_state_uint32(state); + memcpy(b, &rv, bytes_remaining); + } +} + +void +munit_rand_memory(size_t size, munit_uint8_t data[MUNIT_ARRAY_PARAM(size)]) { + munit_uint32_t old, state; + + do { + state = old = munit_atomic_load(&munit_rand_state); + munit_rand_state_memory(&state, size, data); + } while (!munit_atomic_cas(&munit_rand_state, &old, state)); +} + +static munit_uint32_t +munit_rand_state_at_most(munit_uint32_t* state, munit_uint32_t salt, munit_uint32_t max) { + /* We want (UINT32_MAX + 1) % max, which in unsigned arithmetic is the same + * as (UINT32_MAX + 1 - max) % max = -max % max. We compute -max using not + * to avoid compiler warnings. + */ + const munit_uint32_t min = (~max + 1U) % max; + munit_uint32_t x; + + if (max == (~((munit_uint32_t) 0U))) + return munit_rand_state_uint32(state) ^ salt; + + max++; + + do { + x = munit_rand_state_uint32(state) ^ salt; + } while (x < min); + + return x % max; +} + +static munit_uint32_t +munit_rand_at_most(munit_uint32_t salt, munit_uint32_t max) { + munit_uint32_t old, state; + munit_uint32_t retval; + + do { + state = old = munit_atomic_load(&munit_rand_state); + retval = munit_rand_state_at_most(&state, salt, max); + } while (!munit_atomic_cas(&munit_rand_state, &old, state)); + + return retval; +} + +int +munit_rand_int_range(int min, int max) { + munit_uint64_t range = (munit_uint64_t) max - (munit_uint64_t) min; + + if (min > max) + return munit_rand_int_range(max, min); + + if (range > (~((munit_uint32_t) 0U))) + range = (~((munit_uint32_t) 0U)); + + return min + munit_rand_at_most(0, (munit_uint32_t) range); +} + +double +munit_rand_double(void) { + munit_uint32_t old, state; + double retval = 0.0; + + do { + state = old = munit_atomic_load(&munit_rand_state); + + /* See http://mumble.net/~campbell/tmp/random_real.c for how to do + * this right. Patches welcome if you feel that this is too + * biased. */ + retval = munit_rand_state_uint32(&state) / ((~((munit_uint32_t) 0U)) + 1.0); + } while (!munit_atomic_cas(&munit_rand_state, &old, state)); + + return retval; +} + +/*** Test suite handling ***/ + +typedef struct { + unsigned int successful; + unsigned int skipped; + unsigned int failed; + unsigned int errored; +#if defined(MUNIT_ENABLE_TIMING) + munit_uint64_t cpu_clock; + munit_uint64_t wall_clock; +#endif +} MunitReport; + +typedef struct { + const char* prefix; + const MunitSuite* suite; + const char** tests; + munit_uint32_t seed; + unsigned int iterations; + MunitParameter* parameters; + bool single_parameter_mode; + void* user_data; + MunitReport report; + bool colorize; + bool fork; + bool show_stderr; + bool fatal_failures; +} MunitTestRunner; + +const char* +munit_parameters_get(const MunitParameter params[], const char* key) { + const MunitParameter* param; + + for (param = params ; param != NULL && param->name != NULL ; param++) + if (strcmp(param->name, key) == 0) + return param->value; + return NULL; +} + +static void +munit_print_time(FILE* fp, munit_uint64_t nanoseconds) { + fprintf(fp, "%" MUNIT_TEST_TIME_FORMAT, ((double) nanoseconds) / ((double) PSNIP_CLOCK_NSEC_PER_SEC)); +} + +/* Add a paramter to an array of parameters. */ +static MunitResult +munit_parameters_add(size_t* params_size, MunitParameter* params[MUNIT_ARRAY_PARAM(*params_size)], char* name, char* value) { + *params = realloc(*params, sizeof(MunitParameter) * (*params_size + 2)); + if (*params == NULL) + return MUNIT_ERROR; + + (*params)[*params_size].name = name; + (*params)[*params_size].value = value; + (*params_size)++; + (*params)[*params_size].name = NULL; + (*params)[*params_size].value = NULL; + + return MUNIT_OK; +} + +/* Concatenate two strings, but just return one of the components + * unaltered if the other is NULL or "". */ +static char* +munit_maybe_concat(size_t* len, char* prefix, char* suffix) { + char* res; + size_t res_l; + const size_t prefix_l = prefix != NULL ? strlen(prefix) : 0; + const size_t suffix_l = suffix != NULL ? strlen(suffix) : 0; + if (prefix_l == 0 && suffix_l == 0) { + res = NULL; + res_l = 0; + } else if (prefix_l == 0 && suffix_l != 0) { + res = suffix; + res_l = suffix_l; + } else if (prefix_l != 0 && suffix_l == 0) { + res = prefix; + res_l = prefix_l; + } else { + res_l = prefix_l + suffix_l; + res = malloc(res_l + 1); + memcpy(res, prefix, prefix_l); + memcpy(res + prefix_l, suffix, suffix_l); + res[res_l] = 0; + } + + if (len != NULL) + *len = res_l; + + return res; +} + +/* Possbily free a string returned by munit_maybe_concat. */ +static void +munit_maybe_free_concat(char* s, const char* prefix, const char* suffix) { + if (prefix != s && suffix != s) + free(s); +} + +/* Cheap string hash function, just used to salt the PRNG. */ +static munit_uint32_t +munit_str_hash(const char* name) { + const char *p; + munit_uint32_t h = 5381U; + + for (p = name; *p != '\0'; p++) + h = (h << 5) + h + *p; + + return h; +} + +static void +munit_splice(int from, int to) { + munit_uint8_t buf[1024]; +#if !defined(_WIN32) + ssize_t len; + ssize_t bytes_written; + ssize_t write_res; +#else + int len; + int bytes_written; + int write_res; +#endif + do { + len = read(from, buf, sizeof(buf)); + if (len > 0) { + bytes_written = 0; + do { + write_res = write(to, buf + bytes_written, len - bytes_written); + if (write_res < 0) + break; + bytes_written += write_res; + } while (bytes_written < len); + } + else + break; + } while (true); +} + +/* This is the part that should be handled in the child process */ +static MunitResult +munit_test_runner_exec(MunitTestRunner* runner, const MunitTest* test, const MunitParameter params[], MunitReport* report) { + unsigned int iterations = runner->iterations; + MunitResult result = MUNIT_FAIL; +#if defined(MUNIT_ENABLE_TIMING) + struct PsnipClockTimespec wall_clock_begin = { 0, }, wall_clock_end = { 0, }; + struct PsnipClockTimespec cpu_clock_begin = { 0, }, cpu_clock_end = { 0, }; +#endif + unsigned int i = 0; + + if ((test->options & MUNIT_TEST_OPTION_SINGLE_ITERATION) == MUNIT_TEST_OPTION_SINGLE_ITERATION) + iterations = 1; + else if (iterations == 0) + iterations = runner->suite->iterations; + + munit_rand_seed(runner->seed); + + do { + void* data = (test->setup == NULL) ? runner->user_data : test->setup(params, runner->user_data); + +#if defined(MUNIT_ENABLE_TIMING) + psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_begin); + psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_begin); +#endif + + result = test->test(params, data); + +#if defined(MUNIT_ENABLE_TIMING) + psnip_clock_get_time(PSNIP_CLOCK_TYPE_WALL, &wall_clock_end); + psnip_clock_get_time(PSNIP_CLOCK_TYPE_CPU, &cpu_clock_end); +#endif + + if (test->tear_down != NULL) + test->tear_down(data); + + if (MUNIT_LIKELY(result == MUNIT_OK)) { + report->successful++; +#if defined(MUNIT_ENABLE_TIMING) + report->wall_clock += munit_clock_get_elapsed(&wall_clock_begin, &wall_clock_end); + report->cpu_clock += munit_clock_get_elapsed(&cpu_clock_begin, &cpu_clock_end); +#endif + } else { + switch ((int) result) { + case MUNIT_SKIP: + report->skipped++; + break; + case MUNIT_FAIL: + report->failed++; + break; + case MUNIT_ERROR: + report->errored++; + break; + default: + break; + } + break; + } + } while (++i < iterations); + + return result; +} + +#if defined(MUNIT_EMOTICON) +# define MUNIT_RESULT_STRING_OK ":)" +# define MUNIT_RESULT_STRING_SKIP ":|" +# define MUNIT_RESULT_STRING_FAIL ":(" +# define MUNIT_RESULT_STRING_ERROR ":o" +# define MUNIT_RESULT_STRING_TODO ":/" +#else +# define MUNIT_RESULT_STRING_OK "OK " +# define MUNIT_RESULT_STRING_SKIP "SKIP " +# define MUNIT_RESULT_STRING_FAIL "FAIL " +# define MUNIT_RESULT_STRING_ERROR "ERROR" +# define MUNIT_RESULT_STRING_TODO "TODO " +#endif + +static void +munit_test_runner_print_color(const MunitTestRunner* runner, const char* string, char color) { + if (runner->colorize) + fprintf(MUNIT_OUTPUT_FILE, "\x1b[3%cm%s\x1b[39m", color, string); + else + fputs(string, MUNIT_OUTPUT_FILE); +} + +#if !defined(MUNIT_NO_BUFFER) +static int +munit_replace_stderr(FILE* stderr_buf) { + if (stderr_buf != NULL) { + const int orig_stderr = dup(STDERR_FILENO); + + int errfd = fileno(stderr_buf); + if (MUNIT_UNLIKELY(errfd == -1)) { + exit(EXIT_FAILURE); + } + + dup2(errfd, STDERR_FILENO); + + return orig_stderr; + } + + return -1; +} + +static void +munit_restore_stderr(int orig_stderr) { + if (orig_stderr != -1) { + dup2(orig_stderr, STDERR_FILENO); + close(orig_stderr); + } +} +#endif /* !defined(MUNIT_NO_BUFFER) */ + +/* Run a test with the specified parameters. */ +static void +munit_test_runner_run_test_with_params(MunitTestRunner* runner, const MunitTest* test, const MunitParameter params[]) { + MunitResult result = MUNIT_OK; + MunitReport report = { + 0, 0, 0, 0, +#if defined(MUNIT_ENABLE_TIMING) + 0, 0 +#endif + }; + unsigned int output_l; + bool first; + const MunitParameter* param; + FILE* stderr_buf; +#if !defined(MUNIT_NO_FORK) + int pipefd[2]; + pid_t fork_pid; + int orig_stderr; + ssize_t bytes_written = 0; + ssize_t write_res; + ssize_t bytes_read = 0; + ssize_t read_res; + int status = 0; + pid_t changed_pid; +#endif + + if (params != NULL) { + output_l = 2; + fputs(" ", MUNIT_OUTPUT_FILE); + first = true; + for (param = params ; param != NULL && param->name != NULL ; param++) { + if (!first) { + fputs(", ", MUNIT_OUTPUT_FILE); + output_l += 2; + } else { + first = false; + } + + output_l += fprintf(MUNIT_OUTPUT_FILE, "%s=%s", param->name, param->value); + } + while (output_l++ < MUNIT_TEST_NAME_LEN) { + fputc(' ', MUNIT_OUTPUT_FILE); + } + } + + fflush(MUNIT_OUTPUT_FILE); + + stderr_buf = NULL; +#if !defined(_WIN32) || defined(__MINGW32__) + stderr_buf = tmpfile(); +#else + tmpfile_s(&stderr_buf); +#endif + if (stderr_buf == NULL) { + munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create buffer for stderr"); + result = MUNIT_ERROR; + goto print_result; + } + +#if !defined(MUNIT_NO_FORK) + if (runner->fork) { + pipefd[0] = -1; + pipefd[1] = -1; + if (pipe(pipefd) != 0) { + munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to create pipe"); + result = MUNIT_ERROR; + goto print_result; + } + + fork_pid = fork(); + if (fork_pid == 0) { + close(pipefd[0]); + + orig_stderr = munit_replace_stderr(stderr_buf); + munit_test_runner_exec(runner, test, params, &report); + + /* Note that we don't restore stderr. This is so we can buffer + * things written to stderr later on (such as by + * asan/tsan/ubsan, valgrind, etc.) */ + close(orig_stderr); + + do { + write_res = write(pipefd[1], ((munit_uint8_t*) (&report)) + bytes_written, sizeof(report) - bytes_written); + if (write_res < 0) { + if (stderr_buf != NULL) { + munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to write to pipe"); + } + exit(EXIT_FAILURE); + } + bytes_written += write_res; + } while ((size_t) bytes_written < sizeof(report)); + + if (stderr_buf != NULL) + fclose(stderr_buf); + close(pipefd[1]); + + exit(EXIT_SUCCESS); + } else if (fork_pid == -1) { + close(pipefd[0]); + close(pipefd[1]); + if (stderr_buf != NULL) { + munit_log_errno(MUNIT_LOG_ERROR, stderr, "unable to fork"); + } + report.errored++; + result = MUNIT_ERROR; + } else { + close(pipefd[1]); + do { + read_res = read(pipefd[0], ((munit_uint8_t*) (&report)) + bytes_read, sizeof(report) - bytes_read); + if (read_res < 1) + break; + bytes_read += read_res; + } while (bytes_read < (ssize_t) sizeof(report)); + + changed_pid = waitpid(fork_pid, &status, 0); + + if (MUNIT_LIKELY(changed_pid == fork_pid) && MUNIT_LIKELY(WIFEXITED(status))) { + if (bytes_read != sizeof(report)) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child exited unexpectedly with status %d", WEXITSTATUS(status)); + report.errored++; + } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child exited with status %d", WEXITSTATUS(status)); + report.errored++; + } + } else { + if (WIFSIGNALED(status)) { +#if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700) + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child killed by signal %d (%s)", WTERMSIG(status), strsignal(WTERMSIG(status))); +#else + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child killed by signal %d", WTERMSIG(status)); +#endif + } else if (WIFSTOPPED(status)) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr_buf, "child stopped by signal %d", WSTOPSIG(status)); + } + report.errored++; + } + + close(pipefd[0]); + waitpid(fork_pid, NULL, 0); + } + } else +#endif + { +#if !defined(MUNIT_NO_BUFFER) + const volatile int orig_stderr = munit_replace_stderr(stderr_buf); +#endif + +#if defined(MUNIT_THREAD_LOCAL) + if (MUNIT_UNLIKELY(setjmp(munit_error_jmp_buf) != 0)) { + result = MUNIT_FAIL; + report.failed++; + } else { + munit_error_jmp_buf_valid = true; + result = munit_test_runner_exec(runner, test, params, &report); + } +#else + result = munit_test_runner_exec(runner, test, params, &report); +#endif + +#if !defined(MUNIT_NO_BUFFER) + munit_restore_stderr(orig_stderr); +#endif + + /* Here just so that the label is used on Windows and we don't get + * a warning */ + goto print_result; + } + + print_result: + + fputs("[ ", MUNIT_OUTPUT_FILE); + if ((test->options & MUNIT_TEST_OPTION_TODO) == MUNIT_TEST_OPTION_TODO) { + if (report.failed != 0 || report.errored != 0 || report.skipped != 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_TODO, '3'); + result = MUNIT_OK; + } else { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1'); + if (MUNIT_LIKELY(stderr_buf != NULL)) + munit_log_internal(MUNIT_LOG_ERROR, stderr_buf, "Test marked TODO, but was successful."); + runner->report.failed++; + result = MUNIT_ERROR; + } + } else if (report.failed > 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_FAIL, '1'); + runner->report.failed++; + result = MUNIT_FAIL; + } else if (report.errored > 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_ERROR, '1'); + runner->report.errored++; + result = MUNIT_ERROR; + } else if (report.skipped > 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_SKIP, '3'); + runner->report.skipped++; + result = MUNIT_SKIP; + } else if (report.successful > 1) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2'); +#if defined(MUNIT_ENABLE_TIMING) + fputs(" ] [ ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock / report.successful); + fputs(" / ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock / report.successful); + fprintf(MUNIT_OUTPUT_FILE, " CPU ]\n %-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s Total: [ ", ""); + munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock); + fputs(" / ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock); + fputs(" CPU", MUNIT_OUTPUT_FILE); +#endif + runner->report.successful++; + result = MUNIT_OK; + } else if (report.successful > 0) { + munit_test_runner_print_color(runner, MUNIT_RESULT_STRING_OK, '2'); +#if defined(MUNIT_ENABLE_TIMING) + fputs(" ] [ ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.wall_clock); + fputs(" / ", MUNIT_OUTPUT_FILE); + munit_print_time(MUNIT_OUTPUT_FILE, report.cpu_clock); + fputs(" CPU", MUNIT_OUTPUT_FILE); +#endif + runner->report.successful++; + result = MUNIT_OK; + } + fputs(" ]\n", MUNIT_OUTPUT_FILE); + + if (stderr_buf != NULL) { + if (result == MUNIT_FAIL || result == MUNIT_ERROR || runner->show_stderr) { + fflush(MUNIT_OUTPUT_FILE); + + rewind(stderr_buf); + munit_splice(fileno(stderr_buf), STDERR_FILENO); + + fflush(stderr); + } + + fclose(stderr_buf); + } +} + +static void +munit_test_runner_run_test_wild(MunitTestRunner* runner, + const MunitTest* test, + const char* test_name, + MunitParameter* params, + MunitParameter* p) { + const MunitParameterEnum* pe; + char** values; + MunitParameter* next; + + for (pe = test->parameters ; pe != NULL && pe->name != NULL ; pe++) { + if (p->name == pe->name) + break; + } + + if (pe == NULL) + return; + + for (values = pe->values ; *values != NULL ; values++) { + next = p + 1; + p->value = *values; + if (next->name == NULL) { + munit_test_runner_run_test_with_params(runner, test, params); + } else { + munit_test_runner_run_test_wild(runner, test, test_name, params, next); + } + if (runner->fatal_failures && (runner->report.failed != 0 || runner->report.errored != 0)) + break; + } +} + +/* Run a single test, with every combination of parameters + * requested. */ +static void +munit_test_runner_run_test(MunitTestRunner* runner, + const MunitTest* test, + const char* prefix) { + char* test_name = munit_maybe_concat(NULL, (char*) prefix, (char*) test->name); + /* The array of parameters to pass to + * munit_test_runner_run_test_with_params */ + MunitParameter* params = NULL; + size_t params_l = 0; + /* Wildcard parameters are parameters which have possible values + * specified in the test, but no specific value was passed to the + * CLI. That means we want to run the test once for every + * possible combination of parameter values or, if --single was + * passed to the CLI, a single time with a random set of + * parameters. */ + MunitParameter* wild_params = NULL; + size_t wild_params_l = 0; + const MunitParameterEnum* pe; + const MunitParameter* cli_p; + bool filled; + unsigned int possible; + char** vals; + size_t first_wild; + const MunitParameter* wp; + int pidx; + + munit_rand_seed(runner->seed); + + fprintf(MUNIT_OUTPUT_FILE, "%-" MUNIT_XSTRINGIFY(MUNIT_TEST_NAME_LEN) "s", test_name); + + if (test->parameters == NULL) { + /* No parameters. Simple, nice. */ + munit_test_runner_run_test_with_params(runner, test, NULL); + } else { + fputc('\n', MUNIT_OUTPUT_FILE); + + for (pe = test->parameters ; pe != NULL && pe->name != NULL ; pe++) { + /* Did we received a value for this parameter from the CLI? */ + filled = false; + for (cli_p = runner->parameters ; cli_p != NULL && cli_p->name != NULL ; cli_p++) { + if (strcmp(cli_p->name, pe->name) == 0) { + if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name, cli_p->value) != MUNIT_OK)) + goto cleanup; + filled = true; + break; + } + } + if (filled) + continue; + + /* Nothing from CLI, is the enum NULL/empty? We're not a + * fuzzer… */ + if (pe->values == NULL || pe->values[0] == NULL) + continue; + + /* If --single was passed to the CLI, choose a value from the + * list of possibilities randomly. */ + if (runner->single_parameter_mode) { + possible = 0; + for (vals = pe->values ; *vals != NULL ; vals++) + possible++; + /* We want the tests to be reproducible, even if you're only + * running a single test, but we don't want every test with + * the same number of parameters to choose the same parameter + * number, so use the test name as a primitive salt. */ + pidx = munit_rand_at_most(munit_str_hash(test_name), possible - 1); + if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name, pe->values[pidx]) != MUNIT_OK)) + goto cleanup; + } else { + /* We want to try every permutation. Put in a placeholder + * entry, we'll iterate through them later. */ + if (MUNIT_UNLIKELY(munit_parameters_add(&wild_params_l, &wild_params, pe->name, NULL) != MUNIT_OK)) + goto cleanup; + } + } + + if (wild_params_l != 0) { + first_wild = params_l; + for (wp = wild_params ; wp != NULL && wp->name != NULL ; wp++) { + for (pe = test->parameters ; pe != NULL && pe->name != NULL && pe->values != NULL ; pe++) { + if (strcmp(wp->name, pe->name) == 0) { + if (MUNIT_UNLIKELY(munit_parameters_add(¶ms_l, ¶ms, pe->name, pe->values[0]) != MUNIT_OK)) + goto cleanup; + } + } + } + + munit_test_runner_run_test_wild(runner, test, test_name, params, params + first_wild); + } else { + munit_test_runner_run_test_with_params(runner, test, params); + } + + cleanup: + free(params); + free(wild_params); + } + + munit_maybe_free_concat(test_name, prefix, test->name); +} + +/* Recurse through the suite and run all the tests. If a list of + * tests to run was provied on the command line, run only those + * tests. */ +static void +munit_test_runner_run_suite(MunitTestRunner* runner, + const MunitSuite* suite, + const char* prefix) { + size_t pre_l; + char* pre = munit_maybe_concat(&pre_l, (char*) prefix, (char*) suite->prefix); + const MunitTest* test; + const char** test_name; + const MunitSuite* child_suite; + + /* Run the tests. */ + for (test = suite->tests ; test != NULL && test->test != NULL ; test++) { + if (runner->tests != NULL) { /* Specific tests were requested on the CLI */ + for (test_name = runner->tests ; test_name != NULL && *test_name != NULL ; test_name++) { + if ((pre_l == 0 || strncmp(pre, *test_name, pre_l) == 0) && + strncmp(test->name, *test_name + pre_l, strlen(*test_name + pre_l)) == 0) { + munit_test_runner_run_test(runner, test, pre); + if (runner->fatal_failures && (runner->report.failed != 0 || runner->report.errored != 0)) + goto cleanup; + } + } + } else { /* Run all tests */ + munit_test_runner_run_test(runner, test, pre); + } + } + + if (runner->fatal_failures && (runner->report.failed != 0 || runner->report.errored != 0)) + goto cleanup; + + /* Run any child suites. */ + for (child_suite = suite->suites ; child_suite != NULL && child_suite->prefix != NULL ; child_suite++) { + munit_test_runner_run_suite(runner, child_suite, pre); + } + + cleanup: + + munit_maybe_free_concat(pre, prefix, suite->prefix); +} + +static void +munit_test_runner_run(MunitTestRunner* runner) { + munit_test_runner_run_suite(runner, runner->suite, NULL); +} + +static void +munit_print_help(int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)], void* user_data, const MunitArgument arguments[]) { + const MunitArgument* arg; + (void) argc; + + printf("USAGE: %s [OPTIONS...] [TEST...]\n\n", argv[0]); + puts(" --seed SEED\n" + " Value used to seed the PRNG. Must be a 32-bit integer in decimal\n" + " notation with no separators (commas, decimals, spaces, etc.), or\n" + " hexidecimal prefixed by \"0x\".\n" + " --iterations N\n" + " Run each test N times. 0 means the default number.\n" + " --param name value\n" + " A parameter key/value pair which will be passed to any test with\n" + " takes a parameter of that name. If not provided, the test will be\n" + " run once for each possible parameter value.\n" + " --list Write a list of all available tests.\n" + " --list-params\n" + " Write a list of all available tests and their possible parameters.\n" + " --single Run each parameterized test in a single configuration instead of\n" + " every possible combination\n" + " --log-visible debug|info|warning|error\n" + " --log-fatal debug|info|warning|error\n" + " Set the level at which messages of different severities are visible,\n" + " or cause the test to terminate.\n" +#if !defined(MUNIT_NO_FORK) + " --no-fork Do not execute tests in a child process. If this option is supplied\n" + " and a test crashes (including by failing an assertion), no further\n" + " tests will be performed.\n" +#endif + " --fatal-failures\n" + " Stop executing tests as soon as a failure is found.\n" + " --show-stderr\n" + " Show data written to stderr by the tests, even if the test succeeds.\n" + " --color auto|always|never\n" + " Colorize (or don't) the output.\n" + /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */ + " --help Print this help message and exit.\n"); +#if defined(MUNIT_NL_LANGINFO) + setlocale(LC_ALL, ""); + fputs((strcasecmp("UTF-8", nl_langinfo(CODESET)) == 0) ? "µnit" : "munit", stdout); +#else + puts("munit"); +#endif + printf(" %d.%d.%d\n" + "Full documentation at: https://nemequ.github.io/munit/\n", + (MUNIT_CURRENT_VERSION >> 16) & 0xff, + (MUNIT_CURRENT_VERSION >> 8) & 0xff, + (MUNIT_CURRENT_VERSION >> 0) & 0xff); + for (arg = arguments ; arg != NULL && arg->name != NULL ; arg++) + arg->write_help(arg, user_data); +} + +static const MunitArgument* +munit_arguments_find(const MunitArgument arguments[], const char* name) { + const MunitArgument* arg; + + for (arg = arguments ; arg != NULL && arg->name != NULL ; arg++) + if (strcmp(arg->name, name) == 0) + return arg; + + return NULL; +} + +static void +munit_suite_list_tests(const MunitSuite* suite, bool show_params, const char* prefix) { + size_t pre_l; + char* pre = munit_maybe_concat(&pre_l, (char*) prefix, (char*) suite->prefix); + const MunitTest* test; + const MunitParameterEnum* params; + bool first; + char** val; + const MunitSuite* child_suite; + + for (test = suite->tests ; + test != NULL && test->name != NULL ; + test++) { + if (pre != NULL) + fputs(pre, stdout); + puts(test->name); + + if (show_params) { + for (params = test->parameters ; + params != NULL && params->name != NULL ; + params++) { + fprintf(stdout, " - %s: ", params->name); + if (params->values == NULL) { + puts("Any"); + } else { + first = true; + for (val = params->values ; + *val != NULL ; + val++ ) { + if(!first) { + fputs(", ", stdout); + } else { + first = false; + } + fputs(*val, stdout); + } + putc('\n', stdout); + } + } + } + } + + for (child_suite = suite->suites ; child_suite != NULL && child_suite->prefix != NULL ; child_suite++) { + munit_suite_list_tests(child_suite, show_params, pre); + } + + munit_maybe_free_concat(pre, prefix, suite->prefix); +} + +static bool +munit_stream_supports_ansi(FILE *stream) { +#if !defined(_WIN32) + return isatty(fileno(stream)); +#else + +#if !defined(__MINGW32__) + size_t ansicon_size = 0; +#endif + + if (isatty(fileno(stream))) { +#if !defined(__MINGW32__) + getenv_s(&ansicon_size, NULL, 0, "ANSICON"); + return ansicon_size != 0; +#else + return getenv("ANSICON") != NULL; +#endif + } + return false; +#endif +} + +int +munit_suite_main_custom(const MunitSuite* suite, void* user_data, + int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)], + const MunitArgument arguments[]) { + int result = EXIT_FAILURE; + MunitTestRunner runner; + size_t parameters_size = 0; + size_t tests_size = 0; + int arg; + + char* envptr; + unsigned long ts; + char* endptr; + unsigned long long iterations; + MunitLogLevel level; + const MunitArgument* argument; + const char** runner_tests; + unsigned int tests_run; + unsigned int tests_total; + + runner.prefix = NULL; + runner.suite = NULL; + runner.tests = NULL; + runner.seed = 0; + runner.iterations = 0; + runner.parameters = NULL; + runner.single_parameter_mode = false; + runner.user_data = NULL; + + runner.report.successful = 0; + runner.report.skipped = 0; + runner.report.failed = 0; + runner.report.errored = 0; +#if defined(MUNIT_ENABLE_TIMING) + runner.report.cpu_clock = 0; + runner.report.wall_clock = 0; +#endif + + runner.colorize = false; +#if !defined(_WIN32) + runner.fork = true; +#else + runner.fork = false; +#endif + runner.show_stderr = false; + runner.fatal_failures = false; + runner.suite = suite; + runner.user_data = user_data; + runner.seed = munit_rand_generate_seed(); + runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE); + + for (arg = 1 ; arg < argc ; arg++) { + if (strncmp("--", argv[arg], 2) == 0) { + if (strcmp("seed", argv[arg] + 2) == 0) { + if (arg + 1 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires an argument", argv[arg]); + goto cleanup; + } + + envptr = argv[arg + 1]; + ts = strtoul(argv[arg + 1], &envptr, 0); + if (*envptr != '\0' || ts > (~((munit_uint32_t) 0U))) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); + goto cleanup; + } + runner.seed = (munit_uint32_t) ts; + + arg++; + } else if (strcmp("iterations", argv[arg] + 2) == 0) { + if (arg + 1 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires an argument", argv[arg]); + goto cleanup; + } + + endptr = argv[arg + 1]; + iterations = strtoul(argv[arg + 1], &endptr, 0); + if (*endptr != '\0' || iterations > UINT_MAX) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); + goto cleanup; + } + + runner.iterations = (unsigned int) iterations; + + arg++; + } else if (strcmp("param", argv[arg] + 2) == 0) { + if (arg + 2 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires two arguments", argv[arg]); + goto cleanup; + } + + runner.parameters = realloc(runner.parameters, sizeof(MunitParameter) * (parameters_size + 2)); + if (runner.parameters == NULL) { + munit_log_internal(MUNIT_LOG_ERROR, stderr, "failed to allocate memory"); + goto cleanup; + } + runner.parameters[parameters_size].name = (char*) argv[arg + 1]; + runner.parameters[parameters_size].value = (char*) argv[arg + 2]; + parameters_size++; + runner.parameters[parameters_size].name = NULL; + runner.parameters[parameters_size].value = NULL; + arg += 2; + } else if (strcmp("color", argv[arg] + 2) == 0) { + if (arg + 1 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires an argument", argv[arg]); + goto cleanup; + } + + if (strcmp(argv[arg + 1], "always") == 0) + runner.colorize = true; + else if (strcmp(argv[arg + 1], "never") == 0) + runner.colorize = false; + else if (strcmp(argv[arg + 1], "auto") == 0) + runner.colorize = munit_stream_supports_ansi(MUNIT_OUTPUT_FILE); + else { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); + goto cleanup; + } + + arg++; + } else if (strcmp("help", argv[arg] + 2) == 0) { + munit_print_help(argc, argv, user_data, arguments); + result = EXIT_SUCCESS; + goto cleanup; + } else if (strcmp("single", argv[arg] + 2) == 0) { + runner.single_parameter_mode = true; + } else if (strcmp("show-stderr", argv[arg] + 2) == 0) { + runner.show_stderr = true; +#if !defined(_WIN32) + } else if (strcmp("no-fork", argv[arg] + 2) == 0) { + runner.fork = false; +#endif + } else if (strcmp("fatal-failures", argv[arg] + 2) == 0) { + runner.fatal_failures = true; + } else if (strcmp("log-visible", argv[arg] + 2) == 0 || + strcmp("log-fatal", argv[arg] + 2) == 0) { + if (arg + 1 >= argc) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "%s requires an argument", argv[arg]); + goto cleanup; + } + + if (strcmp(argv[arg + 1], "debug") == 0) + level = MUNIT_LOG_DEBUG; + else if (strcmp(argv[arg + 1], "info") == 0) + level = MUNIT_LOG_INFO; + else if (strcmp(argv[arg + 1], "warning") == 0) + level = MUNIT_LOG_WARNING; + else if (strcmp(argv[arg + 1], "error") == 0) + level = MUNIT_LOG_ERROR; + else { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "invalid value ('%s') passed to %s", argv[arg + 1], argv[arg]); + goto cleanup; + } + + if (strcmp("log-visible", argv[arg] + 2) == 0) + munit_log_level_visible = level; + else + munit_log_level_fatal = level; + + arg++; + } else if (strcmp("list", argv[arg] + 2) == 0) { + munit_suite_list_tests(suite, false, NULL); + result = EXIT_SUCCESS; + goto cleanup; + } else if (strcmp("list-params", argv[arg] + 2) == 0) { + munit_suite_list_tests(suite, true, NULL); + result = EXIT_SUCCESS; + goto cleanup; + } else { + argument = munit_arguments_find(arguments, argv[arg] + 2); + if (argument == NULL) { + munit_logf_internal(MUNIT_LOG_ERROR, stderr, "unknown argument ('%s')", argv[arg]); + goto cleanup; + } + + if (!argument->parse_argument(suite, user_data, &arg, argc, argv)) + goto cleanup; + } + } else { + runner_tests = realloc((void*) runner.tests, sizeof(char*) * (tests_size + 2)); + if (runner_tests == NULL) { + munit_log_internal(MUNIT_LOG_ERROR, stderr, "failed to allocate memory"); + goto cleanup; + } + runner.tests = runner_tests; + runner.tests[tests_size++] = argv[arg]; + runner.tests[tests_size] = NULL; + } + } + + fflush(stderr); + fprintf(MUNIT_OUTPUT_FILE, "Running test suite with seed 0x%08" PRIx32 "...\n", runner.seed); + + munit_test_runner_run(&runner); + + tests_run = runner.report.successful + runner.report.failed + runner.report.errored; + tests_total = tests_run + runner.report.skipped; + if (tests_run == 0) { + fprintf(stderr, "No tests run, %d (100%%) skipped.\n", runner.report.skipped); + } else { + fprintf(MUNIT_OUTPUT_FILE, "%d of %d (%0.0f%%) tests successful, %d (%0.0f%%) test skipped.\n", + runner.report.successful, tests_run, + (((double) runner.report.successful) / ((double) tests_run)) * 100.0, + runner.report.skipped, + (((double) runner.report.skipped) / ((double) tests_total)) * 100.0); + } + + if (runner.report.failed == 0 && runner.report.errored == 0) { + result = EXIT_SUCCESS; + } + + cleanup: + free(runner.parameters); + free((void*) runner.tests); + + return result; +} + +int +munit_suite_main(const MunitSuite* suite, void* user_data, + int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)]) { + return munit_suite_main_custom(suite, user_data, argc, argv, NULL); +} diff --git a/test/munit/munit.h b/test/munit/munit.h new file mode 100644 index 0000000..bdc58bd --- /dev/null +++ b/test/munit/munit.h @@ -0,0 +1,533 @@ +/* µnit Testing Framework + * Copyright (c) 2013-2017 Evan Nemerson + * + * 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. + */ + +#if !defined(MUNIT_H) +#define MUNIT_H + +#include +#include + +#define MUNIT_VERSION(major, minor, revision) \ + (((major) << 16) | ((minor) << 8) | (revision)) + +#define MUNIT_CURRENT_VERSION MUNIT_VERSION(0, 4, 1) + +#if defined(_MSC_VER) && (_MSC_VER < 1600) +# define munit_int8_t __int8 +# define munit_uint8_t unsigned __int8 +# define munit_int16_t __int16 +# define munit_uint16_t unsigned __int16 +# define munit_int32_t __int32 +# define munit_uint32_t unsigned __int32 +# define munit_int64_t __int64 +# define munit_uint64_t unsigned __int64 +#else +# include +# define munit_int8_t int8_t +# define munit_uint8_t uint8_t +# define munit_int16_t int16_t +# define munit_uint16_t uint16_t +# define munit_int32_t int32_t +# define munit_uint32_t uint32_t +# define munit_int64_t int64_t +# define munit_uint64_t uint64_t +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1800) +# if !defined(PRIi8) +# define PRIi8 "i" +# endif +# if !defined(PRIi16) +# define PRIi16 "i" +# endif +# if !defined(PRIi32) +# define PRIi32 "i" +# endif +# if !defined(PRIi64) +# define PRIi64 "I64i" +# endif +# if !defined(PRId8) +# define PRId8 "d" +# endif +# if !defined(PRId16) +# define PRId16 "d" +# endif +# if !defined(PRId32) +# define PRId32 "d" +# endif +# if !defined(PRId64) +# define PRId64 "I64d" +# endif +# if !defined(PRIx8) +# define PRIx8 "x" +# endif +# if !defined(PRIx16) +# define PRIx16 "x" +# endif +# if !defined(PRIx32) +# define PRIx32 "x" +# endif +# if !defined(PRIx64) +# define PRIx64 "I64x" +# endif +# if !defined(PRIu8) +# define PRIu8 "u" +# endif +# if !defined(PRIu16) +# define PRIu16 "u" +# endif +# if !defined(PRIu32) +# define PRIu32 "u" +# endif +# if !defined(PRIu64) +# define PRIu64 "I64u" +# endif +# if !defined(bool) +# define bool int +# endif +# if !defined(true) +# define true (!0) +# endif +# if !defined(false) +# define false (!!0) +# endif +#else +# include +# include +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(__GNUC__) +# define MUNIT_LIKELY(expr) (__builtin_expect ((expr), 1)) +# define MUNIT_UNLIKELY(expr) (__builtin_expect ((expr), 0)) +# define MUNIT_UNUSED __attribute__((__unused__)) +#else +# define MUNIT_LIKELY(expr) (expr) +# define MUNIT_UNLIKELY(expr) (expr) +# define MUNIT_UNUSED +#endif + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__PGI) +# define MUNIT_ARRAY_PARAM(name) name +#else +# define MUNIT_ARRAY_PARAM(name) +#endif + +#if !defined(_WIN32) +# define MUNIT_SIZE_MODIFIER "z" +# define MUNIT_CHAR_MODIFIER "hh" +# define MUNIT_SHORT_MODIFIER "h" +#else +# if defined(_M_X64) || defined(__amd64__) +# define MUNIT_SIZE_MODIFIER "I64" +# else +# define MUNIT_SIZE_MODIFIER "" +# endif +# define MUNIT_CHAR_MODIFIER "" +# define MUNIT_SHORT_MODIFIER "" +#endif + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +# define MUNIT_NO_RETURN _Noreturn +#elif defined(__GNUC__) +# define MUNIT_NO_RETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) +# define MUNIT_NO_RETURN __declspec(noreturn) +#else +# define MUNIT_NO_RETURN +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1500) +# define MUNIT__PUSH_DISABLE_MSVC_C4127 __pragma(warning(push)) __pragma(warning(disable:4127)) +# define MUNIT__POP_DISABLE_MSVC_C4127 __pragma(warning(pop)) +#else +# define MUNIT__PUSH_DISABLE_MSVC_C4127 +# define MUNIT__POP_DISABLE_MSVC_C4127 +#endif + +typedef enum { + MUNIT_LOG_DEBUG, + MUNIT_LOG_INFO, + MUNIT_LOG_WARNING, + MUNIT_LOG_ERROR +} MunitLogLevel; + +#if defined(__GNUC__) && !defined(__MINGW32__) +# define MUNIT_PRINTF(string_index, first_to_check) __attribute__((format (printf, string_index, first_to_check))) +#else +# define MUNIT_PRINTF(string_index, first_to_check) +#endif + +MUNIT_PRINTF(4, 5) +void munit_logf_ex(MunitLogLevel level, const char* filename, int line, const char* format, ...); + +#define munit_logf(level, format, ...) \ + munit_logf_ex(level, __FILE__, __LINE__, format, __VA_ARGS__) + +#define munit_log(level, msg) \ + munit_logf(level, "%s", msg) + +MUNIT_NO_RETURN +MUNIT_PRINTF(3, 4) +void munit_errorf_ex(const char* filename, int line, const char* format, ...); + +#define munit_errorf(format, ...) \ + munit_errorf_ex(__FILE__, __LINE__, format, __VA_ARGS__) + +#define munit_error(msg) \ + munit_errorf("%s", msg) + +#define munit_assert(expr) \ + do { \ + if (!MUNIT_LIKELY(expr)) { \ + munit_error("assertion failed: " #expr); \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#define munit_assert_true(expr) \ + do { \ + if (!MUNIT_LIKELY(expr)) { \ + munit_error("assertion failed: " #expr " is not true"); \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#define munit_assert_false(expr) \ + do { \ + if (!MUNIT_LIKELY(!(expr))) { \ + munit_error("assertion failed: " #expr " is not false"); \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#define munit_assert_type_full(prefix, suffix, T, fmt, a, op, b) \ + do { \ + T munit_tmp_a_ = (a); \ + T munit_tmp_b_ = (b); \ + if (!(munit_tmp_a_ op munit_tmp_b_)) { \ + munit_errorf("assertion failed: " #a " " #op " " #b " (" prefix "%" fmt suffix " " #op " " prefix "%" fmt suffix ")", \ + munit_tmp_a_, munit_tmp_b_); \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#define munit_assert_type(T, fmt, a, op, b) \ + munit_assert_type_full("", "", T, fmt, a, op, b) + +#define munit_assert_char(a, op, b) \ + munit_assert_type_full("'\\x", "'", char, "02" MUNIT_CHAR_MODIFIER "x", a, op, b) +#define munit_assert_uchar(a, op, b) \ + munit_assert_type_full("'\\x", "'", unsigned char, "02" MUNIT_CHAR_MODIFIER "x", a, op, b) +#define munit_assert_short(a, op, b) \ + munit_assert_type(short, MUNIT_SHORT_MODIFIER "d", a, op, b) +#define munit_assert_ushort(a, op, b) \ + munit_assert_type(unsigned short, MUNIT_SHORT_MODIFIER "u", a, op, b) +#define munit_assert_int(a, op, b) \ + munit_assert_type(int, "d", a, op, b) +#define munit_assert_uint(a, op, b) \ + munit_assert_type(unsigned int, "u", a, op, b) +#define munit_assert_long(a, op, b) \ + munit_assert_type(long int, "ld", a, op, b) +#define munit_assert_ulong(a, op, b) \ + munit_assert_type(unsigned long int, "lu", a, op, b) +#define munit_assert_llong(a, op, b) \ + munit_assert_type(long long int, "lld", a, op, b) +#define munit_assert_ullong(a, op, b) \ + munit_assert_type(unsigned long long int, "llu", a, op, b) + +#define munit_assert_size(a, op, b) \ + munit_assert_type(size_t, MUNIT_SIZE_MODIFIER "u", a, op, b) + +#define munit_assert_float(a, op, b) \ + munit_assert_type(float, "f", a, op, b) +#define munit_assert_double(a, op, b) \ + munit_assert_type(double, "g", a, op, b) +#define munit_assert_ptr(a, op, b) \ + munit_assert_type(const void*, "p", a, op, b) + +#define munit_assert_int8(a, op, b) \ + munit_assert_type(munit_int8_t, PRIi8, a, op, b) +#define munit_assert_uint8(a, op, b) \ + munit_assert_type(munit_uint8_t, PRIu8, a, op, b) +#define munit_assert_int16(a, op, b) \ + munit_assert_type(munit_int16_t, PRIi16, a, op, b) +#define munit_assert_uint16(a, op, b) \ + munit_assert_type(munit_uint16_t, PRIu16, a, op, b) +#define munit_assert_int32(a, op, b) \ + munit_assert_type(munit_int32_t, PRIi32, a, op, b) +#define munit_assert_uint32(a, op, b) \ + munit_assert_type(munit_uint32_t, PRIu32, a, op, b) +#define munit_assert_int64(a, op, b) \ + munit_assert_type(munit_int64_t, PRIi64, a, op, b) +#define munit_assert_uint64(a, op, b) \ + munit_assert_type(munit_uint64_t, PRIu64, a, op, b) + +#define munit_assert_double_equal(a, b, precision) \ + do { \ + const double munit_tmp_a_ = (a); \ + const double munit_tmp_b_ = (b); \ + const double munit_tmp_diff_ = ((munit_tmp_a_ - munit_tmp_b_) < 0) ? \ + -(munit_tmp_a_ - munit_tmp_b_) : \ + (munit_tmp_a_ - munit_tmp_b_); \ + if (MUNIT_UNLIKELY(munit_tmp_diff_ > 1e-##precision)) { \ + munit_errorf("assertion failed: " #a " == " #b " (%0." #precision "g == %0." #precision "g)", \ + munit_tmp_a_, munit_tmp_b_); \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#include +#define munit_assert_string_equal(a, b) \ + do { \ + const char* munit_tmp_a_ = a; \ + const char* munit_tmp_b_ = b; \ + if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) != 0)) { \ + munit_errorf("assertion failed: string " #a " == " #b " (\"%s\" == \"%s\")", \ + munit_tmp_a_, munit_tmp_b_); \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#define munit_assert_string_not_equal(a, b) \ + do { \ + const char* munit_tmp_a_ = a; \ + const char* munit_tmp_b_ = b; \ + if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) == 0)) { \ + munit_errorf("assertion failed: string " #a " != " #b " (\"%s\" == \"%s\")", \ + munit_tmp_a_, munit_tmp_b_); \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#define munit_assert_memory_equal(size, a, b) \ + do { \ + const unsigned char* munit_tmp_a_ = (const unsigned char*) (a); \ + const unsigned char* munit_tmp_b_ = (const unsigned char*) (b); \ + const size_t munit_tmp_size_ = (size); \ + if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) != 0) { \ + size_t munit_tmp_pos_; \ + for (munit_tmp_pos_ = 0 ; munit_tmp_pos_ < munit_tmp_size_ ; munit_tmp_pos_++) { \ + if (munit_tmp_a_[munit_tmp_pos_] != munit_tmp_b_[munit_tmp_pos_]) { \ + munit_errorf("assertion failed: memory " #a " == " #b ", at offset %" MUNIT_SIZE_MODIFIER "u", munit_tmp_pos_); \ + break; \ + } \ + } \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#define munit_assert_memory_not_equal(size, a, b) \ + do { \ + const unsigned char* munit_tmp_a_ = (const unsigned char*) (a); \ + const unsigned char* munit_tmp_b_ = (const unsigned char*) (b); \ + const size_t munit_tmp_size_ = (size); \ + if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) == 0) { \ + munit_errorf("assertion failed: memory" #a " != " #b " (%zu bytes)", munit_tmp_size_); \ + } \ + MUNIT__PUSH_DISABLE_MSVC_C4127 \ + } while (0) \ + MUNIT__POP_DISABLE_MSVC_C4127 + +#define munit_assert_ptr_equal(a, b) \ + munit_assert_ptr(a, ==, b) +#define munit_assert_ptr_not_equal(a, b) \ + munit_assert_ptr(a, !=, b) +#define munit_assert_null(ptr) \ + munit_assert_ptr(ptr, ==, NULL) +#define munit_assert_not_null(ptr) \ + munit_assert_ptr(ptr, !=, NULL) +#define munit_assert_ptr_null(ptr) \ + munit_assert_ptr(ptr, ==, NULL) +#define munit_assert_ptr_not_null(ptr) \ + munit_assert_ptr(ptr, !=, NULL) + +/*** Memory allocation ***/ + +void* munit_malloc_ex(const char* filename, int line, size_t size); + +#define munit_malloc(size) \ + munit_malloc_ex(__FILE__, __LINE__, (size)) + +#define munit_new(type) \ + ((type*) munit_malloc(sizeof(type))) + +#define munit_calloc(nmemb, size) \ + munit_malloc((nmemb) * (size)) + +#define munit_newa(type, nmemb) \ + ((type*) munit_calloc((nmemb), sizeof(type))) + +/*** Random number generation ***/ + +void munit_rand_seed(munit_uint32_t seed); +munit_uint32_t munit_rand_uint32(void); +int munit_rand_int_range(int min, int max); +double munit_rand_double(void); +void munit_rand_memory(size_t size, munit_uint8_t buffer[MUNIT_ARRAY_PARAM(size)]); + +/*** Tests and Suites ***/ + +typedef enum { + /* Test successful */ + MUNIT_OK, + /* Test failed */ + MUNIT_FAIL, + /* Test was skipped */ + MUNIT_SKIP, + /* Test failed due to circumstances not intended to be tested + * (things like network errors, invalid parameter value, failure to + * allocate memory in the test harness, etc.). */ + MUNIT_ERROR +} MunitResult; + +typedef struct { + char* name; + char** values; +} MunitParameterEnum; + +typedef struct { + char* name; + char* value; +} MunitParameter; + +const char* munit_parameters_get(const MunitParameter params[], const char* key); + +typedef enum { + MUNIT_TEST_OPTION_NONE = 0, + MUNIT_TEST_OPTION_SINGLE_ITERATION = 1 << 0, + MUNIT_TEST_OPTION_TODO = 1 << 1 +} MunitTestOptions; + +typedef MunitResult (* MunitTestFunc)(const MunitParameter params[], void* user_data_or_fixture); +typedef void* (* MunitTestSetup)(const MunitParameter params[], void* user_data); +typedef void (* MunitTestTearDown)(void* fixture); + +typedef struct { + char* name; + MunitTestFunc test; + MunitTestSetup setup; + MunitTestTearDown tear_down; + MunitTestOptions options; + MunitParameterEnum* parameters; +} MunitTest; + +typedef enum { + MUNIT_SUITE_OPTION_NONE = 0 +} MunitSuiteOptions; + +typedef struct MunitSuite_ MunitSuite; + +struct MunitSuite_ { + char* prefix; + MunitTest* tests; + MunitSuite* suites; + unsigned int iterations; + MunitSuiteOptions options; +}; + +int munit_suite_main(const MunitSuite* suite, void* user_data, int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)]); + +/* Note: I'm not very happy with this API; it's likely to change if I + * figure out something better. Suggestions welcome. */ + +typedef struct MunitArgument_ MunitArgument; + +struct MunitArgument_ { + char* name; + bool (* parse_argument)(const MunitSuite* suite, void* user_data, int* arg, int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)]); + void (* write_help)(const MunitArgument* argument, void* user_data); +}; + +int munit_suite_main_custom(const MunitSuite* suite, + void* user_data, + int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)], + const MunitArgument arguments[]); + +#if defined(MUNIT_ENABLE_ASSERT_ALIASES) + +#define assert_true(expr) munit_assert_true(expr) +#define assert_false(expr) munit_assert_false(expr) +#define assert_char(a, op, b) munit_assert_char(a, op, b) +#define assert_uchar(a, op, b) munit_assert_uchar(a, op, b) +#define assert_short(a, op, b) munit_assert_short(a, op, b) +#define assert_ushort(a, op, b) munit_assert_ushort(a, op, b) +#define assert_int(a, op, b) munit_assert_int(a, op, b) +#define assert_uint(a, op, b) munit_assert_uint(a, op, b) +#define assert_long(a, op, b) munit_assert_long(a, op, b) +#define assert_ulong(a, op, b) munit_assert_ulong(a, op, b) +#define assert_llong(a, op, b) munit_assert_llong(a, op, b) +#define assert_ullong(a, op, b) munit_assert_ullong(a, op, b) +#define assert_size(a, op, b) munit_assert_size(a, op, b) +#define assert_float(a, op, b) munit_assert_float(a, op, b) +#define assert_double(a, op, b) munit_assert_double(a, op, b) +#define assert_ptr(a, op, b) munit_assert_ptr(a, op, b) + +#define assert_int8(a, op, b) munit_assert_int8(a, op, b) +#define assert_uint8(a, op, b) munit_assert_uint8(a, op, b) +#define assert_int16(a, op, b) munit_assert_int16(a, op, b) +#define assert_uint16(a, op, b) munit_assert_uint16(a, op, b) +#define assert_int32(a, op, b) munit_assert_int32(a, op, b) +#define assert_uint32(a, op, b) munit_assert_uint32(a, op, b) +#define assert_int64(a, op, b) munit_assert_int64(a, op, b) +#define assert_uint64(a, op, b) munit_assert_uint64(a, op, b) + +#define assert_double_equal(a, b, precision) munit_assert_double_equal(a, b, precision) +#define assert_string_equal(a, b) munit_assert_string_equal(a, b) +#define assert_string_not_equal(a, b) munit_assert_string_not_equal(a, b) +#define assert_memory_equal(size, a, b) munit_assert_memory_equal(size, a, b) +#define assert_memory_not_equal(size, a, b) munit_assert_memory_not_equal(size, a, b) +#define assert_ptr_equal(a, b) munit_assert_ptr_equal(a, b) +#define assert_ptr_not_equal(a, b) munit_assert_ptr_not_equal(a, b) +#define assert_ptr_null(ptr) munit_assert_null_equal(ptr) +#define assert_ptr_not_null(ptr) munit_assert_not_null(ptr) + +#define assert_null(ptr) munit_assert_null(ptr) +#define assert_not_null(ptr) munit_assert_not_null(ptr) + +#endif /* defined(MUNIT_ENABLE_ASSERT_ALIASES) */ + +#if defined(__cplusplus) +} +#endif + +#endif /* !defined(MUNIT_H) */ + +#if defined(MUNIT_ENABLE_ASSERT_ALIASES) +# if defined(assert) +# undef assert +# endif +# define assert(expr) munit_assert(expr) +#endif diff --git a/test/test.c b/test/test.c deleted file mode 100644 index 9f5008c..0000000 --- a/test/test.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "test.h" - -void dummy_error(const PDS_error_description *desc, void *unused) -{ - (void)unused; - fprintf(stderr, "line %d:%d: %s\n", desc->number, desc->position, desc->msg); -} diff --git a/test/test.h b/test/test.h deleted file mode 100644 index 25ce6f9..0000000 --- a/test/test.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef TEST_H -#define TEST_H - -#include -#include -#include - -#include - -#define check(e) if(!(e)) { fprintf(stderr, "%s+%d: test failed \"%s\"\n", __FILE__, __LINE__, #e); return EXIT_FAILURE; } - -#define begin_test_data(expected_t) \ -struct \ -{ \ - const char *str; \ - off_t end; \ - expected_t expected; \ - int status; \ -} tv[] = { - -#define test_data(s, e, x, a) { .str=s, .end=e, .expected=x, .status=a } - -#define test_pair(a,b) { a, b } - -#define end_test_data() }; - -#define test_str(i) (tv[(i)].str) -#define test_end(i) (tv[(i)].str+tv[(i)].end) -#define test_status(i) (tv[(i)].status) -#define test_expected(i) (tv[(i)].expected) -#define test_foreach(i) for(i=0; i<(sizeof(tv)/sizeof(tv[0])); ++i) - -void dummy_error(const PDS_error_description* desc, void *user_data); - -#endif /* TEST_H */ diff --git a/test/utils.c b/test/utils.c index 5d81f67..6da9061 100644 --- a/test/utils.c +++ b/test/utils.c @@ -2,29 +2,29 @@ #include #include -void print_string(const char *first, const char *last) -{ - for(; first<=last; first++) - { +void print_tab(int depth) { + while(depth--) { + printf(" "); + } +} + +void print_string(const char *first, const char *last) { + for(; first<=last; first++) { fputc(*first, stdout); } } -void print_scalar(const PDS_scalar *scalar) -{ - switch(scalar->type) - { +void print_scalar(const PDS_scalar *scalar) { + switch(scalar->type) { case PDS_INTEGER_VALUE: printf("%ld ", scalar->integer.value); - if(scalar->integer.unit.first && scalar->integer.unit.last) - { + if(scalar->integer.unit.first && scalar->integer.unit.last) { print_string(scalar->integer.unit.first, scalar->integer.unit.last); } break; case PDS_REAL_VALUE: printf("%f ", scalar->real.value); - if(scalar->real.unit.first && scalar->real.unit.last) - { + if(scalar->real.unit.first && scalar->real.unit.last) { print_string(scalar->real.unit.first, scalar->real.unit.last); } break; @@ -32,12 +32,10 @@ void print_scalar(const PDS_scalar *scalar) printf("%04d-%02d-%02d ", scalar->date_time.year, scalar->date_time.month, scalar->date_time.day); printf("%02d:%02d:%02d:%02d ", scalar->date_time.hour, scalar->date_time.minute, scalar->date_time.second, scalar->date_time.microsecond); - if(PDS_ZONED_TIME == scalar->date_time.time_type) - { + if(PDS_ZONED_TIME == scalar->date_time.time_type) { printf("%02d:%02d ", scalar->date_time.hour_offset, scalar->date_time.minute_offset); } - else if(PDS_UTC_TIME == scalar->date_time.time_type) - { + else if(PDS_UTC_TIME == scalar->date_time.time_type) { printf("UTC"); } break; @@ -57,279 +55,3 @@ void print_scalar(const PDS_scalar *scalar) } printf("\n"); } - -int compare_scalar(const PDS_scalar *s0, const PDS_scalar *s1) -{ - if(s0->type != s1->type) - { - return 0; - } - switch(s0->type) - { - case PDS_INTEGER_VALUE: - if(s0->integer.value == s1->integer.value) - { - if( (s0->integer.unit.first && s1->integer.unit.first) - && (s0->integer.unit.last && s1->integer.unit.last ) ) - { - return PDS_string_compare(s0->integer.unit.first, s0->integer.unit.last, - s1->integer.unit.first, s1->integer.unit.last); - } - else if( (0 == s0->integer.unit.first) && (0 == s1->integer.unit.first) - && (0 == s1->integer.unit.first) && (0 == s1->integer.unit.last) ) - { - return 1; - } - } - return 0; - case PDS_REAL_VALUE: - if(s0->real.value == s1->real.value) - { - if( (s0->real.unit.first && s1->real.unit.first) - && (s0->real.unit.last && s1->real.unit.last ) ) - { - return PDS_string_compare(s0->real.unit.first, s0->real.unit.last, - s1->real.unit.first, s1->real.unit.last); - } - else if( (0 == s0->real.unit.first) && (0 == s1->real.unit.first) - && (0 == s1->real.unit.first) && (0 == s1->real.unit.last) ) - { - return 1; - } - } - return 0; - case PDS_DATE_TIME_VALUE: - return ( (s0->date_time.time_type == s1->date_time.time_type) - && (s0->date_time.year == s1->date_time.year) - && (s0->date_time.day == s1->date_time.day) - && (s0->date_time.month == s1->date_time.month) - && (s0->date_time.hour == s1->date_time.hour) - && (s0->date_time.minute == s1->date_time.minute) - && (s0->date_time.second == s1->date_time.second) - && (s0->date_time.microsecond == s1->date_time.microsecond) - && (s0->date_time.hour_offset == s1->date_time.hour_offset) - && (s0->date_time.minute_offset == s1->date_time.minute_offset) ); - case PDS_SYMBOLIC_VALUE: - return PDS_string_compare(s0->symbolic.first, s0->symbolic.last, s1->symbolic.first, s1->symbolic.last); - case PDS_TEXT_STRING_VALUE: - return PDS_string_compare(s0->text.first, s0->text.last, s1->text.first, s1->text.last); - case PDS_IDENTIFIER_VALUE: - return PDS_string_compare(s0->identifier.first, s0->identifier.last, s1->identifier.first, s1->identifier.last); - default: - return 0; - } -} - -int attribute_begin_callback(const char *first, const char *last, void *user_data) -{ - state_t *state = (state_t*)user_data; - const expected_t *expected = state->expected; - if(!PDS_string_compare(first, last, expected->name, expected->name+strlen(expected->name)-1)) - { - fprintf(stderr, "attribute name mismatch\n"); - return 0; - } - printf("%s = ", expected->name); - return 1; -} - -int attribute_end_callback(const char *first, const char *last, void *user_data) -{ - (void)first; - (void)last; - (void)user_data; - printf("\n"); - return 1; -} - -int pointer_begin_callback(const char *first, const char *last, void *user_data) -{ - state_t *state = (state_t*)user_data; - const expected_t *expected = state->expected; - if(!PDS_string_compare(first, last, expected->name, expected->name+strlen(expected->name)-1)) - { - fprintf(stderr, "pointer name mismatch\n"); - return 0; - } - printf("%s = ", expected->name); - return 1; -} - -int pointer_end_callback(const char *first, const char *last, void *user_data) -{ - (void)first; - (void)last; - (void)user_data; - printf("\n"); - return 1; -} - -int scalar_callback(const PDS_scalar *scalar, void *user_data) -{ - state_t *state = (state_t*)user_data; - const expected_t *expected = state->expected; - print_scalar(scalar); - if(!compare_scalar(scalar, expected->scalar)) - { - fprintf(stderr, "scalar value mismatch\n"); - return 0; - } - return 1; -} - -int set_begin_callback(void *user_data) -{ - (void)user_data; - printf(" = {\n"); - return 1; -} - -int set_element_callback(const PDS_scalar *scalar, void *user_data) -{ - state_t *state = (state_t*)user_data; - const expected_t *expected = state->expected; - if(state->index >= expected->count) - { - fprintf(stderr, "too many scalars\n"); - return 0; - } - if(!compare_scalar(scalar, &(expected->scalar[state->index]))) - { - fprintf(stderr, "scalar value mismatch\n"); - return 0; - } - state->index++; - - printf("\t"); - print_scalar(scalar); - - return 1; -} - -int set_end_callback(void *user_data) -{ - (void)user_data; - printf("}\n"); - return 1; -} - -int sequence_begin_callback(void *user_data) -{ - state_t *state = (state_t*)user_data; - const expected_t *expected = state->expected; - state->depth++; - if(state->depth > expected->depth) - { - fprintf(stderr, "invalid sequence dimension\n"); - return 0; - } - for(int i=1; idepth; i++) - { - printf("\t"); - } - printf(" = (\n"); - return 1; -} - -int sequence_element_callback(const PDS_scalar *scalar, void *user_data) -{ - state_t *state = (state_t*)user_data; - const expected_t *expected = state->expected; - if(state->index >= expected->count) - { - fprintf(stderr, "too many scalars\n"); - return 0; - } - if(!compare_scalar(scalar, &(expected->scalar[state->index]))) - { - fprintf(stderr, "scalar value mismatch\n"); - return 0; - } - state->index++; - for(int i=0; idepth; i++) - { - printf("\t"); - } - print_scalar(scalar); - return 1; -} - -int sequence_end_callback(void *user_data) -{ - state_t *state = (state_t*)user_data; - state->depth--; - if(state->depth < 0) - { - fprintf(stderr, "invalid sequence dimension\n"); - return 0; - } - for(int i=0; idepth; i++) - { - printf("\t"); - } - printf(")\n"); - return 1; -} - -static int declaration_check_name(const char *what, const char *first, const char *last, void *user_data) -{ - state_t *state = (state_t*)user_data; - const expected_t *expected = state->expected; - const PDS_scalar *scalar; - int i = state->index++; - if(i >= expected->count) - { - fprintf(stderr, "too many elements\n"); - return 0; - } - scalar = &expected->scalar[i]; - if(!PDS_string_compare(scalar->identifier.first, scalar->identifier.last, first, last)) - { - fprintf(stderr, "%s name mismatch\n", what); - return 0; - } - return 1; -} - -static int declaration_begin(const char *what, const char *first, const char *last, void *user_data) -{ - if(!declaration_check_name(what, first, last, user_data)) - { - return 0; - } - printf("%s ", what); - print_string(first, last); - printf(" = [\n"); - return 1; -} - -static int declaration_end(const char *what, const char *first, const char *last, void *user_data) -{ - if(!declaration_check_name(what, first, last, user_data)) - { - return 0; - } - printf("]\n"); - return 1; -} - -int group_begin_callback(const char *first, const char *last, void *user_data) -{ - return declaration_begin("group", first, last, user_data); -} - -int group_end_callback(const char *first, const char *last, void *user_data) -{ - return declaration_end("group", first, last, user_data); -} - -int object_begin_callback(const char *first, const char *last, void *user_data) -{ - return declaration_begin("object", first, last, user_data); -} - -int object_end_callback(const char *first, const char *last, void *user_data) -{ - return declaration_end("object", first, last, user_data); -} - diff --git a/test/utils.h b/test/utils.h index 093f67a..cb65ba8 100644 --- a/test/utils.h +++ b/test/utils.h @@ -3,46 +3,10 @@ #include -typedef struct -{ - int depth; - int count; - const char *name; - const PDS_scalar *scalar; -} expected_t; - -typedef struct -{ - int depth; - int index; - const expected_t *expected; -} state_t; - +void print_tab(int depth); void print_string(const char *first, const char *last); void print_scalar(const PDS_scalar *scalar); int compare_scalar(const PDS_scalar *s0, const PDS_scalar *s1); -int attribute_begin_callback(const char *first, const char *last, void *user_data); -int attribute_end_callback(const char *first, const char *last, void *user_data); - -int pointer_begin_callback(const char *first, const char *last, void *user_data); -int pointer_end_callback(const char *first, const char *last, void *user_data); - -int group_begin_callback(const char *first, const char *last, void *user_data); -int group_end_callback(const char *first, const char *last, void *user_data); - -int object_begin_callback(const char *first, const char *last, void *user_data); -int object_end_callback(const char *first, const char *last, void *user_data); - -int set_begin_callback(void *user_data); -int set_element_callback(const PDS_scalar *scalar, void *user_data); -int set_end_callback(void *user_data); - -int sequence_begin_callback(void *user_data); -int sequence_element_callback(const PDS_scalar *scalar, void *user_data); -int sequence_end_callback(void *user_data); - -int scalar_callback(const PDS_scalar *scalar, void *user_data); - #endif // UTILS_H diff --git a/tinypds.h b/tinypds.h index e1fc403..3849253 100644 --- a/tinypds.h +++ b/tinypds.h @@ -25,7 +25,8 @@ enum PDS_STATUS { PDS_INVALID_VALUE, PDS_INVALID_RANGE, PDS_INVALID_ARGUMENT, - PDS_INCOMPLETE + PDS_INCOMPLETE, + PDS_FAILURE }; /** * Value types. diff --git a/tinypds_dom.h b/tinypds_dom.h index 2cf4dfc..e30022b 100644 --- a/tinypds_dom.h +++ b/tinypds_dom.h @@ -6,20 +6,19 @@ * #define TINY_PDS_DOM_IMPL * * Licensed under the MIT License - * (c) 2016 Vincent Cruz + * (c) 2016-2018 Vincent Cruz */ #ifndef TINY_PDS_DOM_H #define TINY_PDS_DOM_H -#include "tinypds.h" +#include #ifdef __cplusplus extern "C" { #endif /** PDS item type **/ -typedef enum -{ +typedef enum { /** * PDS attribute. * An attribute can contain a scalar, a set, a one dimensional or @@ -45,8 +44,7 @@ typedef enum } PDS_type; /** PDS scalar type **/ -typedef enum -{ +typedef enum { /** * No scalar is associated to current item. This is generally the * case for objects and groups. @@ -63,8 +61,7 @@ typedef enum } PDS_scalar_type; /** PDS item **/ -typedef struct -{ +typedef struct { /** Name **/ PDS_string name; /** Item type. **/ @@ -223,8 +220,7 @@ void PDS_DOM_delete(PDS_item *pds); /** * Defines the way the PDS tree is traversed. */ -typedef enum -{ +typedef enum { /** Only the siblings of the current item are checked. **/ PDS_ONLY_SIBLINGS = 0, /** Only the children of the current item are checked. @@ -269,13 +265,21 @@ PDS_item* PDS_DOM_find(const char *name, PDS_item *current, PDS_search_type sear #define PDS_DOM_FREE(p) free(p) #endif +#if defined(PDS_DOM_MEMCPY) && defined(PDS_DOM_MEMSET) +// ok +#elif !defined(PDS_DOM_MEMCPY) && !defined(PDS_DOM_MEMSET) +// ok +#else +#error "Must define all or none of PDS_DOM_MEMCPY and PDS_DOM_MEMSET" +#endif + #if !defined(PDS_DOM_MEMCPY) #include #define PDS_DOM_MEMCPY(dst, src, sz) memcpy(dst, src, sz) +#define PDS_DOM_MEMSET(dst, c, sz) memset(dst,c, sz) #endif -typedef struct PDS_item_impl_t -{ +typedef struct PDS_item_impl_t { PDS_item info; PDS_scalar_type scalar_type; @@ -291,8 +295,7 @@ typedef struct PDS_item_impl_t struct PDS_item_impl_t *next; } PDS_item_impl; -typedef struct -{ +typedef struct { PDS_item_impl *root; PDS_item_impl *parent; PDS_item_impl *current; @@ -302,12 +305,10 @@ typedef struct } PDS_DOM_payload; /* Create new PDS item */ -static PDS_item_impl* PDS_DOM_create(PDS_type type, const char *first, const char *last) -{ +static PDS_item_impl* PDS_DOM_create(PDS_type type, const char *first, const char *last) { PDS_item_impl *item; item = PDS_DOM_MALLOC(sizeof(PDS_item_impl)); - if(NULL == item) - { + if(NULL == item) { return NULL; } @@ -329,7 +330,6 @@ static PDS_item_impl* PDS_DOM_create(PDS_type type, const char *first, const cha item->parent = NULL; return item; } - /* Create a new PDS_item and add it to the hierarchy. */ static int PDS_DOM_add(PDS_DOM_payload *payload, PDS_type type, const char *first, const char *last) { @@ -340,23 +340,19 @@ static int PDS_DOM_add(PDS_DOM_payload *payload, PDS_type type, const char *firs } item->parent = payload->parent; - if( (PDS_GROUP == type) || (PDS_OBJECT == type) ) - { + if( (PDS_GROUP == type) || (PDS_OBJECT == type) ) { payload->parent = item; } - if(payload->current) - { + if(payload->current) { payload->current->sibling = item; } payload->current = item; - if(NULL == payload->root) - { + if(NULL == payload->root) { payload->root = item; } - if(payload->previous) - { + if(payload->previous) { payload->previous->next = item; } payload->previous = item; @@ -364,31 +360,26 @@ static int PDS_DOM_add(PDS_DOM_payload *payload, PDS_type type, const char *firs return PDS_OK; } /* Scalar callback */ -static int PDS_DOM_parse_scalar(const PDS_scalar *scalar, void *user_data) -{ +static int PDS_DOM_parse_scalar(const PDS_scalar *scalar, void *user_data) { int index; int last; PDS_scalar *tmp; PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; /* Sanity check */ - if(NULL == payload) - { + if(NULL == payload) { return 0; } - if(NULL == payload->current) - { + if(NULL == payload->current) { return 0; } - if((PDS_ATTRIBUTE != payload->current->info.type) && (PDS_POINTER != payload->current->info.type)) - { + if((PDS_ATTRIBUTE != payload->current->info.type) && (PDS_POINTER != payload->current->info.type)) { return 0; } /* Copy scalar */ index = payload->current->count-1; last = payload->current->last[index]; tmp = PDS_DOM_REALLOC(payload->current->scalar, (last+1)*sizeof(PDS_scalar)); - if(NULL == tmp) - { + if(NULL == tmp) { return 0; } payload->current->scalar = tmp; @@ -397,24 +388,19 @@ static int PDS_DOM_parse_scalar(const PDS_scalar *scalar, void *user_data) return 1; } /* Set start callback. */ -static int PDS_DOM_parse_set_begin(void *user_data) -{ +static int PDS_DOM_parse_set_begin(void *user_data) { PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; /* Sanity check */ - if(NULL == payload) - { + if(NULL == payload) { return 0; } - if(NULL == payload->current) - { + if(NULL == payload->current) { return 0; } - if((PDS_ATTRIBUTE != payload->current->info.type) && (PDS_POINTER != payload->current->info.type)) - { + if((PDS_ATTRIBUTE != payload->current->info.type) && (PDS_POINTER != payload->current->info.type)) { return 0; } - if(payload->dimension) - { + if(payload->dimension) { return 0; } payload->current->scalar_type = PDS_SET; @@ -422,136 +408,112 @@ static int PDS_DOM_parse_set_begin(void *user_data) return 1; } /* Set end callback. */ -static int PDS_DOM_parse_set_end(void *user_data) -{ +static int PDS_DOM_parse_set_end(void *user_data) { PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; /* Sanity check */ - if(NULL == payload) - { + if(NULL == payload) { return 0; } - if(NULL == payload->current) - { + if(NULL == payload->current) { return 0; } payload->dimension = 0; return 1; } /* Sequence start callback. */ -static int PDS_DOM_parse_sequence_begin(void *user_data) -{ +static int PDS_DOM_parse_sequence_begin(void *user_data) { PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; /* Sanity check */ - if(NULL == payload) - { + if(NULL == payload) { return 0; } - if(NULL == payload->current) - { + if(NULL == payload->current) { return 0; } - if((PDS_ATTRIBUTE != payload->current->info.type) && (PDS_POINTER != payload->current->info.type)) - { + if((PDS_ATTRIBUTE != payload->current->info.type) && (PDS_POINTER != payload->current->info.type)) { return 0; } /* Check that the sequence is either 1D or 2D */ - if(payload->dimension >= 2) - { + if(payload->dimension >= 2) { return 0; } - if(payload->dimension == 1) - { + if(payload->dimension == 1) { int *tmp; int index = payload->current->count; tmp = PDS_DOM_REALLOC(payload->current->last, (index+1)*sizeof(int)); - if(NULL == tmp) - { + if(NULL == tmp) { return 0; } payload->current->last[index] = index ? payload->current->last[index-1] : 0; payload->current->count++; payload->current->scalar_type = PDS_SEQUENCE_2D; } - else if(PDS_SINGLE == payload->current->scalar_type) - { + else if(PDS_SINGLE == payload->current->scalar_type) { payload->current->scalar_type = PDS_SEQUENCE_1D; } payload->dimension++; return 1; } /* Sequence end callback. */ -static int PDS_DOM_parse_sequence_end(void *user_data) -{ +static int PDS_DOM_parse_sequence_end(void *user_data) { PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; /* Sanity check */ - if(NULL == payload) - { + if(NULL == payload) { return 0; } - if(NULL == payload->current) - { + if(NULL == payload->current) { return 0; } payload->dimension--; return 1; } /* Attribute callback */ -static int PDS_DOM_parse_attribute_begin(const char *first, const char *last, void *user_data) -{ +static int PDS_DOM_parse_attribute_begin(const char *first, const char *last, void *user_data) { int ret; PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; - if(NULL == payload) - { + if(NULL == payload) { return 0; } ret = PDS_DOM_add(payload, PDS_ATTRIBUTE, first, last); - if(PDS_OK != ret) - { + if(PDS_OK != ret) { return 0; } payload->current->count = 1; payload->current->last = PDS_DOM_MALLOC(sizeof(int)); - if(NULL == payload->current->last) - { + if(NULL == payload->current->last) { return 0; } payload->current->last[0] = 0; payload->current->scalar_type = PDS_SINGLE; return 1; } -static int PDS_DOM_parse_attribute_end(const char *first, const char *last, void *user_data) -{ +static int PDS_DOM_parse_attribute_end(const char *first, const char *last, void *user_data) { (void)first; (void)last; (void)user_data; return 1; } /* Pointer callback */ -static int PDS_DOM_parse_pointer_begin(const char *first, const char *last, void *user_data) -{ +static int PDS_DOM_parse_pointer_begin(const char *first, const char *last, void *user_data) { int ret; PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; - if(NULL == payload) - { + if(NULL == payload) { return 0; } ret = PDS_DOM_add(payload, PDS_POINTER, first, last); - if(PDS_OK != ret) - { + if(PDS_OK != ret) { return 0; } payload->current->count = 1; payload->current->last = PDS_DOM_MALLOC(sizeof(int)); - if(NULL == payload->current->last) - { + if(NULL == payload->current->last) { return 0; } payload->current->last[0] = 0; payload->current->scalar_type = PDS_SINGLE; return 1; } -static int PDS_DOM_parse_pointer_end(const char *first, const char *last, void *user_data) -{ +static int PDS_DOM_parse_pointer_end(const char *first, const char *last, void *user_data) { (void)user_data; (void)first; (void)last; @@ -559,31 +521,26 @@ static int PDS_DOM_parse_pointer_end(const char *first, const char *last, void * } /* Group start callback. */ -static int PDS_DOM_parse_group_begin(const char *first, const char *last, void *user_data) -{ +static int PDS_DOM_parse_group_begin(const char *first, const char *last, void *user_data) { int ret; PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; - if(NULL == payload) - { + if(NULL == payload) { return 0; } ret = PDS_DOM_add(payload, PDS_GROUP, first, last); return (PDS_OK == ret); } /* Group end callback. */ -static int PDS_DOM_parse_group_end(const char *first, const char *last, void *user_data) -{ +static int PDS_DOM_parse_group_end(const char *first, const char *last, void *user_data) { PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; - if(NULL == payload) - { + if(NULL == payload) { return 0; } payload->current->sibling = NULL; payload->current = payload->parent; - if(payload->parent) - { + if(payload->parent) { payload->parent->child = payload->parent->sibling; payload->parent->sibling = NULL; payload->parent = payload->parent->parent; @@ -608,19 +565,16 @@ static int PDS_DOM_parse_object_begin(const char *first, const char *last, void return (PDS_OK == ret); } /* Object end callback. */ -static int PDS_DOM_parse_object_end(const char *first, const char *last, void *user_data) -{ +static int PDS_DOM_parse_object_end(const char *first, const char *last, void *user_data) { PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; - if(NULL == payload) - { + if(NULL == payload) { return 0; } payload->current->sibling = NULL; payload->current = payload->parent; - if(payload->parent) - { + if(payload->parent) { payload->parent->child = payload->parent->sibling; payload->parent->sibling = NULL; payload->parent = payload->parent->parent; @@ -632,11 +586,9 @@ static int PDS_DOM_parse_object_end(const char *first, const char *last, void *u return 1; } /* Error callback. */ -static void PDS_DOM_error(const PDS_error_description* desc, void *user_data) -{ +static void PDS_DOM_error(const PDS_error_description* desc, void *user_data) { PDS_DOM_payload *payload = (PDS_DOM_payload*)user_data; - if(NULL == payload) - { + if(NULL == payload) { return; } payload->error = *desc; @@ -651,12 +603,10 @@ int PDS_DOM_parse(const char *buffer, size_t len, PDS_item **item, PDS_error_des int ret; /* Sanity check */ - if( NULL == error ) - { + if( NULL == error ) { return 0; } - if( (NULL == buffer) || (0 == len) || (NULL == item) ) - { + if( (NULL == buffer) || (0 == len) || (NULL == item) ) { error->line = 0; error->msg = "invalid parameters"; return 0; @@ -678,8 +628,10 @@ int PDS_DOM_parse(const char *buffer, size_t len, PDS_item **item, PDS_error_des PDS_set_scalar_callback(&callbacks, PDS_DOM_parse_scalar); PDS_set_error_callback(&callbacks, PDS_DOM_error); - payload.root = payload.parent = NULL; - payload.current = payload.previous = NULL; + payload.root = NULL; + payload.parent = PDS_DOM_create(PDS_OBJECT, NULL, NULL); + payload.current = NULL; + payload.previous = payload.parent; payload.dimension = 0; @@ -694,8 +646,7 @@ int PDS_DOM_parse(const char *buffer, size_t len, PDS_item **item, PDS_error_des /* Parse buffer */ ret = PDS_parse(&callbacks, buffer, len, &payload); *error = payload.error; - if(!ret) - { + if(!ret) { PDS_DOM_delete((PDS_item*)payload.root); return ret; } @@ -705,43 +656,36 @@ int PDS_DOM_parse(const char *buffer, size_t len, PDS_item **item, PDS_error_des } /* Release object resources. */ -void PDS_DOM_delete(PDS_item *pds) -{ +void PDS_DOM_delete(PDS_item *pds) { PDS_item_impl *it; PDS_item_impl *end; - if(NULL == pds) - { + if(NULL == pds) { return; } it = (PDS_item_impl*)pds; - if(it->parent) - { + if(it->parent) { end = it->parent->sibling; it->parent->child = NULL; it->parent->next = end; } - else - { + else { end = NULL; } - while(it != end) - { + while(it != end) { PDS_item_impl *obj = it; - it = it->next; - - if(obj->scalar) - { + it = it->next; + if(obj->scalar) { PDS_DOM_FREE(obj->scalar); } - if(obj->last) - { + if(obj->last) { PDS_DOM_FREE(obj->last); } PDS_DOM_FREE(obj); } } + /* Get the type of the scalar attribute of current item. */ PDS_scalar_type PDS_DOM_scalar_typeof(PDS_item *pds) { @@ -784,71 +728,58 @@ static int PDS_DOM_scalar_get_internal(PDS_item *pds, PDS_scalar *scalar, PDS_sc return 1; } /* Gets the scalar associated to current item. */ -int PDS_DOM_scalar_get(PDS_item *pds, PDS_scalar *scalar) -{ +int PDS_DOM_scalar_get(PDS_item *pds, PDS_scalar *scalar) { return PDS_DOM_scalar_get_internal(pds, scalar, PDS_SINGLE, 0, 0); } /* Gets the scalar at specified position in the set associated to the * current item, with bounds checking. */ -int PDS_DOM_set_get(PDS_item *pds, PDS_scalar *scalar, int i) -{ +int PDS_DOM_set_get(PDS_item *pds, PDS_scalar *scalar, int i) { return PDS_DOM_scalar_get_internal(pds, scalar, PDS_SET, 0, i); } /* Gets the scalar at specified position in the 1 dimensional sequence * associated to the current item, with bounds checking. */ -int PDS_DOM_sequence1d_get(PDS_item *pds, PDS_scalar *scalar, int i) -{ +int PDS_DOM_sequence1d_get(PDS_item *pds, PDS_scalar *scalar, int i) { return PDS_DOM_scalar_get_internal(pds, scalar, PDS_SEQUENCE_1D, 0, i); } /* Gets the scalar at specified position in the 2 dimensional sequence * associated to the current item, with bounds checking. */ -int PDS_DOM_sequence2d_get(PDS_item *pds, PDS_scalar *scalar, int i, int j) -{ +int PDS_DOM_sequence2d_get(PDS_item *pds, PDS_scalar *scalar, int i, int j) { return PDS_DOM_scalar_get_internal(pds, scalar, PDS_SEQUENCE_2D, i, j); } /* Returns the number of rows. */ -int PDS_DOM_sequence2d_rows(PDS_item *pds) -{ - if(PDS_SEQUENCE_2D != PDS_DOM_scalar_typeof(pds)) - { +int PDS_DOM_sequence2d_rows(PDS_item *pds) { + if(PDS_SEQUENCE_2D != PDS_DOM_scalar_typeof(pds)) { return -1; } return ((PDS_item_impl*)pds)->count; } /* Returns the number of columns for the specified row. */ -int PDS_DOM_sequence2d_cols(PDS_item *pds, int i) -{ +int PDS_DOM_sequence2d_cols(PDS_item *pds, int i) { PDS_item_impl *item = (PDS_item_impl*)pds; - if(PDS_SEQUENCE_2D != PDS_DOM_scalar_typeof(pds)) - { + if(PDS_SEQUENCE_2D != PDS_DOM_scalar_typeof(pds)) { return -1; } - if((i >= item->count) || (0 == item->count)) - { + if((i >= item->count) || (0 == item->count)) { return 0; } return item->last[i] - ((i > 0) ? item->last[i-1] : 0); } /* Get the sibbling of current PDS object. */ -PDS_item* PDS_DOM_sibling(PDS_item* pds) -{ +PDS_item* PDS_DOM_sibling(PDS_item* pds) { PDS_item_impl *current = (PDS_item_impl*)pds; return (NULL != current) ? (PDS_item*)(current->sibling) : NULL; } /* Get the parent of current PDS object. */ -PDS_item* PDS_DOM_parent(PDS_item* pds) -{ +PDS_item* PDS_DOM_parent(PDS_item* pds) { PDS_item_impl *current = (PDS_item_impl*)pds; return (NULL != current) ? (PDS_item*)(current->parent) : NULL; } /* Returns a pointer to the first item of current object. */ -PDS_item* PDS_DOM_object_begin(PDS_item* pds) -{ +PDS_item* PDS_DOM_object_begin(PDS_item* pds) { return (PDS_item*)(PDS_DOM_is_object(pds) ? ((PDS_item_impl*)pds)->child : NULL); } /* Returns a pointer to one past the last item of current object. */ -PDS_item* PDS_DOM_object_end(PDS_item* pds) -{ +PDS_item* PDS_DOM_object_end(PDS_item* pds) { if(PDS_DOM_is_object(pds)) { PDS_item_impl* item = (PDS_item_impl*)pds; @@ -860,47 +791,39 @@ PDS_item* PDS_DOM_object_end(PDS_item* pds) return NULL; } /* Returns a pointer to the first item of current group. */ -PDS_item* PDS_DOM_group_begin(PDS_item* pds) -{ +PDS_item* PDS_DOM_group_begin(PDS_item* pds) { return (PDS_item*)(PDS_DOM_is_group(pds) ? ((PDS_item_impl*)pds)->child : NULL); } /* Returns a pointer to one past the last item of current group. */ -PDS_item* PDS_DOM_group_end(PDS_item* pds) -{ - if(PDS_DOM_is_group(pds)) - { +PDS_item* PDS_DOM_group_end(PDS_item* pds) { + if(PDS_DOM_is_group(pds)) { PDS_item_impl* item = (PDS_item_impl*)pds; - if(NULL != item->child) - { + if(NULL != item->child) { return (PDS_item*)item->sibling; } } return NULL; } /* Finds the first item which name matches. */ -PDS_item* PDS_DOM_find(const char *name, PDS_item *current, PDS_search_type search, int check_current) -{ +PDS_item* PDS_DOM_find(const char *name, PDS_item *current, PDS_search_type search, int check_current) { const char *first; const char *last; PDS_item_impl *begin = (PDS_item_impl*)current; PDS_item_impl *it; PDS_item_impl *end; - + /* sanity check */ - if((NULL == name) || (NULL == current)) - { + if((NULL == name) || (NULL == current)) { return NULL; } first = name; last = name + strlen(name) - 1; - if(check_current && PDS_string_compare(current->name.first, current->name.last, first, last)) - { + if(check_current && PDS_string_compare(current->name.first, current->name.last, first, last)) { return current; } - switch(search) - { + switch(search) { case PDS_ONLY_SIBLINGS: for(it=begin->sibling, end=NULL; (it!=end) && !PDS_string_compare(it->info.name.first, it->info.name.last, first, last); it=it->sibling) {} @@ -922,5 +845,4 @@ PDS_item* PDS_DOM_find(const char *name, PDS_item *current, PDS_search_type sear return (PDS_item*)((it != end) ? it : NULL); } - #endif /* TINY_PDS_DOM_IMPL */