| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- #include <stdio.h>
- #include <string.h>
- #include <assert.h>
- #include <stdlib.h>
- #include "./csv.h"
- #define CSV_END 0
- #define CSV_COL 1
- #define CSV_ROW 2
- #define CSV_ERR 3
- #define DO_TEST(name, options) test_parser("test" #name, options, test ## name ## _data, \
- sizeof(test ## name ## _data) - 1, test ## name ## _results, \
- CSV_COMMA, CSV_QUOTE, NULL, NULL)
- #define DO_TEST_CUSTOM(name, options, d, q, s, t) test_parser("custom" #name, options, custom ## name ## _data, \
- sizeof(custom ## name ## _data) - 1, custom ## name ## _results, d, q, s, t)
- struct event {
- int event_type;
- int retval;
- size_t size; /* Number of bytes in this event or return value from csv_fini */
- char *data; /* Data for this event */
- };
- struct event *event_ptr;
- int event_idx;
- size_t row, col;
- void
- fail_parser (char * test_name, char * message)
- {
- fprintf(stderr, "Parser test %s failed on event %d: %s\n", test_name, event_idx, message);
- exit(EXIT_FAILURE);
- }
- void
- fail_writer (char * test_name, char * message)
- {
- fprintf(stderr, "Writer test %s failed: %s\n", test_name, message);
- exit(EXIT_FAILURE);
- }
- void
- cb1 (void *data, size_t len, void *t)
- {
- char * test_name = t;
- /* Make sure we were expecting a column */
- if (event_ptr->event_type != CSV_COL)
- fail_parser(test_name, "didn't expect a column");
- /* Check the actual size against the expected size */
- if (event_ptr->size != len)
- fail_parser(test_name, "actual data length doesn't match expected data length");
- /* Check the actual data against the expected data */
- if ((event_ptr->data == NULL || data == NULL)) {
- if (event_ptr->data != data)
- fail_parser(test_name, "actual data doesn't match expected data");
- }
- else if (memcmp(event_ptr->data, data, len) != 0)
- fail_parser(test_name, "actual data doesn't match expected data");
- event_idx++;
- event_ptr++;
- col++;
- }
- void
- cb2 (int c, void *t)
- {
- char * test_name = t;
- /* Make sure we were expecting an the end of a row */
- if (event_ptr->event_type != CSV_ROW)
- fail_parser(test_name, "didn't expect end of row");
- /* Check that the row ended with the character we expected */
- if (event_ptr->retval != c)
- fail_parser(test_name, "row ended with unexpected character");
- event_idx++;
- event_ptr++;
- col = 1;
- row++;
- }
- void
- test_parser (char *test_name, unsigned char options, void *input, size_t len, struct event expected[],
- char delimiter, char quote, int (*space_func)(unsigned char), int (*term_func)(unsigned char))
- {
- int result = 0;
- size_t retval;
- struct csv_parser p;
- size_t size;
- for (size = 1; size <= len; size++) {
- size_t bytes_processed = 0;
- csv_init(&p, options);
- csv_set_delim(&p, delimiter);
- csv_set_quote(&p, quote);
- csv_set_space_func(&p, space_func);
- csv_set_term_func(&p, term_func);
- row = col = 1;
- event_ptr = &expected[0];
- event_idx = 1;
- do {
- size_t bytes = size < len - bytes_processed ? size : len - bytes_processed;
- retval = csv_parse(&p, input + bytes_processed, bytes, cb1, cb2, test_name);
- if (retval != bytes) {
- if (event_ptr->event_type != CSV_ERR) {
- fail_parser(test_name, "unexpected parse error occurred");
- } else {
- csv_free(&p);
- return;
- }
- }
- bytes_processed += bytes;
- } while (bytes_processed < len);
- result = csv_fini(&p, cb1, cb2, test_name);
- if (result != 0) {
- if (event_ptr->event_type != CSV_ERR) {
- fail_parser(test_name, "unexpected parse error occurred");
- } else {
- csv_free(&p);
- return;
- }
- }
- csv_free(&p);
- if (event_ptr->event_type != CSV_END)
- fail_parser(test_name, "unexpected end of input");
- }
- }
- void
- test_writer (char * test_name, char *input, size_t input_len, char *expected, size_t expected_len)
- {
- size_t actual_len;
- char *temp = malloc(input_len*2 + 2);
- if (!temp) {
- fprintf(stderr, "Failed to allocate memory in test_writer!\n");
- exit(EXIT_FAILURE);
- }
- actual_len = csv_write(temp, input_len*2 + 2, input, input_len);
- if (actual_len != expected_len)
- fail_writer(test_name, "actual length doesn't match expected length");
- if (memcmp(temp, expected, actual_len) != 0)
- fail_writer(test_name, "actual data doesn't match expected data");
- }
- void
- test_writer2 (char * test_name, char *input, size_t input_len, char *expected, size_t expected_len, char quote)
- {
- size_t actual_len;
- char *temp = malloc(input_len*2 + 2);
- if (!temp) {
- fprintf(stderr, "Failed to allocate memory in test_writer!\n");
- exit(EXIT_FAILURE);
- }
- actual_len = csv_write2(temp, input_len*2 + 2, input, input_len, quote);
- if (actual_len != expected_len)
- fail_writer(test_name, "actual length doesn't match expected length");
- if (memcmp(temp, expected, actual_len) != 0)
- fail_writer(test_name, "actual data doesn't match expected data");
- }
- int main (void) {
- /* Parser Tests */
- /* Parser tests presented here consist of two parts:
- 1. the raw data to be parsed as an array of char
- 2. the behavior expected from attempting to parse the data
-
- The latter is encapsulated into an array of struct event, each event
- described the type of event that was expected at that point and details
- of the event. There are four event types:
- 1. CSV_END - signifies the successful end of parsing, no events should
- follow in this test
- 2. CSV_COL - signifies that a column or CSV element is expected to be
- submitted to the appropriate callback function
- 3. CSV_ROW - specifies the end of a row has been reached
- 4. CSV_ERR - signifies that an error is expected at this point, no
- further events should follow in this test
- In addition to the event type, an event is also characterized by a size
- and a data member in the event structure. The meaning of these fields
- depends on the event type.
- The size member for CSV_COL is the size of the expected data column,
- for CSV_ROW is it the size of the terminating character which should
- always be 1. The size for CSV_END should always be zero, for CSV_ERR
- the size is not used.
- The data member represents the actual data after parsing for a CSV_COL,
- the terminating character for CSV_ROW (the first character of the provided
- char array is the one used), and is not used for CSV_END or CSV_ERR, it
- should be NULL in these cases.
- */
- char test01_data[] = " 1,2 , 3 ,4,5\x0d\x0a";
- char test02_data[] = ",,,,,\x0a";
- char test03_data[] = "\",\",\",\",\"\"";
- char test04_data[] = "\"I call our world Flatland,\x0a"
- "not because we call it so,\x0a"
- "but to make its nature clearer\x0a"
- "to you, my happy readers,\x0a"
- "who are privileged to live in Space.\"";
- char test05_data[] = "\"\"\"a,b\"\"\",,\" \"\"\"\" \",\"\"\"\"\" \",\" \"\"\"\"\",\"\"\"\"\"\"";
- char test06_data[] = "\" a, b ,c \", a b c,";
- char test07_data[] = "\" \"\" \" \" \"\" \"";
- char test07b_data[] = "\" \"\" \" \" \"\" \"";
- char test08_data[] = "\" abc\" "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " \", \"123\"";
- char test09_data[] = "";
- char test10_data[] = "a\x0a";
- char test11_data[] = "1,2 ,3,4\x0a";
- char test12_data[] = "\x0a\x0a\x0a\x0a";
- char test12b_data[] = "\x0a\x0a\x0a\x0a";
- char test13_data[] = "\"abc\"";
- char test14_data[] = "1, 2, 3,\x0a\x0d\x0a \"4\", \x0d,";
- char test15_data[] = "1, 2, 3,\x0a\x0d\x0a \"4\", \x0d\"\"";
- char test16_data[] = "\"1\",\"2\",\" 3 ";
- char test16b_data[] = "\"1\",\"2\",\" 3 ";
- char test17_data[] = " a\0b\0c ";
- char test18_data[] = "12345678901234567890123456789012";
- char test19_data[] = " , \"\" ,";
- /* Custom tests */
- char custom01_data[] = "'''a;b''';;' '''' ';''''' ';' ''''';''''''";
- /* |1|2|3|4|5| */
- struct event test01_results[] =
- { {CSV_COL, 0, 1, "1"},
- {CSV_COL, 0, 1, "2"},
- {CSV_COL, 0, 1, "3"},
- {CSV_COL, 0, 1, "4"},
- {CSV_COL, 0, 1, "5"},
- {CSV_ROW, '\x0d', 1, NULL}, {CSV_END, 0, 0, NULL} };
-
- /* ||||||| */
- struct event test02_results[] =
- { {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, '\x0a', 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* |,|,|| */
- struct event test03_results[] =
- { {CSV_COL, 0, 1, ","},
- {CSV_COL, 0, 1, ","},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- struct event test04_results[] =
- { {CSV_COL, 0, 147, "I call our world Flatland,\x0a"
- "not because we call it so,\x0a"
- "but to make its nature clearer\x0a"
- "to you, my happy readers,\x0a"
- "who are privileged to live in Space."},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* |"a,b"|| "" |"" | ""|""| */
- struct event test05_results[] =
- { {CSV_COL, 0, 5, "\"a,b\""},
- {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 4, " \"\" "},
- {CSV_COL, 0, 3, "\"\" "},
- {CSV_COL, 0, 3, " \"\""},
- {CSV_COL, 0, 2, "\"\""},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* | a, b ,c |a b c|| */
- struct event test06_results[] =
- { {CSV_COL, 0, 9, " a, b ,c "},
- {CSV_COL, 0, 6, "a b c"},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* | " " " " | */
- struct event test07_results[] =
- { {CSV_COL, 0, 9, " \" \" \" \" "},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* Will cause error with CSV_STRICT set */
- struct event test07b_results[] =
- { {CSV_ERR, 0, 0, NULL} };
- struct event test08_results[] =
- { {CSV_COL, 0, 463, " abc\" "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "},
- {CSV_COL, 0, 3, "123"},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
-
- /* empty */
- struct event test09_results[] =
- { {CSV_END, 0, 0, NULL} };
- /* |a| */
- struct event test10_results[] =
- { {CSV_COL, 0, 1, "a"},
- {CSV_ROW, '\x0a', 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* |1|2|3|4| */
- struct event test11_results[] =
- { {CSV_COL, 0, 1, "1"},
- {CSV_COL, 0, 1, "2"},
- {CSV_COL, 0, 1, "3"},
- {CSV_COL, 0, 1, "4"},
- {CSV_ROW, '\x0a', 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* empty */
- struct event test12_results[] =
- { {CSV_END, 0, 0, NULL} };
- /* Test CSV_REPALL_NL */
- struct event test12b_results[] =
- { {CSV_ROW, '\x0a', 1, NULL},
- {CSV_ROW, '\x0a', 1, NULL},
- {CSV_ROW, '\x0a', 1, NULL},
- {CSV_ROW, '\x0a', 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* |abc| */
- struct event test13_results[] =
- { {CSV_COL, 0, 3, "abc"},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* |1|2|3||
- |4||
- ||| */
- struct event test14_results[] =
- { {CSV_COL, 0, 1, "1"},
- {CSV_COL, 0, 1, "2"},
- {CSV_COL, 0, 1, "3"},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, '\x0a', 1, NULL},
- {CSV_COL, 0, 1, "4"},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, '\x0d', 1, NULL},
- {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, -1, 0, NULL}, {CSV_END, 0, 0, NULL} };
-
- /* |1|2|3||
- |4||
- || */
- struct event test15_results[] =
- { {CSV_COL, 0, 1, "1"},
- {CSV_COL, 0, 1, "2"},
- {CSV_COL, 0, 1, "3"},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, '\x0a', 1, NULL},
- {CSV_COL, 0, 1, "4"},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, '\x0d', 1, NULL},
- {CSV_COL, 0, 0, ""},
- {CSV_ROW, -1, 0, NULL}, {CSV_END, 0, 0, NULL} };
- /* |1|2| 3 | */
- struct event test16_results[] =
- { {CSV_COL, 0, 1, "1"},
- {CSV_COL, 0, 1, "2"},
- {CSV_COL, 0, 3, " 3 "},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* Will cause an error with CSV_STRICT_FINI set */
- struct event test16b_results[] =
- { {CSV_COL, 0, 1, "1"},
- {CSV_COL, 0, 1, "2"},
- {CSV_ERR, 0, 0, NULL} };
- /* |a\0b\0c| */
- struct event test17_results[] =
- { {CSV_COL, 0, 5, "a\0b\0c"},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* Test CSV_EMPTY_IS_NULL */
- struct event test19_results[] =
- { {CSV_COL, 0, 0, NULL},
- {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 0, NULL},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- /* |'a;b'|| '' |'' | ''|''| */
- struct event custom01_results[] =
- { {CSV_COL, 0, 5, "'a;b'"},
- {CSV_COL, 0, 0, ""},
- {CSV_COL, 0, 4, " '' "},
- {CSV_COL, 0, 3, "'' "},
- {CSV_COL, 0, 3, " ''"},
- {CSV_COL, 0, 2, "''"},
- {CSV_ROW, -1, 1, NULL}, {CSV_END, 0, 0, NULL} };
- DO_TEST(01, 0);
- DO_TEST(01, CSV_STRICT);
- DO_TEST(01, CSV_STRICT | CSV_EMPTY_IS_NULL);
- DO_TEST(02, 0);
- DO_TEST(02, CSV_STRICT);
- DO_TEST(03, 0);
- DO_TEST(03, CSV_STRICT);
- DO_TEST(04, 0);
- DO_TEST(04, CSV_STRICT);
- DO_TEST(05, 0);
- DO_TEST(05, CSV_STRICT);
- DO_TEST(05, CSV_STRICT | CSV_STRICT_FINI);
- DO_TEST(06, 0);
- DO_TEST(06, CSV_STRICT);
- DO_TEST(07, 0);
- DO_TEST(07b, CSV_STRICT);
- DO_TEST(08, 0);
- DO_TEST(09, 0);
- DO_TEST(09, CSV_EMPTY_IS_NULL);
- DO_TEST(10, 0);
- DO_TEST(11, 0);
- DO_TEST(11, CSV_EMPTY_IS_NULL);
- DO_TEST(12, 0);
- DO_TEST(12, CSV_EMPTY_IS_NULL);
- DO_TEST(12b, CSV_REPALL_NL);
- DO_TEST(12b, CSV_REPALL_NL | CSV_EMPTY_IS_NULL);
- DO_TEST(13, 0);
- DO_TEST(14, 0);
- DO_TEST(14, CSV_STRICT);
- DO_TEST(15, 0);
- DO_TEST(15, CSV_STRICT);
- DO_TEST(16, 0);
- DO_TEST(16, CSV_STRICT);
- DO_TEST(16b, CSV_STRICT | CSV_STRICT_FINI);
- DO_TEST(16, 0);
- DO_TEST(16, CSV_STRICT);
- DO_TEST(17, 0);
- DO_TEST(17, CSV_STRICT);
- DO_TEST(17, CSV_STRICT | CSV_EMPTY_IS_NULL);
- DO_TEST(19, CSV_EMPTY_IS_NULL);
- DO_TEST_CUSTOM(01, 0, ';', '\'', NULL, NULL);
- /* Writer Tests */
- /* The writer tests are simpler, the test_writer function is used to
- test the csv_write function and takes five arguments: the name of
- the test, the data to convert, the length of that data, the expected
- resulting CSV data, and the expected length of the result.
- */
- test_writer("1", "abc", 3, "\"abc\"", 5);
- test_writer("2", "\"\"\"\"\"\"\"\"", 8, "\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"", 18);
- test_writer2("1", "abc", 3, "'abc'", 5, '\'');
- test_writer2("2", "''''''''", 8, "''''''''''''''''''", 18, '\'');
- puts("All tests passed");
- return 0;
- }
|