Line data Source code
1 : #include "testitall.h"
2 : #include <limits.h>
3 : #include <stdint.h>
4 : #include <wordexp.h>
5 :
6 : #ifndef ARG_MAX
7 : #ifdef _POSIX_ARG_MAX
8 : #define ARG_MAX _POSIX_ARG_MAX
9 : #else
10 : #define ARG_MAX 4096
11 : #endif
12 : #endif
13 :
14 : extern int test_main(
15 : int argc,
16 : char **argv) __attribute__((weak));
17 :
18 : enum run_mode run_external = EXTERNAL_CALL;
19 :
20 : static struct {
21 : int argc;
22 : char **argv;
23 : int result;
24 : } test_main_context = {0};
25 :
26 102 : static void test_main_wrapper(void)
27 : {
28 102 : test_main_context.result = test_main(test_main_context.argc,test_main_context.argv);
29 102 : }
30 :
31 204 : Return runit(
32 : const char *arguments,
33 : memory *result,
34 : const int expected_return_code,
35 : unsigned int buffer_policy)
36 : {
37 : // Base status for the whole sequence; subsequent steps update it on errors.
38 : /** @var Return status
39 : * @brief The status that will be passed to return() before exiting
40 : * @details By default, the function worked without errors
41 : */
42 204 : Return status = SUCCESS;
43 :
44 : // Arguments string to parse and forward into test_main
45 204 : const char *safe_arguments = arguments;
46 204 : const bool suppress_stdout = (buffer_policy & STDOUT_SUPPRESS) != 0U;
47 204 : const bool suppress_stderr = (buffer_policy & STDERR_SUPPRESS) != 0U;
48 :
49 204 : if(NULL == safe_arguments || safe_arguments[0] == '\0')
50 : {
51 2 : safe_arguments = "";
52 : }
53 :
54 : // External mode: build shell command and exit early.
55 204 : if(EXTERNAL_CALL == run_external)
56 : {
57 : char command[ARG_MAX];
58 102 : int written = snprintf(
59 : command,
60 : sizeof(command),
61 : "cd ${TMPDIR} && ${BINDIR}/precizer %s",
62 : safe_arguments);
63 :
64 102 : if(written < 0 || (size_t)written >= sizeof(command))
65 : {
66 0 : echo(STDERR,"Command length exceeds ARG_MAX (%zu bytes)",sizeof(command));
67 0 : status = FAILURE;
68 :
69 : } else {
70 102 : run(execute_command(command,result,expected_return_code,buffer_policy));
71 : }
72 :
73 102 : provide(status);
74 : }
75 :
76 : // Prepare wordexp and argv/argc for test_main.
77 102 : wordexp_t parsed_arguments = {0};
78 102 : bool words_allocated = false;
79 102 : size_t argc = 0U;
80 102 : char **argv = NULL;
81 :
82 : // Save current working directory to restore after test_main.
83 102 : char *previous_cwd = NULL;
84 102 : bool changed_directory = false;
85 :
86 : // Capture working directory before switching to TMPDIR.
87 102 : if(SUCCESS == status)
88 : {
89 102 : previous_cwd = getcwd(NULL,0);
90 :
91 102 : if(NULL == previous_cwd)
92 : {
93 0 : serp("Failed to get current working directory");
94 0 : status = FAILURE;
95 : }
96 : }
97 :
98 : // Switch into TMPDIR (tests expect to run precizer from a temp directory).
99 102 : const char *tmpdir = getenv("TMPDIR");
100 :
101 102 : if(SUCCESS == status)
102 : {
103 102 : if(NULL == tmpdir)
104 : {
105 0 : echo(STDERR,"Environment variable TMPDIR is not set");
106 0 : status = FAILURE;
107 :
108 : } else {
109 102 : if(0 != chdir(tmpdir))
110 : {
111 0 : serp("Failed to change directory to TMPDIR");
112 0 : status = FAILURE;
113 :
114 : } else {
115 102 : changed_directory = true;
116 : }
117 : }
118 : }
119 :
120 : // Parse argument string into words without command substitution.
121 102 : if(SUCCESS == status)
122 : {
123 102 : int word_status = wordexp(safe_arguments,&parsed_arguments,WRDE_NOCMD);
124 :
125 102 : if(0 != word_status)
126 : {
127 0 : echo(STDERR,"Failed to parse arguments \"%s\" (wordexp code %d)",safe_arguments,word_status);
128 0 : status = FAILURE;
129 :
130 : } else {
131 102 : words_allocated = true;
132 : }
133 : }
134 :
135 : // Ensure argument count fits into int and set argc.
136 102 : if(SUCCESS == status)
137 : {
138 102 : argc = parsed_arguments.we_wordc + 1U;
139 :
140 102 : if(argc > (size_t)INT_MAX)
141 : {
142 0 : echo(STDERR,"Too many arguments for test_main: %zu",argc);
143 0 : status = FAILURE;
144 : }
145 : }
146 :
147 : // Build argv: argv[0] is a program name, the rest are parsed words.
148 102 : if(SUCCESS == status)
149 : {
150 102 : argv = calloc(argc + 1U,sizeof(char *));
151 :
152 102 : if(NULL == argv)
153 : {
154 0 : report("Memory allocation failed, requested size: %zu bytes",(argc + 1U) * sizeof(char *));
155 0 : status = FAILURE;
156 :
157 : } else {
158 102 : argv[0] = (char *)(uintptr_t)"precizer";
159 :
160 443 : for(size_t i = 0U; i < parsed_arguments.we_wordc; i++)
161 : {
162 341 : argv[i + 1U] = parsed_arguments.we_wordv[i];
163 : }
164 :
165 102 : argv[argc] = NULL;
166 : }
167 : }
168 :
169 : // Run test_main in-process while capturing stdout/stderr.
170 102 : if(SUCCESS == status)
171 : {
172 102 : call(del(STDERR));
173 102 : call(del(STDOUT));
174 :
175 102 : test_main_context.argc = (int)argc;
176 102 : test_main_context.argv = argv;
177 102 : test_main_context.result = 0;
178 :
179 102 : run(function_capture(test_main_wrapper,STDOUT,STDERR));
180 :
181 102 : if(SUCCESS == status)
182 : {
183 102 : int exit_code = test_main_context.result;
184 :
185 : // Handle stderr: either suppress it or format a warning and fail.
186 102 : if(STDERR->length > 0U)
187 : {
188 1 : if(true == suppress_stderr)
189 : {
190 1 : call(del(STDERR));
191 :
192 : } else {
193 : char *str;
194 0 : const char *stderr_view = getcstring(STDERR);
195 0 : int rt = asprintf(&str,
196 : YELLOW "Warning! STDERR buffer is not empty!\n"
197 : "Internal call:\n" YELLOW ">>" RESET "precizer %s" YELLOW "<<" RESET "\n"
198 : "Stderr output:\n" YELLOW ">>" RESET "%s" YELLOW "<<" RESET "\n",
199 : safe_arguments,
200 : stderr_view);
201 :
202 0 : if(rt > -1)
203 : {
204 0 : if(SUCCESS == resize(STDERR,(size_t)rt + 1U))
205 : {
206 0 : run(copy_literal(STDERR,str));
207 : }
208 :
209 : } else {
210 0 : report("Memory allocation failed, requested size: %zu bytes",(size_t)rt + 1U);
211 0 : status = FAILURE;
212 : }
213 :
214 0 : free(str);
215 :
216 0 : if(SUCCESS == status)
217 : {
218 0 : status = FAILURE;
219 : }
220 : }
221 : }
222 :
223 : // Suppress stdout if requested.
224 102 : if(STDOUT->length > 0U && true == suppress_stdout)
225 : {
226 0 : call(del(STDOUT));
227 : }
228 :
229 : // Compare exit code with expected and format a report on mismatch.
230 102 : if(SUCCESS == status)
231 : {
232 102 : if(expected_return_code != exit_code)
233 : {
234 : char *str;
235 0 : const char *stderr_view = getcstring(STDERR);
236 0 : const char *stdout_view = getcstring(STDOUT);
237 0 : int rt = asprintf(&str,
238 : YELLOW "ERROR: Unexpected exit code!" RESET "\n"
239 : YELLOW "Internal call:" RESET "\n" YELLOW ">>" RESET "precizer %s" YELLOW "<<" RESET "\n"
240 : YELLOW "Exited with code " RESET "%d" YELLOW " but expected " RESET "%d\n"
241 : YELLOW "Stderr output:" RESET "\n" YELLOW ">>" RESET "%s" YELLOW "<<" RESET "\n"
242 : YELLOW "Stdout output:" RESET "\n" YELLOW ">>" RESET "%s" YELLOW "<<" RESET "\n",
243 : safe_arguments,
244 : exit_code,
245 : expected_return_code,
246 : stderr_view,
247 : stdout_view);
248 :
249 0 : if(rt > -1)
250 : {
251 0 : if(SUCCESS == resize(STDERR,(size_t)rt + 1U))
252 : {
253 0 : run(copy_literal(STDERR,str));
254 : }
255 :
256 : } else {
257 0 : report("Memory allocation failed, requested size: %zu bytes",(size_t)rt + 1U);
258 0 : status = FAILURE;
259 : }
260 :
261 0 : free(str);
262 :
263 0 : if(SUCCESS == status)
264 : {
265 0 : status = FAILURE;
266 : }
267 : }
268 : }
269 : }
270 : }
271 :
272 : // Restore original working directory if changed.
273 102 : if(true == changed_directory && NULL != previous_cwd)
274 : {
275 102 : if(0 != chdir(previous_cwd))
276 : {
277 0 : serp("Failed to restore working directory");
278 0 : status = FAILURE;
279 : }
280 : }
281 :
282 102 : free(previous_cwd);
283 102 : free(argv);
284 :
285 : // Copy stdout into caller-provided result buffer when requested.
286 102 : if(true == words_allocated)
287 : {
288 102 : wordfree(&parsed_arguments);
289 : }
290 :
291 102 : if(NULL != result)
292 : {
293 92 : if(STDOUT->length > 0U)
294 : {
295 82 : run(copy(result,STDOUT));
296 : }
297 : }
298 :
299 102 : call(del(STDOUT));
300 :
301 102 : provide(status);
302 : }
|