/*************************************************************************\
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
*     National Laboratory.
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/

/**
 * \file epicsUnitTest.h
 * \brief Unit test routines
 * \author Andrew Johnson
 *
 * @section unittest Unit test routines
 *
 * The unit test routines make it easy for a test program to generate output
 * that is compatible with the Test Anything Protocol and can thus be used with
 * Perl's automated Test::Harness as well as generating human-readable output.
 * The routines detect whether they are being run automatically and print a
 * summary of the results at the end if not.

 * A test program starts with a call to testPlan(), announcing how many tests
 * are to be conducted. If this number is not known a value of zero can be
 * used during development, but it is recommended that the correct value be
 * substituted after the test program has been completed.
 *
 * Individual test results are reported using any of testOk(), testOk1(),
 * testOkV(), testPass() or testFail(). The testOk() call takes and also
 * returns a logical pass/fail result (zero means failure, any other value
 * is success) and a printf-like format string and arguments which describe
 * the test. The convenience macro testOk1() is provided which stringifies its
 * single condition argument, reducing the effort needed when writing test
 * programs. The individual testPass() and testFail() routines can be used when
 * the test program takes a different path on success than on failure, but one
 * or other must always be called for any particular test. The testOkV() routine
 * is a varargs form of testOk() included for internal purposes which may prove
 * useful in some cases.
 *
 * If some program condition or failure makes it impossible to run some tests,
 * the testSkip() routine can be used to indicate how many tests are being omitted
 * from the run, thus keeping the test counts correct; the constant string why is
 * displayed as an explanation to the user (this string is not printf-like).
 *
 * If some tests are expected to fail because functionality in the module under
 * test has not yet been fully implemented, these tests may still be executed,
 * wrapped between calls to testTodoBegin() and testTodoEnd(). testTodoBegin()
 * takes a constant string indicating why these tests are not expected to
 * succeed. This modifies the counting of the results so the wrapped tests will not
 * be recorded as failures.
 *
 * Additional information can be supplied using the testDiag() routine, which
 * displays the relevant information as a comment in the result output. None of
 * the printable strings passed to any testXxx() routine should contain a newline
 * '\\n' character, newlines will be added by the test routines as part of the
 * Test Anything Protocol. For multiple lines of diagnostic output, call
 * testDiag() as many times as necessary.
 *
 * If at any time the test program is unable to continue for some catastrophic
 * reason, calling testAbort() with an appropriate message will ensure that the
 * test harness understands this. testAbort() does not return, but calls the ANSI
 * C routine abort() to cause the program to stop immediately.
 *
 * After all of the tests have been completed, the return value from
 * testDone() can be used as the return status code from the program's main()
 * routine.
 *
 * On vxWorks and RTEMS, an alternative test harness can be used to run a
 * series of tests in order and summarize the results from them all at the end
 * just like the Perl harness does. The routine testHarness() is called once at
 * the beginning of the test harness program. Each test program is run by
 * passing its main routine name to the runTest() macro which expands into a call
 * to the runTestFunc() routine. The last test program or the harness program
 * itself must finish by calling testHarnessDone() which triggers the summary
 * mechanism to generate its result outputs (from an epicsAtExit() callback
 * routine).
 *
 * @see @ref dbunittest
 *
 * @section unittestexample Example
 *
 * The following is a simple example of a test program using the epicsUnitTest
 * routines:
 * \code{.c}
 * #include <math.h>
 * #include "epicsUnitTest.h"
 * #include "testMain.h"
 *
 * MAIN(mathTest)
 * {
 *     testPlan(3);
 *     testOk(sin(0.0) == 0.0, "Sine starts");
 *     testOk(cos(0.0) == 1.0, "Cosine continues");
 *     if (!testOk1(M_PI == 4.0*atan(1.0)))
 *         testDiag("4 * atan(1) = %g", 4.0 * atan(1.0));
 *     return testDone();
 * }
 * \endcode
 *
 * The output from running the above program looks like this:
 * \code
 * 1..3
 * ok  1 - Sine starts
 * ok  2 - Cosine continues
 * ok  3 - M_PI == 4.0*atan(1.0)
 *
 *     Results
 *     =======
 *        Tests: 3
 *       Passed:  3 = 100%
 * \endcode
 */

#ifndef INC_epicsUnitTest_H
#define INC_epicsUnitTest_H

#include <stdarg.h>

#include "compilerDependencies.h"
#include "libComAPI.h"

/* Allow to test deprecated functions without warnings */
#undef EPICS_DEPRECATED
#define EPICS_DEPRECATED

#ifdef __cplusplus
extern "C" {
#endif

/** \brief Declare the test plan, required.
 * \param tests Number of tests to be run. May be zero if not known but the
 * test harness then can't tell if the program dies prematurely.
 */
LIBCOM_API void testPlan(int tests);

/** \name Announcing Test Results
 * Routines that declare individual test results.
 */
/** @{ */
/** \brief Test result with printf-style description.
 * \param pass True/False value indicating result.
 * \param fmt A printf-style format string describing the test.
 * \param ... Any parameters required for the format string.
 * \return The value of \p pass.
 */
LIBCOM_API int  testOk(int pass, EPICS_PRINTF_FMT(const char *fmt), ...)
    EPICS_PRINTF_STYLE(2, 3);
/** \brief Test result using expression as description
 * \param cond Expression to be evaluated and displayed.
 * \return The value of \p cond.
 */
#define testOk1(cond) testOk(cond, "%s", #cond)
/** \brief Test result with var-args description.
 * \param pass True/False value indicating result.
 * \param fmt A printf-style format string describing the test.
 * \param pvar A var-args pointer to any parameters for the format string.
 * \return The value of \p pass.
 */
LIBCOM_API int  testOkV(int pass, const char *fmt, va_list pvar);
/** \brief Passing test result with printf-style description.
 * \param fmt A printf-style format string describing the test.
 * \param ... Any parameters required for the format string.
 */
LIBCOM_API void testPass(EPICS_PRINTF_FMT(const char *fmt), ...)
    EPICS_PRINTF_STYLE(1, 2);
/** \brief Failing test result with printf-style description.
 * \param fmt A printf-style format string describing the test.
 * \param ... Any parameters required for the format string.
 */
LIBCOM_API void testFail(EPICS_PRINTF_FMT(const char *fmt), ...)
    EPICS_PRINTF_STYLE(1, 2);
/** @} */

/** \name Missing or Failing Tests
 * Routines for handling special situations.
 */
/** @{ */

/** \brief Place-holders for tests that can't be run.
 * \param skip How many tests are being skipped.
 * \param why Reason for skipping these tests.
 */
LIBCOM_API void testSkip(int skip, const char *why);
/** \brief Mark the start of a group of tests that are expected to fail
 * \param why Reason for expected failures.
 */
LIBCOM_API void testTodoBegin(const char *why);
/** \brief Mark the end of a failing test group.
 */
LIBCOM_API void testTodoEnd(void);
/** \brief Stop testing, program cannot continue.
 * \param fmt A printf-style format string giving the reason for stopping.
 * \param ... Any parameters required for the format string.
 */
LIBCOM_API EPICS_NORETURN
void testAbort(EPICS_PRINTF_FMT(const char *fmt), ...)
    EPICS_PRINTF_STYLE(1, 2);
/** @} */

/** \brief Output additional diagnostics
 * \param fmt A printf-style format string containing diagnostic information.
 * \param ... Any parameters required for the format string.
 */
LIBCOM_API int  testDiag(EPICS_PRINTF_FMT(const char *fmt), ...)
    EPICS_PRINTF_STYLE(1, 2);
/** \brief Mark the end of testing.
 */
LIBCOM_API int  testDone(void);

/** \brief Return non-zero in shared/oversubscribed testing environments
 *
 * May be used to testSkip(), or select longer timeouts, for some cases
 * when the test process may be preempted for arbitrarily long times.
 * This is common in shared CI environments.
 *
 * The environment variable $EPICS_TEST_IMPRECISE_TIMING=YES should be
 * set in by such testing environments.
 */
LIBCOM_API
int testImpreciseTiming(void);

/** \name Test Harness for Embedded OSs
 * These routines are used to create a test-harness that can run
 * multiple test programs, collect their names and results, then
 * display a summary at the end of testing.
 */
/** @{ */

typedef int (*TESTFUNC)(void);
/** \brief Initialize test harness
 */
LIBCOM_API void testHarness(void);
/** \brief End of testing
 */
LIBCOM_API void testHarnessExit(void *dummy);
/** \brief Run a single test program
 * \param name Program name
 * \param func Function implementing test program
 */
LIBCOM_API void runTestFunc(const char *name, TESTFUNC func);

/** \brief Run a test program
 * \param func Name of the test program.
 */
#define runTest(func) runTestFunc(#func, func)
/** \brief Declare all test programs finished
 */
#define testHarnessDone() testHarnessExit(0)
/** @} */

#ifdef __cplusplus
}
#endif

#endif /* INC_epicsUnitTest_H */
