1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #define _GNU_SOURCE
27
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <sys/stat.h>
34 #include <string.h>
35 #include <assert.h>
36 #include <dlfcn.h>
37 #include <errno.h>
38 #include <limits.h>
39 #include <sys/ptrace.h>
40 #include <sys/prctl.h>
41 #ifndef PR_SET_PTRACER
42 # define PR_SET_PTRACER 0x59616d61
43 #endif
44
45 #include "test-runner.h"
46
47 /* when set to 1, check if tests are not leaking opened files.
48 * It is turned on by default. It can be turned off by
49 * WAYLAND_TEST_NO_LEAK_CHECK environment variable. */
50 int fd_leak_check_enabled;
51
52 /* when this var is set to 0, every call to test_set_timeout() is
53 * suppressed - handy when debugging the test. Can be set by
54 * WAYLAND_TEST_NO_TIMEOUTS environment variable. */
55 static int timeouts_enabled = 1;
56
57 /* set to one if the output goes to the terminal */
58 static int is_atty = 0;
59
60 extern const struct test __start_test_section, __stop_test_section;
61
62 static const struct test *
find_test(const char * name)63 find_test(const char *name)
64 {
65 const struct test *t;
66
67 for (t = &__start_test_section; t < &__stop_test_section; t++)
68 if (strcmp(t->name, name) == 0)
69 return t;
70
71 return NULL;
72 }
73
74 static void
usage(const char * name,int status)75 usage(const char *name, int status)
76 {
77 const struct test *t;
78
79 fprintf(stderr, "Usage: %s [TEST]\n\n"
80 "With no arguments, run all test. Specify test case to run\n"
81 "only that test without forking. Available tests:\n\n",
82 name);
83
84 for (t = &__start_test_section; t < &__stop_test_section; t++)
85 fprintf(stderr, " %s\n", t->name);
86
87 fprintf(stderr, "\n");
88
89 exit(status);
90 }
91
92 void
test_set_timeout(unsigned int to)93 test_set_timeout(unsigned int to)
94 {
95 int re;
96
97 if (!timeouts_enabled) {
98 fprintf(stderr, "Timeouts suppressed.\n");
99 return;
100 }
101
102 re = alarm(to);
103 fprintf(stderr, "Timeout was %sset", re ? "re-" : "");
104
105 if (to != 0)
106 fprintf(stderr, " to %d second%s from now.\n",
107 to, to > 1 ? "s" : "");
108 else
109 fprintf(stderr, " off.\n");
110 }
111
112 static void
sigalrm_handler(int signum)113 sigalrm_handler(int signum)
114 {
115 fprintf(stderr, "Test timed out.\n");
116 abort();
117 }
118
119 void
check_fd_leaks(int supposed_fds)120 check_fd_leaks(int supposed_fds)
121 {
122 int num_fds;
123
124 if (fd_leak_check_enabled) {
125 num_fds = count_open_fds();
126 if (supposed_fds != num_fds) {
127 fprintf(stderr, "fd leak detected in test. "
128 "Opened %d files, unclosed %d\n", num_fds,
129 num_fds - supposed_fds);
130 abort();
131 }
132 } else {
133 fprintf(stderr, "FD leak checks disabled\n");
134 }
135 }
136
137 static void
run_test(const struct test * t)138 run_test(const struct test *t)
139 {
140 int cur_fds;
141 struct sigaction sa;
142
143 if (timeouts_enabled) {
144 sa.sa_handler = sigalrm_handler;
145 sa.sa_flags = 0;
146 sigemptyset(&sa.sa_mask);
147 assert(sigaction(SIGALRM, &sa, NULL) == 0);
148 }
149
150 //cur_alloc = get_current_alloc_num();
151 cur_fds = count_open_fds();
152
153 t->run();
154
155 /* turn off timeout (if any) after test completion */
156 if (timeouts_enabled)
157 alarm(0);
158
159 check_fd_leaks(cur_fds);
160
161 exit(EXIT_SUCCESS);
162 }
163
164 #ifndef PATH_MAX
165 #define PATH_MAX 256
166 #endif
167
168 static void
set_xdg_runtime_dir(void)169 set_xdg_runtime_dir(void)
170 {
171 char xdg_runtime_dir[PATH_MAX];
172 const char *xrd_env;
173
174 xrd_env = getenv("XDG_RUNTIME_DIR");
175 /* if XDG_RUNTIME_DIR is not set in environ, fallback to /tmp */
176 assert((snprintf(xdg_runtime_dir, PATH_MAX, "%s/wayland-tests-XXXXXX",
177 xrd_env ? xrd_env : "/tmp") < PATH_MAX)
178 && "test error: XDG_RUNTIME_DIR too long");
179
180 assert(mkdtemp(xdg_runtime_dir) && "test error: mkdtemp failed");
181 if (mkdir(xdg_runtime_dir, 0700) == -1)
182 if (errno != EEXIST) {
183 perror("Creating XDG_RUNTIME_DIR");
184 abort();
185 }
186
187 if (setenv("XDG_RUNTIME_DIR", xdg_runtime_dir, 1) == -1) {
188 perror("Setting XDG_RUNTIME_DIR");
189 abort();
190 }
191 }
192
193 static void
rmdir_xdg_runtime_dir(void)194 rmdir_xdg_runtime_dir(void)
195 {
196 const char *xrd_env = getenv("XDG_RUNTIME_DIR");
197 assert(xrd_env && "No XDG_RUNTIME_DIR set");
198
199 /* rmdir may fail if some test didn't do clean up */
200 if (rmdir(xrd_env) == -1)
201 perror("Cleaning XDG_RUNTIME_DIR");
202 }
203
204 #define RED "\033[31m"
205 #define GREEN "\033[32m"
206
207 static void
stderr_set_color(const char * color)208 stderr_set_color(const char *color)
209 {
210 /* use colors only when the output is connected to
211 * the terminal */
212 if (is_atty)
213 fprintf(stderr, "%s", color);
214 }
215
216 static void
stderr_reset_color(void)217 stderr_reset_color(void)
218 {
219 if (is_atty)
220 fprintf(stderr, "\033[0m");
221 }
222
223 /* this function is taken from libinput/test/litest.c
224 * (rev 028513a0a723e97941c39)
225 *
226 * Returns: 1 if a debugger is confirmed present; 0 if no debugger is
227 * present or if it can't be determined.
228 */
229 static int
is_debugger_attached(void)230 is_debugger_attached(void)
231 {
232 int status;
233 int rc;
234 pid_t pid;
235 int pipefd[2];
236
237 if (pipe(pipefd) == -1) {
238 perror("pipe");
239 return 0;
240 }
241
242 pid = fork();
243 if (pid == -1) {
244 perror("fork");
245 close(pipefd[0]);
246 close(pipefd[1]);
247 return 0;
248 } else if (pid == 0) {
249 char buf;
250 pid_t ppid = getppid();
251
252 /* Wait until parent is ready */
253 close(pipefd[1]); /* Close unused write end */
254 read(pipefd[0], &buf, 1);
255 close(pipefd[0]);
256 if (buf == '-')
257 _exit(1);
258 if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) != 0)
259 _exit(1);
260 if (!waitpid(-1, NULL, 0))
261 _exit(1);
262 ptrace(PTRACE_CONT, NULL, NULL);
263 ptrace(PTRACE_DETACH, ppid, NULL, NULL);
264 _exit(0);
265 } else {
266 close(pipefd[0]);
267
268 /* Enable child to ptrace the parent process */
269 rc = prctl(PR_SET_PTRACER, pid);
270 if (rc != 0 && errno != EINVAL) {
271 /* An error prevents us from telling if a debugger is attached.
272 * Instead of propagating the error, assume no debugger present.
273 * But note the error to the log as a clue for troubleshooting.
274 * Then flag the error state to the client by sending '-'.
275 */
276 perror("prctl");
277 write(pipefd[1], "-", 1);
278 } else {
279 /* Signal to client that parent is ready by passing '+' */
280 write(pipefd[1], "+", 1);
281 }
282 close(pipefd[1]);
283
284 waitpid(pid, &status, 0);
285 rc = WEXITSTATUS(status);
286 }
287
288 return rc;
289 }
290
main(int argc,char * argv[])291 int main(int argc, char *argv[])
292 {
293 const struct test *t;
294 pid_t pid;
295 int total, pass;
296 siginfo_t info;
297
298 if (isatty(fileno(stderr)))
299 is_atty = 1;
300
301 if (is_debugger_attached()) {
302 fd_leak_check_enabled = 0;
303 timeouts_enabled = 0;
304 } else {
305 fd_leak_check_enabled = !getenv("WAYLAND_TEST_NO_LEAK_CHECK");
306 timeouts_enabled = !getenv("WAYLAND_TEST_NO_TIMEOUTS");
307 }
308
309 if (argc == 2 && strcmp(argv[1], "--help") == 0)
310 usage(argv[0], EXIT_SUCCESS);
311
312 if (argc == 2) {
313 t = find_test(argv[1]);
314 if (t == NULL) {
315 fprintf(stderr, "unknown test: \"%s\"\n", argv[1]);
316 usage(argv[0], EXIT_FAILURE);
317 }
318
319 set_xdg_runtime_dir();
320 /* run_test calls exit() */
321 assert(atexit(rmdir_xdg_runtime_dir) == 0);
322
323 run_test(t);
324 }
325
326 /* set our own XDG_RUNTIME_DIR */
327 set_xdg_runtime_dir();
328
329 pass = 0;
330 for (t = &__start_test_section; t < &__stop_test_section; t++) {
331 int success = 0;
332
333 pid = fork();
334 assert(pid >= 0);
335
336 if (pid == 0)
337 run_test(t); /* never returns */
338
339 if (waitid(P_PID, pid, &info, WEXITED)) {
340 stderr_set_color(RED);
341 fprintf(stderr, "waitid failed: %s\n",
342 strerror(errno));
343 stderr_reset_color();
344
345 abort();
346 }
347
348 switch (info.si_code) {
349 case CLD_EXITED:
350 if (info.si_status == EXIT_SUCCESS)
351 success = !t->must_fail;
352 else
353 success = t->must_fail;
354
355 stderr_set_color(success ? GREEN : RED);
356 fprintf(stderr, "test \"%s\":\texit status %d",
357 t->name, info.si_status);
358
359 break;
360 case CLD_KILLED:
361 case CLD_DUMPED:
362 if (t->must_fail)
363 success = 1;
364
365 stderr_set_color(success ? GREEN : RED);
366 fprintf(stderr, "test \"%s\":\tsignal %d",
367 t->name, info.si_status);
368
369 break;
370 }
371
372 if (success) {
373 pass++;
374 fprintf(stderr, ", pass.\n");
375 } else
376 fprintf(stderr, ", fail.\n");
377
378 stderr_reset_color();
379
380 /* print separator line */
381 fprintf(stderr, "----------------------------------------\n");
382 }
383
384 total = &__stop_test_section - &__start_test_section;
385 fprintf(stderr, "%d tests, %d pass, %d fail\n",
386 total, pass, total - pass);
387
388 /* cleaning */
389 rmdir_xdg_runtime_dir();
390
391 return pass == total ? EXIT_SUCCESS : EXIT_FAILURE;
392 }
393