Line data Source code
1 1 : /**
2 : * @file test/test.c
3 : *
4 : * @brief Test framework source
5 : *
6 : * The source of the minimal test framework used in the code base tests
7 : *
8 : * SPDX-FileCopyrightText: 2008-2021 HPDCS Group <rootsim@googlegroups.com>
9 : * SPDX-License-Identifier: GPL-3.0-only
10 : */
11 : #include <test.h>
12 :
13 : #include <arch/thread.h>
14 :
15 : #include <limits.h>
16 : #include <memory.h>
17 : #include <stdarg.h>
18 : #include <stdatomic.h>
19 : #include <stdbool.h>
20 : #include <stdlib.h>
21 :
22 : #ifndef ROOTSIM_TEST_NAME
23 0 : #define ROOTSIM_TEST_NAME "rs_test"
24 : #endif
25 :
26 0 : static char *t_out_buf;
27 0 : static size_t t_out_buf_size;
28 0 : static size_t t_out_wrote;
29 :
30 0 : static char **test_argv;
31 :
32 1 : __attribute__((weak)) lp_id_t n_lps;
33 : #ifdef ROOTSIM_MPI
34 1 : __attribute__((weak)) nid_t nid;
35 0 : __attribute__((weak)) nid_t n_nodes = 1;
36 : #endif
37 1 : __attribute__((weak)) rid_t n_threads;
38 1 : __attribute__((weak)) __thread rid_t rid;
39 :
40 1 : __attribute__((weak)) int log_level;
41 :
42 : __attribute__((weak))
43 1 : void _log_log(int level, const char *file, unsigned line, const char *fmt, ...)
44 : {
45 : (void) level;
46 : (void) file;
47 : (void) line;
48 : (void) fmt;
49 : }
50 :
51 1 : int main(int argc, char **argv);
52 :
53 : /**
54 : * @brief Initializes ISO C compliant argc and argv from the test configuration
55 : * @param argc_p a pointer to a variable which will hold the computed argc value
56 : * @param argv_p a pointer to a variable which will hold the computer argv value
57 : * @return 0 in case of success, -1 in case of failure
58 : */
59 1 : static int init_arguments(int *argc_p, char ***argv_p)
60 : {
61 : int argc = 0;
62 : if (test_config.test_arguments) {
63 : while (test_config.test_arguments[argc]) {
64 : ++argc;
65 : }
66 : }
67 : ++argc;
68 :
69 : char **argv = malloc(sizeof(*argv) * (argc + 1));
70 : if(argv == NULL)
71 : return -1;
72 :
73 : argv[0] = ROOTSIM_TEST_NAME;
74 :
75 : if (test_config.test_arguments) {
76 : memcpy(&argv[1], test_config.test_arguments,
77 : sizeof(*argv) * argc);
78 : } else {
79 : argv[1] = NULL;
80 : }
81 :
82 : *argc_p = argc;
83 : *argv_p = argv;
84 : return 0;
85 : }
86 :
87 : /**
88 : * @brief The exit handler, to exit cleanly even in case of errors
89 : */
90 1 : static void test_atexit(void)
91 : {
92 : free(test_argv);
93 : free(t_out_buf);
94 : }
95 :
96 : /**
97 : * @brief The test wrapper which allows to intervene before the actual main()
98 : */
99 : __attribute__((constructor))
100 1 : void main_wrapper(void)
101 : {
102 : int test_argc = 0;
103 :
104 : puts("Starting " ROOTSIM_TEST_NAME " test");
105 :
106 : n_threads = test_config.threads_count;
107 :
108 : atexit(test_atexit);
109 :
110 : if (init_arguments(&test_argc, &test_argv) == -1)
111 : exit(TEST_BAD_FAIL_EXIT_CODE);
112 :
113 : t_out_buf_size = 1;
114 : t_out_buf = malloc(t_out_buf_size);
115 : if (t_out_buf == NULL)
116 : exit(TEST_BAD_FAIL_EXIT_CODE);
117 :
118 : int test_ret = main(test_argc, test_argv);
119 : if (test_ret)
120 : exit(test_ret);
121 :
122 : if (t_out_wrote < test_config.expected_output_size) {
123 : puts("Test failed: output is shorter than the expected one");
124 : exit(-1);
125 : }
126 :
127 : puts("Successfully run " ROOTSIM_TEST_NAME " test");
128 : exit(0);
129 : }
130 :
131 0 : static int test_printf_internal(const char *restrict fmt, va_list args)
132 : {
133 : va_list args_cpy;
134 : va_copy(args_cpy, args);
135 : size_t p_size = vsnprintf(t_out_buf, t_out_buf_size, fmt, args_cpy);
136 : va_end(args_cpy);
137 :
138 : if (t_out_wrote + p_size > test_config.expected_output_size) {
139 : puts("Test failed: output is longer than the expected one");
140 : exit(-1);
141 : }
142 :
143 : if (p_size >= t_out_buf_size) {
144 : do {
145 : t_out_buf_size *= 2;
146 : } while(p_size >= t_out_buf_size);
147 :
148 : free(t_out_buf);
149 : t_out_buf = malloc(t_out_buf_size);
150 : if (t_out_buf == NULL)
151 : exit(TEST_BAD_FAIL_EXIT_CODE);
152 :
153 : vsnprintf(t_out_buf, t_out_buf_size, fmt, args);
154 : }
155 :
156 : if (memcmp(t_out_buf, test_config.expected_output + t_out_wrote, p_size)) {
157 : printf("Test failed: output is different from the expected one %zu\n", t_out_wrote);
158 : exit(-1);
159 : }
160 : t_out_wrote += p_size;
161 :
162 : return p_size;
163 : }
164 :
165 : /**
166 : * @brief Registers a formatted string to compare against the expected output
167 : * @return the number of successfully registered characters
168 : */
169 1 : int test_printf(const char *restrict fmt, ...)
170 : {
171 : va_list args;
172 : va_start(args, fmt);
173 : int ret = test_printf_internal(fmt, args);
174 : va_end(args);
175 : return ret;
176 : }
177 :
178 : /**
179 : * @brief Registers a formatted string to compare against the expected output
180 : * @return the number of successfully registered characters
181 : *
182 : * Cloned definition needed because the LLVM plugin expects a suffixed symbol
183 : */
184 1 : int test_printf_pr(const char *restrict fmt, ...)
185 : {
186 : va_list args;
187 : va_start(args, fmt);
188 : int ret = test_printf_internal(fmt, args);
189 : va_end(args);
190 : return ret;
191 : }
192 :
193 : /**
194 : * @brief Synchronizes threads on a barrier
195 : * @return true if this thread has been elected as leader, false otherwise
196 : *
197 : * This is a more battle tested although less performing version of the thread
198 : * barrier. We can't rely on the pthread barrier because it's not portable.
199 : */
200 1 : bool test_thread_barrier(void)
201 : {
202 : static atomic_uint b_in, b_out, b_cr;
203 :
204 : unsigned i;
205 : unsigned count = test_config.threads_count;
206 : unsigned max_in_before_reset = (UINT_MAX / 2) - (UINT_MAX / 2) % count;
207 : do {
208 : i = atomic_fetch_add_explicit(
209 : &b_in, 1U, memory_order_acq_rel) + 1;
210 : } while (__builtin_expect(i > max_in_before_reset, 0));
211 :
212 : unsigned cr = atomic_load_explicit(&b_cr, memory_order_relaxed);
213 :
214 : bool leader = i == cr + count;
215 : if (leader) {
216 : atomic_store_explicit(&b_cr, cr + count, memory_order_release);
217 : } else {
218 : while (i > cr) {
219 : cr = atomic_load_explicit(&b_cr, memory_order_relaxed);
220 : }
221 : }
222 : atomic_thread_fence(memory_order_acquire);
223 :
224 : unsigned o = atomic_fetch_add_explicit(&b_out, 1, memory_order_release) + 1;
225 : if (__builtin_expect(o == max_in_before_reset, 0)) {
226 : atomic_thread_fence(memory_order_acquire);
227 : atomic_store_explicit(&b_cr, 0, memory_order_relaxed);
228 : atomic_store_explicit(&b_out, 0, memory_order_relaxed);
229 : atomic_store_explicit(&b_in, 0, memory_order_release);
230 : }
231 : return leader;
232 : }
|