From 6a99bd773e986a615272ed0611d4fc7636b01a03 Mon Sep 17 00:00:00 2001 From: maxime Date: Tue, 9 Aug 2022 16:37:34 +0900 Subject: [PATCH 1/8] Fixed warning when compiling on Windows: warning C4996: 'strdup': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _strdup. See online help for details. --- include/cmocka_private.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/cmocka_private.h b/include/cmocka_private.h index 4d3ff30..7ee4a76 100644 --- a/include/cmocka_private.h +++ b/include/cmocka_private.h @@ -40,6 +40,7 @@ # define strcasecmp _stricmp # define strncasecmp _strnicmp # define strtok_r strtok_s +# define strdup _strdup # if defined(HAVE__SNPRINTF_S) # undef snprintf -- GitLab From 7f2705e48bcde685189764303246601c13d66718 Mon Sep 17 00:00:00 2001 From: maxime Date: Tue, 9 Aug 2022 16:40:08 +0900 Subject: [PATCH 2/8] Improve the implementation of the TAP output by printing the message, severity, data, etc. in a YAML diagnostics section, as specified by the TAP14 specification. --- src/cmocka.c | 66 +++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/cmocka.c b/src/cmocka.c index 2518590..1ab88fe 100644 --- a/src/cmocka.c +++ b/src/cmocka.c @@ -2725,7 +2725,7 @@ static void cmprintf_group_start_tap(const size_t num_tests) { static bool version_printed = false; if (!version_printed) { - print_message("TAP version 13\n"); + print_message("TAP version 14\n"); version_printed = true; } @@ -2749,6 +2749,8 @@ static void cmprintf_tap(enum cm_printf_type type, const char *test_name, const char *error_message) { + int print_error_message = 0; + switch (type) { case PRINTF_TEST_START: break; @@ -2756,42 +2758,48 @@ static void cmprintf_tap(enum cm_printf_type type, print_message("ok %u - %s\n", (unsigned)test_number, test_name); break; case PRINTF_TEST_FAILURE: + case PRINTF_TEST_ERROR: print_message("not ok %u - %s\n", (unsigned)test_number, test_name); - if (error_message != NULL) { - char *msg; - char *p; + print_error_message = 1; + break; + case PRINTF_TEST_SKIPPED: + print_message("ok %u - %s # SKIP\n", (unsigned)test_number, test_name); + break; + } - msg = strdup(error_message); - if (msg == NULL) { - return; - } - p = msg; + if ((print_error_message == 1) && (error_message != NULL)) { + char *msg; + char *p; + const char *indent = ""; - while (p[0] != '\0') { - char *q = p; + msg = strdup(error_message); + if (msg == NULL) { + return; + } + p = msg; - p = strchr(q, '\n'); - if (p != NULL) { - p[0] = '\0'; - } + print_message(" ---\n"); - print_message("# %s\n", q); + print_message(" message: \""); + while (p[0] != '\0') { + char *q = p; - if (p == NULL) { - break; - } - p++; + p = strchr(q, '\n'); + if (p != NULL) { + p[0] = '\0'; + print_message("%s%s\\n\\\n", indent, q); + } else { + print_message("%s%s\"\n", indent, q); + break; } - libc_free(msg); + p++; + indent = " "; } - break; - case PRINTF_TEST_SKIPPED: - print_message("ok %u # SKIP %s\n", (unsigned)test_number, test_name); - break; - case PRINTF_TEST_ERROR: - print_message("not ok %u - %s %s\n", - (unsigned)test_number, test_name, error_message); - break; + libc_free(msg); + + print_message(" severity: %s\n", (type == PRINTF_TEST_FAILURE) ? "fail" : "error"); + + print_message(" ...\n"); } } -- GitLab From 9874464dc7858475ace081dc748b0f399213b473 Mon Sep 17 00:00:00 2001 From: maxime Date: Tue, 9 Aug 2022 16:44:18 +0900 Subject: [PATCH 3/8] Added functions so that custom data can be attached to the current test. I'm open to a different implementation, but I basically needed a mechanism that is flexible enough to print any kind of data whether a test passes or fails. For example, on the app using cmocka, I wanted to be able to redefine the assert_* macros to automatically include data such as 'expected', 'got' when a test fails. Currently the data are only used by the TAP output. --- include/cmocka.h | 51 +++++++++++++ src/cmocka.c | 185 ++++++++++++++++++++++++++++++++++++++++++++--- src/cmocka.def | 6 ++ 3 files changed, 231 insertions(+), 11 deletions(-) diff --git a/include/cmocka.h b/include/cmocka.h index 1897240..18f0da3 100644 --- a/include/cmocka.h +++ b/include/cmocka.h @@ -1742,6 +1742,45 @@ void stop(void); #define stop() _stop() #endif +#ifdef DOXYGEN +/** + * @brief Initialize a list of data for the current test. + */ +int attach_test_data(size_t size); + +/** + * @brief Expand the list of data for the current test. + */ +int extend_test_data(size_t size); + +/** + * @brief Append Boolean data for the current test. + */ +void append_test_data_boolean(const char * const name, int value); + +/** + * @brief Append Integer data for the current test. + */ +void append_test_data_integer(const char * const name, uintmax_t value); + +/** + * @brief Append Pointer data for the current test. + */ +void append_test_data_pointer(const char * const name, void * const value); + +/** + * @brief Append String data for the current test. + */ +void append_test_data_string(const char * const name, const char * value); +#else +#define attach_test_data(s) _attach_test_data(s, __FILE__, __LINE__) +#define extend_test_data(s) _extend_test_data(s, __FILE__, __LINE__) +#define append_test_data_boolean(n, v) _append_test_data_boolean(n, v, __FILE__, __LINE__) +#define append_test_data_integer(n, v) _append_test_data_integer(n, v, __FILE__, __LINE__) +#define append_test_data_pointer(n, v) _append_test_data_pointer(n, v, __FILE__, __LINE__) +#define append_test_data_string(n, v) _append_test_data_string(n, v, __FILE__, __LINE__) +#endif + #ifdef DOXYGEN /** * @brief Forces the test to fail immediately and quit, printing the reason. @@ -2436,6 +2475,18 @@ CMOCKA_NORETURN void _skip(const char * const file, const int line); CMOCKA_NORETURN void _stop(void); +int _attach_test_data(size_t size, const char * const file, const int line); + +int _extend_test_data(size_t size, const char * const file, const int line); + +void _append_test_data_boolean(const char * const name, int value, const char * const file, const int line); + +void _append_test_data_integer(const char * const name, uintmax_t value, const char * const file, const int line); + +void _append_test_data_pointer(const char * const name, void * const value, const char * const file, const int line); + +void _append_test_data_string(const char * const name, const char * value, const char * const file, const int line); + /* Test runner */ int _cmocka_run_group_tests(const char *group_name, const struct CMUnitTest * const tests, diff --git a/src/cmocka.c b/src/cmocka.c index 1ab88fe..8bc13d8 100644 --- a/src/cmocka.c +++ b/src/cmocka.c @@ -201,6 +201,19 @@ typedef struct CheckMemoryData { size_t size; } CheckMemoryData; +typedef enum CMDataType { + DATA_TYPE_BOOLEAN, + DATA_TYPE_INTEGER, + DATA_TYPE_POINTER, + DATA_TYPE_STRING, +} CMDataType; + +typedef struct CMData { + char *name; + void *value; + CMDataType type; +} CMData; + static ListNode* list_initialize(ListNode * const node); static ListNode* list_add(ListNode * const head, ListNode *new_node); static ListNode* list_add_value(ListNode * const head, const void *value, @@ -270,6 +283,11 @@ const char *global_last_failed_assert = NULL; static int global_skip_test; static int global_stop_test; +/* Keeps an array of CMData to be printed for TAP output. */ +static CMOCKA_THREAD CMData** global_test_data = NULL; +static CMOCKA_THREAD size_t global_test_data_max_size; +static CMOCKA_THREAD size_t global_test_data_len; + /* Keeps a map of the values that functions will have to return to provide */ /* mocked interfaces. */ static CMOCKA_THREAD ListNode global_function_result_map_head; @@ -410,6 +428,107 @@ void _stop(void) exit(EXIT_FAILURE); } +static void free_test_data(void) { + if (global_test_data) { + CMData* d; + size_t i; + for (i = 0; i < global_test_data_len; i++) { + d = global_test_data[i]; + if (d) { + free(d->name); + switch (d->type) { + case DATA_TYPE_STRING: + free(d->value); + default: + break; + } + free(d); + } + } + free(global_test_data); + + global_test_data = NULL; + global_test_data_max_size = 0; + global_test_data_len = 0; + } +} + +int _attach_test_data(size_t size, const char * const file, const int line) { + if (size > 0) { + free_test_data(); + global_test_data = (CMData**)malloc(size * sizeof(CMData*)); + if (!global_test_data) { + cmocka_print_error(SOURCE_LOCATION_FORMAT ": error: Failed to initialize data list.\n", file, line); + return 1; + } else { + global_test_data_max_size = size; + global_test_data_len = 0; + } + } + return 0; +} + +int _extend_test_data(size_t size, const char * const file, const int line) { + if (size > 0) { + CMData** tmp; + size_t tmp_size = global_test_data_max_size + size; + + tmp = (CMData**)realloc(global_test_data, tmp_size * sizeof(CMData*)); + if (!tmp) { + cmocka_print_error(SOURCE_LOCATION_FORMAT ": error: Failed to expand data list.\n", file, line); + return 1; + } else { + global_test_data = tmp; + global_test_data_max_size = tmp_size; + } + } + return 0; +} + +static void _append_test_data(const char * const name, void * const value, CMDataType type, const char * const file, const int line) { + CMData* d; + + if (!global_test_data) { + if (_attach_test_data(10, file, line) == 1) { + return; + } + } else if (global_test_data_len + 1 > global_test_data_max_size) { + if (_extend_test_data(10, file, line) == 1) { + return; + } + } + + d = (CMData*)malloc(sizeof(CMData)); + if (d) { + d->name = strdup(name); + switch (type) { + case DATA_TYPE_STRING: + d->value = strdup(value); + break; + default: + d->value = value; + } + d->type = type; + global_test_data[global_test_data_len++] = d; + } +} + +void _append_test_data_boolean(const char * const name, int value, const char * const file, const int line) { + _append_test_data(name, cast_uintmax_type_to_pointer(void * const, value), DATA_TYPE_BOOLEAN, file, line); +} + +void _append_test_data_integer(const char * const name, uintmax_t value, const char * const file, const int line) { + _append_test_data(name, cast_uintmax_type_to_pointer(void * const, value), DATA_TYPE_INTEGER, file, line); +} + +void _append_test_data_pointer(const char * const name, void * const value, const char * const file, const int line) { + _append_test_data(name, value, DATA_TYPE_POINTER, file, line); +} + +void _append_test_data_string(const char * const name, const char * value, const char * const file, const int line) { + _append_test_data(name, (void * const)value, DATA_TYPE_STRING, file, line); +} + /* Initialize a SourceLocation structure. */ static void initialize_source_location(SourceLocation * const location) { assert_non_null(location); @@ -2747,8 +2866,12 @@ static void cmprintf_group_finish_tap(const char *group_name, static void cmprintf_tap(enum cm_printf_type type, size_t test_number, const char *test_name, - const char *error_message) + const char *error_message, + CMData** test_data) { + CMData* d; + size_t i; + int diagnostics_opened = 0; int print_error_message = 0; switch (type) { @@ -2779,6 +2902,7 @@ static void cmprintf_tap(enum cm_printf_type type, p = msg; print_message(" ---\n"); + diagnostics_opened = 1; print_message(" message: \""); while (p[0] != '\0') { @@ -2798,7 +2922,39 @@ static void cmprintf_tap(enum cm_printf_type type, libc_free(msg); print_message(" severity: %s\n", (type == PRINTF_TEST_FAILURE) ? "fail" : "error"); + } + + if (test_data) { + if (diagnostics_opened == 0) { + print_message(" ---\n"); + diagnostics_opened = 1; + } + print_message(" data:\n"); + + for (i = 0; i < global_test_data_len; i++) { + d = test_data[i]; + switch (d->type) { + case DATA_TYPE_BOOLEAN: + print_message(" %s: %s\n", + d->name, (int)cast_ptr_to_uintmax_type(d->value) != (int)0 ? "true" : "false"); + break; + case DATA_TYPE_INTEGER: + print_message(" %s: %#jx\n", + d->name, cast_ptr_to_uintmax_type(d->value)); + break; + case DATA_TYPE_POINTER: + print_message(" %s: %p\n", + d->name, d->value); + break; + case DATA_TYPE_STRING: + print_message(" %s: '%s'\n", + d->name, (const char *)(d->value)); + break; + } + } + } + if (diagnostics_opened == 1) { print_message(" ...\n"); } } @@ -2886,7 +3042,8 @@ static void cmprintf_group_finish(const char *group_name, static void cmprintf(enum cm_printf_type type, size_t test_number, const char *test_name, - const char *error_message) + const char *error_message, + CMData** test_data) { uint32_t output; @@ -2899,7 +3056,7 @@ static void cmprintf(enum cm_printf_type type, cmprintf_subunit(type, test_name, error_message); } if (output & CM_OUTPUT_TAP) { - cmprintf_tap(type, test_number, test_name, error_message); + cmprintf_tap(type, test_number, test_name, error_message, test_data); } } @@ -3253,7 +3410,7 @@ int _cmocka_run_group_tests(const char *group_name, struct CMUnitTestState *cmtest = &cm_tests[i]; size_t test_number = i + 1; - cmprintf(PRINTF_TEST_START, test_number, cmtest->test->name, NULL); + cmprintf(PRINTF_TEST_START, test_number, cmtest->test->name, NULL, NULL); if (group_state != NULL) { cmtest->state = group_state; @@ -3270,28 +3427,32 @@ int _cmocka_run_group_tests(const char *group_name, cmprintf(PRINTF_TEST_SUCCESS, test_number, cmtest->test->name, - cmtest->error_message); + cmtest->error_message, + global_test_data); total_passed++; break; case CM_TEST_SKIPPED: cmprintf(PRINTF_TEST_SKIPPED, test_number, cmtest->test->name, - cmtest->error_message); + cmtest->error_message, + global_test_data); total_skipped++; break; case CM_TEST_FAILED: cmprintf(PRINTF_TEST_FAILURE, test_number, cmtest->test->name, - cmtest->error_message); + cmtest->error_message, + global_test_data); total_failed++; break; default: cmprintf(PRINTF_TEST_ERROR, test_number, cmtest->test->name, - "Internal cmocka error"); + "Internal cmocka error", + NULL); total_errors++; break; } @@ -3305,9 +3466,11 @@ int _cmocka_run_group_tests(const char *group_name, cmprintf(PRINTF_TEST_ERROR, test_number, cmtest->test->name, - err_msg); + err_msg, + NULL); total_errors++; } + free_test_data(); } } else { if (cm_error_message != NULL) { @@ -3316,7 +3479,7 @@ int _cmocka_run_group_tests(const char *group_name, cm_error_message = NULL; } cmprintf(PRINTF_TEST_ERROR, 0, - group_name, "[ FAILED ] GROUP SETUP"); + group_name, "[ FAILED ] GROUP SETUP", NULL); total_errors++; } @@ -3334,7 +3497,7 @@ int _cmocka_run_group_tests(const char *group_name, cm_error_message = NULL; } cmprintf(PRINTF_TEST_ERROR, 0, - group_name, "[ FAILED ] GROUP TEARDOWN"); + group_name, "[ FAILED ] GROUP TEARDOWN", NULL); } } diff --git a/src/cmocka.def b/src/cmocka.def index f56b9a5..edeeffa 100644 --- a/src/cmocka.def +++ b/src/cmocka.def @@ -1,5 +1,9 @@ LIBRARY cmocka EXPORTS + _append_test_data_boolean + _append_test_data_integer + _append_test_data_pointer + _append_test_data_string _assert_double_equal _assert_double_not_equal _assert_float_equal @@ -20,6 +24,7 @@ EXPORTS _assert_uint_equal _assert_uint_in_range _assert_uint_not_equal + _attach_test_data _check_expected _cmocka_run_group_tests _expect_any @@ -35,6 +40,7 @@ EXPORTS _expect_not_value _expect_string _expect_value + _extend_test_data _fail _function_called _mock -- GitLab From 3565734f7029a8a0c1600b292e641ec90e63ae17 Mon Sep 17 00:00:00 2001 From: maxime Date: Tue, 9 Aug 2022 16:47:28 +0900 Subject: [PATCH 4/8] Added skip_with_note() function to provide an explanation about why the test is to be skipped. --- include/cmocka.h | 13 ++++++++++++ src/cmocka.c | 55 ++++++++++++++++++++++++++++++++++++++++-------- src/cmocka.def | 1 + 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/include/cmocka.h b/include/cmocka.h index 18f0da3..827e7a0 100644 --- a/include/cmocka.h +++ b/include/cmocka.h @@ -1728,6 +1728,17 @@ void skip(void); #define skip() _skip(__FILE__, __LINE__) #endif +#ifdef DOXYGEN +/** + * @brief Forces the test to not be executed, but marked as skipped. + * + * @param[in] note Explanation about why the test is skipped. + */ +void skip_with_note(const char * const note, void); +#else +#define skip_with_note(note) _skip_with_note(note, __FILE__, __LINE__) +#endif + #ifdef DOXYGEN /** * @brief Forces the test to be stopped immediately. @@ -2473,6 +2484,8 @@ CMOCKA_NORETURN void _fail(const char * const file, const int line); CMOCKA_NORETURN void _skip(const char * const file, const int line); +CMOCKA_NORETURN void _skip_with_note(const char * const note, const char * const file, const int line); + CMOCKA_NORETURN void _stop(void); int _attach_test_data(size_t size, const char * const file, const int line); diff --git a/src/cmocka.c b/src/cmocka.c index 8bc13d8..9c8c340 100644 --- a/src/cmocka.c +++ b/src/cmocka.c @@ -281,6 +281,7 @@ jmp_buf global_expect_assert_env; int global_expecting_assert = 0; const char *global_last_failed_assert = NULL; static int global_skip_test; +static const char * global_skip_test_note = NULL; static int global_stop_test; /* Keeps an array of CMData to be printed for TAP output. */ @@ -385,6 +386,7 @@ struct CMUnitTestState { const char *error_message; /* The error messages by the test */ enum CMUnitTestStatus status; /* PASSED, FAILED, ABORT ... */ double runtime; /* Time calculations */ + const char *skip_note; /* Explanation when the test is skipped */ }; /* Exit the currently executing test. */ @@ -419,6 +421,17 @@ void _skip(const char * const file, const int line) exit(EXIT_FAILURE); } +void _skip_with_note(const char * const note, const char * const file, const int line) +{ + cmocka_print_error(SOURCE_LOCATION_FORMAT ": Skipped (%s)!\n", file, line, note); + global_skip_test = 1; + global_skip_test_note = note; + exit_test(true); + + /* Unreachable */ + exit(EXIT_FAILURE); +} + void _stop(void) { global_stop_test = 1; @@ -2738,7 +2751,12 @@ static void cmprintf_group_finish_xml(const char *group_name, } break; case CM_TEST_SKIPPED: - fprintf(fp, " \n"); + if (cmtest->skip_note) { + fprintf(fp, " \n", + cmtest->skip_note); + } else { + fprintf(fp, " \n"); + } break; case CM_TEST_PASSED: @@ -2813,7 +2831,8 @@ static void cmprintf_group_finish_standard(const char *group_name, static void cmprintf_standard(enum cm_printf_type type, const char *test_name, - const char *error_message) + const char *error_message, + const char *skip_note) { switch (type) { case PRINTF_TEST_START: @@ -2829,7 +2848,11 @@ static void cmprintf_standard(enum cm_printf_type type, print_message("[ FAILED ] %s\n", test_name); break; case PRINTF_TEST_SKIPPED: - print_message("[ SKIPPED ] %s\n", test_name); + if (skip_note) { + print_message("[ SKIPPED ] %s - %s\n", test_name, skip_note); + } else { + print_message("[ SKIPPED ] %s\n", test_name); + } break; case PRINTF_TEST_ERROR: if (error_message != NULL) { @@ -2867,6 +2890,7 @@ static void cmprintf_tap(enum cm_printf_type type, size_t test_number, const char *test_name, const char *error_message, + const char *skip_note, CMData** test_data) { CMData* d; @@ -2886,7 +2910,11 @@ static void cmprintf_tap(enum cm_printf_type type, print_error_message = 1; break; case PRINTF_TEST_SKIPPED: - print_message("ok %u - %s # SKIP\n", (unsigned)test_number, test_name); + if (skip_note) { + print_message("ok %u - %s # SKIP %s\n", (unsigned)test_number, test_name, skip_note); + } else { + print_message("ok %u - %s # SKIP\n", (unsigned)test_number, test_name); + } break; } @@ -2977,6 +3005,7 @@ static void cmprintf_subunit(enum cm_printf_type type, } break; case PRINTF_TEST_SKIPPED: + /* TODO: should we print the explanation in DETAILS region as explained here: https://github.com/testing-cabal/subunit */ print_message("skip: %s\n", test_name); break; case PRINTF_TEST_ERROR: @@ -3043,6 +3072,7 @@ static void cmprintf(enum cm_printf_type type, size_t test_number, const char *test_name, const char *error_message, + const char *skip_note, CMData** test_data) { uint32_t output; @@ -3050,13 +3080,13 @@ static void cmprintf(enum cm_printf_type type, output = cm_get_output(); if (output & CM_OUTPUT_STANDARD) { - cmprintf_standard(type, test_name, error_message); + cmprintf_standard(type, test_name, error_message, skip_note); } if (output & CM_OUTPUT_SUBUNIT) { cmprintf_subunit(type, test_name, error_message); } if (output & CM_OUTPUT_TAP) { - cmprintf_tap(type, test_number, test_name, error_message, test_data); + cmprintf_tap(type, test_number, test_name, error_message, skip_note, test_data); } } @@ -3298,7 +3328,9 @@ static int cmocka_run_one_tests(struct CMUnitTestState *test_state) } else { if (global_skip_test) { test_state->status = CM_TEST_SKIPPED; + test_state->skip_note = global_skip_test_note; global_skip_test = 0; /* Do not skip the next test */ + global_skip_test_note = NULL; } else { test_state->status = CM_TEST_FAILED; } @@ -3410,7 +3442,7 @@ int _cmocka_run_group_tests(const char *group_name, struct CMUnitTestState *cmtest = &cm_tests[i]; size_t test_number = i + 1; - cmprintf(PRINTF_TEST_START, test_number, cmtest->test->name, NULL, NULL); + cmprintf(PRINTF_TEST_START, test_number, cmtest->test->name, NULL, NULL, NULL); if (group_state != NULL) { cmtest->state = group_state; @@ -3428,6 +3460,7 @@ int _cmocka_run_group_tests(const char *group_name, test_number, cmtest->test->name, cmtest->error_message, + cmtest->skip_note, global_test_data); total_passed++; break; @@ -3436,6 +3469,7 @@ int _cmocka_run_group_tests(const char *group_name, test_number, cmtest->test->name, cmtest->error_message, + cmtest->skip_note, global_test_data); total_skipped++; break; @@ -3444,6 +3478,7 @@ int _cmocka_run_group_tests(const char *group_name, test_number, cmtest->test->name, cmtest->error_message, + cmtest->skip_note, global_test_data); total_failed++; break; @@ -3452,6 +3487,7 @@ int _cmocka_run_group_tests(const char *group_name, test_number, cmtest->test->name, "Internal cmocka error", + NULL, NULL); total_errors++; break; @@ -3467,6 +3503,7 @@ int _cmocka_run_group_tests(const char *group_name, test_number, cmtest->test->name, err_msg, + NULL, NULL); total_errors++; } @@ -3479,7 +3516,7 @@ int _cmocka_run_group_tests(const char *group_name, cm_error_message = NULL; } cmprintf(PRINTF_TEST_ERROR, 0, - group_name, "[ FAILED ] GROUP SETUP", NULL); + group_name, "[ FAILED ] GROUP SETUP", NULL, NULL); total_errors++; } @@ -3497,7 +3534,7 @@ int _cmocka_run_group_tests(const char *group_name, cm_error_message = NULL; } cmprintf(PRINTF_TEST_ERROR, 0, - group_name, "[ FAILED ] GROUP TEARDOWN", NULL); + group_name, "[ FAILED ] GROUP TEARDOWN", NULL, NULL); } } diff --git a/src/cmocka.def b/src/cmocka.def index edeeffa..3b29014 100644 --- a/src/cmocka.def +++ b/src/cmocka.def @@ -45,6 +45,7 @@ EXPORTS _function_called _mock _skip + _skip_with_note _stop _test_calloc _test_free -- GitLab From e28858ac8ddab8237497db6330b51d0e0500a6ef Mon Sep 17 00:00:00 2001 From: maxime Date: Tue, 9 Aug 2022 16:50:06 +0900 Subject: [PATCH 5/8] Added support for TODO tests, which represent features to be implemented. According to the TAP specification, these tests must not be treated as a test failure if they fail (see https://testanything.org/tap-version-14-specification.html). --- include/cmocka.h | 24 +++++++ src/cmocka.c | 175 +++++++++++++++++++++++++++++++++++++++++++---- src/cmocka.def | 2 + 3 files changed, 189 insertions(+), 12 deletions(-) diff --git a/include/cmocka.h b/include/cmocka.h index 827e7a0..10138d7 100644 --- a/include/cmocka.h +++ b/include/cmocka.h @@ -1753,6 +1753,26 @@ void stop(void); #define stop() _stop() #endif +#ifdef DOXYGEN +/** + * @brief Mark the test as TODO, for a feature that is not fully implemented yet. + */ +void todo(void); +#else +#define todo() _todo(__FILE__, __LINE__) +#endif + +#ifdef DOXYGEN +/** + * @brief Mark the test as TODO, for a feature that is not fully implemented yet. + * + * @param[in] note Explanation about why the test is marked as TODO. + */ +void todo_with_note(const char * const note, void); +#else +#define todo_with_note(note) _todo_with_note(note, __FILE__, __LINE__) +#endif + #ifdef DOXYGEN /** * @brief Initialize a list of data for the current test. @@ -2488,6 +2508,10 @@ CMOCKA_NORETURN void _skip_with_note(const char * const note, const char * const CMOCKA_NORETURN void _stop(void); +void _todo(const char * const file, const int line); + +void _todo_with_note(const char * const note, const char * const file, const int line); + int _attach_test_data(size_t size, const char * const file, const int line); int _extend_test_data(size_t size, const char * const file, const int line); diff --git a/src/cmocka.c b/src/cmocka.c index 9c8c340..978a477 100644 --- a/src/cmocka.c +++ b/src/cmocka.c @@ -283,6 +283,8 @@ const char *global_last_failed_assert = NULL; static int global_skip_test; static const char * global_skip_test_note = NULL; static int global_stop_test; +static int global_todo_test; +static const char * global_todo_test_note = NULL; /* Keeps an array of CMData to be printed for TAP output. */ static CMOCKA_THREAD CMData** global_test_data = NULL; @@ -377,6 +379,8 @@ enum CMUnitTestStatus { CM_TEST_FAILED, CM_TEST_ERROR, CM_TEST_SKIPPED, + CM_TEST_TODO_OK, + CM_TEST_TODO, }; struct CMUnitTestState { @@ -387,6 +391,7 @@ struct CMUnitTestState { enum CMUnitTestStatus status; /* PASSED, FAILED, ABORT ... */ double runtime; /* Time calculations */ const char *skip_note; /* Explanation when the test is skipped */ + const char *todo_note; /* Explanation when the test is marked as TODO */ }; /* Exit the currently executing test. */ @@ -441,6 +446,17 @@ void _stop(void) exit(EXIT_FAILURE); } +void _todo(const char * const file, const int line) +{ + global_todo_test = 1; +} + +void _todo_with_note(const char * const note, const char * const file, const int line) +{ + global_todo_test = 1; + global_todo_test_note = note; +} + static void free_test_data(void) { if (global_test_data) { CMData* d; @@ -2661,6 +2677,8 @@ enum cm_printf_type { PRINTF_TEST_FAILURE, PRINTF_TEST_ERROR, PRINTF_TEST_SKIPPED, + PRINTF_TEST_TODO_OK, + PRINTF_TEST_TODO, }; static int xml_printed; @@ -2671,6 +2689,8 @@ static void cmprintf_group_finish_xml(const char *group_name, size_t total_failed, size_t total_errors, size_t total_skipped, + size_t total_todo_ok, + size_t total_todo, double total_runtime, struct CMUnitTestState *cm_tests) { @@ -2726,13 +2746,15 @@ static void cmprintf_group_finish_xml(const char *group_name, fprintf(fp, "\n"); fprintf(fp, " \n", + "tests=\"%u\" failures=\"%u\" errors=\"%u\" skipped=\"%u\" todo_ok=\"%u\" todo=\"%u\" >\n", group_name, total_runtime, /* seconds */ (unsigned)total_executed, (unsigned)total_failed, (unsigned)total_errors, - (unsigned)total_skipped); + (unsigned)total_skipped, + (unsigned)total_todo_ok, + (unsigned)total_todo); for (i = 0; i < total_executed; i++) { struct CMUnitTestState *cmtest = &cm_tests[i]; @@ -2741,6 +2763,18 @@ static void cmprintf_group_finish_xml(const char *group_name, cmtest->test->name, cmtest->runtime); switch (cmtest->status) { + case CM_TEST_TODO_OK: + case CM_TEST_TODO: + if (cmtest->todo_note) { + fprintf(fp, " \n", + cmtest->todo_note); + } else { + fprintf(fp, " \n"); + } + if (cmtest->status == CM_TEST_TODO_OK) { + break; + } + // fall through case CM_TEST_ERROR: case CM_TEST_FAILED: if (cmtest->error_message != NULL) { @@ -2789,6 +2823,8 @@ static void cmprintf_group_finish_standard(const char *group_name, size_t total_failed, size_t total_errors, size_t total_skipped, + size_t total_todo_ok, + size_t total_todo, struct CMUnitTestState *cm_tests) { size_t i; @@ -2799,6 +2835,34 @@ static void cmprintf_group_finish_standard(const char *group_name, print_error("[ PASSED ] %u test(s).\n", (unsigned)(total_passed)); + if (total_todo_ok) { + print_message("[ TODO OK ] %s: %zu test(s), listed below:\n", + group_name, + total_todo_ok); + for (i = 0; i < total_executed; i++) { + struct CMUnitTestState *cmtest = &cm_tests[i]; + + if (cmtest->status == CM_TEST_TODO_OK) { + print_message("[ TODO OK ] %s\n", cmtest->test->name); + } + } + print_message("\n %u TODO TEST(S) PASSED - Consider promoting to normal test(s).\n", (unsigned)(total_todo_ok)); + } + + if (total_todo) { + print_message("[ TODO ] %s: %zu test(s), listed below:\n", + group_name, + total_todo); + for (i = 0; i < total_executed; i++) { + struct CMUnitTestState *cmtest = &cm_tests[i]; + + if (cmtest->status == CM_TEST_TODO) { + print_message("[ TODO ] %s\n", cmtest->test->name); + } + } + print_message("\n %u TODO TEST(S)\n", (unsigned)(total_todo)); + } + if (total_skipped) { print_error("[ SKIPPED ] %s: %zu test(s), listed below:\n", group_name, @@ -2832,7 +2896,8 @@ static void cmprintf_group_finish_standard(const char *group_name, static void cmprintf_standard(enum cm_printf_type type, const char *test_name, const char *error_message, - const char *skip_note) + const char *skip_note, + const char *todo_note) { switch (type) { case PRINTF_TEST_START: @@ -2854,6 +2919,23 @@ static void cmprintf_standard(enum cm_printf_type type, print_message("[ SKIPPED ] %s\n", test_name); } break; + case PRINTF_TEST_TODO_OK: + if (todo_note) { + print_message("[ TODO OK ] %s - %s\n", test_name, todo_note); + } else { + print_message("[ TODO OK ] %s\n", test_name); + } + break; + case PRINTF_TEST_TODO: + if (todo_note) { + print_message("[ TODO ] %s - %s\n", test_name, todo_note); + } else { + print_message("[ TODO ] %s\n", test_name); + } + if (error_message != NULL) { + print_error("[ ERROR ] --- %s\n", error_message); + } + break; case PRINTF_TEST_ERROR: if (error_message != NULL) { print_error("%s\n", error_message); @@ -2877,10 +2959,12 @@ static void cmprintf_group_start_tap(const size_t num_tests) static void cmprintf_group_finish_tap(const char *group_name, size_t total_executed, size_t total_passed, - size_t total_skipped) + size_t total_skipped, + size_t total_todo_ok, + size_t total_todo) { const char *status = "not ok"; - if (total_passed + total_skipped == total_executed) { + if (total_passed + total_skipped + total_todo_ok + total_todo == total_executed) { status = "ok"; } print_message("# %s - %s\n", status, group_name); @@ -2891,6 +2975,7 @@ static void cmprintf_tap(enum cm_printf_type type, const char *test_name, const char *error_message, const char *skip_note, + const char *todo_note, CMData** test_data) { CMData* d; @@ -2916,6 +3001,21 @@ static void cmprintf_tap(enum cm_printf_type type, print_message("ok %u - %s # SKIP\n", (unsigned)test_number, test_name); } break; + case PRINTF_TEST_TODO_OK: + if (todo_note) { + print_message("ok %u - %s # TODO %s\n", (unsigned)test_number, test_name, todo_note); + } else { + print_message("ok %u - %s # TODO\n", (unsigned)test_number, test_name); + } + break; + case PRINTF_TEST_TODO: + if (todo_note) { + print_message("not ok %u - %s # TODO %s\n", (unsigned)test_number, test_name, todo_note); + } else { + print_message("not ok %u - %s # TODO\n", (unsigned)test_number, test_name); + } + print_error_message = 1; + break; } if ((print_error_message == 1) && (error_message != NULL)) { @@ -3004,6 +3104,9 @@ static void cmprintf_subunit(enum cm_printf_type type, print_message(" [\n%s\n]\n", error_message); } break; + case PRINTF_TEST_TODO_OK: + case PRINTF_TEST_TODO: + /* TODO: not sure about the subunit specification for TODO tests. Looks like they consider them as skipped. */ case PRINTF_TEST_SKIPPED: /* TODO: should we print the explanation in DETAILS region as explained here: https://github.com/testing-cabal/subunit */ print_message("skip: %s\n", test_name); @@ -3035,6 +3138,8 @@ static void cmprintf_group_finish(const char *group_name, size_t total_failed, size_t total_errors, size_t total_skipped, + size_t total_todo_ok, + size_t total_todo, double total_runtime, struct CMUnitTestState *cm_tests) { @@ -3049,13 +3154,17 @@ static void cmprintf_group_finish(const char *group_name, total_failed, total_errors, total_skipped, + total_todo_ok, + total_todo, cm_tests); } if (output & CM_OUTPUT_TAP) { cmprintf_group_finish_tap(group_name, total_executed, total_passed, - total_skipped); + total_skipped, + total_todo_ok, + total_todo); } if (output & CM_OUTPUT_XML) { cmprintf_group_finish_xml(group_name, @@ -3063,6 +3172,8 @@ static void cmprintf_group_finish(const char *group_name, total_failed, total_errors, total_skipped, + total_todo_ok, + total_todo, total_runtime, cm_tests); } @@ -3073,6 +3184,7 @@ static void cmprintf(enum cm_printf_type type, const char *test_name, const char *error_message, const char *skip_note, + const char *todo_note, CMData** test_data) { uint32_t output; @@ -3080,13 +3192,13 @@ static void cmprintf(enum cm_printf_type type, output = cm_get_output(); if (output & CM_OUTPUT_STANDARD) { - cmprintf_standard(type, test_name, error_message, skip_note); + cmprintf_standard(type, test_name, error_message, skip_note, todo_note); } if (output & CM_OUTPUT_SUBUNIT) { cmprintf_subunit(type, test_name, error_message); } if (output & CM_OUTPUT_TAP) { - cmprintf_tap(type, test_number, test_name, error_message, skip_note, test_data); + cmprintf_tap(type, test_number, test_name, error_message, skip_note, todo_note, test_data); } } @@ -3324,17 +3436,27 @@ static int cmocka_run_one_tests(struct CMUnitTestState *test_state) &test_state->state, NULL); if (rc == 0) { - test_state->status = CM_TEST_PASSED; + if (global_todo_test) { + test_state->status = CM_TEST_TODO_OK; + test_state->todo_note = global_todo_test_note; + } else { + test_state->status = CM_TEST_PASSED; + } } else { if (global_skip_test) { test_state->status = CM_TEST_SKIPPED; test_state->skip_note = global_skip_test_note; global_skip_test = 0; /* Do not skip the next test */ global_skip_test_note = NULL; + } else if (global_todo_test) { + test_state->status = CM_TEST_TODO; + test_state->todo_note = global_todo_test_note; } else { test_state->status = CM_TEST_FAILED; } } + global_todo_test = 0; + global_todo_test_note = NULL; rc = 0; } @@ -3380,6 +3502,8 @@ int _cmocka_run_group_tests(const char *group_name, size_t total_executed = 0; size_t total_errors = 0; size_t total_skipped = 0; + size_t total_todo_ok = 0; + size_t total_todo = 0; double total_runtime = 0; size_t i; int rc; @@ -3442,7 +3566,7 @@ int _cmocka_run_group_tests(const char *group_name, struct CMUnitTestState *cmtest = &cm_tests[i]; size_t test_number = i + 1; - cmprintf(PRINTF_TEST_START, test_number, cmtest->test->name, NULL, NULL, NULL); + cmprintf(PRINTF_TEST_START, test_number, cmtest->test->name, NULL, NULL, NULL, NULL); if (group_state != NULL) { cmtest->state = group_state; @@ -3461,6 +3585,7 @@ int _cmocka_run_group_tests(const char *group_name, cmtest->test->name, cmtest->error_message, cmtest->skip_note, + cmtest->todo_note, global_test_data); total_passed++; break; @@ -3470,6 +3595,7 @@ int _cmocka_run_group_tests(const char *group_name, cmtest->test->name, cmtest->error_message, cmtest->skip_note, + cmtest->todo_note, global_test_data); total_skipped++; break; @@ -3479,15 +3605,37 @@ int _cmocka_run_group_tests(const char *group_name, cmtest->test->name, cmtest->error_message, cmtest->skip_note, + cmtest->todo_note, global_test_data); total_failed++; break; + case CM_TEST_TODO_OK: + cmprintf(PRINTF_TEST_TODO_OK, + test_number, + cmtest->test->name, + cmtest->error_message, + cmtest->skip_note, + cmtest->todo_note, + global_test_data); + total_todo_ok++; + break; + case CM_TEST_TODO: + cmprintf(PRINTF_TEST_TODO, + test_number, + cmtest->test->name, + cmtest->error_message, + cmtest->skip_note, + cmtest->todo_note, + global_test_data); + total_todo++; + break; default: cmprintf(PRINTF_TEST_ERROR, test_number, cmtest->test->name, "Internal cmocka error", NULL, + NULL, NULL); total_errors++; break; @@ -3504,6 +3652,7 @@ int _cmocka_run_group_tests(const char *group_name, cmtest->test->name, err_msg, NULL, + NULL, NULL); total_errors++; } @@ -3516,7 +3665,7 @@ int _cmocka_run_group_tests(const char *group_name, cm_error_message = NULL; } cmprintf(PRINTF_TEST_ERROR, 0, - group_name, "[ FAILED ] GROUP SETUP", NULL, NULL); + group_name, "[ FAILED ] GROUP SETUP", NULL, NULL, NULL); total_errors++; } @@ -3534,7 +3683,7 @@ int _cmocka_run_group_tests(const char *group_name, cm_error_message = NULL; } cmprintf(PRINTF_TEST_ERROR, 0, - group_name, "[ FAILED ] GROUP TEARDOWN", NULL, NULL); + group_name, "[ FAILED ] GROUP TEARDOWN", NULL, NULL, NULL); } } @@ -3544,6 +3693,8 @@ int _cmocka_run_group_tests(const char *group_name, total_failed, total_errors, total_skipped, + total_todo_ok, + total_todo, total_runtime, cm_tests); diff --git a/src/cmocka.def b/src/cmocka.def index 3b29014..19e09e8 100644 --- a/src/cmocka.def +++ b/src/cmocka.def @@ -51,6 +51,8 @@ EXPORTS _test_free _test_malloc _test_realloc + _todo + _todo_with_note _will_return cmocka_print_error cmocka_set_message_output -- GitLab From a671237d6719c5c9b244ba4c589b8ca7bfc5d2bd Mon Sep 17 00:00:00 2001 From: maxime Date: Tue, 9 Aug 2022 16:51:00 +0900 Subject: [PATCH 6/8] Quick fix on cmprintf_group_finish_standard() because the line breaks for messages such as "\n %u SKIPPED TEST(S)\n" made them appear closer to the next list of tests than the lists they are related to. --- src/cmocka.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cmocka.c b/src/cmocka.c index 978a477..54fb01a 100644 --- a/src/cmocka.c +++ b/src/cmocka.c @@ -2836,7 +2836,7 @@ static void cmprintf_group_finish_standard(const char *group_name, (unsigned)(total_passed)); if (total_todo_ok) { - print_message("[ TODO OK ] %s: %zu test(s), listed below:\n", + print_message("\n[ TODO OK ] %s: %zu test(s), listed below:\n", group_name, total_todo_ok); for (i = 0; i < total_executed; i++) { @@ -2846,11 +2846,11 @@ static void cmprintf_group_finish_standard(const char *group_name, print_message("[ TODO OK ] %s\n", cmtest->test->name); } } - print_message("\n %u TODO TEST(S) PASSED - Consider promoting to normal test(s).\n", (unsigned)(total_todo_ok)); + print_message(" %u TODO TEST(S) PASSED - Consider promoting to normal test(s).\n", (unsigned)(total_todo_ok)); } if (total_todo) { - print_message("[ TODO ] %s: %zu test(s), listed below:\n", + print_message("\n[ TODO ] %s: %zu test(s), listed below:\n", group_name, total_todo); for (i = 0; i < total_executed; i++) { @@ -2860,11 +2860,11 @@ static void cmprintf_group_finish_standard(const char *group_name, print_message("[ TODO ] %s\n", cmtest->test->name); } } - print_message("\n %u TODO TEST(S)\n", (unsigned)(total_todo)); + print_message(" %u TODO TEST(S)\n", (unsigned)(total_todo)); } if (total_skipped) { - print_error("[ SKIPPED ] %s: %zu test(s), listed below:\n", + print_error("\n[ SKIPPED ] %s: %zu test(s), listed below:\n", group_name, total_skipped); for (i = 0; i < total_executed; i++) { @@ -2874,11 +2874,11 @@ static void cmprintf_group_finish_standard(const char *group_name, print_error("[ SKIPPED ] %s\n", cmtest->test->name); } } - print_error("\n %zu SKIPPED TEST(S)\n", total_skipped); + print_error(" %zu SKIPPED TEST(S)\n", total_skipped); } if (total_failed) { - print_error("[ FAILED ] %s: %zu test(s), listed below:\n", + print_error("\n[ FAILED ] %s: %zu test(s), listed below:\n", group_name, total_failed); for (i = 0; i < total_executed; i++) { @@ -2888,7 +2888,7 @@ static void cmprintf_group_finish_standard(const char *group_name, print_error("[ FAILED ] %s\n", cmtest->test->name); } } - print_error("\n %zu FAILED TEST(S)\n", + print_error(" %zu FAILED TEST(S)\n", (total_failed + total_errors)); } } -- GitLab From 6c99594f6fb42128565a7bd9108fdf7efeb09ad5 Mon Sep 17 00:00:00 2001 From: maxime Date: Sun, 28 Aug 2022 00:58:11 +0900 Subject: [PATCH 7/8] fixed compilation warnings --- src/cmocka.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/cmocka.c b/src/cmocka.c index 54fb01a..cdb8d50 100644 --- a/src/cmocka.c +++ b/src/cmocka.c @@ -468,6 +468,7 @@ static void free_test_data(void) { switch (d->type) { case DATA_TYPE_STRING: free(d->value); + break; default: break; } @@ -2762,21 +2763,19 @@ static void cmprintf_group_finish_xml(const char *group_name, fprintf(fp, " \n", cmtest->test->name, cmtest->runtime); - switch (cmtest->status) { - case CM_TEST_TODO_OK: - case CM_TEST_TODO: + if ((cmtest->status == CM_TEST_TODO) || (cmtest->status == CM_TEST_TODO_OK)) { if (cmtest->todo_note) { fprintf(fp, " \n", cmtest->todo_note); } else { fprintf(fp, " \n"); } - if (cmtest->status == CM_TEST_TODO_OK) { - break; - } - // fall through + } + + switch (cmtest->status) { case CM_TEST_ERROR: case CM_TEST_FAILED: + case CM_TEST_TODO: if (cmtest->error_message != NULL) { fprintf(fp, " \n", cmtest->error_message); @@ -2784,6 +2783,7 @@ static void cmprintf_group_finish_xml(const char *group_name, fprintf(fp, " \n"); } break; + case CM_TEST_SKIPPED: if (cmtest->skip_note) { fprintf(fp, " \n", @@ -2795,6 +2795,7 @@ static void cmprintf_group_finish_xml(const char *group_name, case CM_TEST_PASSED: case CM_TEST_NOT_STARTED: + case CM_TEST_TODO_OK: break; } -- GitLab From 7e3088264336fafb43bc4dbf842c657320a4c749 Mon Sep 17 00:00:00 2001 From: maxime Date: Sun, 28 Aug 2022 01:06:58 +0900 Subject: [PATCH 8/8] Fixed YAML syntax errors that occurred when messages or string data printed to the TAP output contained quotes or ended with a newline. --- src/cmocka.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/src/cmocka.c b/src/cmocka.c index cdb8d50..78288d9 100644 --- a/src/cmocka.c +++ b/src/cmocka.c @@ -2971,6 +2971,43 @@ static void cmprintf_group_finish_tap(const char *group_name, print_message("# %s - %s\n", status, group_name); } +static char* escape_quotes(const char* str) { + size_t len = strlen(str); + const char *p1 = str; + const char *p2 = NULL; + char *result; + + while ((p1 = strchr(p1, '"'))) { + len++; + p1++; + } + + result = (char*)malloc(sizeof(char) * (len + 1)); + if (!result) { + return NULL; + } + result[0] = 0; + + p1 = str; + while ((p2 = strchr(p1, '"'))) { +#ifdef _WIN32 + strncat_s(result, len + 1, p1, p2 - p1); + strcat_s(result, len + 1, "\\\""); +#else + strncat(result, p1, p2 - p1); + strcat(result, "\\\""); +#endif + p1 = p2 + 1; + } +#ifdef _WIN32 + strcat_s(result, len + 1, p1); +#else + strcat(result, p1); +#endif + + return result; +} + static void cmprintf_tap(enum cm_printf_type type, size_t test_number, const char *test_name, @@ -2980,6 +3017,7 @@ static void cmprintf_tap(enum cm_printf_type type, CMData** test_data) { CMData* d; + const char *valueStr; size_t i; int diagnostics_opened = 0; int print_error_message = 0; @@ -3021,19 +3059,33 @@ static void cmprintf_tap(enum cm_printf_type type, if ((print_error_message == 1) && (error_message != NULL)) { char *msg; + char *tmp; char *p; + char quoteChar; const char *indent = ""; msg = strdup(error_message); if (msg == NULL) { return; } - p = msg; + + // Use double quotes if the message contains single quotes, then escape double quotes if needed. + quoteChar = strchr(msg, '\'') ? '"' : '\''; + if ((quoteChar == '"') && strchr(msg, '"')) { + tmp = escape_quotes(msg); + libc_free(msg); + if (tmp == NULL) { + return; + } + msg = tmp; + } print_message(" ---\n"); diagnostics_opened = 1; - print_message(" message: \""); + print_message(" message: %c", quoteChar); + + p = msg; while (p[0] != '\0') { char *q = p; @@ -3042,7 +3094,7 @@ static void cmprintf_tap(enum cm_printf_type type, p[0] = '\0'; print_message("%s%s\\n\\\n", indent, q); } else { - print_message("%s%s\"\n", indent, q); + print_message("%s%s", indent, q); break; } p++; @@ -3050,6 +3102,8 @@ static void cmprintf_tap(enum cm_printf_type type, } libc_free(msg); + print_message("%c\n", quoteChar); + print_message(" severity: %s\n", (type == PRINTF_TEST_FAILURE) ? "fail" : "error"); } @@ -3076,8 +3130,21 @@ static void cmprintf_tap(enum cm_printf_type type, d->name, d->value); break; case DATA_TYPE_STRING: - print_message(" %s: '%s'\n", - d->name, (const char *)(d->value)); + valueStr = (const char *)(d->value); + if (strchr(valueStr, '\'')) { + if (strchr(valueStr, '"')) { + char *escapedVal = escape_quotes(valueStr); + print_message(" %s: \"%s\"\n", + d->name, escapedVal ? escapedVal : ""); + free(escapedVal); + } else { + print_message(" %s: \"%s\"\n", + d->name, valueStr); + } + } else { + print_message(" %s: '%s'\n", + d->name, valueStr); + } break; } } -- GitLab