This is xnu-12377.1.9. See this file in:
/*
 * Copyright (c) 2000-2025 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

#pragma once


/* BEGIN IGNORE CODESTYLE */

/* Dynamic mock allows an individual test executable to control what a mock does.
 * T_MOCK_DYNAMIC_DECLARE()
 *   Declare a dynamic mock. This declaration should come in a header file under the mocks/ folder.
 *   The header file should be included in both the respective .c file and in the test .c file that
 *   wants to set the behaviour of the mock.
 *   It declares the signature of the mocked function so that if the signature changes the compiler
 *   can assure that the mock and its setters are in sync.
 * T_MOCK_DYNAMIC()
 *   Define the dynamic mock. This should come in a .c file under the mocks/ folder.
 *   This defines the mock function itself using the T_MOCK() macro.
 *
 * The test has 4 possible way to control the mock. It can temporarily set the return value,
 * it can set a temporary block callback, it can set a permanent return value or a permanent function.
 * @argument args_def is how the function arguments are defined in a function definition.
 *           This can be copy-pasted directly from the original function definition.
 * @argument args_invoke is how the same arguments are passed to a function call
 * @argument (optional) default_action should be a scope of code that will be executed if no mock control
 *           is set up. it can reference the arguments in args_def and also call the original
 *           function. If this argument is not supplied, the default action is to call the original XNU
 *           function with the same arguments.
 *
 * Example:
 * // we want to mock a function from XNU that has the signature:
 * size_t foobar(int a, char b);
 *
 * // in a header in the mocks library (tests/unit/mocks) add:
 * T_MOCK_DYNAMIC_DECLARE(size_t, foobar, (int a, char b));
 *
 * // in a .c file in the mock library (tests/unit/mocks) add:
 * T_MOCK_DYNAMIC(size_t, foobar, (int a, char b), (a, b), { return 0 });
 *
 * // Now to control the mock, in a T_DECL test you can do:
 * T_DECL(test, "test") {
 *     T_MOCK_SET_RETVAL(foobar, size_t, 42);
 *     // ... call into XNU which will call foobar()
 *
 *     T_MOCK_SET_CALLBACK(foobar, size_t, (int a, char b), {
 *         T_ASSERT_EQ(a, b, "args equal");
 *         return a + b;
 *     });
 *     // ... call into XNU which will call foobar()
 * }
 *
 * // The third option is to define a permanent return value for the mock that will
 * // be in effect for all tests in the executable.
 * // This essentially overrides the default-value that's defined in the T_MOCK_DYNAMIC()
 * T_MOCK_SET_PERM_RETVAL(foobar, size_t, 43);
 *
 * // The fourth option is for the test to define a permanent function in the global scope
 * // that will be called every time the mock is called.
 * T_MOCK_SET_PERM_FUNC(size_t, foobar, (int a, char b)) {
 *     return b - a;
 * }
 *
 * It's possible for multiple mock controls of different types to be active at the same time. The priority
 * in which the dynamic mock tries to find them is
 *   1. ret-val
 *   2. block call back
 *   3. permanent ret-val / permanent function
 * The effect of the ret-val and callback setters is limited to the scope the they are in. This
 * is achieved using a cleanup function in the setter.
 * It is possible for multiple setters of the same type to be invoked during the flow of the same scope.
 * In that case, the last setter that was invoked is in effect.
 *
 * It is not possible to have multiple static function setters and/or permanent ret-val setter for the
 * same mock in the same test executable. This would cause a compile/link error due to duplicate symbol.
 */

#define _T_MOCK_RETVAL_CALLBACK(name)       _mock_retval_callback_ ## name
#define _T_MOCK_CALLBACK(name)              _mock_callback_ ## name
#define _T_MOCK_PERM_RETVAL_FUNC(name)      _mock_p_retval_func_ ## name
#define _T_MOCK_PERM_FUNC(name)             _mock_func_ ## name

#define T_MOCK_DYNAMIC_DECLARE(ret, name, args_def)       \
    extern ret (^_T_MOCK_RETVAL_CALLBACK(name))(void);    \
    extern ret (^_T_MOCK_CALLBACK(name)) args_def;        \
    extern ret (*_T_MOCK_PERM_RETVAL_FUNC(name))(void);   \
    extern ret (*_T_MOCK_PERM_FUNC(name)) args_def;       \
    extern ret name args_def

#define _T_MOCK_DYNAMIC_WITH_IMPL(ret, name, args_def, args_invoke, default_action)  \
    ret (^_T_MOCK_RETVAL_CALLBACK(name)) (void) = NULL;                   \
    ret (^_T_MOCK_CALLBACK(name)) args_def = NULL;                        \
    ret (*_T_MOCK_PERM_RETVAL_FUNC(name)) (void) = NULL;                  \
    ret (*_T_MOCK_PERM_FUNC(name)) args_def = NULL;                       \
    T_MOCK(ret, name, args_def) {                                         \
        if (_T_MOCK_RETVAL_CALLBACK(name) != NULL) {                      \
            return _T_MOCK_RETVAL_CALLBACK(name)();                       \
        }                                                                 \
        if (_T_MOCK_CALLBACK(name) != NULL) {                             \
            return _T_MOCK_CALLBACK(name) args_invoke;                    \
        }                                                                 \
        if (_T_MOCK_PERM_RETVAL_FUNC(name) != NULL) {                     \
            return _T_MOCK_PERM_RETVAL_FUNC(name)();                      \
        }                                                                 \
        if (_T_MOCK_PERM_FUNC(name) != NULL) {                            \
            return _T_MOCK_PERM_FUNC(name) args_invoke;                   \
        }                                                                 \
        default_action;                                                   \
    }

#define _T_MOCK_DYNAMIC_DEFAULT_IMPL(ret, name, args_def, args_invoke) \
    _T_MOCK_DYNAMIC_WITH_IMPL(ret, name, args_def, args_invoke, { return name args_invoke; })

/* T_MOCK_DYNAMIC() selects which of the above versions to call depending on the number of arguments it gets
 * - T_MOCK_DYNAMIC(a, b, c, d) with 4 arguments expands to
 *   _T_MOCK_GET_INSTANCE(a, b, c, d, _T_MOCK_DYNAMIC_WITH_IMPL, _T_MOCK_DYNAMIC_DEFAULT_IMPL)(a, b, c, d)
 *   then NAME is _T_MOCK_DYNAMIC_DEFAULT_IMPL so this expands to
 *   _T_MOCK_DYNAMIC_DEFAULT_IMPL(a, b, c, d)
 * - T_MOCK_DYNAMIC(a, b, c, d, e) with 5 arguments expands to
 *   _T_MOCK_GET_INSTANCE(a, b, c, d, e, _T_MOCK_DYNAMIC_WITH_IMPL, _T_MOCK_DYNAMIC_DEFAULT_IMPL)(a, b, c, d, e)
 *   then NAME is _T_MOCK_DYNAMIC_WITH_IMPL so this expands to
 *   _T_MOCK_DYNAMIC_WITH_IMPL(a, b, c, e, e)
 */
#define _T_MOCK_GET_INSTANCE(_1, _2, _3, _4, _5, NAME, ...) NAME
#define T_MOCK_DYNAMIC(...) _T_MOCK_GET_INSTANCE(__VA_ARGS__, _T_MOCK_DYNAMIC_WITH_IMPL, _T_MOCK_DYNAMIC_DEFAULT_IMPL)(__VA_ARGS__)



#define _UT_CONCAT2(a, b) a ## b
#define _UT_CONCAT(a, b) _UT_CONCAT2(a, b)

static inline void
_mock_set_cleaner(void ***ptr) {
	**ptr = NULL;
}

/* How it works?
 * - For each mock that is defined using T_MOCK_DYNAMIC() the macro above defines a few
 * global variables with the function name suffixed, and also defines the mock function to check
 * these global variables.
 * - The test executable can then set any of them using the T_MOCK_SET_X() macros below
 * - T_MOCK_SET_RETVAL() and T_MOCK_SET_CALLBACK() should be used from inside T_DECL and have a
 * cleaner that undoes their effect at the end of the scope they are defined in.
 * The cleaner has a __COUNTER__ concatenated so that it's possible to have more than one such
 * T_MOCK_SET_X() invocation in the same scope
 * - T_MOCK_SET_PERM_RETVAL() and T_MOCK_SET_PERM_FUNC() should be used in the global scope
 * and has a constructor function that sets the global variable when the executable loads
 */

#define _T_MOCK_CLEANER(name) _UT_CONCAT(_cleaner_ ## name, __COUNTER__)
#define _T_MOCK_RETVAL_CAPTURE(name, N) _UT_CONCAT(_mock_retval_capture_ ## name, N)

/* to set a return value, we set a global that holds a callback block that returns the value.
 * The callback variable is a pointer and NULL indicates it's not set
 * The value expression the user gives is first captured in a local variable since some
 * expressions can't be captured by a block (array reference for instance) */
#define _T_MOCK_SET_RETVAL_IMPL(name, ret, val, N)                                              \
        ret _T_MOCK_RETVAL_CAPTURE(name, N) = val;                                              \
        _T_MOCK_RETVAL_CALLBACK(name) = ^ret(void) { return _T_MOCK_RETVAL_CAPTURE(name, N); }; \
        __attribute__((cleanup(_mock_set_cleaner))) void **_T_MOCK_CLEANER(name) =              \
            (void**)&_T_MOCK_RETVAL_CALLBACK(name)
#define T_MOCK_SET_RETVAL(name, ret, val) _T_MOCK_SET_RETVAL_IMPL(name, ret, val, __COUNTER__)

/* to set a mock callback block from the user we set a dedicated callback for that, so it doesn't
 * interfere with SET_RETVAL */
#define T_MOCK_SET_CALLBACK(name, ret, args_def, body)                              \
        _T_MOCK_CALLBACK(name) = ^ret args_def body;                                \
        __attribute__((cleanup(_mock_set_cleaner))) void **_T_MOCK_CLEANER(name) =  \
            (void**)&_T_MOCK_CALLBACK(name)

#define _T_MOCK_CTOR_SETTER(name) _ctor_setter_ ## name
#define _T_MOCK_PERM_HOOK(name)   PERM_HOOK_ ## name

/* To set a permanent return value, we define a function that returns it, and set it to the
 * extern global in a constructor.
 * This setter needs to be in the global scope of the tester */
#define T_MOCK_SET_PERM_RETVAL(name, ret, val)                          \
        ret _T_MOCK_PERM_HOOK(name)(void) { return (val); }             \
        __attribute__((constructor)) void _T_MOCK_CTOR_SETTER(name)() { \
            _T_MOCK_PERM_RETVAL_FUNC(name) = _T_MOCK_PERM_HOOK(name);   \
        }

/* To set a permanent function that will be called from the mock we declare it, set it to the extern
 * in a constructor and define it.
 * This needs to be in the global scope and the body of the function needs to follows it immediately */
#define T_MOCK_SET_PERM_FUNC(ret, name, args_def)                        \
        ret _T_MOCK_PERM_HOOK(name) args_def;                            \
        __attribute__((constructor)) void _T_MOCK_CTOR_SETTER(name)() {  \
            _T_MOCK_PERM_FUNC(name) = _T_MOCK_PERM_HOOK(name);           \
        }                                                                \
        ret _T_MOCK_PERM_HOOK(name) args_def


/* T_MOCK_CALL_QUEUE()
 *   Allow tests to define a call expectation queue for a mock
 *
 * This macro wraps a definition of a struct and defines easy helpers to
 * manage a global queue of elements of that struct.
 * A test can use this along with a mock callback to verify and control what the mock
 * does in every call it gets.
 * @argument type_name the name of the struct to define
 * @argument struct_body the elements of the struct
 *
 * Example:
 * // for mocking the function foobar() we'll define a struct that will allow the mock
 * // to verify its arguments and control its return value. The elements of the struct can
 * // be anything.
 * T_MOCK_CALL_QUEUE(fb_call, {
 *     int expected_a_eq;
 *     bool expected_b_small;
 *     size_t ret_val;
 * })
 *
 * T_MOCK_SET_PERM_FUNC(size_t, foobar, (int a, char b)) {
 *     fb_call call = dequeue_fb_call();
 *     T_ASSERT_EQ(a, call.expected_a_eq, "a arg");
 *     if (call.expected_b_small)
 *         T_ASSERT_LE(b, 127, "b arg too big");
 *     return call.ret_val;
 * }
 *
 * // in the test we set up the expected calls before calling the code that ends up in the mock
 * T_DECL(test, "test") {
 *     enqueue_fb_call( (fb_call){ .expected_a = 1, .expected_b = 2, .ret_val = 3 });
 * 	   enqueue_fb_call( (fb_call){ .expected_a = 10, .expected_b = 20, .ret_val = 30 });
 *     // ... call into XNU which will call foobar()
 *     assert_empty_fb_call(); // check all calls were consumed
 * }
 */

#define _T_MOCK_CALL_LST(type_name)  _lst_ ## type_name

#define T_MOCK_CALL_QUEUE(type_name, struct_body)                                         \
    typedef struct s_ ## type_name struct_body type_name;                                 \
    struct _node_ ## type_name {                                                          \
        STAILQ_ENTRY(_node_ ## type_name) next;                                           \
        type_name d;                                                                      \
    };                                                                                    \
    static STAILQ_HEAD(, _node_ ## type_name) _T_MOCK_CALL_LST(type_name) =               \
        STAILQ_HEAD_INITIALIZER(_T_MOCK_CALL_LST(type_name));                             \
    static void enqueue_ ## type_name (type_name value) {                                 \
        struct _node_ ## type_name *node = calloc(1, sizeof(struct _node_ ## type_name)); \
        node->d = value;                                                                  \
        STAILQ_INSERT_TAIL(&_T_MOCK_CALL_LST(type_name), node, next);                     \
    }                                                                                     \
    static type_name dequeue_ ## type_name (void) {                                       \
        struct _node_ ## type_name *node = STAILQ_FIRST(&_T_MOCK_CALL_LST(type_name));    \
        T_QUIET; T_ASSERT_NOTNULL(node, "consumed too many " #type_name);                 \
        type_name d = node->d;                                                            \
        STAILQ_REMOVE_HEAD(&_T_MOCK_CALL_LST(type_name), next);                           \
        free(node);                                                                       \
        return d;                                                                         \
    }                                                                                     \
    static void assert_empty_ ## type_name (void) {                                       \
        T_QUIET; T_ASSERT_TRUE( STAILQ_EMPTY(&_T_MOCK_CALL_LST(type_name)),               \
                  "calls not fully consumed " #type_name);                                \
    }                                                                                     \
    static void clear_ ## type_name (void) {                                              \
        STAILQ_INIT(&_T_MOCK_CALL_LST(type_name));                                        \
    }

/* END IGNORE CODESTYLE */