#undef NDEBUG #include #include #include #include #include #include "csv.h" #define FMEMOPEN(f, data) \ f = fmemopen(data, sizeof(data) - 1, "r"); \ assert(f != NULL) #define FMEMCLOSE(f) \ (void) fclose(f); \ f = NULL #define UNUSED(x) (void) (sizeof((x), 0)) void test_empty_object(void) { { csv_t csv = { 0 }; assert(csv_nfields(&csv) == 0); } { csv_t csv; csv_init(&csv); assert(csv_nfields(&csv) == 0); } } void test_line_endings1(void) { FILE *file = NULL; csv_t csv = { 0 }; char data[] = "A,B,C\r\n" "D,E,F\n"; FMEMOPEN(file, data); assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "D") == 0); assert(strcmp(csv_field(&csv, 1), "E") == 0); assert(strcmp(csv_field(&csv, 2), "F") == 0); assert(csv_read(&csv, file) == 0); FMEMCLOSE(file); } void test_line_endings2(void) { FILE *file = NULL; csv_t csv = { 0 }; char data[] = "A,B,C\r\n" "D,E,F"; FMEMOPEN(file, data); assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "D") == 0); assert(strcmp(csv_field(&csv, 1), "E") == 0); assert(strcmp(csv_field(&csv, 2), "F") == 0); assert(csv_read(&csv, file) == 0); FMEMCLOSE(file); } void test_empty_fields(void) { FILE *file = NULL; csv_t csv = { 0 }; char data[] = "\n" "\r" "\r\n" "\"\"\n" "\"\"\r" "\"\"\r\n" ",\n" ",\r" ",\r\n" "\"\",\"\"\n" "\"\",\"\"\r" "\"\",\"\"\r\n" ",,\n" ",,\r" ",,\r\n" "\"\",\"\",\n" "\"\",\"\",\r" "\"\",\"\",\r\n" ",,"; FMEMOPEN(file, data); // Line 1 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "") == 0); // Line 2 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "") == 0); // Line 3 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "") == 0); // Line 4 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "") == 0); // Line 5 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "") == 0); // Line 6 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "") == 0); // Line 7 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); // Line 8 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); // Line 9 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); // Line 10 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); // Line 11 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); // Line 12 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); // Line 13 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); assert(strcmp(csv_field(&csv, 2), "") == 0); // Line 14 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); assert(strcmp(csv_field(&csv, 2), "") == 0); // Line 15 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); assert(strcmp(csv_field(&csv, 2), "") == 0); // Line 16 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); assert(strcmp(csv_field(&csv, 2), "") == 0); // Line 17 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); assert(strcmp(csv_field(&csv, 2), "") == 0); // Line 18 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); assert(strcmp(csv_field(&csv, 2), "") == 0); // Line 19 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "") == 0); assert(strcmp(csv_field(&csv, 1), "") == 0); assert(strcmp(csv_field(&csv, 2), "") == 0); // EOF assert(csv_read(&csv, file) == 0); FMEMCLOSE(file); } void test_simple_fields(void) { FILE *file = NULL; csv_t csv = { 0 }; char data[] = "A\n" "A\r" "A\r\n" "\"A\"\n" "\"A\"\r" "\"A\"\r\n" "A,B\n" "A,B\r" "A,B\r\n" "\"A\",\"B\"\n" "\"A\",\"B\"\r" "\"A\",\"B\"\r\n" "A,B,C\n" "A,B,C\r" "A,B,C\r\n" "\"A\",\"B\",C\n" "\"A\",\"B\",C\r" "\"A\",\"B\",C\r\n" "A,B,C"; FMEMOPEN(file, data); // Line 1 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "A") == 0); // Line 2 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "A") == 0); // Line 3 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "A") == 0); // Line 4 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "A") == 0); // Line 5 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "A") == 0); // Line 6 assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "A") == 0); // Line 7 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); // Line 8 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); // Line 9 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); // Line 10 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); // Line 11 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); // Line 12 assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); // Line 13 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); // Line 14 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); // Line 15 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); // Line 16 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); // Line 17 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); // Line 18 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); // Line 19 assert(csv_read(&csv, file) == 3); assert(csv_nfields(&csv) == 3); assert(strcmp(csv_field(&csv, 0), "A") == 0); assert(strcmp(csv_field(&csv, 1), "B") == 0); assert(strcmp(csv_field(&csv, 2), "C") == 0); // EOF assert(csv_read(&csv, file) == 0); FMEMCLOSE(file); } void test_quoted_fields(void) { FILE *file = NULL; csv_t csv = { 0 }; char data[] = "foo \"baz\" bar,foo \"\"baz\"\" bar,\"foo \"\"baz\"\" bar\",\"foo \"baz\" bar\",\"foo \"\"baz\"\", bar\""; FMEMOPEN(file, data); assert(csv_read(&csv, file) == 5); assert(csv_nfields(&csv) == 5); assert(strcmp(csv_field(&csv, 0), "foo \"baz\" bar") == 0); assert(strcmp(csv_field(&csv, 1), "foo \"\"baz\"\" bar") == 0); assert(strcmp(csv_field(&csv, 2), "foo \"baz\" bar") == 0); assert(strcmp(csv_field(&csv, 3), "foo \"baz\" bar") == 0); assert(strcmp(csv_field(&csv, 4), "foo \"baz\", bar") == 0); assert(csv_read(&csv, file) == 0); FMEMCLOSE(file); } void test_wrong_quoted_field(void) { FILE *file = NULL; csv_t csv = { 0 }; char data[] = "\"foo"; FMEMOPEN(file, data); assert(csv_read(&csv, file) == 1); assert(csv_nfields(&csv) == 1); assert(strcmp(csv_field(&csv, 0), "foo") == 0); assert(csv_read(&csv, file) == 0); FMEMCLOSE(file); } static void cb_error(csv_err_t err, void *cb_arg) { longjmp(*((jmp_buf *) cb_arg), err); } void test_out_of_range_error(void) { jmp_buf env; csv_options_t csv_options = csv_default_options; csv_options.cb_error = cb_error; csv_options.cb_error_arg = &env; csv_t csv; csv_init_opt(&csv, &csv_options); FILE *file = NULL; char data[] = ","; FMEMOPEN(file, data); switch ( setjmp(env) ) { case CSV_ERR_OK: assert(csv_read(&csv, file) == 2); assert(csv_nfields(&csv) == 2); // force an "out of range"-Exception! assert(strcmp(csv_field(&csv, 2), "will fail") == 0); break; case CSV_ERR_OUT_OF_RANGE: csv_cleanup(&csv); break; default: assert(!"this should not be happen!"); } FMEMCLOSE(file); } void test_read_error(void) { jmp_buf env; csv_options_t csv_options = csv_default_options; csv_options.cb_error = cb_error; csv_options.cb_error_arg = &env; csv_t csv; csv_init_opt(&csv, &csv_options); FILE *file = fopen("/dev/null", "r"); assert(file != NULL); (void) fputc('T', file); // Set Error-Flag! switch ( setjmp(env) ) { case CSV_ERR_OK: assert(csv_read(&csv, file) == 0); break; case CSV_ERR_IO_READ: csv_cleanup(&csv); break; default: assert(!"this should not be happen!"); } (void) fclose(file); } void * my_allocate(size_t n, size_t size, void *cb_arg) { UNUSED(cb_arg); void *ptr = calloc(n, size); return ptr; } void * my_allocate_null(size_t n, size_t size, void *cb_arg) { UNUSED(n); UNUSED(size); UNUSED(cb_arg); return NULL; } void * my_reallocate(void *ptr, size_t n, size_t size, void *cb_arg) { UNUSED(ptr); UNUSED(n); UNUSED(size); UNUSED(cb_arg); return NULL; } void my_free(void *ptr, size_t n, size_t size, void *cb_arg) { UNUSED(n); UNUSED(size); UNUSED(cb_arg); free(ptr); } void test_allocation_error1(void) { jmp_buf env; csv_options_t csv_options = csv_default_options; csv_options.cb_error = cb_error; csv_options.cb_error_arg = &env; csv_options.cb_allocate = my_allocate; csv_options.cb_reallocate = my_reallocate; csv_options.cb_free = my_free; csv_t csv; csv_init_opt(&csv, &csv_options); char data[] = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T"; FILE *file = NULL; FMEMOPEN(file, data); assert(file != NULL); switch ( setjmp(env) ) { case CSV_ERR_OK: assert(csv_read(&csv, file) == 20); assert(!"this should not be happen!"); break; case CSV_ERR_OUT_OF_MEMORY: csv_cleanup(&csv); break; default: assert(!"this should not be happen!"); } FMEMCLOSE(file); } void show_version(void) { const char *version = csv_version; printf("libcsv - version: "); for ( ; *version; ++version ) { putchar(*version); } printf(", build date: "); for ( ++version; *version; ++version ) { putchar(*version); } printf(", build time: "); for ( ++version; *version; ++version ) { putchar(*version); } putchar('\n'); } int main(void) { show_version(); test_empty_object(); test_line_endings1(); test_line_endings2(); test_empty_fields(); test_simple_fields(); test_quoted_fields(); test_wrong_quoted_field(); test_out_of_range_error(); test_read_error(); test_allocation_error1(); puts("all tests passed..."); return EXIT_SUCCESS; }