From 67ed214c59ddf9bd95703c79e354631161494a97 Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Mon, 10 May 2021 08:00:27 -0400 Subject: [PATCH] Restructure unit test program to include unit tests and alternate error handling. Don't log all strings after adding one. Don't trim string buffer after reading a token. --- pdfio-string.c | 10 +- pdfio-token.c | 2 + testpdfio.c | 390 ++++++++++++++++++++++++++++++++++--------------- 3 files changed, 279 insertions(+), 123 deletions(-) diff --git a/pdfio-string.c b/pdfio-string.c index a632619..0de2e95 100644 --- a/pdfio-string.c +++ b/pdfio-string.c @@ -75,15 +75,7 @@ pdfioStringCreate( if (pdf->num_strings > 1) qsort(pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings); -#ifdef DEBUG - { - size_t i; // Looping var - - PDFIO_DEBUG("pdfioStringCreate: %lu strings\n", (unsigned long)pdf->num_strings); - for (i = 0; i < pdf->num_strings; i ++) - PDFIO_DEBUG("pdfioStringCreate: strings[%lu]=%p(\"%s\")\n", (unsigned long)i, pdf->strings[i], pdf->strings[i]); - } -#endif // DEBUG + PDFIO_DEBUG("pdfioStringCreate: %lu strings\n", (unsigned long)pdf->num_strings); return (news); } diff --git a/pdfio-token.c b/pdfio-token.c index daa53d9..4a2e6e5 100644 --- a/pdfio-token.c +++ b/pdfio-token.c @@ -453,6 +453,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack while (tb->bufptr < tb->bufend && isspace(*(tb->bufptr))) tb->bufptr ++; +#if 0 if (tb->bufptr > tb->buffer) { size_t remaining = (size_t)(tb->bufend - tb->bufptr); @@ -474,6 +475,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack tb->bufptr = tb->bufend = tb->buffer; } } +#endif // 0 *bufptr = '\0'; diff --git a/testpdfio.c b/testpdfio.c index 371f7f8..1ae5978 100644 --- a/testpdfio.c +++ b/testpdfio.c @@ -14,6 +14,17 @@ #include "pdfio-private.h" +// +// Local functions... +// + +static int do_test_file(const char *filename); +static int do_unit_tests(void); +static bool error_cb(bool *error, const char *message); +static ssize_t token_consume_cb(const char **s, size_t bytes); +static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes); + + // // 'main()' - Main entry for test program. // @@ -24,125 +35,276 @@ main(int argc, // I - Number of command-line arguments { if (argc > 1) { - int i; // Looping var - pdfio_file_t *pdf; // PDF file - size_t n, // Object/page index - num_objs, // Number of objects - num_pages; // Number of pages - pdfio_obj_t *obj; // Object - pdfio_dict_t *dict; // Object dictionary - const char *type; // Object type + int i; // Looping var for (i = 1; i < argc; i ++) { - if ((pdf = pdfioFileOpen(argv[i], NULL, NULL)) != NULL) - { - const char *filename; // Base filename for messages - - if ((filename = strrchr(argv[i], '/')) != NULL) - filename ++; - else - filename = argv[i]; - - num_objs = pdfioFileGetNumObjects(pdf); - num_pages = pdfioFileGetNumPages(pdf); - - printf("%s: PDF %s, %d pages, %d objects.\n", filename, pdfioFileGetVersion(pdf), (int)num_pages, (int)num_objs); - - for (n = 0; n < num_pages; n ++) - { - if ((obj = pdfioFileGetPage(pdf, n)) == NULL) - { - printf("%s: Unable to get page #%d.\n", filename, (int)n + 1); - } - else - { - pdfio_rect_t media_box; // MediaBox value - - memset(&media_box, 0, sizeof(media_box)); - dict = pdfioObjGetDict(obj); - - if (!pdfioDictGetRect(dict, "MediaBox", &media_box)) - { - if ((obj = pdfioDictGetObject(dict, "Parent")) != NULL) - { - dict = pdfioObjGetDict(obj); - pdfioDictGetRect(dict, "MediaBox", &media_box); - } - } - - printf("%s: Page #%d is %gx%g.\n", filename, (int)n + 1, media_box.x2, media_box.y2); - } - } - - for (n = 0; n < num_objs; n ++) - { - if ((obj = pdfioFileGetObject(pdf, n)) == NULL) - { - printf("%s: Unable to get object #%d.\n", filename, (int)n); - } - else - { - size_t np; // Number of pairs - _pdfio_pair_t *pair; // Current pair - - dict = pdfioObjGetDict(obj); - - printf("%s: %u %u obj dict=%p(%lu)\n", filename, (unsigned)pdfioObjGetNumber(obj), (unsigned)pdfioObjGetGeneration(obj), dict, dict ? (unsigned long)dict->num_pairs : 0UL); - if (dict) - { - for (np = dict->num_pairs, pair = dict->pairs; np > 0; np --, pair ++) - { - switch (pair->value.type) - { - case PDFIO_VALTYPE_INDIRECT : - printf(" /%s %u %u R\n", pair->key, (unsigned)pair->value.value.indirect.number, pair->value.value.indirect.generation); - break; - case PDFIO_VALTYPE_NAME : - printf(" /%s /%s\n", pair->key, pair->value.value.name); - break; - case PDFIO_VALTYPE_STRING : - printf(" /%s (%s)\n", pair->key, pair->value.value.string); - break; - case PDFIO_VALTYPE_BINARY : - { - size_t bn; - unsigned char *bptr; - - printf(" /%s <", pair->key); - for (bn = pair->value.value.binary.datalen, bptr = pair->value.value.binary.data; bn > 0; bn --, bptr ++) - printf("%02X", *bptr); - puts(">"); - } - break; - case PDFIO_VALTYPE_NUMBER : - printf(" /%s %g\n", pair->key, pair->value.value.number); - break; - case PDFIO_VALTYPE_BOOLEAN : - printf(" /%s %s\n", pair->key, pair->value.value.boolean ? "true" : "false"); - break; - case PDFIO_VALTYPE_NULL : - printf(" /%s null\n", pair->key); - break; - case PDFIO_VALTYPE_ARRAY : - printf(" /%s [...]\n", pair->key); - break; - case PDFIO_VALTYPE_DICT : - printf(" /%s <<...>>\n", pair->key); - break; - default : - printf(" /%s ...\n", pair->key); - break; - } - } - } - } - } - - pdfioFileClose(pdf); - } + if (!do_test_file(argv[i])) + return (1); } } + else + { + do_unit_tests(); + } return (0); } + + +// +// 'do_test_file()' - Try loading a PDF file and listing pages and objects. +// + +static int // O - Exit status +do_test_file(const char *filename) // I - PDF filename +{ + pdfio_file_t *pdf; // PDF file + size_t n, // Object/page index + num_objs, // Number of objects + num_pages; // Number of pages + pdfio_obj_t *obj; // Object + pdfio_dict_t *dict; // Object dictionary + const char *type; // Object type + + + // Try opening the file... + if ((pdf = pdfioFileOpen(filename, NULL, NULL)) != NULL) + { + // Show basic stats... + num_objs = pdfioFileGetNumObjects(pdf); + num_pages = pdfioFileGetNumPages(pdf); + + printf("%s: PDF %s, %d pages, %d objects.\n", filename, pdfioFileGetVersion(pdf), (int)num_pages, (int)num_objs); + + // Show a summary of each page... + for (n = 0; n < num_pages; n ++) + { + if ((obj = pdfioFileGetPage(pdf, n)) == NULL) + { + printf("%s: Unable to get page #%d.\n", filename, (int)n + 1); + } + else + { + pdfio_rect_t media_box; // MediaBox value + + memset(&media_box, 0, sizeof(media_box)); + dict = pdfioObjGetDict(obj); + + if (!pdfioDictGetRect(dict, "MediaBox", &media_box)) + { + if ((obj = pdfioDictGetObject(dict, "Parent")) != NULL) + { + dict = pdfioObjGetDict(obj); + pdfioDictGetRect(dict, "MediaBox", &media_box); + } + } + + printf("%s: Page #%d is %gx%g.\n", filename, (int)n + 1, media_box.x2, media_box.y2); + } + } + + // Show the associated value with each object... + for (n = 0; n < num_objs; n ++) + { + if ((obj = pdfioFileGetObject(pdf, n)) == NULL) + { + printf("%s: Unable to get object #%d.\n", filename, (int)n); + } + else + { + size_t np; // Number of pairs + _pdfio_pair_t *pair; // Current pair + + dict = pdfioObjGetDict(obj); + + printf("%s: %u %u obj dict=%p(%lu pairs)\n", filename, (unsigned)pdfioObjGetNumber(obj), (unsigned)pdfioObjGetGeneration(obj), dict, dict ? (unsigned long)dict->num_pairs : 0UL); + if (dict) + { + // Show a summary of each pair in the dictionary... + for (np = dict->num_pairs, pair = dict->pairs; np > 0; np --, pair ++) + { + switch (pair->value.type) + { + case PDFIO_VALTYPE_INDIRECT : + printf(" /%s %u %u R\n", pair->key, (unsigned)pair->value.value.indirect.number, pair->value.value.indirect.generation); + break; + case PDFIO_VALTYPE_NAME : + printf(" /%s /%s\n", pair->key, pair->value.value.name); + break; + case PDFIO_VALTYPE_STRING : + printf(" /%s (%s)\n", pair->key, pair->value.value.string); + break; + case PDFIO_VALTYPE_BINARY : + { + size_t bn; + unsigned char *bptr; + + printf(" /%s <", pair->key); + for (bn = pair->value.value.binary.datalen, bptr = pair->value.value.binary.data; bn > 0; bn --, bptr ++) + printf("%02X", *bptr); + puts(">"); + } + break; + case PDFIO_VALTYPE_NUMBER : + printf(" /%s %g\n", pair->key, pair->value.value.number); + break; + case PDFIO_VALTYPE_BOOLEAN : + printf(" /%s %s\n", pair->key, pair->value.value.boolean ? "true" : "false"); + break; + case PDFIO_VALTYPE_NULL : + printf(" /%s null\n", pair->key); + break; + case PDFIO_VALTYPE_ARRAY : + printf(" /%s [...]\n", pair->key); + break; + case PDFIO_VALTYPE_DICT : + printf(" /%s <<...>>\n", pair->key); + break; + default : + printf(" /%s ...\n", pair->key); + break; + } + } + } + } + } + + // Close the file and return success... + pdfioFileClose(pdf); + return (0); + } + else + { + // Error message will already be displayed so just indicate failure... + return (1); + } +} + + +// +// 'do_unit_tests()' - Do unit tests. +// + +static int // O - Exit status +do_unit_tests(void) +{ + pdfio_file_t *pdf; // PDF file + bool error = false; // Error callback data + _pdfio_token_t tb; // Token buffer + const char *s; // String buffer + _pdfio_value_t value; // Value + static const char *complex_dict = // Complex dictionary value + "<>/ExtGState<>/Font<<" + "/TT0 21384 0 R/TT1 21390 0 R/TT2 21423 0 R/TT3 21403 0 R/TT4 21397 0 R>>" + "/ProcSet[/PDF/Text/ImageC]/Properties<>" + "/XObject<>>>/Rotate 0/StructParents 2105" + "/Tabs/S/Type/Page>>"; + + + // First open the test PDF file... + fputs("pdfioFileOpen(testpdfio.pdf): ", stdout); + if ((pdf = pdfioFileOpen("testpdfio.pdf", (pdfio_error_cb_t)error_cb, &error)) != NULL) + puts("PASS"); + else + return (1); + + // TODO: Test for known values in this test file. + + // Test the value parsers for edge cases... + fputs("_pdfioValueRead(complex_dict): ", stdout); + s = complex_dict; + _pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, &s); + if (_pdfioValueRead(pdf, &tb, &value)) + { + // TODO: Check value... + puts("PASS"); + } + else + return (1); + + // Close the test PDF file... + fputs("pdfioFileClose: ", stdout); + if (pdfioFileClose(pdf)) + puts("PASS"); + else + return (1); + + return (0); +} + + +// +// 'error_cb()' - Display an error message during a unit test. +// + +static bool // O - `true` to stop, `false` to continue +error_cb(bool *error, // IO - Have we displayed an error? + const char *message) // I - Error message +{ + if (!*error) + { + // First error, so show a "FAIL" indicator + *error = true; + + printf("FAIL (%s)\n", message); + } + else + { + // Subsequent errors are just indented... + printf(" %s\n", message); + } + + // Continue to catch more errors... + return (false); +} + + +// +// 'token_consume_cb()' - Consume bytes from a test string. +// + +static ssize_t // O - Number of bytes consumed +token_consume_cb(const char **s, // IO - Test string + size_t bytes) // I - Bytes to consume +{ + size_t len = strlen(*s); // Number of bytes remaining + + + // "Consume" bytes by incrementing the string pointer, limiting to the + // remaining length... + if (bytes > len) + bytes = len; + + *s += bytes; + + return ((ssize_t)bytes); +} + + +// +// 'token_peek_cb()' - Peek bytes from a test string. +// + +static ssize_t // O - Number of bytes peeked +token_peek_cb(const char **s, // IO - Test string + char *buffer, // I - Buffer + size_t bytes) // I - Bytes to peek +{ + size_t len = strlen(*s); // Number of bytes remaining + + + // Copy bytes from the test string as possible... + if (bytes > len) + bytes = len; + + if (bytes > 0) + memcpy(buffer, *s, bytes); + + return ((ssize_t)bytes); +}