/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#pragma once

#include <inttypes.h>
#include <lk/compiler.h>
#include <lk/list.h>
#include <stdbool.h>
#include <string.h>

__BEGIN_CDECLS

/*
 * This function returns a time in nanoseconds based on hardware counters
 * it is expected to:
 *  - Be non-wrapping or have very long (years) roll-over period
 *  - Have a resolution below 100nsc
 */
uint64_t get_current_time_ns(void);

/*
 * Test functions can be defined with:
 * TEST(SuiteName, TestName) {
 *   ... test body ...
 * }
 * or with:
 * TEST_F(SuiteName, TestName) {
 *   ... test body ...
 * }
 * or with:
 * TEST_P(SuiteName, TestName) {
 *   ... test body ...
 * }
 *
 * NOTE: SuiteName and TestName should not contain underscores.
 *
 * Use EXPECT_<op> or ASSERT_<op> directly in test functions or from nested
 * functions to check test conditions. Where <op> can be:
 *   EQ for ==
 *   NE for !=
 *   LT for <
 *   LE for <=
 *   GT for >
 *   GE for >=
 *
 * The test functions follows this pattern:
 *   <EXPECT|ASSERT>_<op>(val1, val2 [, format, ...])
 * If val1 <op> val2 is not true, then both values will be printed and a test
 * failure will be recorded. For ASSERT_<op> it will also jump to a test_abort
 * label in the calling function.
 *
 * Call RUN_ALL_TESTS() to run all tests defined by TEST (or
 * RUN_ALL_SUITE_TESTS("SuiteName") to only run tests with the specified
 * SuiteName). RUN_ALL_TESTS and RUN_ALL_SUITE_TESTS return true if all the
 * tests passed.
 *
 * Test functions defined with TEST_F or TEST_P expect the type <SuiteName>_t
 * and <SuiteName>_SetUp and <SuiteName>_TearDown functions to be defined.
 * The <SuiteName>_SetUp function will be called once before each test in
 * SuiteName in run and the <SuiteName>_TearDown function will be called once
 * after each test in SuiteName is run. These functions can be defined with
 * TEST_F_SETUP(<SuiteName>) {
 *  ... setup body ...
 * }
 * and with:
 * TEST_F_TEARDOWN(<SuiteName>) {
 *  ... teardown body ...
 * }
 * A pointer to a <SuiteName>_t variable will be passed as "_state" to the
 * setup, teardown and test functions.
 *
 * TEST_FIXTURE_ALIAS(NewSuiteName, OldSuiteName) can be used to use the test
 * fixture defined for OldSuiteName with NewSuiteName.
 *
 * Tests defined with TEST_P will only run when their suite is run if they have
 * been instantiated with parameters using INSTANTIATE_TEST_SUITE_P. These tests
 * can access their parameter using GetParam()
 */

#ifndef trusty_unittest_printf
#error trusty_unittest_printf must be defined
#endif

/**
 * struct test_context - struct representing the state of a test run.
 * @tests_total:      Number of conditions checked
 * @tests_skipped:    Number of tests skipped
 * @tests_disabled:   Number of disabled tests skipped
 * @tests_failed:     Number of conditions failed
 * @inst_name:        Name of the current parameter instantiation
 * @suite_name:       Name of the current test suite
 * @param_name:       Name of the current parameter
 * @test_name:        Name of current test case
 * @test_param:       The current test parameter
 * @all_ok:           State of current test case
 * @skipped:          Current test was skipped.
 * @hard_fail:        Type of test failure (when @all_ok is false)
 * @test_start_time:  Test Start Time in ns
 * @suite_duration_ms:Test Suite duration in ms
 */
struct test_context {
    unsigned int tests_total;
    unsigned int tests_skipped;
    unsigned int tests_disabled;
    unsigned int tests_failed;
    const char* inst_name;
    const char* suite_name;
    const char* param_name;
    const char* test_name;
    const void* test_param;
    bool all_ok;
    bool skipped;
    bool hard_fail;
    uint64_t test_start_time;
    uint64_t suite_duration_ms;
};

/**
 * struct test_list_node - node to hold test function in list of tests
 * @node:           List node
 * @suite:          Name of test suite (optionally used for filtering)
 * @name:           Name of test (optionally used for filtering)
 * @func:           Test function
 * @needs_param:    Indicates if the test function is parameterized
 */

struct test_list_node {
    struct list_node node;
    const char* suite;
    const char* name;
    void (*func)(void);
    bool needs_param;
};

/**
 * struct test_param_gen -  struct representing a parameter generator
 * @gen_param:              Function to generate the parameter for a test
 * @priv:                   Private data passed to gen_param
 */
struct test_param_gen {
    const void* (*gen_param)(void*, int);
    void* priv;
};

/**
 * typedef test_param_to_string_t - Converts a test parameter to its string form
 * @param:      Parameter to convert
 * @buf:        Buffer to fill with a NULL terminated string representation of
 *              @param
 * @buf_size:   Size in bytes of @buf
 *
 * When called, this function is passed a pointer to the parameter for the test
 * that is being executed in @param and must return a null-terminated string
 * representing the passed in parameter in @buf of at most size @buf_size.
 */
typedef void (*test_param_to_string_t)(const void* param,
                                       char* buf,
                                       size_t buf_size);

/**
 * struct test_param_list_node - holds parameter generators
 * @node:               List node
 * @param_gen:          Parameter generator
 * @to_string:          Function to convert a parameter to its string form
 * @inst_name:          Name of the instantiation associated with the generator
 * @suite:              Name of test suite associated with the generator
 */

struct test_param_list_node {
    struct list_node node;
    struct test_param_gen param_gen;
    test_param_to_string_t to_string;
    const char* inst_name;
    const char* suite;
};

static struct test_context _test_context;

/*
 * List of tests. Tests are added by a __attribute__((constructor)) function
 * per test defined by the TEST macro.
 */
static struct list_node _test_list = LIST_INITIAL_VALUE(_test_list);

/*
 * List of parameter generators. Parameter generators  are added by a
 * __attribute__((constructor)) function per instantiation defined with
 * INSTANTIATE_TEST_SUITE_P.
 */
static struct list_node _test_param_list = LIST_INITIAL_VALUE(_test_param_list);

static inline void trusty_unittest_print_status_name_param_duration(
        const char* status,
        const char* param_gen_inst_name, /* parameter generator instance name */
        const char* suite_name,
        const char* test_name,
        const char* param_name,
        const char* duration_ms) {
    if (param_gen_inst_name) {
        trusty_unittest_printf("[ %s ] %s/%s.%s/%s%s\n", status,
                               param_gen_inst_name, suite_name, test_name,
                               param_name, duration_ms);
    } else {
        trusty_unittest_printf("[ %s ] %s.%s%s\n", status, suite_name,
                               test_name, duration_ms);
    }
}

static inline void trusty_unittest_print_status_name(const char* suite_name,
                                                     const char* test_name,
                                                     const char* status) {
    trusty_unittest_print_status_name_param_duration(
            status, _test_context.inst_name, suite_name, test_name,
            _test_context.param_name, "");
}

static inline void trusty_unittest_print_status(const char* status) {
    trusty_unittest_print_status_name_param_duration(
            status, _test_context.inst_name, _test_context.suite_name,
            _test_context.test_name, _test_context.param_name, "");
}

static inline void trusty_unittest_print_status_duration(
        const char* status,
        uint64_t test_duration_ms) {
    char duration_str[16] = "";

    /* print duration at end of test case */
    snprintf(duration_str, sizeof(duration_str), " (%" PRIu64 " ms)",
             test_duration_ms);
    trusty_unittest_print_status_name_param_duration(
            status, _test_context.inst_name, _test_context.suite_name,
            _test_context.test_name, _test_context.param_name,
            (const char*)duration_str);
}

static inline void TEST_BEGIN_FUNC(const char* suite_name,
                                   const char* test_name) {
    _test_context.suite_name = suite_name;
    _test_context.test_name = test_name;
    _test_context.all_ok = true;
    _test_context.hard_fail = false;
    _test_context.skipped = false;
    _test_context.tests_total++;
    trusty_unittest_print_status("RUN     ");
    /*
     * initialize the test start time
     * (after the print status is slightly better)
     */
    _test_context.test_start_time = get_current_time_ns();
}

static inline void TEST_END_FUNC(void) {
    uint64_t test_duration_ms =
            (get_current_time_ns() - _test_context.test_start_time) / 1000000;
    _test_context.suite_duration_ms += test_duration_ms;
    if (_test_context.skipped) {
        trusty_unittest_print_status_duration(" SKIPPED", test_duration_ms);
    } else if (_test_context.all_ok) {
        trusty_unittest_print_status_duration("      OK", test_duration_ms);
    } else {
        trusty_unittest_print_status_duration(" FAILED ", test_duration_ms);
    }
    _test_context.test_name = NULL;
}

#define STRINGIFY(x) #x

#define TEST_FIXTURE_ALIAS(new_suite_name, old_suite_name)              \
    typedef old_suite_name##_t new_suite_name##_t;                      \
                                                                        \
    static void new_suite_name##_SetUp(new_suite_name##_t* _state) {    \
        old_suite_name##_SetUp(_state);                                 \
    }                                                                   \
    static void new_suite_name##_TearDown(new_suite_name##_t* _state) { \
        old_suite_name##_TearDown(_state);                              \
    }

#define TEST_INTERNAL(suite_name, test_name, w_param, pre, post, arg, argp)  \
    static void suite_name##_##test_name##_inner argp;                       \
                                                                             \
    static void suite_name##_##test_name(void) {                             \
        TEST_BEGIN_FUNC(STRINGIFY(suite_name), STRINGIFY(test_name));        \
        {                                                                    \
            pre;                                                             \
            if (!_test_context.hard_fail && !_test_context.skipped) {        \
                suite_name##_##test_name##_inner arg;                        \
            }                                                                \
            post;                                                            \
        }                                                                    \
        TEST_END_FUNC();                                                     \
    }                                                                        \
                                                                             \
    static struct test_list_node suite_name##_##test_name##_node = {         \
            .node = LIST_INITIAL_CLEARED_VALUE,                              \
            .suite = #suite_name,                                            \
            .name = #test_name,                                              \
            .func = suite_name##_##test_name,                                \
            .needs_param = w_param,                                          \
    };                                                                       \
                                                                             \
    __attribute__((constructor)) void suite_name##_##test_name##_add(void) { \
        list_add_tail(&_test_list, &suite_name##_##test_name##_node.node);   \
    }                                                                        \
                                                                             \
    static void suite_name##_##test_name##_inner argp

#define TEST_F_SETUP(suite_name) \
    static void suite_name##_SetUp(suite_name##_t* _state)

#define TEST_F_TEARDOWN(suite_name) \
    static void suite_name##_TearDown(suite_name##_t* _state)

#define TEST(suite_name, test_name) \
    TEST_INTERNAL(suite_name, test_name, false, , , (), (void))

#define TEST_F_CUSTOM_ARGS(suite_name, test_name, arg, argp)                  \
    TEST_INTERNAL(suite_name, test_name, false, suite_name##_t state;         \
                  suite_name##_SetUp(&state);, suite_name##_TearDown(&state); \
                  , arg, argp)

#define TEST_F(suite_name, test_name)                   \
    TEST_F_CUSTOM_ARGS(suite_name, test_name, (&state), \
                       (suite_name##_t * _state))

#define TEST_P_CUSTOM_ARGS(suite_name, test_name, arg, argp)                  \
    TEST_INTERNAL(suite_name, test_name, true, suite_name##_t state;          \
                  suite_name##_SetUp(&state);, suite_name##_TearDown(&state); \
                  , arg, argp)

#define TEST_P(suite_name, test_name)                   \
    TEST_P_CUSTOM_ARGS(suite_name, test_name, (&state), \
                       (suite_name##_t * _state))

struct test_array_param {
    const void* arr;
    int elem_size;
    int count;
};

static inline const void* test_gen_array_param(void* priv, int i) {
    struct test_array_param* param = (struct test_array_param*)priv;

    if (i >= param->count) {
        return NULL;
    }

    return (uint8_t*)param->arr + param->elem_size * i;
}

struct test_range_param {
    long begin;
    long end;
    long step;
    long current;
};

static inline const void* test_gen_range_param(void* priv, int i) {
    struct test_range_param* range_param = (struct test_range_param*)priv;

    range_param->current = range_param->begin + range_param->step * i;

    if (range_param->current >= range_param->end) {
        return NULL;
    }

    return &range_param->current;
}

struct combined_params {
    struct test_param_gen* generators;
    int generator_count;
    int* idxs;
    const void** current;
};

static inline void update_combined_params(struct combined_params* params,
                                          int j,
                                          bool reset) {
    if (reset) {
        params->idxs[j] = 0;
    }

    params->current[j] = params->generators[j].gen_param(
            params->generators[j].priv, params->idxs[j]);
    params->idxs[j]++;
}

static inline const void* test_gen_combined_param(void* priv, int i) {
    struct combined_params* params = (struct combined_params*)priv;

    if (i == 0) {
        for (int j = 0; j < params->generator_count; j++) {
            update_combined_params(params, j, true);
        }
        return params->current;
    }

    for (int j = 0; j < params->generator_count; j++) {
        update_combined_params(params, j, false);

        if (params->current[j] != NULL) {
            return params->current;
        }

        update_combined_params(params, j, true);
    }

    return NULL;
}

#define FIRST_ARG(arg0, args...) arg0
#define SECOND_ARG(arg0, arg1, args...) arg1
/* Parentheses are used to prevent commas from being interpreted when they are
 * passed in macro arguments. DELETE_PAREN is used to remove these parentheses
 * inside the macro that uses the commas e.g.:
 *
 * MY_MACRO((1, 2, 3))
 *
 * #define MY_MACRO(arg)
 *      DELETE_PAREN arg
 */
#define DELETE_PAREN(args...) args

#define testing_Range(_begin, end_step...)                  \
    (static struct test_range_param range_param =           \
             {                                              \
                     .begin = _begin,                       \
                     .end = FIRST_ARG(end_step, ),          \
                     .step = SECOND_ARG(end_step, 1, ),     \
             };                                             \
     param_node.param_gen.gen_param = test_gen_range_param; \
     param_node.param_gen.priv = &range_param;)

#define testing_ValuesIn(array)                             \
    (static struct test_array_param array_param =           \
             {                                              \
                     .arr = array,                          \
                     .elem_size = sizeof(array[0]),         \
                     .count = countof(array),               \
             };                                             \
                                                            \
     param_node.param_gen.gen_param = test_gen_array_param; \
     param_node.param_gen.priv = &array_param;)

/*
 * (args, args) is passed to __typeof__ to guarantee that it resolves to const
 * char* instead of const char[] in cases where args contains a single string.
 * When args is a single string, it is inlined and typeof will resolve to const
 * char[].
 */
#define testing_Values(args...)                        \
    (static __typeof__(args, args) new_arr[] = {args}; \
     DELETE_PAREN testing_ValuesIn(new_arr))

#define testing_Bool() testing_Values(false, true)

#define test_set_combine_params(generator, i, count)                  \
    {                                                                 \
        DELETE_PAREN generator;                                       \
        if (i < count) {                                              \
            param_gens[i].gen_param = param_node.param_gen.gen_param; \
            param_gens[i].priv = param_node.param_gen.priv;           \
        }                                                             \
    }

#define testing_Combine_internal(arg0, arg1, arg2, arg3, arg4, arg5, arg6,   \
                                 arg7, arg8, arg9, da0, da1, da2, da3, da4,  \
                                 da5, da6, da7, da8, da9, count, args...)    \
    (static struct test_param_gen param_gens[count]; static int idxs[count]; \
     static const void* current_params[count];                               \
     static struct combined_params combined_params =                         \
             {                                                               \
                     param_gens,                                             \
                     count,                                                  \
                     idxs,                                                   \
                     current_params,                                         \
             };                                                              \
                                                                             \
     test_set_combine_params(arg0, 0, count);                                \
     test_set_combine_params(arg1, 1, count);                                \
     test_set_combine_params(arg2, 2, count);                                \
     test_set_combine_params(arg3, 3, count);                                \
     test_set_combine_params(arg4, 4, count);                                \
     test_set_combine_params(arg5, 5, count);                                \
     test_set_combine_params(arg6, 6, count);                                \
     test_set_combine_params(arg7, 7, count);                                \
     test_set_combine_params(arg8, 8, count);                                \
     test_set_combine_params(arg9, 9, count);                                \
     param_node.param_gen.gen_param = test_gen_combined_param;               \
     param_node.param_gen.priv = &combined_params;)

#define testing_Combine(generators...)                                       \
    testing_Combine_internal(generators, (), (), (), (), (), (), (), (), (), \
                             (), 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define INSTANTIATE_TEST_SUITE_P_INTERNAL(_inst_name, suite_name, param_gen, \
                                          param_to_string, args...)          \
                                                                             \
    __attribute__((constructor)) void suite_name##_##_inst_name##param_add(  \
            void) {                                                          \
        static struct test_param_list_node param_node = {                    \
                .node = LIST_INITIAL_CLEARED_VALUE,                          \
                .to_string = param_to_string,                                \
                .inst_name = STRINGIFY(_inst_name),                          \
                .suite = #suite_name,                                        \
        };                                                                   \
                                                                             \
        DELETE_PAREN param_gen;                                              \
                                                                             \
        list_add_tail(&_test_param_list, &param_node.node);                  \
    }

static inline bool has_disabled_prefix(const char* str) {
    const char disabled_prefix[] = "DISABLED_";
    return strncmp(str, disabled_prefix, strlen(disabled_prefix)) == 0;
}

static inline bool trusty_unittest_test_is_disabled(
        struct test_list_node* entry) {
    return has_disabled_prefix(entry->suite) ||
           has_disabled_prefix(entry->name);
}

/**
 * trusty_unittest_count_param_entries - Count parameter entries for a given
 * test suite.
 *
 * @suite: Name of test suite associated with the parameters
 *         suite is never going to be NULL (see invocation),
 *         no need to guard against this case.
 *
 * For each parameter generator associated with the suite, accrue
 * the number of parameter entries.
 *
 * Not meant for external use.
 *
 * Return: count of parameter entries
 */
static int trusty_unittest_count_param_entries(
        struct test_list_node* test_case) {
    int param_count = 0;
    int i;
    struct test_param_list_node* param_entry;
    bool has_param_gen = 0;
    bool invalid_run = false;
    /*
     * for each parameter generator associated with the suite,
     * accrue the number of parameter entries
     */
    list_for_every_entry(&_test_param_list, param_entry,
                         struct test_param_list_node, node) {
        if (!strcmp(test_case->suite, param_entry->suite)) {
            i = 0;
            has_param_gen = true;
            /* For each parameter from the generator */
            while (param_entry->param_gen.gen_param(param_entry->param_gen.priv,
                                                    i)) {
                i++;
            }
            if (!i) {
                /*
                 * No parameter entries: parameterized test case exist
                 * but the test generator is empty
                 */
                trusty_unittest_print_status_name_param_duration(
                        " FAILED ", param_entry->inst_name, test_case->suite,
                        test_case->name,
                        "NO PARAMS: Parameterized Test Case Generator without Params!",
                        "");
                invalid_run = true;
            }
            param_count += i;
        }
    }
    /*
     * No parameter generator: parameterized test case exist
     * but the test suite is not associated with any param generator
     */
    if (!has_param_gen) {
        trusty_unittest_print_status_name_param_duration(
                " FAILED ", "NO PARAM GENERATOR", test_case->suite,
                test_case->name,
                "NO PARAMS: Parameterized Test Case without Param Generator!",
                "");
    }
    return invalid_run ? 0 : param_count;
}

/**
 * trusty_unittest_has_parameterized_test_case - test suite with parameterized
 * test cases
 *
 * @suite: Name of test suite associated with the parameters.
 *         suite is never going to be NULL (see invocation),
 *         no need to guard against this case.
 *
 * Check whether a test suite has parameterized test cases.
 * Not meant for external use.
 *
 * Return: True if parameterized test
 */
static bool trusty_unittest_has_parameterized_test_case(const char* suite) {
    struct test_list_node* test_case;
    list_for_every_entry(&_test_list, test_case, struct test_list_node, node) {
        if (!strcmp(suite, test_case->suite)) {
            if (test_case->needs_param) {
                return true;
            }
        }
    }
    return false;
}

/**
 * trusty_unittest_count_test_cases - Count test cases associated with test
 * suite.
 *
 * @suite: Name of test suite.
 *         When suite is NULL, all test cases from all test suites are counted.
 *
 * This test case count shall comply to the GTest parser requirements
 * and thus shall not include disabled test cases.
 * Not meant for external use.
 *
 * Return: count of test cases, -1 in case of detected test suite coding error
 */
static int trusty_unittest_count_test_cases(const char* suite) {
    struct test_list_node* test_case;
    struct test_param_list_node* param_entry;
    bool test_code_error = false;
    size_t test_case_count = 0;
    bool disabled;
    int param_entries = 0;
    const char* param_entries_suite = NULL;
    int current_test_case_count;
    /* count all non parameterized and parameterized test cases */
    list_for_every_entry(&_test_list, test_case, struct test_list_node, node) {
        /* exclude tests not part of the requested suite */
        if (suite && strcmp(suite, test_case->suite)) {
            continue;
        }
        /* only count non-disabled test case as required by the GTest parser */
        disabled = trusty_unittest_test_is_disabled(test_case);
        if (test_case->needs_param) {
            if (!param_entries_suite || !param_entries ||
                strcmp(param_entries_suite, test_case->suite)) {
                /* count param_entries for test_case->suite */
                param_entries = trusty_unittest_count_param_entries(test_case);
                param_entries_suite = test_case->suite;
            }
            if (!param_entries) {
                /*
                 * Test code error shall be fixed and will prevent test
                 * execution however we don't bail right away with the goal of
                 * logging all erroneous test cases.
                 */
                test_code_error = true;
                continue;
            }
            current_test_case_count = param_entries;
        } else {
            current_test_case_count = 1;
        }
        if (!disabled) {
            /* non parameterized (singular) test case */
            test_case_count += current_test_case_count;
        }
    }
    /*
     * Search for a test coding issue where a test generator exists
     * but is not backed by existing test case
     */
    list_for_every_entry(&_test_param_list, param_entry,
                         struct test_param_list_node, node) {
        if (!trusty_unittest_has_parameterized_test_case(param_entry->suite)) {
            test_code_error = true;
            trusty_unittest_print_status_name_param_duration(
                    " FAILED ", param_entry->inst_name, param_entry->suite,
                    "NO_TESTS", "Parameter Generator without tests!", "");
        }
    }
    return test_code_error ? -1 : test_case_count;
}

/**
 * trusty_unittest_run_test_suite - run each test case associated with test
 * suite.
 *
 * @suite:      Name of test suite
 *              when suite is NULL, all test cases
 *              from all test suites are executed.
 * @needs_param: when true run the parameterised test cases,
 *               otherwise run the non-parameterised test cases
 *
 * Not meant for external use.
 *
 * Return: count of executed test cases
 */
static int trusty_unittest_run_test_suite(const char* suite, bool needs_param) {
    struct test_list_node* entry;
    int test_case_count = 0;

    list_for_every_entry(&_test_list, entry, struct test_list_node, node) {
        if ((!suite || !strcmp(suite, entry->suite)) &&
            (entry->needs_param == needs_param)) {
            if (trusty_unittest_test_is_disabled(entry)) {
                trusty_unittest_print_status_name(entry->suite, entry->name,
                                                  "DISABLED");
                _test_context.tests_disabled++;
            } else {
                test_case_count++;
                entry->func();
            }
        }
    }
    return test_case_count;
}

/*
 * The testing framework uses 3 global variables to keep track of tests and
 * related data:
 *
 * _test_context: contains information about the overall execution of the
 * framework (e.g. total tests run) and information about the currently
 * executing test (e.g. test name, suite name).
 *
 * _test_list: contains a list of tests that can be run. Each test belongs to a
 * test suite and may require parameters to be run.
 *
 * _test_param_list: contains a list of parameter generators for tests that
 * require parameters. Each generator is associated with a specific test suite.
 * Parameter generators are functions that return parameters that apply to all
 * the tests that require parameters (i.e. parameterized tests) in a given test
 * suite.
 *
 * Tests are only run as part of test suites. When a test suite is run all of
 * the non-paremeterized tests belonging to that suite are run first followed by
 * the parameterized tests in the suite. All of the parameterized tests in a
 * suite are run once for each value returned by a parameter generator
 * associated with that suite.
 */
static inline bool RUN_ALL_SUITE_TESTS(const char* suite) {
    struct test_param_list_node* param_entry;
    const void* test_param;
    int i;
    char param_str[64];
    int actual_test_count = 0;
    int expected_test_count = trusty_unittest_count_test_cases(suite);
    if (expected_test_count == -1) {
        trusty_unittest_printf("Test Coding Error - aborting execution.\n");
        return false;
    }

    trusty_unittest_printf(
            "[==========] Running %d tests from %s test suite%s.\n",
            expected_test_count, suite ? suite : "all", suite ? "" : "s");

    _test_context.tests_total = 0;
    _test_context.tests_disabled = 0;
    _test_context.tests_failed = 0;
    _test_context.test_param = NULL;
    _test_context.inst_name = NULL;
    _test_context.param_name = param_str;
    _test_context.suite_duration_ms = 0;
    /* Run all the non-parameterized tests in the suite */
    actual_test_count = trusty_unittest_run_test_suite(suite, false);

    /* For each parameter generator associated with the suite */
    list_for_every_entry(&_test_param_list, param_entry,
                         struct test_param_list_node, node) {
        if (!suite || !strcmp(suite, param_entry->suite)) {
            i = 0;
            /* For each parameter from the generator */
            while ((test_param = param_entry->param_gen.gen_param(
                            param_entry->param_gen.priv, i))) {
                /* Set the parameter for the next run */
                _test_context.inst_name = param_entry->inst_name;
                _test_context.test_param = test_param;
                if (param_entry->to_string) {
                    param_entry->to_string(test_param, param_str,
                                           sizeof(param_str));
                } else {
                    snprintf(param_str, sizeof(param_str), "%d", i);
                }
                /* Run all the parameterized tests in the suite */
                actual_test_count += trusty_unittest_run_test_suite(
                        param_entry->suite, true);
                i++;
            }
        }
    }
    if (actual_test_count != expected_test_count) {
        trusty_unittest_printf("[ RUN      ] %s.test_count_match_check\n",
                               suite ? suite : "all_suites");
        trusty_unittest_printf(
                "[----------] %d tests ran, but expected %d tests.\n",
                actual_test_count, expected_test_count);
        trusty_unittest_print_status_name(suite ? suite : "all_suites",
                                          "test_count_match_check", " FAILED ");
        ++_test_context.tests_failed;
        ++_test_context.tests_total;
    } else if (actual_test_count == 0 && _test_context.tests_disabled == 0) {
        trusty_unittest_printf("[ RUN      ] %s.test_count_empty_check\n",
                               suite ? suite : "all_suites");
        trusty_unittest_printf("[----------] 0 tests but none disabled.\n");
        trusty_unittest_print_status_name(suite ? suite : "all_suites",
                                          "test_count_empty_check", " FAILED ");
        ++_test_context.tests_failed;
        ++_test_context.tests_total;
    }

    trusty_unittest_printf(
            "[==========] %d tests ran (%" PRIu64 " ms total).\n",
            _test_context.tests_total, _test_context.suite_duration_ms);

    if (_test_context.tests_total != _test_context.tests_failed) {
        trusty_unittest_printf(
                "[  PASSED  ] %d tests.\n",
                _test_context.tests_total - _test_context.tests_failed);
    }
    if (_test_context.tests_skipped) {
        trusty_unittest_printf("[  SKIPPED ] %d tests.\n",
                               _test_context.tests_skipped);
    }
    if (_test_context.tests_disabled) {
        trusty_unittest_printf("[ DISABLED ] %d tests.\n",
                               _test_context.tests_disabled);
    }
    if (_test_context.tests_failed) {
        trusty_unittest_printf("[  FAILED  ] %d tests.\n",
                               _test_context.tests_failed);
    }
    return _test_context.tests_failed == 0;
}

static inline bool RUN_ALL_TESTS(void) {
    return RUN_ALL_SUITE_TESTS(NULL);
}

/**
 * GTEST_SKIP() - Skip current test
 *
 * This will skip the current test without triggering a failure. It will use
 * same test_abort label as the ASSERT_... macros. Calling this after a test has
 * failed or calling ASSERT_.../EXPECT_... macros after GTEST_SKIP has jumped
 * to test_abort is not supported.
 */
#define GTEST_SKIP()                       \
    {                                      \
        if (!_test_context.skipped) {      \
            _test_context.skipped = true;  \
            _test_context.tests_skipped++; \
        }                                  \
        goto test_abort;                   \
    }

#define ASSERT_EXPECT_TEST(op, op_pre, op_sep, op_args, is_hard_fail,        \
                           fail_action, vals_type, vals_format_placeholder,  \
                           print_cast, print_op, val1, val2, extra_msg...)   \
    {                                                                        \
        vals_type _val1 = val1;                                              \
        vals_type _val2 = val2;                                              \
        if (!op_pre(_val1 DELETE_PAREN op_sep _val2 DELETE_PAREN op_args)) { \
            trusty_unittest_printf("%s: @ %s:%d\n", _test_context.test_name, \
                                   __FILE__, __LINE__);                      \
            trusty_unittest_printf(                                          \
                    "  expected: %s (" vals_format_placeholder ") " print_op \
                    " %s (" vals_format_placeholder ")\n",                   \
                    #val1, print_cast _val1, #val2, print_cast _val2);       \
            trusty_unittest_printf("  " extra_msg);                          \
            trusty_unittest_printf("\n");                                    \
            if (_test_context.all_ok) {                                      \
                _test_context.all_ok = false;                                \
                _test_context.tests_failed++;                                \
            }                                                                \
            _test_context.hard_fail |= is_hard_fail;                         \
            fail_action                                                      \
        }                                                                    \
    }

static inline bool HasFailure(void) {
    return !_test_context.all_ok;
}

/**
 * INSTANTIATE_TEST_SUITE_P - Instantiate parameters for a test suite
 * @inst_name:          Name for instantiation of parameters. Should not contain
 *                      underscores.
 * @suite_name:         Name of test suite associated with the parameters
 * @param_gen:          One of the parameter generators (see below)
 * @param_to_string:    Function of type &typedef test_param_to_string_t
 *                      used to convert a parameter to its string form. This
 *                      argument is optional.
 *
 * Parameter Generators:
 *  testing_Range(being, end, step):
 *  Returns the values {begin, being+step, being+step+step, ...} up to but not
 *  including end. step is optional and defaults to 1.
 *
 *  testing_Values(v1, v2, ..., vN):
 *  Returns the values {v1, v2, ..., vN)
 *
 *  testing_ValuesIn(array)
 *  Returns the values in array
 *
 *  testing_Bool()
 *  Returns {false, true}
 *
 *  testing_Combine(g1, [g2, g3, g4, g5]):
 *  Returns the values of the combinations of the provided generators
 *  (min 1, max 5) an as an array.
 */
#define INSTANTIATE_TEST_SUITE_P(inst_name, suite_name, param_gen_args...)   \
    INSTANTIATE_TEST_SUITE_P_INTERNAL(inst_name, suite_name, param_gen_args, \
                                      NULL, )

/**
 * GetParam() - Returns a pointer to the current test parameter
 *
 * Context: This function can be called within a parameterized test to
 *          retrieve the current parameter to the test.
 *
 * Return: a pointer to the current test parameter.
 *
 * This pointer should be cast to the expected parameter type for the executing
 * test.
 */
static inline const void* GetParam(void) {
    return _test_context.test_param;
}

#define ASSERT_EXPECT_LONG_TEST(op, is_hard_fail, fail_action, val1, val2, \
                                args...)                                   \
    ASSERT_EXPECT_TEST(op, , (op), (), is_hard_fail, fail_action,          \
                       __typeof__(val2), "%ld", (long), #op, val1, val2, args)

#define ASSERT_EXPECT_STR_TEST(func, is_hard_fail, fail_action, args...) \
    ASSERT_EXPECT_TEST(func, func, (, ), (), is_hard_fail, fail_action,  \
                       const char*, "\"%s\"", , args)

#define ASSERT_EXPECT_STRN_TEST(func, n, is_hard_fail, fail_action, args...) \
    ASSERT_EXPECT_TEST(func, func, (, ), (, n), is_hard_fail, fail_action,   \
                       const char*, "\"%s\"", , args)

#define EXPECT_TEST(op, args...) ASSERT_EXPECT_LONG_TEST(op, false, , args)
#define EXPECT_EQ(args...) EXPECT_TEST(==, args)
#define EXPECT_NE(args...) EXPECT_TEST(!=, args)
#define EXPECT_LT(args...) EXPECT_TEST(<, args)
#define EXPECT_LE(args...) EXPECT_TEST(<=, args)
#define EXPECT_GT(args...) EXPECT_TEST(>, args)
#define EXPECT_GE(args...) EXPECT_TEST(>=, args)
#define EXPECT_STR_TEST(func, args...) \
    ASSERT_EXPECT_STR_TEST(func, false, , args)
#define EXPECT_STREQ(args...) EXPECT_STR_TEST(!strcmp, "==", args)
#define EXPECT_STRNE(args...) EXPECT_STR_TEST(strcmp, "!=", args)
#define EXPECT_STRCASEEQ(args...) \
    EXPECT_STR_TEST(!strcasecmp, "== (ignoring case)", args)
#define EXPECT_STRCASENE(args...) \
    EXPECT_STR_TEST(strcasecmp, "!= (ignoring case)", args)
#define EXPECT_STRN_TEST(func, n, args...) \
    ASSERT_EXPECT_STRN_TEST(func, n, false, , args)
#define EXPECT_STREQN(val1, val2, n, args...) \
    EXPECT_STR_TEST(!strncmp, n, "==", val1, val2, args)
#define EXPECT_STRNEN(val1, val2, n, args...) \
    EXPECT_STR_TEST(strncmp, n, "!=", val1, val2, args)
#define EXPECT_STRCASEEQN(val1, val2, n, args...) \
    EXPECT_STR_TEST(!strncasecmp, n, "== (ignoring case)", val1, val2, args)
#define EXPECT_STRCASENEN(val1, val2, n, args...) \
    EXPECT_STR_TEST(strncasecmp, n, "!= (ignoring case)", val1, val2, args)

#define ASSERT_TEST(op, args...) \
    ASSERT_EXPECT_LONG_TEST(op, true, goto test_abort;, args)
#define ASSERT_EQ(args...) ASSERT_TEST(==, args)
#define ASSERT_NE(args...) ASSERT_TEST(!=, args)
#define ASSERT_LT(args...) ASSERT_TEST(<, args)
#define ASSERT_LE(args...) ASSERT_TEST(<=, args)
#define ASSERT_GT(args...) ASSERT_TEST(>, args)
#define ASSERT_GE(args...) ASSERT_TEST(>=, args)
#define ASSERT_STR_TEST(func, args...) \
    ASSERT_EXPECT_STR_TEST(func, true, goto test_abort;, args)
#define ASSERT_STREQ(args...) ASSERT_STR_TEST(!strcmp, "==", args)
#define ASSERT_STRNE(args...) ASSERT_STR_TEST(strcmp, "!=", args)
#define ASSERT_STRCASEEQ(args...) \
    ASSERT_STR_TEST(!strcasecmp, "== (ignoring case)", args)
#define ASSERT_STRCASENE(args...) \
    ASSERT_STR_TEST(strcasecmp, "!= (ignoring case)", args)
#define ASSERT_STRN_TEST(func, n, args...) \
    ASSERT_EXPECT_STRN_TEST(func, n, true, goto test_abort;, args)
#define ASSERT_STREQN(val1, val2, n, args...) \
    ASSERT_STRN_TEST(!strncmp, n, "==", val1, val2, args)
#define ASSERT_STRNEN(val1, val2, n, args...) \
    ASSERT_STRN_TEST(strncmp, n, "!=", val1, val2, args)
#define ASSERT_STRCASEEQN(val1, val2, n, args...) \
    ASSERT_STRN_TEST(!strncasecmp, n, "== (ignoring case)", val1, val2, args)
#define ASSERT_STRCASENEN(val1, val2, n, args...) \
    ASSERT_STRN_TEST(strncasecmp, n, "!= (ignoring case)", val1, val2, args)

__END_CDECLS
