michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: /* michael@0: * testutil.h michael@0: * michael@0: * Utility functions for handling test errors michael@0: * michael@0: */ michael@0: michael@0: #ifndef _TESTUTIL_H michael@0: #define _TESTUTIL_H michael@0: michael@0: #include "pkix.h" michael@0: #include "plstr.h" michael@0: #include "prprf.h" michael@0: #include "prlong.h" michael@0: #include "pkix_pl_common.h" michael@0: #include "secutil.h" michael@0: #include michael@0: #include michael@0: michael@0: #ifdef __cplusplus michael@0: extern "C" { michael@0: #endif michael@0: michael@0: /* michael@0: * In order to have a consistent format for displaying test information, michael@0: * all tests are REQUIRED to use the functions provided by this library michael@0: * (libtestutil.a) for displaying their information. michael@0: * michael@0: * A test using this library begins with a call to startTests with the test michael@0: * name as the arg (which is used only for formatting). Before the first michael@0: * subtest, a call to subTest should be made with the subtest name as the arg michael@0: * (again, for formatting). If the subTest is successful, then no action michael@0: * is needed. However, if the subTest is not successful, then a call michael@0: * to testError should be made with a descriptive error message as the arg. michael@0: * Note that a subTest MUST NOT call testError more than once. michael@0: * Finally, a call to endTests is made with the test name as the arg (for michael@0: * formatting). Note that most of these macros assume that a variable named michael@0: * "plContext" of type (void *) has been defined by the test. As such, it michael@0: * is essential that the test satisfy this condition. michael@0: */ michael@0: michael@0: /* michael@0: * PKIX_TEST_STD_VARS should be called at the beginning of every function michael@0: * that uses PKIX_TEST_RETURN (e.g. subTests), but it should be called only michael@0: * AFTER declaring local variables (so we don't get compiler warnings about michael@0: * declarations after statements). PKIX_TEST_STD_VARS declares and initializes michael@0: * several variables needed by the other test macros. michael@0: */ michael@0: #define PKIX_TEST_STD_VARS() \ michael@0: PKIX_Error *pkixTestErrorResult = NULL; \ michael@0: char *pkixTestErrorMsg = NULL; michael@0: michael@0: /* michael@0: * PKIX_TEST_EXPECT_NO_ERROR should be used to wrap a standard PKIX function michael@0: * call (one which returns a pointer to PKIX_Error) that is expected to return michael@0: * NULL (i.e. to succeed). If "pkixTestErrorResult" is not NULL, michael@0: * "goto cleanup" is executed, where a testError call is made if there were michael@0: * unexpected results. This macro MUST NOT be called after the "cleanup" label. michael@0: * michael@0: * Example Usage: PKIX_TEST_EXPECT_NO_ERROR(pkixFunc_expected_to_succeed(...)); michael@0: */ michael@0: michael@0: #define PKIX_TEST_EXPECT_NO_ERROR(func) \ michael@0: do { \ michael@0: pkixTestErrorResult = (func); \ michael@0: if (pkixTestErrorResult) { \ michael@0: goto cleanup; \ michael@0: } \ michael@0: } while (0) michael@0: michael@0: /* michael@0: * PKIX_TEST_EXPECT_ERROR should be used to wrap a standard PKIX function call michael@0: * (one which returns a pointer to PKIX_Error) that is expected to return michael@0: * a non-NULL value (i.e. to fail). If "pkixTestErrorResult" is NULL, michael@0: * "pkixTestErrorMsg" is set to a standard string and "goto cleanup" michael@0: * is executed, where a testError call is made if there were unexpected michael@0: * results. This macro MUST NOT be called after the "cleanup" label. michael@0: * michael@0: * Example Usage: PKIX_TEST_EXPECT_ERROR(pkixFunc_expected_to_fail(...)); michael@0: */ michael@0: michael@0: #define PKIX_TEST_EXPECT_ERROR(func) \ michael@0: do { \ michael@0: pkixTestErrorResult = (func); \ michael@0: if (!pkixTestErrorResult){ \ michael@0: pkixTestErrorMsg = \ michael@0: "Should have thrown an error here."; \ michael@0: goto cleanup; \ michael@0: } \ michael@0: PKIX_TEST_DECREF_BC(pkixTestErrorResult); \ michael@0: } while (0) michael@0: michael@0: /* michael@0: * PKIX_TEST_DECREF_BC is a convenience macro which should only be called michael@0: * BEFORE the "cleanup" label ("BC"). If the input parameter is non-NULL, it michael@0: * DecRefs the input parameter and wraps the function with michael@0: * PKIX_TEST_EXPECT_NO_ERROR, which executes "goto cleanup" upon error. michael@0: * This macro MUST NOT be called after the "cleanup" label. michael@0: */ michael@0: michael@0: #define PKIX_TEST_DECREF_BC(obj) \ michael@0: do { \ michael@0: if (obj){ \ michael@0: PKIX_TEST_EXPECT_NO_ERROR \ michael@0: (PKIX_PL_Object_DecRef \ michael@0: ((PKIX_PL_Object*)(obj), plContext)); \ michael@0: obj = NULL; \ michael@0: } \ michael@0: } while (0) michael@0: michael@0: /* michael@0: * PKIX_TEST_DECREF_AC is a convenience macro which should only be called michael@0: * AFTER the "cleanup" label ("AC"). If the input parameter is non-NULL, it michael@0: * DecRefs the input parameter. A pkixTestTempResult variable is used to prevent michael@0: * incorrectly overwriting pkixTestErrorResult with NULL. michael@0: * In the case DecRef succeeds, pkixTestTempResult will be NULL, and we won't michael@0: * overwrite a previously set pkixTestErrorResult (if any). If DecRef fails, michael@0: * then we do want to overwrite a previously set pkixTestErrorResult since a michael@0: * DecRef failure is fatal and may be indicative of memory corruption. michael@0: */ michael@0: michael@0: #define PKIX_TEST_DECREF_AC(obj) \ michael@0: do { \ michael@0: if (obj){ \ michael@0: PKIX_Error *pkixTestTempResult = NULL; \ michael@0: pkixTestTempResult = \ michael@0: PKIX_PL_Object_DecRef \ michael@0: ((PKIX_PL_Object*)(obj), plContext); \ michael@0: if (pkixTestTempResult) \ michael@0: pkixTestErrorResult = pkixTestTempResult; \ michael@0: obj = NULL; \ michael@0: } \ michael@0: } while (0) michael@0: michael@0: /* michael@0: * PKIX_TEST_RETURN must always be AFTER the "cleanup" label. It does nothing michael@0: * if everything went as expected. However, if there were unexpected results, michael@0: * PKIX_TEST_RETURN calls testError, which displays a standard failure message michael@0: * and increments the number of subtests that have failed. In the case michael@0: * of an unexpected error, testError is called using the error's description michael@0: * as an input and the error is DecRef'd. In the case of unexpected success michael@0: * testError is called with a standard string. michael@0: */ michael@0: #define PKIX_TEST_RETURN() \ michael@0: { \ michael@0: if (pkixTestErrorMsg){ \ michael@0: testError(pkixTestErrorMsg); \ michael@0: } else if (pkixTestErrorResult){ \ michael@0: pkixTestErrorMsg = \ michael@0: PKIX_Error2ASCII \ michael@0: (pkixTestErrorResult, plContext); \ michael@0: if (pkixTestErrorMsg) { \ michael@0: testError(pkixTestErrorMsg); \ michael@0: PKIX_PL_Free \ michael@0: ((PKIX_PL_Object *)pkixTestErrorMsg, \ michael@0: plContext); \ michael@0: } else { \ michael@0: testError("PKIX_Error2ASCII Failed"); \ michael@0: } \ michael@0: if (pkixTestErrorResult != PKIX_ALLOC_ERROR()){ \ michael@0: PKIX_PL_Object_DecRef \ michael@0: ((PKIX_PL_Object*)pkixTestErrorResult, \ michael@0: plContext); \ michael@0: pkixTestErrorResult = NULL; \ michael@0: } \ michael@0: } \ michael@0: } michael@0: michael@0: /* michael@0: * PKIX_TEST_EQ_HASH_TOSTR_DUP is a convenience macro which executes the michael@0: * standard set of operations that test the Equals, Hashcode, ToString, and michael@0: * Duplicate functions of an object type. The goodObj, equalObj, and diffObj michael@0: * are as the names suggest. The expAscii parameter is the expected result of michael@0: * calling ToString on the goodObj. If expAscii is NULL, then ToString will michael@0: * not be called on the goodObj. The checkDuplicate parameter is treated as michael@0: * a Boolean to indicate whether the Duplicate function should be tested. If michael@0: * checkDuplicate is NULL, then Duplicate will not be called on the goodObj. michael@0: * The type is the name of the function's family. For example, if the type is michael@0: * Cert, this macro will call PKIX_PL_Cert_Equals, PKIX_PL_Cert_Hashcode, and michael@0: * PKIX_PL_Cert_ToString. michael@0: * michael@0: * Note: If goodObj uses the default Equals and Hashcode functions, then michael@0: * for goodObj and equalObj to be equal, they must have the same pointer value. michael@0: */ michael@0: #define PKIX_TEST_EQ_HASH_TOSTR_DUP(goodObj, equalObj, diffObj, \ michael@0: expAscii, type, checkDuplicate) \ michael@0: do { \ michael@0: subTest("PKIX_PL_" #type "_Equals "); \ michael@0: testEqualsHelper \ michael@0: ((PKIX_PL_Object *)(goodObj), \ michael@0: (PKIX_PL_Object *)(equalObj), \ michael@0: PKIX_TRUE, \ michael@0: plContext); \ michael@0: subTest("PKIX_PL_" #type "_Hashcode "); \ michael@0: testHashcodeHelper \ michael@0: ((PKIX_PL_Object *)(goodObj), \ michael@0: (PKIX_PL_Object *)(equalObj), \ michael@0: PKIX_TRUE, \ michael@0: plContext); \ michael@0: subTest("PKIX_PL_" #type "_Equals "); \ michael@0: testEqualsHelper \ michael@0: ((PKIX_PL_Object *)(goodObj), \ michael@0: (PKIX_PL_Object *)(diffObj), \ michael@0: PKIX_FALSE, \ michael@0: plContext); \ michael@0: subTest("PKIX_PL_" #type "_Hashcode "); \ michael@0: testHashcodeHelper \ michael@0: ((PKIX_PL_Object *)(goodObj), \ michael@0: (PKIX_PL_Object *)(diffObj), \ michael@0: PKIX_FALSE, \ michael@0: plContext); \ michael@0: if (expAscii){ \ michael@0: subTest("PKIX_PL_" #type "_ToString"); \ michael@0: testToStringHelper \ michael@0: ((PKIX_PL_Object *)(goodObj), \ michael@0: (expAscii), \ michael@0: plContext); } \ michael@0: if (checkDuplicate){ \ michael@0: subTest("PKIX_PL_" #type "_Duplicate"); \ michael@0: testDuplicateHelper \ michael@0: ((PKIX_PL_Object *)goodObj, plContext); } \ michael@0: } while (0) michael@0: michael@0: /* michael@0: * PKIX_TEST_DECREF_BC is a convenience macro which should only be called michael@0: * BEFORE the "cleanup" label ("BC"). If the input parameter is non-NULL, it michael@0: * DecRefs the input parameter and wraps the function with michael@0: * PKIX_TEST_EXPECT_NO_ERROR, which executes "goto cleanup" upon error. michael@0: * This macro MUST NOT be called after the "cleanup" label. michael@0: */ michael@0: michael@0: #define PKIX_TEST_ABORT_ON_NULL(obj) \ michael@0: do { \ michael@0: if (!obj){ \ michael@0: goto cleanup; \ michael@0: } \ michael@0: } while (0) michael@0: michael@0: #define PKIX_TEST_ARENAS_ARG(arena) \ michael@0: (arena? \ michael@0: (PORT_Strcmp(arena, "arenas") ? PKIX_FALSE : (j++, PKIX_TRUE)): \ michael@0: PKIX_FALSE) michael@0: michael@0: #define PKIX_TEST_ERROR_RECEIVED (pkixTestErrorMsg || pkixTestErrorResult) michael@0: michael@0: /* see source file for function documentation */ michael@0: michael@0: void startTests(char *testName); michael@0: michael@0: void endTests(char *testName); michael@0: michael@0: void subTest(char *subTestName); michael@0: michael@0: void testError(char *msg); michael@0: michael@0: extern PKIX_Error * michael@0: _ErrorCheck(PKIX_Error *errorResult); michael@0: michael@0: extern PKIX_Error * michael@0: _OutputError(PKIX_Error *errorResult); michael@0: michael@0: char* PKIX_String2ASCII(PKIX_PL_String *string, void *plContext); michael@0: michael@0: char* PKIX_Error2ASCII(PKIX_Error *error, void *plContext); michael@0: michael@0: char* PKIX_Object2ASCII(PKIX_PL_Object *object); michael@0: michael@0: char *PKIX_Cert2ASCII(PKIX_PL_Cert *cert); michael@0: michael@0: void michael@0: testHashcodeHelper( michael@0: PKIX_PL_Object *goodObject, michael@0: PKIX_PL_Object *otherObject, michael@0: PKIX_Boolean match, michael@0: void *plContext); michael@0: michael@0: void michael@0: testToStringHelper( michael@0: PKIX_PL_Object *goodObject, michael@0: char *expected, michael@0: void *plContext); michael@0: michael@0: void michael@0: testEqualsHelper( michael@0: PKIX_PL_Object *goodObject, michael@0: PKIX_PL_Object *otherObject, michael@0: PKIX_Boolean match, michael@0: void *plContext); michael@0: michael@0: void michael@0: testDuplicateHelper( michael@0: PKIX_PL_Object *object, michael@0: void *plContext); michael@0: void michael@0: testErrorUndo(char *msg); michael@0: michael@0: #ifdef __cplusplus michael@0: } michael@0: #endif michael@0: michael@0: #endif /* TESTUTIL_H */