#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) \ fclose(f); \ f = NULL void test_line_endings1(void) { FILE *f; csv_t csv = { 0 }; char data[] = "A,B,C\r\n" "D,E,F\n"; FMEMOPEN(f, data); assert( csv_read(&csv, f) == 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, f) == 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, f) == -1 ); FMEMCLOSE(f); } void test_line_endings2(void) { FILE *f; csv_t csv = { 0 }; char data[] = "A,B,C\r\n" "D,E,F"; FMEMOPEN(f, data); assert( csv_read(&csv, f) == 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, f) == 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, f) == -1 ); FMEMCLOSE(f); } void test_empty_fields(void) { FILE *f; 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(f, data); // Line 1 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "") == 0 ); // Line 2 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "") == 0 ); // Line 3 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "") == 0 ); // Line 4 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "") == 0 ); // Line 5 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "") == 0 ); // Line 6 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "") == 0 ); // Line 7 assert( csv_read(&csv, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == -1 ); FMEMCLOSE(f); } void test_simple_fields(void) { FILE *f; 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(f, data); // Line 1 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "A") == 0 ); // Line 2 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "A") == 0 ); // Line 3 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "A") == 0 ); // Line 4 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "A") == 0 ); // Line 5 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "A") == 0 ); // Line 6 assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ); assert( strcmp(csv_field(&csv, 0), "A") == 0 ); // Line 7 assert( csv_read(&csv, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == 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, f) == -1 ); FMEMCLOSE(f); } void test_quoted_fields(void) { FILE *f; csv_t csv = { 0 }; char data[] = "foo \"baz\" bar,foo \"\"baz\"\" bar,\"foo \"\"baz\"\" bar\",\"foo \"baz\" bar\",\"foo \"\"baz\"\", bar\""; FMEMOPEN(f, data); assert( csv_read(&csv, f) == 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, f) == -1 ); FMEMCLOSE(f); } void test_wrong_quoted_field(void) { FILE *f; csv_t csv = { 0 }; char data[] = "\"foo"; FMEMOPEN(f, data); assert( csv_read(&csv, f) == 1 ); assert( csv_nfields(&csv) == 1 ), assert( strcmp(csv_field(&csv, 0), "foo") == 0 ); assert( csv_read(&csv, f) == -1 ); FMEMCLOSE(f); } 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 *f; char data[] = ","; FMEMOPEN(f, data); switch ( setjmp(env) ) { case CSV_ERR_OK: assert( csv_read(&csv, f) == 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(f); } 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 *f = fopen("/dev/null", "r"); assert(f != NULL); fputc('T', f); // Set Error-Flag! switch ( setjmp(env) ) { case CSV_ERR_OK: assert( csv_read(&csv, f) == -1 ); break; case CSV_ERR_IO_READ: csv_cleanup(&csv); break; default: assert(!"this should not be happen!"); } fclose(f); } void * my_allocate(size_t n, size_t sz, void *cb_arg) { (void) cb_arg; void *ptr = calloc(n, sz); return ptr; } void * my_allocate_null(size_t n, size_t sz, void *cb_arg) { (void) n; (void) sz; (void) cb_arg; return NULL; } void * my_reallocate(void *ptr, size_t n, size_t sz, void *cb_arg) { (void) ptr; (void) n; (void) sz; (void) cb_arg; return NULL; } void my_free(void *ptr, size_t n, size_t sz, void *cb_arg) { (void) n; (void) sz; (void) 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 *f; FMEMOPEN(f, data); assert(f != NULL); switch ( setjmp(env) ) { case CSV_ERR_OK: assert( csv_read(&csv, f) == 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(f); } int main(void) { 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(); return EXIT_SUCCESS; }