From dbb1248bfbb1d469bae030084e6672772702f89d Mon Sep 17 00:00:00 2001 From: David Steele Date: Sun, 22 Mar 2020 20:44:51 -0400 Subject: [PATCH] Implement TEST_RESULT_*() macros with functions, mostly. The prior macros had grown over time to be pretty significant pieces of code that required a lot of compile time, though runtime was efficient. Move most of the macro code into functions to reduce compile time, perhaps at a slight expense to runtime. The overall performance benefit is 10-15% so this seems like a good tradeoff. Add TEST_RESULT_UINT_INT() to safely compare uint to int with range checking. --- test/src/common/harnessTest.c | 222 +++++++++++++++++++++++++++ test/src/common/harnessTest.h | 218 ++++++++++---------------- test/src/common/harnessTest.intern.h | 18 +++ test/src/module/common/cryptoTest.c | 4 +- test/src/test.c | 3 + 5 files changed, 330 insertions(+), 135 deletions(-) diff --git a/test/src/common/harnessTest.c b/test/src/common/harnessTest.c index f41507323..3b31b7307 100644 --- a/test/src/common/harnessTest.c +++ b/test/src/common/harnessTest.c @@ -471,6 +471,228 @@ hrnTestResultEnd(void) harnessTestLocal.result.running = false; } +/**********************************************************************************************************************************/ +static void hrnTestResultDiff(const char *actual, const char *expected) +{ + if (actual != NULL && expected != NULL && (strstr(actual, "\n") != NULL || strstr(expected, "\n") != NULL)) + { + THROW_FMT( +#ifndef NDEBUG + TestError, +#else + AssertError, +#endif + "STATEMENT: %s\n\nRESULT IS:\n%s\n\nBUT DIFF IS (- remove from expected, + add to expected):\n%s\n\n", + harnessTestLocal.result.statement, actual, hrnDiff(expected, actual)); + } + else + { + THROW_FMT( +#ifndef NDEBUG + TestError, +#else + AssertError, +#endif + "STATEMENT: %s\n\nRESULT IS:\n%s\n\nBUT EXPECTED:\n%s", + harnessTestLocal.result.statement, actual == NULL ? "NULL" : actual, expected == NULL ? "NULL" : expected); \ + } +} + +void hrnTestResultBool(int actual, int expected) +{ + ASSERT(harnessTestLocal.result.running); + + if (actual < 0 || actual > 1 || expected < 0 || expected > 1 || actual != expected) + { + char actualZ[256]; + char expectedZ[256]; + + if (actual < 0 || actual > 1) + snprintf(actualZ, sizeof(actualZ), "INVALID(%d)", actual); + else + actual ? strcpy(actualZ, "true") : strcpy(actualZ, "false"); + + if (expected < 0 || expected > 1) + snprintf(expectedZ, sizeof(expectedZ), "INVALID(%d)", expected); + else + expected ? strcpy(expectedZ, "true") : strcpy(expectedZ, "false"); + + hrnTestResultDiff(actualZ, expectedZ); + } + + hrnTestResultEnd(); +} + +void hrnTestResultDouble(double actual, double expected) +{ + ASSERT(harnessTestLocal.result.running); + + if (actual != expected) + { + char actualZ[256]; + char expectedZ[256]; + + snprintf(actualZ, sizeof(actualZ), "%f", actual); + snprintf(expectedZ, sizeof(expectedZ), "%f", expected); + + hrnTestResultDiff(actualZ, expectedZ); + } + + hrnTestResultEnd(); +} + +void hrnTestResultInt64(int64_t actual, int64_t expected, HarnessTestResultOperation operation) +{ + ASSERT(harnessTestLocal.result.running); + + bool result = false; + + switch (operation) + { + case harnessTestResultOperationEq: + { + result = actual == expected; + break; + } + + case harnessTestResultOperationNe: + { + result = actual != expected; + break; + } + } + + if (!result) + { + char actualZ[256]; + char expectedZ[256]; + + snprintf(actualZ, sizeof(actualZ), "%" PRId64, actual); + snprintf(expectedZ, sizeof(expectedZ), "%" PRId64, expected); + + hrnTestResultDiff(actualZ, expectedZ); + } + + hrnTestResultEnd(); +} + +void hrnTestResultPtr(const void *actual, const void *expected, HarnessTestResultOperation operation) +{ + ASSERT(harnessTestLocal.result.running); + + bool result = false; + + switch (operation) + { + case harnessTestResultOperationEq: + { + result = actual == expected; + break; + } + + case harnessTestResultOperationNe: + { + result = actual != expected; + break; + } + } + + if (!result) + { + char actualZ[256]; + char expectedZ[256]; + + snprintf(actualZ, sizeof(actualZ), "%p", actual); + snprintf(expectedZ, sizeof(expectedZ), "%p", expected); + + hrnTestResultDiff(actualZ, expectedZ); + } + + hrnTestResultEnd(); +} + +void hrnTestResultUInt64(uint64_t actual, uint64_t expected, HarnessTestResultOperation operation) +{ + ASSERT(harnessTestLocal.result.running); + + bool result = false; + + switch (operation) + { + case harnessTestResultOperationEq: + { + result = actual == expected; + break; + } + + case harnessTestResultOperationNe: + { + result = actual != expected; + break; + } + } + + if (!result) + { + char actualZ[256]; + char expectedZ[256]; + + snprintf(actualZ, sizeof(actualZ), "%" PRIu64, actual); + snprintf(expectedZ, sizeof(expectedZ), "%" PRIu64, expected); + + hrnTestResultDiff(actualZ, expectedZ); + } + + hrnTestResultEnd(); +} + +void hrnTestResultUInt64Int64(uint64_t actual, int64_t expected, HarnessTestResultOperation operation) +{ + ASSERT(harnessTestLocal.result.running); + + if (actual <= INT64_MAX && expected >= 0) + hrnTestResultUInt64(actual, (uint64_t)expected, operation); + else + { + char actualZ[256]; + char expectedZ[256]; + + snprintf(actualZ, sizeof(actualZ), "%" PRIu64, actual); + snprintf(expectedZ, sizeof(expectedZ), "%" PRId64, expected); + + hrnTestResultDiff(actualZ, expectedZ); + } +} + +void hrnTestResultZ(const char *actual, const char *expected, HarnessTestResultOperation operation) +{ + ASSERT(harnessTestLocal.result.running); + + bool result = false; + + switch (operation) + { + case harnessTestResultOperationEq: + { + result = (actual == NULL && expected == NULL) || (actual != NULL && expected != NULL && strcmp(actual, expected) == 0); + break; + } + + case harnessTestResultOperationNe: + { + result = + (actual == NULL && expected != NULL) || (actual != NULL && expected == NULL) || + (actual != NULL && expected != NULL && strcmp(actual, expected) == 0); + break; + } + } + + if (!result) + hrnTestResultDiff(actual, expected); + + hrnTestResultEnd(); +} + /*********************************************************************************************************************************** Getters ***********************************************************************************************************************************/ diff --git a/test/src/common/harnessTest.h b/test/src/common/harnessTest.h index 5d336f356..33f2ad06f 100644 --- a/test/src/common/harnessTest.h +++ b/test/src/common/harnessTest.h @@ -84,12 +84,6 @@ const char *testProjectExe(void); // For scaling performance tests uint64_t testScale(void); -/*********************************************************************************************************************************** -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 ***********************************************************************************************************************************/ @@ -138,46 +132,6 @@ Test error with a formatted expected message 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 ***********************************************************************************************************************************/ @@ -187,64 +141,12 @@ Output information about the test 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 uninitialized var. */ \ - type TEST_RESULT_result = (type)TEST_RESULT_resultExpected; \ - \ - hrnTestResultBegin(#statement, __LINE__, true); \ - TEST_RESULT_result = (type)(statement); \ - hrnTestResultEnd(); \ - \ - /* 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 diff error */ \ - if (strcmp(#type, "char *") == 0 && strstr(TEST_RESULT_resultStr, "\n") != NULL) \ - { \ - THROW_FMT( \ - AssertError, \ - "\n\nSTATEMENT: %s\n\nRESULT IS:\n%s\n\nBUT DIFF IS (- remove from expected, + add to expected):\n%s\n\n", \ - #statement, TEST_RESULT_resultStr, hrnDiff(TEST_RESULT_resultExpectedStr, TEST_RESULT_resultStr)); \ - } \ - /* Throw error */ \ - else \ - { \ - 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__); \ - \ hrnTestResultBegin(#statement, __LINE__, false); \ statement; \ hrnTestResultEnd(); \ @@ -255,47 +157,80 @@ Test that a statement does not error and assign it to the specified variable if ***********************************************************************************************************************************/ #define TEST_ASSIGN(lValue, statement, ...) \ { \ - /* Output test info */ \ TEST_RESULT_INFO(__VA_ARGS__); \ - \ hrnTestResultBegin(#statement, __LINE__, true); \ lValue = statement; \ hrnTestResultEnd(); \ } /*********************************************************************************************************************************** -Macros to ease the use of common data types +Macros to compare results 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_BOOL_PARAM(statement, expected, ...) \ + do \ + { \ + TEST_RESULT_INFO(__VA_ARGS__); \ + hrnTestResultBegin(#statement, __LINE__, true); \ + hrnTestResultBool(statement, expected); \ + } \ + while (0) -#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_BOOL(statement, expected, ...) \ + TEST_RESULT_BOOL_PARAM(statement, expected, __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_DOUBLE_PARAM(statement, expected, ...) \ + do \ + { \ + TEST_RESULT_INFO(__VA_ARGS__); \ + hrnTestResultBegin(#statement, __LINE__, true); \ + hrnTestResultDouble(statement, expected); \ + } \ + while (0) -#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_DOUBLE(statement, expected, ...) \ + TEST_RESULT_DOUBLE_PARAM(statement, expected, __VA_ARGS__); -#define TEST_RESULT_Z_PARAM(statement, resultExpected, typeOp, ...) \ - TEST_RESULT(statement, resultExpected, char *, "%s", TEST_TYPE_FORMAT_PTR, typeOp, TEST_TYPE_COMPARE_STR, __VA_ARGS__); -#define TEST_RESULT_Z(statement, resultExpected, ...) \ - TEST_RESULT_Z_PARAM(statement, resultExpected, ==, __VA_ARGS__); -#define TEST_RESULT_Z_NE(statement, resultExpected, ...) \ - TEST_RESULT_Z_PARAM(statement, resultExpected, !=, __VA_ARGS__); +#define TEST_RESULT_INT_PARAM(statement, expected, operation, ...) \ + do \ + { \ + TEST_RESULT_INFO(__VA_ARGS__); \ + hrnTestResultBegin(#statement, __LINE__, true); \ + hrnTestResultInt64(statement, expected, operation); \ + } \ + while (0) + +#define TEST_RESULT_INT(statement, expected, ...) \ + TEST_RESULT_INT_PARAM(statement, expected, harnessTestResultOperationEq, __VA_ARGS__); +#define TEST_RESULT_INT_NE(statement, expected, ...) \ + TEST_RESULT_INT_PARAM(statement, expected, harnessTestResultOperationNe, __VA_ARGS__); + +#define TEST_RESULT_PTR_PARAM(statement, expected, operation, ...) \ + do \ + { \ + TEST_RESULT_INFO(__VA_ARGS__); \ + hrnTestResultBegin(#statement, __LINE__, true); \ + hrnTestResultPtr(statement, expected, operation); \ + } \ + while (0) + +#define TEST_RESULT_PTR(statement, expected, ...) \ + TEST_RESULT_PTR_PARAM(statement, expected, harnessTestResultOperationEq, __VA_ARGS__); +#define TEST_RESULT_PTR_NE(statement, expected, ...) \ + TEST_RESULT_PTR_PARAM(statement, expected, harnessTestResultOperationNe, __VA_ARGS__); + +#define TEST_RESULT_Z_PARAM(statement, expected, operation, ...) \ + do \ + { \ + TEST_RESULT_INFO(__VA_ARGS__); \ + hrnTestResultBegin(#statement, __LINE__, true); \ + hrnTestResultZ(statement, expected, operation); \ + } \ + while (0) + +#define TEST_RESULT_Z(statement, expected, ...) \ + TEST_RESULT_Z_PARAM(statement, expected, harnessTestResultOperationEq, __VA_ARGS__); +#define TEST_RESULT_Z_NE(statement, expected, ...) \ + TEST_RESULT_Z_PARAM(statement, expected, harnessTestResultOperationNe, __VA_ARGS__); #define TEST_RESULT_STR(statement, resultExpected, ...) \ TEST_RESULT_Z(strPtr(statement), strPtr(resultExpected), __VA_ARGS__); @@ -306,12 +241,29 @@ Macros to ease the use of common data types #define TEST_RESULT_Z_STR(statement, resultExpected, ...) \ TEST_RESULT_Z(statement, strPtr(resultExpected), __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__); +#define TEST_RESULT_UINT_PARAM(statement, expected, operation, ...) \ + do \ + { \ + TEST_RESULT_INFO(__VA_ARGS__); \ + hrnTestResultBegin(#statement, __LINE__, true); \ + hrnTestResultUInt64(statement, expected, operation); \ + } \ + while (0) + +#define TEST_RESULT_UINT(statement, expected, ...) \ + TEST_RESULT_UINT_PARAM(statement, expected, harnessTestResultOperationEq, __VA_ARGS__); + +#define TEST_RESULT_UINT_INT_PARAM(statement, expected, operation, ...) \ + do \ + { \ + TEST_RESULT_INFO(__VA_ARGS__); \ + hrnTestResultBegin(#statement, __LINE__, true); \ + hrnTestResultUInt64Int64(statement, expected, operation); \ + } \ + while (0) + +#define TEST_RESULT_UINT_INT(statement, expected, ...) \ + TEST_RESULT_UINT_INT_PARAM(statement, expected, harnessTestResultOperationEq, __VA_ARGS__); /*********************************************************************************************************************************** Test system calls diff --git a/test/src/common/harnessTest.intern.h b/test/src/common/harnessTest.intern.h index ac52f61c7..46876ee0d 100644 --- a/test/src/common/harnessTest.intern.h +++ b/test/src/common/harnessTest.intern.h @@ -8,6 +8,15 @@ C Test Harness Internal #include "common/harnessTest.h" +/*********************************************************************************************************************************** +Test result operations +***********************************************************************************************************************************/ +typedef enum +{ + harnessTestResultOperationEq, + harnessTestResultOperationNe, +} HarnessTestResultOperation; + /*********************************************************************************************************************************** Functions ***********************************************************************************************************************************/ @@ -25,4 +34,13 @@ void hrnTestResultBegin(const char *statement, int lineNo, bool result); bool hrnTestResultException(void); void hrnTestResultEnd(void); +// Test results for various types +void hrnTestResultBool(int actual, int expected); +void hrnTestResultDouble(double actual, double expected); +void hrnTestResultInt64(int64_t actual, int64_t expected, HarnessTestResultOperation operation); +void hrnTestResultPtr(const void *actual, const void *expected, HarnessTestResultOperation operation); +void hrnTestResultUInt64(uint64_t actual, uint64_t expected, HarnessTestResultOperation operation); +void hrnTestResultUInt64Int64(uint64_t actual, int64_t expected, HarnessTestResultOperation operation); +void hrnTestResultZ(const char *actual, const char *expected, HarnessTestResultOperation operation); + #endif diff --git a/test/src/module/common/cryptoTest.c b/test/src/module/common/cryptoTest.c index b17256eb5..33461fe16 100644 --- a/test/src/module/common/cryptoTest.c +++ b/test/src/module/common/cryptoTest.c @@ -165,7 +165,7 @@ testRun(void) "check process size"); ioFilterProcessInOut(blockDecryptFilter, encryptBuffer, decryptBuffer); - TEST_RESULT_UINT(bufUsed(decryptBuffer), EVP_CIPHER_block_size(blockDecrypt->cipher), "decrypt size is one block"); + TEST_RESULT_UINT_INT(bufUsed(decryptBuffer), EVP_CIPHER_block_size(blockDecrypt->cipher), "decrypt size is one block"); ioFilterProcessInOut(blockDecryptFilter, NULL, decryptBuffer); TEST_RESULT_UINT(bufUsed(decryptBuffer), strlen(TEST_PLAINTEXT) * 2, "check final decrypt size"); @@ -205,7 +205,7 @@ testRun(void) blockDecryptFilter, bufNewC(bufPtr(encryptBuffer) + CIPHER_BLOCK_HEADER_SIZE, bufUsed(encryptBuffer) - CIPHER_BLOCK_HEADER_SIZE), decryptBuffer); - TEST_RESULT_UINT(bufUsed(decryptBuffer), EVP_CIPHER_block_size(blockDecrypt->cipher), "decrypt size is one block"); + TEST_RESULT_UINT_INT(bufUsed(decryptBuffer), EVP_CIPHER_block_size(blockDecrypt->cipher), "decrypt size is one block"); ioFilterProcessInOut(blockDecryptFilter, NULL, decryptBuffer); TEST_RESULT_UINT(bufUsed(decryptBuffer), strlen(TEST_PLAINTEXT) * 2, "check final decrypt size"); diff --git a/test/src/test.c b/test/src/test.c index 55f4d40f4..ef3c90076 100644 --- a/test/src/test.c +++ b/test/src/test.c @@ -113,6 +113,9 @@ main(int argListSize, const char *argList[]) CATCH_ANY() { // If a test was running then throw a detailed result exception +#ifndef NDEBUG + if (!errorInstanceOf(&TestError)) +#endif hrnTestResultException(); // Else rethrow the original error