/* unit.c API unit tests driver
 *
 * Copyright (C) 2006-2026 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */


/* Name change compatibility layer no longer need to be included here */

#include <tests/unit.h>

#include <wolfssl/wolfcrypt/types.h>
#ifdef HAVE_ECC
    #include <wolfssl/wolfcrypt/ecc.h>
#endif

#include <stdio.h>
#include <wolfssl/wolfcrypt/fips_test.h>

#ifndef NO_CRYPT_TEST
#include <wolfssl/test.h>
#include "wolfcrypt/test/test.h"
#endif

#ifdef WOLFSSL_SWDEV
#include "swdev/swdev_loader.h"
#endif

int allTesting = 1;
int apiTesting = 1;
int wolfCryptTesting = 1;
int myoptind = 0;
char* myoptarg = NULL;
int unit_test(int argc, char** argv);

#ifndef NO_TESTSUITE_MAIN_DRIVER
int main(int argc, char** argv)
{
    return unit_test(argc, argv);
}
#endif

/* Print usage options for unit test.
 */
static void UnitTest_Usage(void)
{
    printf("Usage: ./tests/unit.test <options>\n");
    printf(" -?, --help        Display this usage information.\n");
    printf(" --list            List the API tests.\n");
    printf(" --api             Only perform API tests.\n");
    printf(" --no-api          Do not perform API tests.\n");
    printf(" --stopOnFail      Stops API testing on first failure.\n");
    printf(" --groups          List known group names.\n");
    printf(" --group <string>  Functions in this group are tested.\n");
    printf(" -<number>         Run the API test identified by number.\n");
    printf("                   Can be specified multiple times.\n");
    printf(" -<string>         Run the API test identified by name.\n");
    printf("                   Can be specified multiple times.\n");
    printf(" -~<string>        Functions with this substring are tested.\n");
    printf(" <filename>        Name of cipher suite testing file.\n");
}

int unit_test(int argc, char** argv)
{
    int ret = 0;

    (void)argc;
    (void)argv;
#ifdef WOLFSSL_FORCE_MALLOC_FAIL_TEST
    if (argc > 1) {
        int memFailCount = atoi(argv[1]);
        fprintf(stderr, "\n--- SET RNG MALLOC FAIL AT %d---\n", memFailCount);
        wolfSSL_SetMemFailCount(memFailCount);
    }
#endif

    printf("starting unit tests...\n");
    fflush(stdout);

#if defined(DEBUG_WOLFSSL) && !defined(HAVE_VALGRIND)
    wolfSSL_Debugging_ON();
#endif

#ifdef WC_RNG_SEED_CB
    wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT);
#endif
#ifdef HAVE_WNR
    if (wc_InitNetRandom(wnrConfig, NULL, 5000) != 0)
        err_sys("Whitewood netRandom global config failed");
#endif /* HAVE_WNR */

#ifndef WOLFSSL_TIRTOS
    ChangeToWolfRoot();
#endif

#if defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION == 5)
#if !defined(NO_AES) && !defined(NO_AES_CBC)
    if (wc_RunCast_fips(FIPS_CAST_AES_CBC) != 0) {
        err_sys("AES-CBC CAST failed");
    }
#endif
#ifdef HAVE_AESGCM
    if (wc_RunCast_fips(FIPS_CAST_AES_GCM) != 0) {
        err_sys("AES-GCM CAST failed");
    }
#endif
#ifndef NO_SHA
    if (wc_RunCast_fips(FIPS_CAST_HMAC_SHA1) != 0) {
        err_sys("HMAC-SHA1 CAST failed");
    }
#endif
    /* the only non-optional CAST */
    if (wc_RunCast_fips(FIPS_CAST_HMAC_SHA2_256) != 0) {
        err_sys("HMAC-SHA2-256 CAST failed");
    }
#ifdef WOLFSSL_SHA512
    if (wc_RunCast_fips(FIPS_CAST_HMAC_SHA2_512) != 0) {
        err_sys("HMAC-SHA2-512 CAST failed");
    }
#endif
#ifdef WOLFSSL_SHA3
    if (wc_RunCast_fips(FIPS_CAST_HMAC_SHA3_256) != 0) {
        err_sys("HMAC-SHA3-256 CAST failed");
    }
#endif
#ifdef HAVE_HASHDRBG
    if (wc_RunCast_fips(FIPS_CAST_DRBG) != 0) {
        err_sys("Hash_DRBG CAST failed");
    }
#endif
#ifndef NO_RSA
    if (wc_RunCast_fips(FIPS_CAST_RSA_SIGN_PKCS1v15) != 0) {
        err_sys("RSA sign CAST failed");
    }
#endif
#if defined(HAVE_ECC_CDH) && defined(HAVE_ECC_CDH_CAST)
    if (wc_RunCast_fips(FIPS_CAST_ECC_CDH) != 0) {
        err_sys("RSA sign CAST failed");
    }
#endif
#ifdef HAVE_ECC_DHE
    if (wc_RunCast_fips(FIPS_CAST_ECC_PRIMITIVE_Z) != 0) {
        err_sys("ECC Primitive Z CAST failed");
    }
#endif
#ifdef HAVE_ECC
    if (wc_RunCast_fips(FIPS_CAST_ECDSA) != 0) {
        err_sys("ECDSA CAST failed");
    }
#endif
#ifndef NO_DH
    if (wc_RunCast_fips(FIPS_CAST_DH_PRIMITIVE_Z) != 0) {
        err_sys("DH Primitive Z CAST failed");
    }
#endif
#ifdef WOLFSSL_HAVE_PRF
    if (wc_RunCast_fips(FIPS_CAST_KDF_TLS12) != 0) {
        err_sys("KDF TLSv1.2 CAST failed");
    }
#endif
#if defined(HAVE_HKDF) && !defined(NO_HMAC)
    if (wc_RunCast_fips(FIPS_CAST_KDF_TLS13) != 0) {
        err_sys("KDF TLSv1.3 CAST failed");
    }
#endif
#ifdef WOLFSSL_WOLFSSH
    if (wc_RunCast_fips(FIPS_CAST_KDF_SSH) != 0) {
        err_sys("KDF SSHv2.0 CAST failed");
    }
#endif
#endif /* HAVE_FIPS && HAVE_FIPS_VERSION == 5 */
#if FIPS_VERSION3_GT(5,2,0)
    if (wc_RunAllCast_fips() != 0) {
        err_sys("wc_RunAllCast_fips() failed\n");
    }
#endif

    while (argc > 1) {
        if (argv[1][0] != '-') {
            break;
        }

        if (XSTRCMP(argv[1], "-?") == 0 || XSTRCMP(argv[1], "--help") == 0) {
            UnitTest_Usage();
            goto exit;
        }
        else if (XSTRCMP(argv[1], "--list") == 0) {
            ApiTest_PrintTestCases();
            goto exit;
        }
        else if (XSTRCMP(argv[1], "--no-wc") == 0) {
            wolfCryptTesting = 0;
        }
        else if (XSTRCMP(argv[1], "--api") == 0) {
            allTesting = 0;
        }
        else if (XSTRCMP(argv[1], "--no-api") == 0) {
            apiTesting = 0;
        }
        else if (XSTRCMP(argv[1], "--stopOnFail") == 0) {
            ApiTest_StopOnFail();
        }
        else if (XSTRCMP(argv[1], "--groups") == 0) {
            ApiTest_PrintGroups();
            goto exit;
        }
        else if (XSTRCMP(argv[1], "--group") == 0) {
            if (argc == 2) {
                fprintf(stderr, "No group name supplied\n");
                ret = -1;
                goto exit;
            }
            ret = ApiTest_RunGroup(argv[2]);
            if (ret != 0) {
                goto exit;
            }
            allTesting = 0;
            argc--;
            argv++;
        }
        else if (argv[1][0] == '-' && argv[1][1] >= '0' && argv[1][1] <= '9') {
            ret = ApiTest_RunIdx(atoi(argv[1] + 1));
            if (ret != 0) {
                goto exit;
            }
            allTesting = 0;
        }
        else if (argv[1][0] == '-' && argv[1][1] == '~') {
            ret = ApiTest_RunPartName(argv[1] + 2);
            if (ret != 0) {
                goto exit;
            }
            allTesting = 0;
        }
        else {
            ret = ApiTest_RunName(argv[1] + 1);
            if (ret != 0) {
                goto exit;
            }
            allTesting = 0;
        }

        argc--;
        argv++;
    }

#ifndef NO_CRYPT_TEST
    /* wc_ test */
    if (allTesting && wolfCryptTesting) {
        func_args wc_args;

        printf("\nwolfCrypt unit test:\n");

        if ((ret = wolfCrypt_Init()) != 0) {
            fprintf(stderr, "wolfCrypt_Init failed: %d\n", (int)ret);
            goto exit;
        }

    #ifdef WOLFSSL_SWDEV
        if ((ret = wc_SwDev_Init()) != 0) {
            fprintf(stderr, "wc_SwDev_Init failed: %d\n", (int)ret);
            goto exit;
        }
    #endif

        XMEMSET(&wc_args, 0, sizeof(wc_args));
        wolfcrypt_test(&wc_args);
        if (wc_args.return_code != 0) {
            ret = 1;
            goto exit;
        }

    #ifdef WOLFSSL_SWDEV
        wc_SwDev_Cleanup();
    #endif

        if ((ret = wolfCrypt_Cleanup()) != 0) {
            fprintf(stderr, "wolfCrypt_Cleanup failed: %d\n", (int)ret);
            goto exit;
        }

        printf("wolfCrypt unit test completed successfully.\n\n");
        fflush(stdout);
    }
#endif

#ifdef WOLFSSL_ALLOW_SKIP_UNIT_TESTS
    if (argc == 1)
#endif
    {
        if (apiTesting) {
            ret = ApiTest();
            fflush(stdout);
            if (ret != 0)
                goto exit;
        }

        if (!allTesting) {
            goto exit;
        }

    #ifdef WOLFSSL_W64_WRAPPER
        ret = w64wrapper_test();
        fflush(stdout);
        if (ret != 0) {
            fprintf(stderr, "w64wrapper test failed with %d\n", ret);
            goto exit;
        }
    #endif /* WOLFSSL_W64_WRAPPER */

    #ifdef WOLFSSL_QUIC
        ret = QuicTest();
        fflush(stdout);
        if (ret != 0) {
            fprintf(stderr, "quic test failed with %d\n", ret);
            goto exit;
        }
    #endif

        SrpTest();
        fflush(stdout);
    }

#if !defined(NO_WOLFSSL_CIPHER_SUITE_TEST) && \
    !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
    !defined(NO_TLS) && \
    !defined(SINGLE_THREADED) && \
    defined(WOLFSSL_PEM_TO_DER)
    #ifdef WOLFSSL_SWDEV
    if (wolfCrypt_Init() != 0) {
        fprintf(stderr, "wolfCrypt_Init before SuiteTest failed\n");
        ret = 1;
        goto exit;
    }
    if (wc_SwDev_Init() != 0) {
        fprintf(stderr, "wc_SwDev_Init before SuiteTest failed\n");
        ret = 1;
        goto exit;
    }
    #endif
    if ((ret = SuiteTest(argc, argv)) != 0) {
        fprintf(stderr, "suite test failed with %d\n", ret);
        goto exit;
    }
    #ifdef WOLFSSL_SWDEV
    wc_SwDev_Cleanup();
    wolfCrypt_Cleanup();
    #endif
#endif

exit:
#ifdef HAVE_WNR
    if (wc_FreeNetRandom() < 0)
        err_sys("Failed to free netRandom context");
#endif /* HAVE_WNR */

    /* Drop process-global ECC caches before exit. wolfCrypt_Cleanup() only
     * runs its cleanup body when initRefCount transitions 2->1 (the body
     * itself does the second decrement to 0); the unit driver's single
     * init/cleanup pair never reaches that state, and individual API tests
     * that create+free a CTX go 0->1->0 without triggering the body either.
     * Without explicit calls here the ECC_CACHE_CURVE entries (and their
     * HAVE_WOLF_BIGINT raw buffers) survive to exit and trip valgrind's
     * --leak-check=full. */
#if defined(HAVE_ECC) && defined(FP_ECC)
    wc_ecc_fp_free();
#endif
#if defined(HAVE_ECC) && defined(ECC_CACHE_CURVE)
    wc_ecc_curve_cache_free();
#endif

#ifdef WOLFSSL_TRACK_MEMORY
    if (ret == 0) {
        ret = wolfSSL_Cleanup(); /* no-op in a successful full run. */

        if (ret == WOLFSSL_SUCCESS)
            ret = 0;
        else
            fprintf(stderr, "wolfSSL_Cleanup() returned %d\n", ret);

        if (wc_MemStats_Ptr->currentBytes > 0)
        {
            fprintf(stderr,
                    "WOLFSSL_TRACK_MEMORY: currentBytes after cleanup is %ld\n",
                    wc_MemStats_Ptr->currentBytes);
            ret = MEMORY_E;
        }
    }
#endif

    if (ret == 0) {
        puts("\nunit_test: Success for all configured tests.");
        fflush(stdout);
    }

    return ret;
}
