1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-02-21 19:48:29 +02:00

Use __noreturn_ on error functions when coverage testing.

The errorInternalThrowSys*() functions were marked as returning during coverage testing even when they had no possibility to return, i.e. the error parameter was set to constant true. This meant the compiler would treat the functions as returning even when they would not.

Instead create completely separate functions for coverage to use for THROW_ON_SYS_ERROR*() that can return and leave the regular functions marked __noreturn__.
This commit is contained in:
David Steele 2020-04-14 11:43:50 -04:00
parent b7d8d61526
commit f03d1b5b7b
4 changed files with 104 additions and 74 deletions

View File

@ -41,6 +41,14 @@
<p>Split session functionality of <code>SocketClient</code> out into <code>SocketSession</code>.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-reviewer id="cynthia.shang"/>
</release-item-contributor-list>
<p>Use <code>__noreturn_</code> on error functions when coverage testing.</p>
</release-item>
</release-development-list>
</release-core-list>
</release>

View File

@ -386,16 +386,8 @@ errorInternalThrowFmt(
/**********************************************************************************************************************************/
void
errorInternalThrowSys(
#ifdef DEBUG_COVERAGE
bool error,
#endif
int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *message)
{
#ifdef DEBUG_COVERAGE
if (error)
{
#endif
// Format message with system message appended
if (errNo == 0)
{
@ -406,24 +398,23 @@ errorInternalThrowSys(
snprintf(messageBufferTemp, ERROR_MESSAGE_BUFFER_SIZE - 1, "%s: [%d] %s", message, errNo, strerror(errNo));
errorInternalThrow(errorType, fileName, functionName, fileLine, messageBufferTemp);
}
#ifdef DEBUG_COVERAGE
}
#endif
void
errorInternalThrowOnSys(
bool error, int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine,
const char *message)
{
if (error)
errorInternalThrowSys(errNo, errorType, fileName, functionName, fileLine, message);
}
#endif
void
errorInternalThrowSysFmt(
#ifdef DEBUG_COVERAGE
bool error,
#endif
int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *format, ...)
{
#ifdef DEBUG_COVERAGE
if (error)
{
#endif
// Format message
va_list argument;
va_start(argument, format);
@ -435,8 +426,30 @@ errorInternalThrowSysFmt(
snprintf(messageBufferTemp + messageSize, ERROR_MESSAGE_BUFFER_SIZE - 1 - messageSize, ": [%d] %s", errNo, strerror(errNo));
errorInternalThrow(errorType, fileName, functionName, fileLine, messageBufferTemp);
}
#ifdef DEBUG_COVERAGE
void
errorInternalThrowOnSysFmt(
bool error, int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine,
const char *format, ...)
{
if (error)
{
// Format message
va_list argument;
va_start(argument, format);
size_t messageSize = (size_t)vsnprintf(messageBufferTemp, ERROR_MESSAGE_BUFFER_SIZE - 1, format, argument);
va_end(argument);
// Append the system message
if (errNo != 0)
{
snprintf(
messageBufferTemp + messageSize, ERROR_MESSAGE_BUFFER_SIZE - 1 - messageSize, ": [%d] %s", errNo, strerror(errNo));
}
errorInternalThrow(errorType, fileName, functionName, fileLine, messageBufferTemp);
}
#endif
}
#endif

View File

@ -175,18 +175,28 @@ The seldom used "THROWP" variants allow an error to be thrown with a pointer to
/***********************************************************************************************************************************
Throw an error when a system call fails
***********************************************************************************************************************************/
// When coverage testing define special versions of the macros that don't contain branches. These macros are less efficient because
// they need to call errorInternalThrowSys*() before determining if there is an error or not, but they allow coverage testing for
#define THROW_SYS_ERROR(errorType, message) \
errorInternalThrowSys(errno, &errorType, __FILE__, __func__, __LINE__, message)
#define THROW_SYS_ERROR_FMT(errorType, ...) \
errorInternalThrowSysFmt(errno, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP_SYS_ERROR(errorType, message) \
errorInternalThrowSys(errno, errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_SYS_ERROR_FMT(errorType, ...) \
errorInternalThrowSysFmt(errno, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROW_SYS_ERROR_CODE(errNo, errorType, message) \
errorInternalThrowSys(errNo, &errorType, __FILE__, __func__, __LINE__, message)
#define THROW_SYS_ERROR_CODE_FMT(errNo, errorType, ...) \
errorInternalThrowSysFmt(errNo, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP_SYS_ERROR_CODE(errNo, errorType, message) \
errorInternalThrowSys(errNo, errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_SYS_ERROR_CODE_FMT(errNo, errorType, ...) \
errorInternalThrowSysFmt(errNo, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
// When coverage testing, define special versions of the macros that don't contain branches. These macros are less efficient because
// they need to call errorInternalThrowOnSys*() before determining if there is an error or not, but they allow coverage testing for
// THROW*_ON*() calls that contain conditionals.
#ifdef DEBUG_COVERAGE
#define THROW_SYS_ERROR(errorType, message) \
errorInternalThrowSys(true, errno, &errorType, __FILE__, __func__, __LINE__, message)
#define THROW_SYS_ERROR_FMT(errorType, ...) \
errorInternalThrowSysFmt(true, errno, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP_SYS_ERROR(errorType, message) \
errorInternalThrowSys(true, errno, errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_SYS_ERROR_FMT(errorType, ...) \
errorInternalThrowSysFmt(true, errno, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
// The expression can't be passed directly to errorInternalThrowSys*() because we need to be sure it is evaluated before passing
// errno. Depending on optimization that might not happen.
@ -194,50 +204,32 @@ Throw an error when a system call fails
do \
{ \
bool error = expression; \
errorInternalThrowSys(error, errno, &errorType, __FILE__, __func__, __LINE__, message); \
errorInternalThrowOnSys(error, errno, &errorType, __FILE__, __func__, __LINE__, message); \
} while(0)
#define THROW_ON_SYS_ERROR_FMT(expression, errorType, ...) \
do \
{ \
bool error = expression; \
errorInternalThrowSysFmt(error, errno, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__); \
errorInternalThrowOnSysFmt(error, errno, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__); \
} while(0)
#define THROWP_ON_SYS_ERROR(expression, errorType, message) \
do \
{ \
bool error = expression; \
errorInternalThrowSys(error, errno, errorType, __FILE__, __func__, __LINE__, message); \
errorInternalThrowOnSys(error, errno, errorType, __FILE__, __func__, __LINE__, message); \
} while(0)
#define THROWP_ON_SYS_ERROR_FMT(expression, errorType, ...) \
do \
{ \
bool error = expression; \
errorInternalThrowSysFmt(error, errno, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__); \
errorInternalThrowOnSysFmt(error, errno, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__); \
} while(0)
#define THROW_SYS_ERROR_CODE(errNo, errorType, message) \
errorInternalThrowSys(true, errNo, &errorType, __FILE__, __func__, __LINE__, message)
#define THROW_SYS_ERROR_CODE_FMT(errNo, errorType, ...) \
errorInternalThrowSysFmt(true, errNo, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP_SYS_ERROR_CODE(errNo, errorType, message) \
errorInternalThrowSys(true, errNo, errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_SYS_ERROR_CODE_FMT(errNo, errorType, ...) \
errorInternalThrowSysFmt(true, errNo, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
// Else define the normal macros which check for an error first
#else
#define THROW_SYS_ERROR(errorType, message) \
errorInternalThrowSys(errno, &errorType, __FILE__, __func__, __LINE__, message)
#define THROW_SYS_ERROR_FMT(errorType, ...) \
errorInternalThrowSysFmt(errno, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP_SYS_ERROR(errorType, message) \
errorInternalThrowSys(errno, errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_SYS_ERROR_FMT(errorType, ...) \
errorInternalThrowSysFmt(errno, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROW_ON_SYS_ERROR(expression, errorType, message) \
do \
{ \
@ -265,15 +257,6 @@ Throw an error when a system call fails
if (expression) \
errorInternalThrowSysFmt(errno, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__); \
} while(0)
#define THROW_SYS_ERROR_CODE(errNo, errorType, message) \
errorInternalThrowSys(errNo, &errorType, __FILE__, __func__, __LINE__, message)
#define THROW_SYS_ERROR_CODE_FMT(errNo, errorType, ...) \
errorInternalThrowSysFmt(errNo, &errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#define THROWP_SYS_ERROR_CODE(errNo, errorType, message) \
errorInternalThrowSys(errNo, errorType, __FILE__, __func__, __LINE__, message)
#define THROWP_SYS_ERROR_CODE_FMT(errNo, errorType, ...) \
errorInternalThrowSysFmt(errNo, errorType, __FILE__, __func__, __LINE__, __VA_ARGS__)
#endif
/***********************************************************************************************************************************
@ -318,25 +301,23 @@ void errorInternalThrowFmt(
// Throw a system error
void errorInternalThrowSys(
#ifdef DEBUG_COVERAGE
bool error,
#endif
int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *message)
#ifdef DEBUG_COVERAGE
;
#else
__attribute__((__noreturn__));
#endif
// Throw a formatted system error
void errorInternalThrowSysFmt(
#ifdef DEBUG_COVERAGE
bool error,
#endif
int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine, const char *format, ...)
#ifdef DEBUG_COVERAGE
__attribute__((format(printf, 7, 8)));
#else
__attribute__((format(printf, 6, 7))) __attribute__((__noreturn__));
// Versions of the above for coverage testing which checks the error condition inside the function
#ifdef DEBUG_COVERAGE
void errorInternalThrowOnSys(
bool error, int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine,
const char *message);
void errorInternalThrowOnSysFmt(
bool error, int errNo, const ErrorType *errorType, const char *fileName, const char *functionName, int fileLine,
const char *format, ...) __attribute__((format(printf, 7, 8)));
#endif
/***********************************************************************************************************************************

View File

@ -261,7 +261,7 @@ testRun(void)
TRY_BEGIN()
{
errno = E2BIG;
THROW_SYS_ERROR(AssertError, "message");
THROW_ON_SYS_ERROR(true, AssertError, "message");
}
CATCH_ANY()
{
@ -271,6 +271,34 @@ testRun(void)
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
errno = 0;
THROW_ON_SYS_ERROR_FMT(true, AssertError, "message %d", 77);
}
CATCH_ANY()
{
printf("%s\n", errorMessage());
assert(errorCode() == AssertError.code);
assert(strcmp(errorMessage(), "message 77") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{
errno = E2BIG;
THROW_ON_SYS_ERROR_FMT(true, AssertError, "message %d", 77);
}
CATCH_ANY()
{
printf("%s\n", errorMessage());
assert(errorCode() == AssertError.code);
assert(strcmp(errorMessage(), "message 77: [7] Argument list too long") == 0);
}
TRY_END();
// -------------------------------------------------------------------------------------------------------------------------
TRY_BEGIN()
{