mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-30 05:39:12 +02:00
f743d4e924
This allows a C unit test to access data in the code repository that might be useful for testing. Add testRepoPathSet() to set the repository path. In passing remove extra whitespace in the TEST_RESULT_VOID() macro.
323 lines
32 KiB
C
323 lines
32 KiB
C
/***********************************************************************************************************************************
|
|
C Test Harness
|
|
***********************************************************************************************************************************/
|
|
#ifndef TEST_COMMON_HARNESS_H
|
|
#define TEST_COMMON_HARNESS_H
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include "common/error.h"
|
|
|
|
/***********************************************************************************************************************************
|
|
Constants
|
|
***********************************************************************************************************************************/
|
|
#define BOGUS_STR "BOGUS"
|
|
|
|
/***********************************************************************************************************************************
|
|
Functions
|
|
***********************************************************************************************************************************/
|
|
void testAdd(int run, bool selected);
|
|
bool testBegin(const char *name);
|
|
void testComplete(void);
|
|
|
|
uint64_t testTimeMSec(void);
|
|
uint64_t testTimeMSecBegin(void);
|
|
|
|
const char *testExe(void);
|
|
void testExeSet(const char *testExe);
|
|
|
|
const char *testPath(void);
|
|
void testPathSet(const char *testPath);
|
|
const char *testExpectPath(void);
|
|
void testExpectPathSet(const char *testExpectPath);
|
|
const char *testRepoPath(void);
|
|
void testRepoPathSet(const char *testRepoPath);
|
|
|
|
/***********************************************************************************************************************************
|
|
Convert a macro to a string -- handy for testing debug macros
|
|
***********************************************************************************************************************************/
|
|
#define MACRO_TO_STR_INNER(macro) \
|
|
#macro
|
|
#define MACRO_TO_STR(macro) \
|
|
MACRO_TO_STR_INNER(macro)
|
|
|
|
/***********************************************************************************************************************************
|
|
Maximum size of a formatted result in the TEST_RESULT macro. Strings don't count as they are output directly, so this only applies
|
|
to the formatting of bools, ints, floats, etc. This should be plenty of room for any of those types.
|
|
***********************************************************************************************************************************/
|
|
#define TEST_RESULT_FORMAT_SIZE 128
|
|
|
|
/***********************************************************************************************************************************
|
|
Test that an expected error is actually thrown and error when it isn't
|
|
***********************************************************************************************************************************/
|
|
#define TEST_ERROR(statement, errorTypeExpected, errorMessageExpected) \
|
|
{ \
|
|
bool TEST_ERROR_catch = false; \
|
|
\
|
|
printf( \
|
|
" %03u.%03us l%04d - expect %s: %s\n", (unsigned int)((testTimeMSec() - testTimeMSecBegin()) / 1000), \
|
|
(unsigned int)((testTimeMSec() - testTimeMSecBegin()) % 1000), __LINE__, errorTypeName(&errorTypeExpected), \
|
|
errorMessageExpected); \
|
|
fflush(stdout); \
|
|
\
|
|
TRY_BEGIN() \
|
|
{ \
|
|
statement; \
|
|
} \
|
|
CATCH_ANY() \
|
|
{ \
|
|
TEST_ERROR_catch = true; \
|
|
\
|
|
if (strcmp(errorMessage(), errorMessageExpected) != 0 || errorType() != &errorTypeExpected) \
|
|
THROW_FMT( \
|
|
AssertError, "EXPECTED %s: %s\n\n BUT GOT %s: %s\n\nTHROWN AT:\n%s", errorTypeName(&errorTypeExpected), \
|
|
errorMessageExpected, errorName(), errorMessage(), errorStackTrace()); \
|
|
} \
|
|
TRY_END(); \
|
|
\
|
|
if (!TEST_ERROR_catch) \
|
|
THROW_FMT( \
|
|
AssertError, "statement '%s' returned but error %s, '%s' was expected", #statement, errorTypeName(&errorTypeExpected), \
|
|
errorMessageExpected); \
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Test error with a formatted expected message
|
|
***********************************************************************************************************************************/
|
|
#define TEST_ERROR_FMT(statement, errorTypeExpected, ...) \
|
|
{ \
|
|
char TEST_ERROR_FMT_buffer[8192]; \
|
|
\
|
|
if (snprintf(TEST_ERROR_FMT_buffer, sizeof(TEST_ERROR_FMT_buffer), __VA_ARGS__) >= (int)sizeof(TEST_ERROR_FMT_buffer)) \
|
|
THROW_FMT(AssertError, "error message needs more than the %zu characters available", sizeof(TEST_ERROR_FMT_buffer)); \
|
|
\
|
|
TEST_ERROR(statement, errorTypeExpected, TEST_ERROR_FMT_buffer); \
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Format the test type into the given buffer -- or return verbatim if char *
|
|
***********************************************************************************************************************************/
|
|
#define TEST_TYPE_FORMAT_VAR(value) \
|
|
char value##StrBuffer[TEST_RESULT_FORMAT_SIZE + 1]; \
|
|
char *value##Str = value##StrBuffer;
|
|
|
|
#define TEST_TYPE_FORMAT_SPRINTF(format, value) \
|
|
if (snprintf((char *)value##Str, TEST_RESULT_FORMAT_SIZE + 1, format, value) > TEST_RESULT_FORMAT_SIZE) \
|
|
{ \
|
|
THROW_FMT( \
|
|
AssertError, "formatted type '%" format "' needs more than the %d characters available", TEST_RESULT_FORMAT_SIZE); \
|
|
}
|
|
|
|
#define TEST_TYPE_FORMAT(type, format, value) \
|
|
TEST_TYPE_FORMAT_VAR(value); \
|
|
TEST_TYPE_FORMAT_SPRINTF(format, value);
|
|
|
|
#define TEST_TYPE_FORMAT_PTR(type, format, value) \
|
|
TEST_TYPE_FORMAT_VAR(value); \
|
|
\
|
|
if (value == NULL) \
|
|
value##Str = (char *)"NULL"; \
|
|
else if (strcmp(#type, "char *") == 0) \
|
|
value##Str = (char *)value; \
|
|
else \
|
|
TEST_TYPE_FORMAT_SPRINTF(format, value);
|
|
|
|
/***********************************************************************************************************************************
|
|
Compare types
|
|
***********************************************************************************************************************************/
|
|
#define TEST_TYPE_COMPARE_STR(result, value, typeOp, valueExpected) \
|
|
if (value != NULL && valueExpected != NULL) \
|
|
result = strcmp((char *)value, (char *)valueExpected) typeOp 0; \
|
|
else \
|
|
result = value typeOp valueExpected;
|
|
|
|
#define TEST_TYPE_COMPARE(result, value, typeOp, valueExpected) \
|
|
result = value typeOp valueExpected;
|
|
|
|
/***********************************************************************************************************************************
|
|
Output information about the test
|
|
***********************************************************************************************************************************/
|
|
#define TEST_RESULT_INFO(...) \
|
|
printf( \
|
|
" %03u.%03us l%04d - ", (unsigned int)((testTimeMSec() - testTimeMSecBegin()) / 1000), \
|
|
(unsigned int)((testTimeMSec() - testTimeMSecBegin()) % 1000), __LINE__); \
|
|
printf(__VA_ARGS__); \
|
|
printf("\n"); \
|
|
fflush(stdout);
|
|
|
|
/***********************************************************************************************************************************
|
|
Test the result of a statement and make sure it matches the expected value. This macro can test any C type given the correct
|
|
parameters.
|
|
***********************************************************************************************************************************/
|
|
#define TEST_RESULT(statement, resultExpectedValue, type, format, formatMacro, typeOp, compareMacro, ...) \
|
|
{ \
|
|
/* Assign expected result to a local variable */ \
|
|
const type TEST_RESULT_resultExpected = (type)(resultExpectedValue); \
|
|
\
|
|
/* Output test info */ \
|
|
TEST_RESULT_INFO(__VA_ARGS__); \
|
|
\
|
|
/* Format the expected result */ \
|
|
formatMacro(type, format, TEST_RESULT_resultExpected); \
|
|
\
|
|
/* Try to run the statement. Assign expected to result to silence compiler warning about unitialized var. */ \
|
|
type TEST_RESULT_result = (type)TEST_RESULT_resultExpected; \
|
|
\
|
|
TRY_BEGIN() \
|
|
{ \
|
|
TEST_RESULT_result = (type)(statement); \
|
|
} \
|
|
/* Catch any errors */ \
|
|
CATCH_ANY() \
|
|
{ \
|
|
/* No errors were expected so error */ \
|
|
THROW_FMT( \
|
|
AssertError, "STATEMENT: %s\n\nTHREW %s: %s\n\nTHROWN AT:\n%s\n\nBUT EXPECTED RESULT:\n%s", \
|
|
#statement, errorName(), errorMessage(), errorStackTrace(), TEST_RESULT_resultExpectedStr); \
|
|
} \
|
|
TRY_END(); \
|
|
\
|
|
/* Test the type operator */ \
|
|
bool TEST_RESULT_resultOp = false; \
|
|
compareMacro(TEST_RESULT_resultOp, TEST_RESULT_result, typeOp, TEST_RESULT_resultExpected); \
|
|
\
|
|
/* If type operator test was not successful */ \
|
|
if (!TEST_RESULT_resultOp) \
|
|
{ \
|
|
/* Format the actual result */ \
|
|
formatMacro(type, format, TEST_RESULT_result); \
|
|
\
|
|
/* Throw error */ \
|
|
THROW_FMT( \
|
|
AssertError, "\n\nSTATEMENT: %s\n\nRESULT IS:\n%s\n\nBUT EXPECTED:\n%s\n\n", \
|
|
#statement, TEST_RESULT_resultStr, TEST_RESULT_resultExpectedStr); \
|
|
} \
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Test that a void statement returns and does not throw an error
|
|
***********************************************************************************************************************************/
|
|
#define TEST_RESULT_VOID(statement, ...) \
|
|
{ \
|
|
/* Output test info */ \
|
|
TEST_RESULT_INFO(__VA_ARGS__); \
|
|
\
|
|
TRY_BEGIN() \
|
|
{ \
|
|
statement; \
|
|
} \
|
|
/* Catch any errors */ \
|
|
CATCH_ANY() \
|
|
{ \
|
|
/* No errors were expected so error */ \
|
|
THROW_FMT( \
|
|
AssertError, "EXPECTED VOID RESULT FROM STATEMENT: %s\n\nBUT GOT %s: %s\n\nTHROWN AT:\n%s", #statement, errorName(), \
|
|
errorMessage(), errorStackTrace()); \
|
|
} \
|
|
TRY_END(); \
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Test that a statement does not error and assign it to the specified variable if not
|
|
***********************************************************************************************************************************/
|
|
#define TEST_ASSIGN(lValue, statement, ...) \
|
|
{ \
|
|
/* Output test info */ \
|
|
TEST_RESULT_INFO(__VA_ARGS__); \
|
|
\
|
|
TRY_BEGIN() \
|
|
{ \
|
|
lValue = statement; \
|
|
} \
|
|
/* Catch any errors */ \
|
|
CATCH_ANY() \
|
|
{ \
|
|
/* No errors were expected so error */ \
|
|
THROW_FMT( \
|
|
AssertError, "EXPECTED ASSIGNMENT FROM STATEMENT: %s\n\nBUT GOT %s: %s\n\nTHROWN AT:\n%s", #statement, errorName(), \
|
|
errorMessage(), errorStackTrace()); \
|
|
} \
|
|
TRY_END(); \
|
|
}
|
|
|
|
/***********************************************************************************************************************************
|
|
Macros to ease the use of common data types
|
|
***********************************************************************************************************************************/
|
|
#define TEST_RESULT_BOOL_PARAM(statement, resultExpected, typeOp, ...) \
|
|
TEST_RESULT(statement, resultExpected, bool, "%d", TEST_TYPE_FORMAT, typeOp, TEST_TYPE_COMPARE, __VA_ARGS__);
|
|
#define TEST_RESULT_BOOL(statement, resultExpected, ...) \
|
|
TEST_RESULT_BOOL_PARAM(statement, resultExpected, ==, __VA_ARGS__);
|
|
|
|
#define TEST_RESULT_CHAR_PARAM(statement, resultExpected, typeOp, ...) \
|
|
TEST_RESULT(statement, resultExpected, char, "%c", TEST_TYPE_FORMAT, typeOp, TEST_TYPE_COMPARE, __VA_ARGS__);
|
|
#define TEST_RESULT_CHAR(statement, resultExpected, ...) \
|
|
TEST_RESULT_CHAR_PARAM(statement, resultExpected, ==, __VA_ARGS__);
|
|
#define TEST_RESULT_CHAR_NE(statement, resultExpected, ...) \
|
|
TEST_RESULT_CHAR_PARAM(statement, resultExpected, !=, __VA_ARGS__);
|
|
|
|
#define TEST_RESULT_DOUBLE_PARAM(statement, resultExpected, typeOp, ...) \
|
|
TEST_RESULT(statement, resultExpected, double, "%f", TEST_TYPE_FORMAT, typeOp, TEST_TYPE_COMPARE, __VA_ARGS__);
|
|
#define TEST_RESULT_DOUBLE(statement, resultExpected, ...) \
|
|
TEST_RESULT_DOUBLE_PARAM(statement, resultExpected, ==, __VA_ARGS__);
|
|
|
|
#define TEST_RESULT_INT_PARAM(statement, resultExpected, typeOp, ...) \
|
|
TEST_RESULT(statement, resultExpected, int64_t, "%" PRId64, TEST_TYPE_FORMAT, typeOp, TEST_TYPE_COMPARE, __VA_ARGS__);
|
|
#define TEST_RESULT_INT(statement, resultExpected, ...) \
|
|
TEST_RESULT_INT_PARAM(statement, resultExpected, ==, __VA_ARGS__);
|
|
#define TEST_RESULT_INT_NE(statement, resultExpected, ...) \
|
|
TEST_RESULT_INT_PARAM(statement, resultExpected, !=, __VA_ARGS__);
|
|
|
|
#define TEST_RESULT_PTR_PARAM(statement, resultExpected, typeOp, ...) \
|
|
TEST_RESULT(statement, resultExpected, void *, "%p", TEST_TYPE_FORMAT_PTR, typeOp, TEST_TYPE_COMPARE, __VA_ARGS__);
|
|
#define TEST_RESULT_PTR(statement, resultExpected, ...) \
|
|
TEST_RESULT_PTR_PARAM(statement, resultExpected, ==, __VA_ARGS__);
|
|
#define TEST_RESULT_PTR_NE(statement, resultExpected, ...) \
|
|
TEST_RESULT_PTR_PARAM(statement, resultExpected, !=, __VA_ARGS__);
|
|
|
|
#define TEST_RESULT_SIZE_PARAM(statement, resultExpected, typeOp, ...) \
|
|
TEST_RESULT(statement, resultExpected, size_t, "%zu", TEST_TYPE_FORMAT, typeOp, TEST_TYPE_COMPARE, __VA_ARGS__);
|
|
#define TEST_RESULT_SIZE(statement, resultExpected, ...) \
|
|
TEST_RESULT_SIZE_PARAM(statement, resultExpected, ==, __VA_ARGS__);
|
|
#define TEST_RESULT_SIZE_NE(statement, resultExpected, ...) \
|
|
TEST_RESULT_SIZE_PARAM(statement, resultExpected, !=, __VA_ARGS__);
|
|
|
|
#define TEST_RESULT_STR_PARAM(statement, resultExpected, typeOp, ...) \
|
|
TEST_RESULT(statement, resultExpected, char *, "%s", TEST_TYPE_FORMAT_PTR, typeOp, TEST_TYPE_COMPARE_STR, __VA_ARGS__);
|
|
#define TEST_RESULT_STR(statement, resultExpected, ...) \
|
|
TEST_RESULT_STR_PARAM(statement, resultExpected, ==, __VA_ARGS__);
|
|
#define TEST_RESULT_STR_NE(statement, resultExpected, ...) \
|
|
TEST_RESULT_STR_PARAM(statement, resultExpected, !=, __VA_ARGS__);
|
|
|
|
#define TEST_RESULT_U16_HEX(statement, resultExpected, ...) \
|
|
TEST_RESULT(statement, resultExpected, uint16_t, "%04X", TEST_TYPE_FORMAT, ==, TEST_TYPE_COMPARE, __VA_ARGS__);
|
|
|
|
#define TEST_RESULT_UINT_PARAM(statement, resultExpected, typeOp, ...) \
|
|
TEST_RESULT(statement, resultExpected, uint64_t, "%" PRIu64, TEST_TYPE_FORMAT, typeOp, TEST_TYPE_COMPARE, __VA_ARGS__);
|
|
#define TEST_RESULT_UINT(statement, resultExpected, ...) \
|
|
TEST_RESULT_UINT_PARAM(statement, resultExpected, ==, __VA_ARGS__);
|
|
#define TEST_RESULT_UINT_NE(statement, resultExpected, ...) \
|
|
TEST_RESULT_UINT_PARAM(statement, resultExpected, !=, __VA_ARGS__);
|
|
|
|
/***********************************************************************************************************************************
|
|
Logging macros
|
|
***********************************************************************************************************************************/
|
|
#define TEST_LOG(message) \
|
|
do \
|
|
{ \
|
|
printf( \
|
|
" %03u.%03us %s\n", (unsigned int)((testTimeMSec() - testTimeMSecBegin()) / 1000), \
|
|
(unsigned int)((testTimeMSec() - testTimeMSecBegin()) % 1000), message); \
|
|
fflush(stdout); \
|
|
} while(0)
|
|
|
|
#define TEST_LOG_FMT(format, ...) \
|
|
do \
|
|
{ \
|
|
printf( \
|
|
" %03u.%03us " format "\n", (unsigned int)((testTimeMSec() - testTimeMSecBegin()) / 1000), \
|
|
(unsigned int)((testTimeMSec() - testTimeMSecBegin()) % 1000), __VA_ARGS__); \
|
|
fflush(stdout); \
|
|
} while(0)
|
|
|
|
#endif
|