From c803b93e8736ed255ff1a6db5ab6add7ccea736c Mon Sep 17 00:00:00 2001 From: Snild Dolkow Date: Fri, 25 Aug 2023 14:49:29 +0200 Subject: [PATCH] minicheck: Add simple subtest support This will be useful when a test runs through several examples and fails somewhere in the middle. The subtest string replaces the phase_info string (i.e. "during actual test") in the failure output. Added subtest info to various tests where I found for loops. CVE: CVE-2024-45490 Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/c803b93e8736ed255ff1a6db5ab6add7ccea736c] Signed-off-by: Archana Polampalli --- tests/minicheck.c | 25 +++++++++++++++++++++++++ tests/minicheck.h | 16 ++++++++++++++++ tests/runtests.c | 13 +++++++++++++ 3 files changed, 54 insertions(+) diff --git a/tests/minicheck.c b/tests/minicheck.c index 1c65748..46db355 100644 --- a/tests/minicheck.c +++ b/tests/minicheck.c @@ -15,6 +15,7 @@ Copyright (c) 2017 Rhodri James Copyright (c) 2018 Marco Maggi Copyright (c) 2019 David Loffredo + Copyright (c) 2023 Sony Corporation / Snild Dolkow Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -37,6 +38,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -132,17 +134,35 @@ srunner_create(Suite *suite) { static jmp_buf env; +#define SUBTEST_LEN (50) // informative, but not too long static char const *_check_current_function = NULL; +static char _check_current_subtest[SUBTEST_LEN]; static int _check_current_lineno = -1; static char const *_check_current_filename = NULL; void _check_set_test_info(char const *function, char const *filename, int lineno) { _check_current_function = function; + set_subtest("%s", ""); _check_current_lineno = lineno; _check_current_filename = filename; } +void +set_subtest(char const *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vsnprintf(_check_current_subtest, SUBTEST_LEN, fmt, ap); + va_end(ap); + // replace line feeds with spaces, for nicer error logs + for (size_t i = 0; i < SUBTEST_LEN; ++i) { + if (_check_current_subtest[i] == '\n') { + _check_current_subtest[i] = ' '; + } + } + _check_current_subtest[SUBTEST_LEN - 1] = '\0'; // ensure termination +} + static void handle_success(int verbosity) { if (verbosity >= CK_VERBOSE) { @@ -154,6 +174,9 @@ static void handle_failure(SRunner *runner, int verbosity, const char *phase_info) { runner->nfailures++; if (verbosity != CK_SILENT) { + if (strlen(_check_current_subtest) != 0) { + phase_info = _check_current_subtest; + } printf("FAIL: %s (%s at %s:%d)\n", _check_current_function, phase_info, _check_current_filename, _check_current_lineno); } @@ -170,6 +193,7 @@ srunner_run_all(SRunner *runner, int verbosity) { volatile int i; for (i = 0; i < tc->ntests; ++i) { runner->nchecks++; + set_subtest("%s", ""); if (tc->setup != NULL) { /* setup */ @@ -185,6 +209,7 @@ srunner_run_all(SRunner *runner, int verbosity) { continue; } (tc->tests[i])(); + set_subtest("%s", ""); /* teardown */ if (tc->teardown != NULL) { diff --git a/tests/minicheck.h b/tests/minicheck.h index cc1f835..a0ff333 100644 --- a/tests/minicheck.h +++ b/tests/minicheck.h @@ -15,6 +15,7 @@ Copyright (c) 2004-2006 Fred L. Drake, Jr. Copyright (c) 2006-2012 Karl Waclawek Copyright (c) 2016-2017 Sebastian Pipping + Copyright (c) 2023 Sony Corporation / Snild Dolkow Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -56,6 +57,19 @@ extern "C" { # define __func__ __FUNCTION__ #endif +/* PRINTF_LIKE has two effects: + 1. Make clang's -Wformat-nonliteral stop warning about non-literal format + strings in annotated functions' code. + 2. Make both clang and gcc's -Wformat-nonliteral warn about *callers* of + the annotated function that use a non-literal format string. +*/ +# if defined(__GNUC__) +# define PRINTF_LIKE(fmtpos, argspos) \ + __attribute__((format(printf, fmtpos, argspos))) +# else +# define PRINTF_LIKE(fmtpos, argspos) +# endif + #define START_TEST(testname) \ static void testname(void) { \ _check_set_test_info(__func__, __FILE__, __LINE__); \ @@ -64,6 +78,8 @@ extern "C" { } \ } +void PRINTF_LIKE(1, 2) set_subtest(char const *fmt, ...); + #define fail(msg) _fail_unless(0, __FILE__, __LINE__, msg) typedef void (*tcase_setup_function)(void); diff --git a/tests/runtests.c b/tests/runtests.c index 915fa52..3e610f7 100644 --- a/tests/runtests.c +++ b/tests/runtests.c @@ -18,6 +18,7 @@ Copyright (c) 2019 David Loffredo Copyright (c) 2020 Tim Gates Copyright (c) 2021 Dong-hee Na + Copyright (c) 2023 Sony Corporation / Snild Dolkow Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -1804,6 +1805,7 @@ START_TEST(test_ext_entity_invalid_parse) { const ExtFaults *fault = faults; for (; fault->parse_text != NULL; fault++) { + set_subtest("\"%s\"", fault->parse_text); XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter); XML_SetUserData(g_parser, (void *)fault); @@ -1904,6 +1906,7 @@ START_TEST(test_dtd_attr_handling) { AttTest *test; for (test = attr_data; test->definition != NULL; test++) { + set_subtest("%s", test->definition); XML_SetAttlistDeclHandler(g_parser, verify_attlist_decl_handler); XML_SetUserData(g_parser, test); if (_XML_Parse_SINGLE_BYTES(g_parser, prolog, (int)strlen(prolog), @@ -2356,6 +2359,7 @@ START_TEST(test_bad_cdata) { size_t i = 0; for (; i < sizeof(cases) / sizeof(struct CaseData); i++) { + set_subtest("%s", cases[i].text); const enum XML_Status actualStatus = _XML_Parse_SINGLE_BYTES( g_parser, cases[i].text, (int)strlen(cases[i].text), XML_TRUE); const enum XML_Error actualError = XML_GetErrorCode(g_parser); @@ -2423,6 +2427,7 @@ START_TEST(test_bad_cdata_utf16) { size_t i; for (i = 0; i < sizeof(cases) / sizeof(struct CaseData); i++) { + set_subtest("case %lu", (long unsigned)(i + 1)); enum XML_Status actual_status; enum XML_Error actual_error; @@ -3323,6 +3328,7 @@ START_TEST(test_ext_entity_invalid_suspended_parse) { ExtFaults *fault; for (fault = &faults[0]; fault->parse_text != NULL; fault++) { + set_subtest("%s", fault->parse_text); XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(g_parser, external_entity_suspending_faulter); @@ -4311,6 +4317,7 @@ START_TEST(test_bad_ignore_section) { ExtFaults *fault; for (fault = &faults[0]; fault->parse_text != NULL; fault++) { + set_subtest("%s", fault->parse_text); XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(g_parser, external_entity_faulter); XML_SetUserData(g_parser, fault); @@ -4400,6 +4407,7 @@ START_TEST(test_external_entity_values) { int i; for (i = 0; data_004_2[i].parse_text != NULL; i++) { + set_subtest("%s", data_004_2[i].parse_text); XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS); XML_SetExternalEntityRefHandler(g_parser, external_entity_valuer); XML_SetUserData(g_parser, &data_004_2[i]); @@ -7585,6 +7593,7 @@ START_TEST(test_ns_separator_in_uri) { size_t i = 0; size_t failCount = 0; for (; i < sizeof(cases) / sizeof(cases[0]); i++) { + set_subtest("%s", cases[i].doc); XML_Parser parser = XML_ParserCreateNS(NULL, cases[i].namesep); XML_SetElementHandler(parser, dummy_start_element, dummy_end_element); if (XML_Parse(parser, cases[i].doc, (int)strlen(cases[i].doc), @@ -7932,6 +7941,7 @@ START_TEST(test_misc_deny_internal_entity_closing_doctype_issue_317) { size_t inputIndex = 0; for (; inputIndex < sizeof(inputs) / sizeof(inputs[0]); inputIndex++) { + set_subtest("%s", inputs[inputIndex]); XML_Parser parser; enum XML_Status parseResult; int setParamEntityResult; @@ -12078,6 +12088,7 @@ START_TEST(test_helper_unsigned_char_to_printable) { // Smoke test unsigned char uc = 0; for (; uc < (unsigned char)-1; uc++) { + set_subtest("char %u", (unsigned)uc); const char *const printable = unsignedCharToPrintable(uc); if (printable == NULL) fail("unsignedCharToPrintable returned NULL"); @@ -12086,8 +12097,10 @@ START_TEST(test_helper_unsigned_char_to_printable) { } // Two concrete samples + set_subtest("char 'A'"); if (strcmp(unsignedCharToPrintable('A'), "A") != 0) fail("unsignedCharToPrintable result mistaken"); + set_subtest("char '\\'"); if (strcmp(unsignedCharToPrintable('\\'), "\\\\") != 0) fail("unsignedCharToPrintable result mistaken"); } -- 2.40.0