/*****************************************************************************
 *                                                                           *
 *  uCUnit - A unit testing framework for microcontrollers                   *
 *                                                                           *
 *  (C) 2007 - 2010 Sven Stefan Krauss                                       *
 *                  https://www.ucunit.org                                   *
 *                                                                           *
 *  File        : uCUnit-v2.0.h                                              *
 *  Description : Macros for Unit-Testing                                    *
 *  Author      : Sven Stefan Krauss                                         *
 *  Contact     : www.ucunit.org                                             *
 *                                                                           *
 *****************************************************************************/

/*
 * This file is part of ucUnit.
 *
 * You can redistribute and/or modify it under the terms of the
 * Common Public License as published by IBM Corporation; either
 * version 1.0 of the License, or (at your option) any later version.
 *
 * uCUnit 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
 * Common Public License for more details.
 *
 * You should have received a copy of the Common Public License
 * along with uCUnit.
 *
 * It may also be available at the following URL:
 *       http://www.opensource.org/licenses/cpl1.0.txt
 *
 * If you cannot obtain a copy of the License, please contact the
 * author.
 */

#ifndef UCUNIT_0200_H_
#define UCUNIT_0200_H_

/*****************************************************************************/
/****** Customizing area ******/
/*****************************************************************************/

/**
 * @Macro:       UCUNIT__WRITE_STRING(msg)
 *
 * @Description: Encapsulates a function which is called for
 *               writing a message string to the host computer.
 *
 * @param msg:   Message which shall be written.
 *
 * @Remarks:     Implement a function to write an integer to a host
 *               computer.
 *
 *               For most microcontrollers a special implementation of
 *               printf is available for writing to a serial
 *               device or network. In some cases you will have
 *               also to implement a putch(char c) function.
 */
#define UCUNIT__WRITE_STRING(msg)    Testbed_WriteString(msg)

/**
 * @Macro:       UCUNIT__WRITE_INT(n)
 *
 * @Description: Encapsulates a function which is called for
 *               writing an integer to the host computer.
 *
 * @param n:     Integer number which shall be written
 *
 * @Remarks:     Implement a function to write an integer to a host
 *               computer.
 *
 *               For most microcontrollers a special implementation of
 *               printf is available for writing to a serial
 *               device or network. In some cases you will have
 *               also to implement a putch(char c) function.
 */
#define UCUNIT__WRITE_INT(n)    Testbed_WriteUint16(n)

/**
 * @Macro:       UCUNIT__SAFESTATE()
 *
 * @Description: Encapsulates a function which is called for
 *               putting the hardware to a safe state.
 *
 * @Remarks:     Implement a function to put your hardware into
 *               a safe state.
 *
 *               For example, imagine a motor controller
 *               application:
 *                 1. Stop the motor
 *                 2. Power brake
 *                 3. Hold the brake
 *                 4. Switch warning lamp on
 *                 5. Wait for acknowledge
 *                    ...
 *
 */
#define UCUNIT__SAFESTATE()         Testbed_Shutdown()

/**
 * @Macro:       UCUNIT__RECOVER()
 *
 * @Description: Encapsulates a function which is called for
 *               recovering the hardware from a safe state.
 *
 * @Remarks:     Implement a function to recover your hardware from
 *               a safe state.
 *
 *               For example, imagine our motor controller
 *               application:
 *                 1. Acknowledge the error with a key switch
 *                 2. Switch warning lamp off
 *                 3. Reboot
 *                    ...
 *
 */
#define UCUNIT__RECOVER()           Testbed_Shutdown()

/**
 * @Macro:       UCUNIT__INIT()
 *
 * @Description: Encapsulates a function which is called for
 *               initializing the hardware.
 *
 * @Remarks:     Implement a function to initialize your microcontroller
 *               hardware. You need at least to initialize the
 *               communication device for transmitting your results to
 *               a host computer.
 *
 */
#define UCUNIT__INIT()             Testbed_Init()

/**
 * @Macro:       UCUNIT__SHUTDOWN()
 *
 * @Description: Encapsulates a function which is called to
 *               stop the tests if a checklist fails.
 *
 * @Remarks:     Implement a function to stop the execution of the
 *               tests.
 *
 */
#define UCUNIT__SHUTDOWN()          Testbed_Shutdown()

/**
 * Verbose Mode.
 * UCUNIT_MODE_SILENT: Checks are performed silently.
 * UCUNIT_MODE_NORMAL: Only checks that fail are displayes
 * UCUNIT__MODE_VERBOSE: Passed and failed checks are displayed
 */
#define _UCUNIT_MODE_SILENT
#define _UCUNIT_MODE_NORMAL
#define UCUNIT__MODE_VERBOSE

/**
 * Max. number of checkpoints. This may depend on your application
 * or limited by your RAM.
 */
#define UCUNIT_MAX_TRACEPOINTS 16

/*****************************************************************************/
/* **** End of customizing area *****                                        */
/*****************************************************************************/

/*****************************************************************************/
/* Some useful constants                                                     */
/*****************************************************************************/

#define UCUNIT_VERSION "v2.0" /* Version info */

#ifndef NULL
#define NULL (void *)0
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

/* Action to take if check fails */
#define UCUNIT__ACTION_WARNING   0 /* Goes through the checks
                                     with message depending on level */
#define UCUNIT__ACTION_SHUTDOWN  1 /* Stops on the end of the checklist
                                     if any check has failed */
#define UCUNIT__ACTION_SAFESTATE 2 /* Goes in safe state if check fails */

/*****************************************************************************/
/* Variables */
/*****************************************************************************/

/* Variables for simple statistics */
static int ucunit_checks_failed = 0; /* Numer of failed checks */
static int ucunit_checks_passed = 0; /* Number of passed checks */

static int ucunit_testcases_failed = 0; /* Number of failed test cases */
static int ucunit_testcases_passed = 0; /* Number of passed test cases */
static int ucunit_testcases_failed_checks = 0; /* Number of failed checks in a testcase */
static int ucunit_checklist_failed_checks = 0; /* Number of failed checks in a checklist */
static int ucunit_action = UCUNIT__ACTION_WARNING; /* Action to take if a check fails */
static int ucunit_checkpoints[UCUNIT_MAX_TRACEPOINTS]; /* Max. number of tracepoints */
static int ucunit_index = 0; /* Tracepoint index */

/*****************************************************************************/
/* Internal (private) Macros                                                 */
/*****************************************************************************/

/**
 * @Macro:       UCUNIT__DEFINE_TO_STRING_HLP(x)
 *
 * @Description: Helper macro for converting a define constant into
 *               a string.
 *
 * @Param x:     Define value to convert.
 *
 * @Remarks:     This macro is used by UCUNIT__DEFINE_TO_STRING().
 *
 */
#define UCUNIT__DEFINE_TO_STRING_HLP(x)    #x

/**
 * @Macro:       UCUNIT__DEFINE_TO_STRING(x)
 *
 * @Description: Converts a define constant into a string.
 *
 * @Param x:     Define value to convert.
 *
 * @Remarks:     This macro uses UCUNIT__DEFINE_TO_STRING_HLP().
 *
 */
#define UCUNIT__DEFINE_TO_STRING(x)   UCUNIT__DEFINE_TO_STRING_HLP(x)

#ifdef UCUNIT__MODE_VERBOSE
/**
 * @Macro:       UCUNIT__WRITE_PASSEDMSG(msg, args)
 *
 * @Description: Writes a message that check has passed.
 *
 * @Param msg:   Message to write. This is the name of the called
 *               Check, without the substring UCUNIT__CHECK.
 * @Param args:  Argument list as string.
 *
 * @Remarks:     This macro is used by UCUNIT__CHECK(). A message will
 *               only be written if verbose mode is set
 *               to UCUNIT__MODE_VERBOSE.
 *
 */
#define UCUNIT__WRITE_PASSEDMSG(msg, args)                        \
    do                                                            \
    {                                                             \
        UCUNIT__WRITE_STRING("CHECK: Line ");                     \
        UCUNIT__WRITE_STRING(UCUNIT__DEFINE_TO_STRING(__LINE__)); \
        UCUNIT__WRITE_STRING(":");                                \
        UCUNIT__WRITE_STRING(msg);                                \
        UCUNIT__WRITE_STRING("(");                                \
        UCUNIT__WRITE_STRING(args);                               \
        UCUNIT__WRITE_STRING(")");                                \
        UCUNIT__WRITE_STRING(":PASSED\n");                        \
    } while(0)
#else
#define UCUNIT__WRITE_PASSEDMSG(msg, args)
#endif

#ifdef UCUNIT_MODE_SILENT
#define UCUNIT__WRITE_FAILEDMSG(msg, args)
#else
/**
 * @Macro:       UCUNIT__WRITE_FAILEDMSG(msg, args)
 *
 * @Description: Writes a message that check has failed.
 *
 * @Param msg:   Message to write. This is the name of the called
 *               Check, without the substring UCUNIT__CHECK.
 * @Param args:  Argument list as string.
 *
 * @Remarks:     This macro is used by UCUNIT__CHECK(). A message will
 *               only be written if verbose mode is set
 *               to UCUNIT_MODE_NORMAL and UCUNIT__MODE_VERBOSE.
 *
 */
#define UCUNIT__WRITE_FAILEDMSG(msg, args) \
    do                                     \
    {                                      \
        UCUNIT__WRITE_STRING("CHECK:");    \
        UCUNIT__WRITE_STRING(msg);         \
        UCUNIT__WRITE_STRING("(");         \
        UCUNIT__WRITE_STRING(args);        \
        UCUNIT__WRITE_STRING(")");         \
        UCUNIT__WRITE_STRING(":FAILED\n"); \
    } while(0)
#endif

/**
 * @Macro:       UCUNIT__FAIL_CHECK(msg, args)
 *
 * @Description: Fails a check.
 *
 * @Param msg:   Message to write. This is the name of the called
 *               Check, without the substring UCUNIT__CHECK.
 * @Param args:  Argument list as string.
 *
 * @Remarks:     This macro is used by UCUNIT__CHECK(). A message will
 *               only be written if verbose mode is set
 *               to UCUNIT_MODE_NORMAL and UCUNIT__MODE_VERBOSE.
 *
 */
#define UCUNIT__FAIL_CHECK(msg, args)                \
    do                                               \
    {                                                \
        if (UCUNIT__ACTION_SAFESTATE==ucunit_action) \
        {                                            \
            UCUNIT__SAFESTATE();                     \
        }                                            \
        UCUNIT__WRITE_FAILEDMSG(msg, args);          \
        ucunit_checks_failed++;                      \
        ucunit_checklist_failed_checks++;            \
    } while(0)

/**
 * @Macro:       UCUNIT__PASS_CHECK(msg, args)
 *
 * @Description: Passes a check.
 *
 * @Param msg:   Message to write. This is the name of the called
 *               Check, without the substring UCUNIT__CHECK.
 * @Param args:  Argument list as string.
 *
 * @Remarks:     This macro is used by UCUNIT__CHECK(). A message will
 *               only be written if verbose mode is set
 *               to UCUNIT__MODE_VERBOSE.
 *
 */                                             \
#define UCUNIT__PASS_CHECK(message, args)       \
    do                                          \
    {                                           \
        UCUNIT__WRITE_PASSEDMSG(message, args); \
        ucunit_checks_passed++;                 \
    } while(0)

/*****************************************************************************/
/* Checklist Macros                                                          */
/*****************************************************************************/

/**
 * @Macro:       UCUNIT__CHECKLIST_BEGIN(action)
 *
 * @Description: Begin of a checklist. You have to tell what action
 *               shall be taken if a check fails.
 *
 * @Param action: Action to take. This can be:
 *                * UCUNIT__ACTION_WARNING:   A warning message will be printed
 *                                           that a check has failed
 *                * UCUNIT__ACTION_SHUTDOWN:  The system will shutdown at
 *                                           the end of the checklist.
 *                * UCUNIT__ACTION_SAFESTATE: The system goes into the safe state
 *                                           on the first failed check.

 * @Remarks:     A checklist must be finished with UCUNIT__CHECKLIST_END()
 *
 */
#define UCUNIT__CHECKLIST_BEGIN(action)     \
    do                                      \
    {                                       \
        ucunit_action = action;             \
        ucunit_checklist_failed_checks = 0; \
    } while (0)

/**
 * @Macro:       UCUNIT__CHECKLIST_END()
 *
 * @Description: End of a checklist. If the action was UCUNIT__ACTION_SHUTDOWN
 *               the system will shutdown.
 *
 * @Remarks:     A checklist must begin with UCUNIT__CHECKLIST_BEGIN(action)
 *
 */
#define UCUNIT__CHECKLIST_END()                     \
    if (ucunit_checklist_failed_checks!=0)          \
    {                                               \
        UCUNIT__WRITE_FAILEDMSG("Checklist","");    \
        if (UCUNIT__ACTION_SHUTDOWN==ucunit_action) \
        {                                           \
            UCUNIT__SHUTDOWN();                     \
        }                                           \
    }                                               \
    else                                            \
    {                                               \
        UCUNIT__WRITE_PASSEDMSG("Checklist","");    \
    }

/*****************************************************************************/
/* Check Macros                                                              */
/*****************************************************************************/

/**
 * @Macro:       UCUNIT__CHECK(condition, msg, args)
 *
 * @Description: Checks a condition and prints a message.
 *
 * @Param msg:   Message to write.
 * @Param args:  Argument list as string
 *
 * @Remarks:     Basic check. This macro is used by all higher level checks.
 *
 */
#define UCUNIT__CHECK(condition, msg, args)             \
    if ( (condition) ) { UCUNIT__PASS_CHECK(msg, args); } else { UCUNIT__FAIL_CHECK(msg, args); }

/**
 * @Macro:       UCUNIT__CHECK_IS_EQUAL(expected,actual)
 *
 * @Description: Checks that actual value equals the expected value.
 *
 * @Param expected: Expected value.
 * @Param actual: Actual value.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_EQUAL(expected,actual)         \
    UCUNIT__CHECK( (expected) == (actual), "IsEqual", #expected "," #actual )

/**
 * @Macro:       UCUNIT__CHECK_IS_NULL(pointer)
 *
 * @Description: Checks that a pointer is NULL.
 *
 * @Param pointer: Pointer to check.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_NULL(pointer)                  \
    UCUNIT__CHECK( (pointer) == NULL, "IsNull", #pointer)

/**
 * @Macro:       UCUNIT__CHECK_IS_NOT_NULL(pointer)
 *
 * @Description: Checks that a pointer is not NULL.
 *
 * @Param pointer: Pointer to check.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_NOT_NULL(pointer)               \
    UCUNIT__CHECK( (pointer) != NULL, "IsNotNull", #pointer)

/**
 * @Macro:       UCUNIT__CHECK_IS_IN_RANGE(value, lower, upper)
 *
 * @Description: Checks if a value is between lower and upper bounds (inclusive)
 *               Mathematical: lower <= value <= upper
 *
 * @Param value: Value to check.
 * @Param lower: Lower bound.
 * @Param upper: Upper bound.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_IN_RANGE(value, lower, upper)   \
    UCUNIT__CHECK( ( (value>=lower) && (value<=upper) ), "IsInRange", #value "," #lower "," #upper)

/**
 * @Macro:       UCUNIT__CHECK_IS_8BIT(value)
 *
 * @Description: Checks if a value fits into 8-bit.
 *
 * @Param value: Value to check.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_8BIT(value)                      \
    UCUNIT__CHECK( value==(value & 0xFF), "Is8Bit", #value )

/**
 * @Macro:       UCUNIT__CHECK_IS_16BIT(value)
 *
 * @Description: Checks if a value fits into 16-bit.
 *
 * @Param value: Value to check.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_16BIT(value)                     \
    UCUNIT__CHECK( value==(value & 0xFFFF), "Is16Bit", #value )

/**
 * @Macro:       UCUNIT__CHECK_IS_32BIT(value)
 *
 * @Description: Checks if a value fits into 32-bit.
 *
 * @Param value: Value to check.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_32BIT(value)                     \
    UCUNIT__CHECK( value==(value & 0xFFFFFFFF), "Is32Bit", #value )

/**
 * Checks if bit is set
 */
/**
 * @Macro:       UCUNIT__CHECK_IS_BIT_SET(value, bitno)
 *
 * @Description: Checks if a bit is set in value.
 *
 * @Param value: Value to check.
 * @Param bitno: Bit number. The least significant bit is 0.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_BIT_SET(value, bitno) \
    UCUNIT__CHECK( (1==(((value)>>(bitno)) & 0x01) ), "IsBitSet", #value "," #bitno)

/**
 * @Macro:       UCUNIT__CHECK_IS_BIT_CLEAR(value, bitno)
 *
 * @Description: Checks if a bit is not set in value.
 *
 * @Param value: Value to check.
 * @Param bitno: Bit number. The least significant bit is 0.
 *
 * @Remarks:     This macro uses UCUNIT__CHECK(condition, msg, args).
 *
 */
#define UCUNIT__CHECK_IS_BIT_CLEAR(value, bitno) \
    UCUNIT__CHECK( (0==(((value)>>(bitno)) & 0x01) ), "IsBitClear", #value "," #bitno)

/*****************************************************************************/
/* Testcases */
/*****************************************************************************/

/**
 * @Macro:       UCUNIT__TESTCASE_BEGIN(name)
 *
 * @Description: Marks the beginning of a test case and resets
 *               the test case statistic.
 *
 * @Param name:  Name of the test case.
 *
 * @Remarks:     This macro uses UCUNIT__WRITE_STRING(msg) to print the name.
 *
 */
#define UCUNIT__TESTCASE_BEGIN(name)                                                 \
    do                                                                               \
    {                                                                                \
        UCUNIT__WRITE_STRING("\n===============================================\n"); \
        UCUNIT__WRITE_STRING("TESTCASE:");                                           \
        UCUNIT__WRITE_STRING(name);                                                  \
        UCUNIT__WRITE_STRING(":BEGIN");                                              \
        UCUNIT__WRITE_STRING("\n-----------------------------------------------\n"); \
        ucunit_testcases_failed_checks = ucunit_checks_failed;                       \
    }                                                                                \
    while(0)

/**
 * @Macro:       UCUNIT__TESTCASE_END()
 *
 * @Description: Marks the end of a test case and calculates
 *               the test case statistics.
 *
 * @Param name:  Name of the test case.
 *
 * @Remarks:     This macro uses UCUNIT__WRITE_STRING(msg) to print the result.
 *
 */
#define UCUNIT__TESTCASE_END(name)                                                 \
    do                                                                             \
    {                                                                              \
        UCUNIT__WRITE_STRING("-----------------------------------------------\n"); \
        if( 0==(ucunit_testcases_failed_checks - ucunit_checks_failed) )           \
        {                                                                          \
        	UCUNIT__WRITE_STRING("TESTCASE:");                                     \
        	UCUNIT__WRITE_STRING(name);                                            \
        	UCUNIT__WRITE_STRING(":");                                             \
            UCUNIT__WRITE_STRING("PASSED\n");                                      \
            ucunit_testcases_passed++;                                             \
        }                                                                          \
        else                                                                       \
        {                                                                          \
        	UCUNIT__WRITE_STRING("TESTCASE:");                                     \
        	UCUNIT__WRITE_STRING(name);                                            \
        	UCUNIT__WRITE_STRING(":");                                             \
            UCUNIT__WRITE_STRING("FAILED\n");                                      \
            ucunit_testcases_failed++;                                             \
        }                                                                          \
        UCUNIT__WRITE_STRING("===============================================\n"); \
    }                                                                              \
    while(0)

/*****************************************************************************/
/* Support for code coverage */
/*****************************************************************************/

/**
 * @Macro:       UCUNIT__TRACEPOINT(index)
 *
 * @Description: Marks a trace point.
 *               If a trace point is executed, its coverage state switches
 *               from 0 to the line number.
  *              If a trace point was never executed, the state
 *               remains 0.
 *
 * @Param index: Index of the tracepoint.
 *
 * @Remarks:     This macro fails if index>UCUNIT_MAX_TRACEPOINTS.
 *
 */
#define UCUNIT__TRACEPOINT(index)                            \
    if(index<UCUNIT_MAX_TRACEPOINTS)                         \
    {                                                        \
        ucunit_checkpoints[index] = __LINE__;                \
    }                                                        \
    else                                                     \
    {                                                        \
        UCUNIT__WRITE_FAILEDMSG("Tracepoint index", #index); \
    }

/**
 * @Macro:       UCUNIT__RESET_TRACEPOINTS()
 *
 * @Description: Resets the trace point coverage state to 0.
 *
 * @Param index: Index of the trace point.
 *
 * @Remarks:     This macro fails if index>UCUNIT_MAX_TRACEPOINTS.
 *
 */
#define UCUNIT__RESET_TRACEPOINTS()                                           \
    for (ucunit_index=0; ucunit_index<UCUNIT_MAX_TRACEPOINTS; ucunit_index++) \
    {                                                                         \
        ucunit_checkpoints[ucunit_index]=0;                                   \
    }

/**
 * @Macro:       UCUNIT__CHECK_TRACEPOINT(index)
 *
 * @Description: Checks if a trace point was covered.
 *
 * @Param index: Index of the trace point.
 *
 * @Remarks:     This macro fails if index>UCUNIT_MAX_TRACEPOINTS.
 *
 */
#define UCUNIT__CHECK_TRACEPOINT(index)    \
    UCUNIT__CHECK( (ucunit_checkpoints[index]!=0), "TracepointCoverage", #index);

/*****************************************************************************/
/* Testsuite Summary                                                         */
/*****************************************************************************/

/**
 * @Macro:       UCUNIT__SUMMARY()
 *
 * @Description: Writes the test suite summary.
 *
 * @Remarks:     This macro uses UCUNIT__WRITE_STRING(msg) and
 *               UCUNIT__WRITE_INT(n) to write the summary.
 *
 */
#define UCUNIT__SUMMARY()                                               \
{                                                                       \
    UCUNIT__WRITE_STRING("\n**************************************");   \
    UCUNIT__WRITE_STRING("\nTestcases: failed: ");                      \
    UCUNIT__WRITE_INT(ucunit_testcases_failed);                         \
    UCUNIT__WRITE_STRING("\n           passed: ");                      \
    UCUNIT__WRITE_INT(ucunit_testcases_passed);                         \
    UCUNIT__WRITE_STRING("\nChecks:    failed: ");                      \
    UCUNIT__WRITE_INT(ucunit_checks_failed);                            \
    UCUNIT__WRITE_STRING("\n           passed: ");                      \
    UCUNIT__WRITE_INT(ucunit_checks_passed);                            \
    UCUNIT__WRITE_STRING("\n**************************************\n"); \
}

#endif /*UCUNIT_H_*/

