root/tools/testing/selftests/sched_ext/scx_test.h
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
 * Copyright (c) 2023 Tejun Heo <tj@kernel.org>
 * Copyright (c) 2023 David Vernet <dvernet@meta.com>
 */

#ifndef __SCX_TEST_H__
#define __SCX_TEST_H__

#include <errno.h>
#include <scx/common.h>
#include <scx/compat.h>

enum scx_test_status {
        SCX_TEST_PASS = 0,
        SCX_TEST_SKIP,
        SCX_TEST_FAIL,
};

#define EXIT_KIND(__ent) __COMPAT_ENUM_OR_ZERO("scx_exit_kind", #__ent)

struct scx_test {
        /**
         * name - The name of the testcase.
         */
        const char *name;

        /**
         * description - A description of your testcase: what it tests and is
         * meant to validate.
         */
        const char *description;

        /*
         * setup - Setup the test.
         * @ctx: A pointer to a context object that will be passed to run and
         *       cleanup.
         *
         * An optional callback that allows a testcase to perform setup for its
         * run. A test may return SCX_TEST_SKIP to skip the run.
         */
        enum scx_test_status (*setup)(void **ctx);

        /*
         * run - Run the test.
         * @ctx: Context set in the setup() callback. If @ctx was not set in
         *       setup(), it is NULL.
         *
         * The main test. Callers should return one of:
         *
         * - SCX_TEST_PASS: Test passed
         * - SCX_TEST_SKIP: Test should be skipped
         * - SCX_TEST_FAIL: Test failed
         *
         * This callback must be defined.
         */
        enum scx_test_status (*run)(void *ctx);

        /*
         * cleanup - Perform cleanup following the test
         * @ctx: Context set in the setup() callback. If @ctx was not set in
         *       setup(), it is NULL.
         *
         * An optional callback that allows a test to perform cleanup after
         * being run. This callback is run even if the run() callback returns
         * SCX_TEST_SKIP or SCX_TEST_FAIL. It is not run if setup() returns
         * SCX_TEST_SKIP or SCX_TEST_FAIL.
         */
        void (*cleanup)(void *ctx);
};

void scx_test_register(struct scx_test *test);

#define REGISTER_SCX_TEST(__test)                       \
        __attribute__((constructor))                    \
        static void ___scxregister##__LINE__(void)      \
        {                                               \
                scx_test_register(__test);              \
        }

#define SCX_ERR(__fmt, ...)                                             \
        do {                                                            \
                fprintf(stderr, "ERR: %s:%d\n", __FILE__, __LINE__);    \
                fprintf(stderr, __fmt"\n", ##__VA_ARGS__);                      \
        } while (0)

#define SCX_FAIL(__fmt, ...)                                            \
        do {                                                            \
                SCX_ERR(__fmt, ##__VA_ARGS__);                          \
                return SCX_TEST_FAIL;                                   \
        } while (0)

#define SCX_FAIL_IF(__cond, __fmt, ...)                                 \
        do {                                                            \
                if (__cond)                                             \
                        SCX_FAIL(__fmt, ##__VA_ARGS__);                 \
        } while (0)

#define SCX_GT(_x, _y) SCX_FAIL_IF((_x) <= (_y), "Expected %s > %s (%lu > %lu)",        \
                                   #_x, #_y, (u64)(_x), (u64)(_y))
#define SCX_GE(_x, _y) SCX_FAIL_IF((_x) < (_y), "Expected %s >= %s (%lu >= %lu)",       \
                                   #_x, #_y, (u64)(_x), (u64)(_y))
#define SCX_LT(_x, _y) SCX_FAIL_IF((_x) >= (_y), "Expected %s < %s (%lu < %lu)",        \
                                   #_x, #_y, (u64)(_x), (u64)(_y))
#define SCX_LE(_x, _y) SCX_FAIL_IF((_x) > (_y), "Expected %s <= %s (%lu <= %lu)",       \
                                   #_x, #_y, (u64)(_x), (u64)(_y))
#define SCX_EQ(_x, _y) SCX_FAIL_IF((_x) != (_y), "Expected %s == %s (%lu == %lu)",      \
                                   #_x, #_y, (u64)(_x), (u64)(_y))
#define SCX_ASSERT(_x) SCX_FAIL_IF(!(_x), "Expected %s to be true (%lu)",               \
                                   #_x, (u64)(_x))

#define SCX_ECODE_VAL(__ecode) ({                                               \
        u64 __val = 0;                                                          \
        bool __found = false;                                                   \
                                                                                \
        __found = __COMPAT_read_enum("scx_exit_code", #__ecode, &__val);        \
        SCX_ASSERT(__found);                                                    \
        (s64)__val;                                                             \
})

#define SCX_KIND_VAL(__kind) ({                                                 \
        u64 __val = 0;                                                          \
        bool __found = false;                                                   \
                                                                                \
        __found = __COMPAT_read_enum("scx_exit_kind", #__kind, &__val);         \
        SCX_ASSERT(__found);                                                    \
        __val;                                                                  \
})

#endif  // # __SCX_TEST_H__