/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
 * testutil.c
 *
 * Utility error handling functions
 *
 */

#include "testutil.h"

/*
 * static global variable to keep track of total number of errors for
 * a particular test suite (eg. all the OID tests)
 */
static int errCount = 0;

/*
 * FUNCTION: startTests
 * DESCRIPTION:
 *
 *  Prints standard message for starting the test suite with the name pointed
 *  to by "testName". This function should be called in the beginning of every
 *  test suite.
 *
 * PARAMETERS:
 *  "testName"
 *      Address of string representing name of test suite.
 * THREAD SAFETY:
 *  Not Thread Safe - assumes exclusive access to "errCount"
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
startTests(char *testName)
{
        (void) printf("*START OF TESTS FOR %s:\n", testName);
        errCount = 0;
}

/*
 * FUNCTION: endTests
 * DESCRIPTION:
 *
 *  Prints standard message for ending the test suite with the name pointed
 *  to by "testName", followed by a success/failure message. This function
 *  should be called at the end of every test suite.
 *
 * PARAMETERS:
 *  "testName"
 *      Address of string representing name of test suite.
 * THREAD SAFETY:
 *  Not Thread Safe - assumes exclusive access to "errCount"
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
endTests(char *testName)
{
        char plural = ' ';

        (void) printf("*END OF TESTS FOR %s: ", testName);
        if (errCount > 0) {
                if (errCount > 1) plural = 's';
                (void) printf("%d SUBTEST%c FAILED.\n\n", errCount, plural);
        } else {
                (void) printf("ALL TESTS COMPLETED SUCCESSFULLY.\n\n");
        }
}

/*
 * FUNCTION: subTest
 * DESCRIPTION:
 *
 *  Prints standard message for starting the subtest with the name pointed to
 *  by "subTestName". This function should be called at the beginning of each
 *  subtest.
 *
 * PARAMETERS:
 *  "subTestName"
 *      Address of string representing name of subTest.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
subTest(char *subTestName)
{
        (void) printf("TESTING: %s ...\n", subTestName);
}

/*
 * FUNCTION: testErrorUndo
 * DESCRIPTION:
 *
 *  Decrements the global variable "errCount" and prints a test failure
 *  expected message followed by the string pointed to by "msg". This function
 *  should be called when an expected error condition is encountered in the
 *  tests. Calling this function *correct* the previous errCount increment.
 *  It should only be called ONCE per subtest.
 *
 * PARAMETERS:
 *  "msg"
 *      Address of text of error message.
 * THREAD SAFETY:
 *  Not Thread Safe - assumes exclusive access to "errCount"
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
testErrorUndo(char *msg)
{
        --errCount;
        (void) printf("TEST FAILURE *** EXPECTED *** :%s\n", msg);
}

/*
 * FUNCTION: testError
 * DESCRIPTION:
 *
 *  Increments the global variable "errCount" and prints a standard test
 *  failure message followed by the string pointed to by "msg". This function
 *  should be called when an unexpected error condition is encountered in the
 *  tests. It should only be called ONCE per subtest.
 *
 * PARAMETERS:
 *  "msg"
 *      Address of text of error message.
 * THREAD SAFETY:
 *  Not Thread Safe - assumes exclusive access to "errCount"
 *  (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
testError(char *msg)
{
        ++errCount;
        (void) printf("TEST FAILURE: %s\n", msg);
}

/*
 * FUNCTION: PKIX_String2ASCII
 * DESCRIPTION:
 *
 *  Converts String object pointed to by "string" to its ASCII representation
 *  and returns the converted value. Returns NULL upon failure.
 *
 *  XXX Might want to use ESCASCII_DEBUG to show control characters, etc.
 *
 * PARAMETERS:
 *  "string"
 *      Address of String to be converted to ASCII. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns the ASCII representation of "string" upon success;
 *  NULL upon failure.
 */
char *
PKIX_String2ASCII(PKIX_PL_String *string, void *plContext)
{
        PKIX_UInt32 length;
        char *asciiString = NULL;
        PKIX_Error *errorResult;

        errorResult = PKIX_PL_String_GetEncoded
                (string,
                PKIX_ESCASCII,
                (void **)&asciiString,
                &length,
                plContext);

        if (errorResult) goto cleanup;

cleanup:

        if (errorResult){
                return (NULL);
        }

        return (asciiString);

}

/*
 * FUNCTION: PKIX_Error2ASCII
 * DESCRIPTION:
 *
 *  Converts Error pointed to by "error" to its ASCII representation and
 *  returns the converted value. Returns NULL upon failure.
 *
 * PARAMETERS:
 *  "error"
 *      Address of Error to be converted to ASCII. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns the ASCII representation of "error" upon success;
 *  NULL upon failure.
 */
char *
PKIX_Error2ASCII(PKIX_Error *error, void *plContext)
{
        PKIX_UInt32 length;
        char *asciiString = NULL;
        PKIX_PL_String *pkixString = NULL;
        PKIX_Error *errorResult = NULL;

        errorResult = PKIX_PL_Object_ToString
                ((PKIX_PL_Object*)error, &pkixString, plContext);
        if (errorResult) goto cleanup;

        errorResult = PKIX_PL_String_GetEncoded
                (pkixString,
                PKIX_ESCASCII,
                (void **)&asciiString,
                &length,
                plContext);

cleanup:

        if (pkixString){
                if (PKIX_PL_Object_DecRef
                    ((PKIX_PL_Object*)pkixString, plContext)){
                        return (NULL);
                }
        }

        if (errorResult){
                return (NULL);
        }

        return (asciiString);
}

/*
 * FUNCTION: PKIX_Object2ASCII
 * DESCRIPTION:
 *
 *  Converts Object pointed to by "object" to its ASCII representation and
 *  returns the converted value. Returns NULL upon failure.
 *
 * PARAMETERS:
 *  "object"
 *      Address of Object to be converted to ASCII. Must be non-NULL.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns the ASCII representation of "object" upon success;
 *  NULL upon failure.
 */
char *
PKIX_Object2ASCII(PKIX_PL_Object *object)
{
        PKIX_UInt32 length;
        char *asciiString = NULL;
        PKIX_PL_String *pkixString = NULL;
        PKIX_Error *errorResult = NULL;

        errorResult = PKIX_PL_Object_ToString
                (object, &pkixString, NULL);
        if (errorResult) goto cleanup;

        errorResult = PKIX_PL_String_GetEncoded
            (pkixString, PKIX_ESCASCII, (void **)&asciiString, &length, NULL);

cleanup:

        if (pkixString){
                if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixString, NULL)){
                        return (NULL);
                }
        }

        if (errorResult){
                return (NULL);
        }

        return (asciiString);
}

/*
 * FUNCTION: PKIX_Cert2ASCII
 * DESCRIPTION:
 *
 *  Converts Cert pointed to by "cert" to its partial ASCII representation and
 *  returns the converted value. Returns NULL upon failure.
 *
 * PARAMETERS:
 *  "cert"
 *      Address of Cert to be converted to ASCII. Must be non-NULL.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns the partial ASCII representation of "cert" upon success;
 *  NULL upon failure.
 */
char *
PKIX_Cert2ASCII(PKIX_PL_Cert *cert)
{
        PKIX_PL_X500Name *issuer = NULL;
        void *issuerAscii = NULL;
        PKIX_PL_X500Name *subject = NULL;
        void *subjectAscii = NULL;
        void *asciiString = NULL;
        PKIX_Error *errorResult = NULL;
        PKIX_UInt32 numChars;

        /* Issuer */
        errorResult = PKIX_PL_Cert_GetIssuer(cert, &issuer, NULL);
        if (errorResult) goto cleanup;

        issuerAscii = PKIX_Object2ASCII((PKIX_PL_Object*)issuer);

        /* Subject */
        errorResult = PKIX_PL_Cert_GetSubject(cert, &subject, NULL);
        if (errorResult) goto cleanup;

        if (subject){
                subjectAscii = PKIX_Object2ASCII((PKIX_PL_Object*)subject);
        }

        errorResult = PKIX_PL_Malloc(200, &asciiString, NULL);
        if (errorResult) goto cleanup;

        numChars =
                PR_snprintf
                (asciiString,
                200,
                "Issuer=%s\nSubject=%s\n",
                issuerAscii,
                subjectAscii);

        if (!numChars) goto cleanup;

cleanup:

        if (issuer){
                if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)issuer, NULL)){
                        return (NULL);
                }
        }

        if (subject){
                if (PKIX_PL_Object_DecRef((PKIX_PL_Object*)subject, NULL)){
                        return (NULL);
                }
        }

        if (PKIX_PL_Free((PKIX_PL_Object*)issuerAscii, NULL)){
                return (NULL);
        }

        if (PKIX_PL_Free((PKIX_PL_Object*)subjectAscii, NULL)){
                return (NULL);
        }

        if (errorResult){
                return (NULL);
        }

        return (asciiString);
}

/*
 * FUNCTION: testHashcodeHelper
 * DESCRIPTION:
 *
 *  Computes the hashcode of the Object pointed to by "goodObject" and the
 *  Object pointed to by "otherObject" and compares them. If the result of the
 *  comparison is not the desired match as specified by "match", an error
 *  message is generated.
 *
 * PARAMETERS:
 *  "goodObject"
 *      Address of an object. Must be non-NULL.
 *  "otherObject"
 *      Address of another object. Must be non-NULL.
 *  "match"
 *      Boolean value representing the desired comparison result.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
testHashcodeHelper(
        PKIX_PL_Object *goodObject,
        PKIX_PL_Object *otherObject,
        PKIX_Boolean match,
        void *plContext)
{

        PKIX_UInt32 goodHash;
        PKIX_UInt32 otherHash;
        PKIX_Boolean cmpResult;
        PKIX_TEST_STD_VARS();

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Object_Hashcode
                        ((PKIX_PL_Object *)goodObject, &goodHash, plContext));

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Object_Hashcode
                        ((PKIX_PL_Object *)otherObject, &otherHash, plContext));

        cmpResult = (goodHash == otherHash);

        if ((match && !cmpResult) || (!match && cmpResult)){
                testError("unexpected mismatch");
                (void) printf("Hash1:\t%d\n", goodHash);
                (void) printf("Hash2:\t%d\n", otherHash);
        }

cleanup:

        PKIX_TEST_RETURN();

}

/*
 * FUNCTION: testToStringHelper
 * DESCRIPTION:
 *
 *  Calls toString on the Object pointed to by "goodObject" and compares the
 *  result to the string pointed to by "expected". If the results are not
 *  equal, an error message is generated.
 *
 * PARAMETERS:
 *  "goodObject"
 *      Address of Object. Must be non-NULL.
 *  "expected"
 *      Address of the desired string.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
testToStringHelper(
        PKIX_PL_Object *goodObject,
        char *expected,
        void *plContext)
{
        PKIX_PL_String *stringRep = NULL;
        char *actual = NULL;
        PKIX_TEST_STD_VARS();

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Object_ToString
                        (goodObject, &stringRep, plContext));

        actual = PKIX_String2ASCII(stringRep, plContext);
        if (actual == NULL){
                pkixTestErrorMsg = "PKIX_String2ASCII Failed";
                goto cleanup;
        }

        /*
         * If you are having trouble matching the string, uncomment the
         * PL_strstr function to figure out what's going on.
         */

        /*
            if (PL_strstr(actual, expected) == NULL){
                testError("PL_strstr failed");
            }
        */


        if (PL_strcmp(actual, expected) != 0){
                testError("unexpected mismatch");
                (void) printf("Actual value:\t%s\n", actual);
                (void) printf("Expected value:\t%s\n", expected);
        }

cleanup:

        PKIX_PL_Free(actual, plContext);

        PKIX_TEST_DECREF_AC(stringRep);

        PKIX_TEST_RETURN();
}

/*
 * FUNCTION: testEqualsHelper
 * DESCRIPTION:
 *
 *  Checks if the Object pointed to by "goodObject" is Equal to the Object
 *  pointed to by "otherObject". If the result of the check is not the desired
 *  match as specified by "match", an error message is generated.
 *
 * PARAMETERS:
 *  "goodObject"
 *      Address of an Object. Must be non-NULL.
 *  "otherObject"
 *      Address of another Object. Must be non-NULL.
 *  "match"
 *      Boolean value representing the desired comparison result.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
testEqualsHelper(
        PKIX_PL_Object *goodObject,
        PKIX_PL_Object *otherObject,
        PKIX_Boolean match,
        void *plContext)
{

        PKIX_Boolean cmpResult;
        PKIX_TEST_STD_VARS();

        PKIX_TEST_EXPECT_NO_ERROR
                (PKIX_PL_Object_Equals
                (goodObject, otherObject, &cmpResult, plContext));

        if ((match && !cmpResult) || (!match && cmpResult)){
                testError("unexpected mismatch");
                (void) printf("Actual value:\t%d\n", cmpResult);
                (void) printf("Expected value:\t%d\n", match);
        }

cleanup:

        PKIX_TEST_RETURN();
}


/*
 * FUNCTION: testDuplicateHelper
 * DESCRIPTION:
 *  Checks if the Object pointed to by "object" is equal to its duplicate.
 *  If the result of the check is not equality, an error message is generated.
 * PARAMETERS:
 *  "object"
 *      Address of Object. Must be non-NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  Returns nothing.
 */
void
testDuplicateHelper(PKIX_PL_Object *object, void *plContext)
{
        PKIX_PL_Object *newObject = NULL;
        PKIX_Boolean cmpResult;

        PKIX_TEST_STD_VARS();

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Object_Duplicate
                                    (object, &newObject, plContext));

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Object_Equals
                                    (object, newObject, &cmpResult, plContext));

        if (!cmpResult){
                testError("unexpected mismatch");
                (void) printf("Actual value:\t%d\n", cmpResult);
                (void) printf("Expected value:\t%d\n", PKIX_TRUE);
        }

cleanup:

        PKIX_TEST_DECREF_AC(newObject);

        PKIX_TEST_RETURN();
}
