/* 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/. */
/*
 * test_string.c
 *
 * Tests Strings.
 *
 */

#include "testutil.h"
#include "testutil_nss.h"

static void *plContext = NULL;

static void
createString(
        PKIX_PL_String **testString,
        PKIX_UInt32 format,
        char *stringAscii,
        PKIX_UInt32 length)
{
        PKIX_TEST_STD_VARS();

        PKIX_TEST_EXPECT_NO_ERROR
                (PKIX_PL_String_Create
                (format, stringAscii, length, testString, plContext));

cleanup:

        PKIX_TEST_RETURN();
}

static void
createStringOther(
        PKIX_PL_String **testEscAscii,
        PKIX_PL_String **testUtf16,
        PKIX_PL_String **ampString,
        PKIX_PL_String **testDebugAscii,
        PKIX_PL_String **testNullString,
        PKIX_UInt32 *utf16data)
{
        char *nullText = "Hi&#x0000; there!";

        char *escAsciiString =
                "&#x00A1;&#x00010000;&#x0FFF;&#x00100001;";

        char *debugAsciiString =
                "string with&#x000A;newlines and&#x0009;tabs";

        char * utfAmp = "\x00&";

        PKIX_TEST_STD_VARS();

        createString(testEscAscii,
                    PKIX_ESCASCII,
                    escAsciiString,
                    PL_strlen(escAsciiString));

        createString(testUtf16, PKIX_UTF16, (char *)utf16data, 12);

        createString(ampString, PKIX_UTF16, utfAmp, 2);

        createString(testDebugAscii,
                    PKIX_ESCASCII_DEBUG,
                    debugAsciiString,
                    PL_strlen(debugAsciiString));

        createString(testNullString,
                    PKIX_ESCASCII_DEBUG,
                    nullText,
                    PL_strlen(nullText));

        goto cleanup;

cleanup:

        PKIX_TEST_RETURN();
}

static void
testGetEncoded(
        PKIX_PL_String *testEscAscii,
        PKIX_PL_String *testString0,
        PKIX_PL_String *testDebugAscii,
        PKIX_PL_String *testNullString,
        PKIX_UInt32 *utf16data)
{
        char *temp = NULL;
        void *dest = NULL;
        void *dest2 = NULL;
        char *plainText = "string with\nnewlines and\ttabs";
        PKIX_UInt32 length, length2, i;

        PKIX_TEST_STD_VARS();

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_String_GetEncoded(testEscAscii,
                                        PKIX_UTF16,
                                        &dest,
                                        &length,
                                        plContext));
        for (i = 0; i < length; i++) {
                if (((char*)dest)[i] != ((char*)utf16data)[i]) {
                        testError("UTF-16 Data Differs from Source");
                        printf("%d-th char is different -%c-%c-\n", i,
                               ((char*)dest)[i], ((char*)utf16data)[i]);
                }
        }

        length = 0;
        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Free(dest, plContext));

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_String_GetEncoded(testNullString,
                                        PKIX_UTF16,
                                        &dest,
                                        &length,
                                        plContext));

        length = 0;
        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Free(dest, plContext));

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_String_GetEncoded(testString0,
                                        PKIX_ESCASCII_DEBUG,
                                        &dest,
                                        &length,
                                        plContext));

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_String_GetEncoded(testDebugAscii,
                                        PKIX_ESCASCII_DEBUG,
                                        &dest2,
                                        &length2,
                                        plContext));

        for (i = 0; (i < length) && (i < length2); i++)
                if (((char*)dest)[i] != ((char*)dest2)[i]) {
                        testError("Equivalent strings are unequal");
                        break;
                }

        length = 0;
        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Free(dest, plContext));
        length2 = 0;
        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Free(dest2, plContext));

        temp = PKIX_String2ASCII(testDebugAscii, plContext);
        if (temp){
                if (PL_strcmp(plainText, temp) != 0)
                        testError("Debugged ASCII does not match "
                                    "equivalent EscAscii");
                PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Free(temp, plContext));
        }

cleanup:

        PKIX_TEST_RETURN();
}

static void
testSprintf(void)
{
        PKIX_Int32 x = 0xCAFE;
        PKIX_Int32 y = -12345;
        PKIX_PL_String *testString = NULL;
        PKIX_PL_String *formatString = NULL;
        PKIX_PL_String *sprintfString = NULL;
        char *plainText = "Testing Sprintf";
        char *format = "%s %x %u %d";
        char *convertedFormat = "%s %lx %lu %ld";
        char *temp = NULL;
        char *temp2 = NULL;

        PKIX_TEST_STD_VARS();

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_String_Create(
                                        PKIX_ESCASCII,
                                        plainText,
                                        PL_strlen(plainText),
                                        &testString,
                                        plContext));

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_String_Create(
                                        PKIX_ESCASCII,
                                        format,
                                        11,
                                        &formatString,
                                        plContext));

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Sprintf(&sprintfString,
                                plContext,
                                formatString,
                                testString, x, y, y));
        PKIX_TEST_DECREF_BC(testString);

        temp = PR_smprintf(convertedFormat, plainText, x, y, y);
        temp2 = PKIX_String2ASCII(sprintfString, plContext);

        if (PL_strcmp(temp, temp2) != 0)
                testError("Sprintf produced incorrect output");

        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Free(temp, plContext));
        PKIX_TEST_EXPECT_NO_ERROR(PKIX_PL_Free(temp2, plContext));


        PKIX_TEST_DECREF_BC(sprintfString);


        PKIX_TEST_DECREF_BC(formatString);


cleanup:

        PKIX_TEST_RETURN();
}

static void
testErrorHandling(void)
{
        char *debugAsciiString =
                "string with&#x000A;newlines and&#x0009;tabs";

        PKIX_PL_String *testString = NULL;

        PKIX_TEST_STD_VARS();

        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                        PKIX_ESCASCII,
                                        NULL,
                                        50,
                                        &testString,
                                        plContext));

        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(PKIX_ESCASCII,
                                        "blah", 4, NULL, plContext));

        PKIX_TEST_EXPECT_ERROR(PKIX_PL_Sprintf(&testString, plContext, NULL));

        PKIX_TEST_EXPECT_ERROR
                (PKIX_PL_GetString(0, NULL, &testString, plContext));

        PKIX_TEST_EXPECT_ERROR(PKIX_PL_GetString(0, "blah", 0, plContext));

        /* ---------------------------- */
        subTest("Unicode Error Handling");

        /* &#x must be followed by 4 hexadecimal digits */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "&#x003k;",
                                            7,
                                            &testString,
                                            plContext));

        /* &#x must be followed by 4 hexadecimal digits */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "abc&#x00",
                                            8,
                                            &testString,
                                            plContext));

        /* &#x must be between 00010000-0010FFFF */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "&#x00200101;",
                                            11,
                                            &testString,
                                            plContext));

        /* &#x must be followed by 8 hexadecimal digits */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "&#x001000",
                                            10,
                                            &testString,
                                            plContext));

        /* &#x must be followed by 8 hexadecimal digits */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "&#x0010m00;",
                                            10,
                                            &testString,
                                            plContext));

        /* Byte values D800-DFFF are reserved */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "&#xD800;",
                                            7,
                                            &testString,
                                            plContext));

        /* Can't use &#x for regular characters */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "&#x0032;",
                                            7,
                                            &testString,
                                            plContext));

        /* Can't use non-printable characters */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "\xA1",
                                            1,
                                            &testString,
                                            plContext));

        /* Only legal \\ characters are \\, u and U */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_ESCASCII,
                                            "&blah",
                                            5,
                                            &testString,
                                            plContext));



        /* Surrogate pairs must be legal */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                            PKIX_UTF16,
                                            "\xd8\x00\x0\x66",
                                            4,
                                            &testString,
                                            plContext));

        /* Debugged EscASCII should not be accepted as EscASCII */
        PKIX_TEST_EXPECT_ERROR(PKIX_PL_String_Create(
                                        PKIX_ESCASCII,
                                        debugAsciiString,
                                        PL_strlen(debugAsciiString),
                                        &testString,
                                        plContext));
cleanup:

        PKIX_TEST_RETURN();
}

static void
testDestroy(
        PKIX_PL_String *string)
{
        PKIX_TEST_STD_VARS();

        PKIX_TEST_DECREF_BC(string);

cleanup:

        PKIX_TEST_RETURN();
}


int test_string(int argc, char *argv[]) {

        PKIX_PL_String *testString[6] = {NULL};
        PKIX_PL_String *testNullString = NULL;
        PKIX_PL_String *testDebugAscii = NULL;
        PKIX_PL_String *testEscAscii = NULL;
        PKIX_PL_String *testUtf16 = NULL;
        PKIX_PL_String *ampString = NULL;
        unsigned char utf16Data[] = {0x00, 0xA1, 0xD8, 0x00,
                            0xDC, 0x00, 0x0F, 0xFF,
                            0xDB, 0xC0, 0xDC, 0x01};
        PKIX_UInt32 i, size = 6;

        char *plainText[6] = {
                "string with\nnewlines and\ttabs",
                "Not an escaped char: &amp;#x0012;",
                "Encode &amp; with &amp;amp; in ASCII",
                "&#x00A1;",
                "&amp;",
                "string with\nnewlines and\ttabs"
        };

        PKIX_UInt32 actualMinorVersion;
        PKIX_UInt32 j = 0;

        PKIX_TEST_STD_VARS();

        startTests("Strings");

        PKIX_TEST_EXPECT_NO_ERROR(
            PKIX_PL_NssContext_Create(0, PKIX_FALSE, NULL, &plContext));

        subTest("PKIX_PL_String_Create <ascii format>");
        for (i = 0; i < size; i++) {
                testString[i] = NULL;
                createString
                        (&testString[i],
                        PKIX_ESCASCII,
                        plainText[i],
                        PL_strlen(plainText[i]));
        }

        subTest("PKIX_PL_String_Create <other formats>");
        createStringOther
                (&testEscAscii,
                &testUtf16,
                &ampString,
                &testDebugAscii,
                &testNullString,
                (PKIX_UInt32 *)utf16Data);

        PKIX_TEST_EQ_HASH_TOSTR_DUP
                (testString[0],
                testString[5],
                testString[1],
                plainText[0],
                String,
                PKIX_TRUE);

        subTest("PKIX_PL_String_GetEncoded");
        testGetEncoded
                (testEscAscii,
                testString[0],
                testDebugAscii,
                testNullString,
                (PKIX_UInt32 *)utf16Data);

        subTest("PKIX_PL_Sprintf");
        testSprintf();

        subTest("PKIX_PL_String_Create <error_handling>");
        testErrorHandling();

        subTest("PKIX_PL_String_Destroy");
        for (i = 0; i < size; i++) {
                testDestroy(testString[i]);
        }
        testDestroy(testEscAscii);
        testDestroy(testUtf16);
        testDestroy(ampString);
        testDestroy(testDebugAscii);
        testDestroy(testNullString);

cleanup:

        PKIX_Shutdown(plContext);

        PKIX_TEST_RETURN();

        endTests("String");

        return (0);

}
