1 /*
2 * Copyright (c) 2000-2025 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #pragma once
30
31
32 /* BEGIN IGNORE CODESTYLE */
33
34 /* Dynamic mock allows an individual test executable to control what a mock does.
35 * T_MOCK_DYNAMIC_DECLARE()
36 * Declare a dynamic mock. This declaration should come in a header file under the mocks/ folder.
37 * The header file should be included in both the respective .c file and in the test .c file that
38 * wants to set the behaviour of the mock.
39 * It declares the signature of the mocked function so that if the signature changes the compiler
40 * can assure that the mock and its setters are in sync.
41 * T_MOCK_DYNAMIC()
42 * Define the dynamic mock. This should come in a .c file under the mocks/ folder.
43 * This defines the mock function itself using the T_MOCK() macro.
44 *
45 * The test has 4 possible way to control the mock. It can temporarily set the return value,
46 * it can set a temporary block callback, it can set a permanent return value or a permanent function.
47 * @argument args_def is how the function arguments are defined in a function definition.
48 * This can be copy-pasted directly from the original function definition.
49 * @argument args_invoke is how the same arguments are passed to a function call
50 * @argument (optional) default_action should be a scope of code that will be executed if no mock control
51 * is set up. it can reference the arguments in args_def and also call the original
52 * function. If this argument is not supplied, the default action is to call the original XNU
53 * function with the same arguments.
54 *
55 * Example:
56 * // we want to mock a function from XNU that has the signature:
57 * size_t foobar(int a, char b);
58 *
59 * // in a header in the mocks library (tests/unit/mocks) add:
60 * T_MOCK_DYNAMIC_DECLARE(size_t, foobar, (int a, char b));
61 *
62 * // in a .c file in the mock library (tests/unit/mocks) add:
63 * T_MOCK_DYNAMIC(size_t, foobar, (int a, char b), (a, b), { return 0 });
64 *
65 * // Now to control the mock, in a T_DECL test you can do:
66 * T_DECL(test, "test") {
67 * T_MOCK_SET_RETVAL(foobar, size_t, 42);
68 * // ... call into XNU which will call foobar()
69 *
70 * T_MOCK_SET_CALLBACK(foobar, size_t, (int a, char b), {
71 * T_ASSERT_EQ(a, b, "args equal");
72 * return a + b;
73 * });
74 * // ... call into XNU which will call foobar()
75 * }
76 *
77 * // The third option is to define a permanent return value for the mock that will
78 * // be in effect for all tests in the executable.
79 * // This essentially overrides the default-value that's defined in the T_MOCK_DYNAMIC()
80 * T_MOCK_SET_PERM_RETVAL(foobar, size_t, 43);
81 *
82 * // The fourth option is for the test to define a permanent function in the global scope
83 * // that will be called every time the mock is called.
84 * T_MOCK_SET_PERM_FUNC(size_t, foobar, (int a, char b)) {
85 * return b - a;
86 * }
87 *
88 * It's possible for multiple mock controls of different types to be active at the same time. The priority
89 * in which the dynamic mock tries to find them is
90 * 1. ret-val
91 * 2. block call back
92 * 3. permanent ret-val / permanent function
93 * The effect of the ret-val and callback setters is limited to the scope the they are in. This
94 * is achieved using a cleanup function in the setter.
95 * It is possible for multiple setters of the same type to be invoked during the flow of the same scope.
96 * In that case, the last setter that was invoked is in effect.
97 *
98 * It is not possible to have multiple static function setters and/or permanent ret-val setter for the
99 * same mock in the same test executable. This would cause a compile/link error due to duplicate symbol.
100 */
101
102 #define _T_MOCK_RETVAL_CALLBACK(name) _mock_retval_callback_ ## name
103 #define _T_MOCK_CALLBACK(name) _mock_callback_ ## name
104 #define _T_MOCK_PERM_RETVAL_FUNC(name) _mock_p_retval_func_ ## name
105 #define _T_MOCK_PERM_FUNC(name) _mock_func_ ## name
106
107 #define T_MOCK_DYNAMIC_DECLARE(ret, name, args_def) \
108 extern ret (^_T_MOCK_RETVAL_CALLBACK(name))(void); \
109 extern ret (^_T_MOCK_CALLBACK(name)) args_def; \
110 extern ret (*_T_MOCK_PERM_RETVAL_FUNC(name))(void); \
111 extern ret (*_T_MOCK_PERM_FUNC(name)) args_def; \
112 extern ret name args_def
113
114 #define _T_MOCK_DYNAMIC_WITH_IMPL(ret, name, args_def, args_invoke, default_action) \
115 ret (^_T_MOCK_RETVAL_CALLBACK(name)) (void) = NULL; \
116 ret (^_T_MOCK_CALLBACK(name)) args_def = NULL; \
117 ret (*_T_MOCK_PERM_RETVAL_FUNC(name)) (void) = NULL; \
118 ret (*_T_MOCK_PERM_FUNC(name)) args_def = NULL; \
119 T_MOCK(ret, name, args_def) { \
120 if (_T_MOCK_RETVAL_CALLBACK(name) != NULL) { \
121 return _T_MOCK_RETVAL_CALLBACK(name)(); \
122 } \
123 if (_T_MOCK_CALLBACK(name) != NULL) { \
124 return _T_MOCK_CALLBACK(name) args_invoke; \
125 } \
126 if (_T_MOCK_PERM_RETVAL_FUNC(name) != NULL) { \
127 return _T_MOCK_PERM_RETVAL_FUNC(name)(); \
128 } \
129 if (_T_MOCK_PERM_FUNC(name) != NULL) { \
130 return _T_MOCK_PERM_FUNC(name) args_invoke; \
131 } \
132 default_action; \
133 }
134
135 #define _T_MOCK_DYNAMIC_DEFAULT_IMPL(ret, name, args_def, args_invoke) \
136 _T_MOCK_DYNAMIC_WITH_IMPL(ret, name, args_def, args_invoke, { return name args_invoke; })
137
138 /* T_MOCK_DYNAMIC() selects which of the above versions to call depending on the number of arguments it gets
139 * - T_MOCK_DYNAMIC(a, b, c, d) with 4 arguments expands to
140 * _T_MOCK_GET_INSTANCE(a, b, c, d, _T_MOCK_DYNAMIC_WITH_IMPL, _T_MOCK_DYNAMIC_DEFAULT_IMPL)(a, b, c, d)
141 * then NAME is _T_MOCK_DYNAMIC_DEFAULT_IMPL so this expands to
142 * _T_MOCK_DYNAMIC_DEFAULT_IMPL(a, b, c, d)
143 * - T_MOCK_DYNAMIC(a, b, c, d, e) with 5 arguments expands to
144 * _T_MOCK_GET_INSTANCE(a, b, c, d, e, _T_MOCK_DYNAMIC_WITH_IMPL, _T_MOCK_DYNAMIC_DEFAULT_IMPL)(a, b, c, d, e)
145 * then NAME is _T_MOCK_DYNAMIC_WITH_IMPL so this expands to
146 * _T_MOCK_DYNAMIC_WITH_IMPL(a, b, c, e, e)
147 */
148 #define _T_MOCK_GET_INSTANCE(_1, _2, _3, _4, _5, NAME, ...) NAME
149 #define T_MOCK_DYNAMIC(...) _T_MOCK_GET_INSTANCE(__VA_ARGS__, _T_MOCK_DYNAMIC_WITH_IMPL, _T_MOCK_DYNAMIC_DEFAULT_IMPL)(__VA_ARGS__)
150
151
152
153 #define _UT_CONCAT2(a, b) a ## b
154 #define _UT_CONCAT(a, b) _UT_CONCAT2(a, b)
155
156 static inline void
_mock_set_cleaner(void *** ptr)157 _mock_set_cleaner(void ***ptr) {
158 **ptr = NULL;
159 }
160
161 /* How it works?
162 * - For each mock that is defined using T_MOCK_DYNAMIC() the macro above defines a few
163 * global variables with the function name suffixed, and also defines the mock function to check
164 * these global variables.
165 * - The test executable can then set any of them using the T_MOCK_SET_X() macros below
166 * - T_MOCK_SET_RETVAL() and T_MOCK_SET_CALLBACK() should be used from inside T_DECL and have a
167 * cleaner that undoes their effect at the end of the scope they are defined in.
168 * The cleaner has a __COUNTER__ concatenated so that it's possible to have more than one such
169 * T_MOCK_SET_X() invocation in the same scope
170 * - T_MOCK_SET_PERM_RETVAL() and T_MOCK_SET_PERM_FUNC() should be used in the global scope
171 * and has a constructor function that sets the global variable when the executable loads
172 */
173
174 #define _T_MOCK_CLEANER(name) _UT_CONCAT(_cleaner_ ## name, __COUNTER__)
175 #define _T_MOCK_RETVAL_CAPTURE(name, N) _UT_CONCAT(_mock_retval_capture_ ## name, N)
176
177 /* to set a return value, we set a global that holds a callback block that returns the value.
178 * The callback variable is a pointer and NULL indicates it's not set
179 * The value expression the user gives is first captured in a local variable since some
180 * expressions can't be captured by a block (array reference for instance) */
181 #define _T_MOCK_SET_RETVAL_IMPL(name, ret, val, N) \
182 ret _T_MOCK_RETVAL_CAPTURE(name, N) = val; \
183 _T_MOCK_RETVAL_CALLBACK(name) = ^ret(void) { return _T_MOCK_RETVAL_CAPTURE(name, N); }; \
184 __attribute__((cleanup(_mock_set_cleaner))) void **_T_MOCK_CLEANER(name) = \
185 (void**)&_T_MOCK_RETVAL_CALLBACK(name)
186 #define T_MOCK_SET_RETVAL(name, ret, val) _T_MOCK_SET_RETVAL_IMPL(name, ret, val, __COUNTER__)
187
188 /* to set a mock callback block from the user we set a dedicated callback for that, so it doesn't
189 * interfere with SET_RETVAL */
190 #define T_MOCK_SET_CALLBACK(name, ret, args_def, body) \
191 _T_MOCK_CALLBACK(name) = ^ret args_def body; \
192 __attribute__((cleanup(_mock_set_cleaner))) void **_T_MOCK_CLEANER(name) = \
193 (void**)&_T_MOCK_CALLBACK(name)
194
195 #define _T_MOCK_CTOR_SETTER(name) _ctor_setter_ ## name
196 #define _T_MOCK_PERM_HOOK(name) PERM_HOOK_ ## name
197
198 /* To set a permanent return value, we define a function that returns it, and set it to the
199 * extern global in a constructor.
200 * This setter needs to be in the global scope of the tester */
201 #define T_MOCK_SET_PERM_RETVAL(name, ret, val) \
202 ret _T_MOCK_PERM_HOOK(name)(void) { return (val); } \
203 __attribute__((constructor)) void _T_MOCK_CTOR_SETTER(name)() { \
204 _T_MOCK_PERM_RETVAL_FUNC(name) = _T_MOCK_PERM_HOOK(name); \
205 }
206
207 /* To set a permanent function that will be called from the mock we declare it, set it to the extern
208 * in a constructor and define it.
209 * This needs to be in the global scope and the body of the function needs to follows it immediately */
210 #define T_MOCK_SET_PERM_FUNC(ret, name, args_def) \
211 ret _T_MOCK_PERM_HOOK(name) args_def; \
212 __attribute__((constructor)) void _T_MOCK_CTOR_SETTER(name)() { \
213 _T_MOCK_PERM_FUNC(name) = _T_MOCK_PERM_HOOK(name); \
214 } \
215 ret _T_MOCK_PERM_HOOK(name) args_def
216
217
218 /* T_MOCK_CALL_QUEUE()
219 * Allow tests to define a call expectation queue for a mock
220 *
221 * This macro wraps a definition of a struct and defines easy helpers to
222 * manage a global queue of elements of that struct.
223 * A test can use this along with a mock callback to verify and control what the mock
224 * does in every call it gets.
225 * @argument type_name the name of the struct to define
226 * @argument struct_body the elements of the struct
227 *
228 * Example:
229 * // for mocking the function foobar() we'll define a struct that will allow the mock
230 * // to verify its arguments and control its return value. The elements of the struct can
231 * // be anything.
232 * T_MOCK_CALL_QUEUE(fb_call, {
233 * int expected_a_eq;
234 * bool expected_b_small;
235 * size_t ret_val;
236 * })
237 *
238 * T_MOCK_SET_PERM_FUNC(size_t, foobar, (int a, char b)) {
239 * fb_call call = dequeue_fb_call();
240 * T_ASSERT_EQ(a, call.expected_a_eq, "a arg");
241 * if (call.expected_b_small)
242 * T_ASSERT_LE(b, 127, "b arg too big");
243 * return call.ret_val;
244 * }
245 *
246 * // in the test we set up the expected calls before calling the code that ends up in the mock
247 * T_DECL(test, "test") {
248 * enqueue_fb_call( (fb_call){ .expected_a = 1, .expected_b = 2, .ret_val = 3 });
249 * enqueue_fb_call( (fb_call){ .expected_a = 10, .expected_b = 20, .ret_val = 30 });
250 * // ... call into XNU which will call foobar()
251 * assert_empty_fb_call(); // check all calls were consumed
252 * }
253 */
254
255 #define _T_MOCK_CALL_LST(type_name) _lst_ ## type_name
256
257 #define T_MOCK_CALL_QUEUE(type_name, struct_body) \
258 typedef struct s_ ## type_name struct_body type_name; \
259 struct _node_ ## type_name { \
260 STAILQ_ENTRY(_node_ ## type_name) next; \
261 type_name d; \
262 }; \
263 static STAILQ_HEAD(, _node_ ## type_name) _T_MOCK_CALL_LST(type_name) = \
264 STAILQ_HEAD_INITIALIZER(_T_MOCK_CALL_LST(type_name)); \
265 static void enqueue_ ## type_name (type_name value) { \
266 struct _node_ ## type_name *node = calloc(1, sizeof(struct _node_ ## type_name)); \
267 node->d = value; \
268 STAILQ_INSERT_TAIL(&_T_MOCK_CALL_LST(type_name), node, next); \
269 } \
270 static type_name dequeue_ ## type_name (void) { \
271 struct _node_ ## type_name *node = STAILQ_FIRST(&_T_MOCK_CALL_LST(type_name)); \
272 T_QUIET; T_ASSERT_NOTNULL(node, "consumed too many " #type_name); \
273 type_name d = node->d; \
274 STAILQ_REMOVE_HEAD(&_T_MOCK_CALL_LST(type_name), next); \
275 free(node); \
276 return d; \
277 } \
278 static void assert_empty_ ## type_name (void) { \
279 T_QUIET; T_ASSERT_TRUE( STAILQ_EMPTY(&_T_MOCK_CALL_LST(type_name)), \
280 "calls not fully consumed " #type_name); \
281 } \
282 static void clear_ ## type_name (void) { \
283 STAILQ_INIT(&_T_MOCK_CALL_LST(type_name)); \
284 }
285
286 /* END IGNORE CODESTYLE */
287