source: branches/libc-0.6/src/libctests/glibc/test-skeleton.c

Last change on this file was 2076, checked in by bird, 20 years ago

libc adjustments. extending some testcases.

  • Property cvs2svn:cvs-rev set to 1.4
  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 10.0 KB
Line 
1/* Skeleton for test programs.
2 Copyright (C) 1998,2000-2004, 2005 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
20
21#include <errno.h>
22#include <getopt.h>
23/*#include <malloc.h> - been replaced by <stdlib.h> ages ago */
24#include <search.h>
25#include <signal.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30#include <sys/resource.h>
31#include <sys/wait.h>
32#include <sys/param.h>
33#include <time.h>
34
35/* bird added for bsd */
36#ifndef TEMP_FAILURE_RETRY /* special GNU idea */
37# define TEMP_FAILURE_RETRY(expression) \
38 (__extension__ \
39 ({ long int __result; \
40 do __result = (long int) (expression); \
41 while (__result == -1L && errno == EINTR); \
42 __result; }))
43#endif
44
45#ifndef HAVE_QELEM
46struct qelem
47 {
48 struct qelem *q_forw;
49 struct qelem *q_back;
50 char q_data[1];
51 };
52#endif
53
54/* The test function is normally called `do_test' and it is called
55 with argc and argv as the arguments. We nevertheless provide the
56 possibility to overwrite this name. */
57#ifndef TEST_FUNCTION
58# define TEST_FUNCTION do_test (argc, argv)
59#endif
60
61#ifndef TEST_DATA_LIMIT
62# define TEST_DATA_LIMIT (64 << 20) /* Data limit (bytes) to run with. */
63#endif
64
65#define OPT_DIRECT 1000
66#define OPT_TESTDIR 1001
67
68static struct option options[] =
69{
70#ifdef CMDLINE_OPTIONS
71 CMDLINE_OPTIONS
72#endif
73 { "direct", no_argument, NULL, OPT_DIRECT },
74 { "test-dir", required_argument, NULL, OPT_TESTDIR },
75 { NULL, 0, NULL, 0 }
76};
77
78/* PID of the test itself. */
79static pid_t pid;
80
81/* Directory to place temporary files in. */
82static const char *test_dir;
83
84/* List of temporary files. */
85struct temp_name_list
86{
87 struct qelem q;
88 const char *name;
89} *temp_name_list;
90
91/* Add temporary files in list. */
92static void
93__attribute__ ((unused))
94add_temp_file (const char *name)
95{
96 struct temp_name_list *newp
97 = (struct temp_name_list *) calloc (sizeof (*newp), 1);
98 if (newp != NULL)
99 {
100 newp->name = name;
101 if (temp_name_list == NULL)
102 temp_name_list = (struct temp_name_list *) &newp->q;
103 else
104 insque (newp, temp_name_list);
105 }
106}
107
108/* Delete all temporary files. */
109static void
110delete_temp_files (void)
111{
112 while (temp_name_list != NULL)
113 {
114 remove (temp_name_list->name);
115 temp_name_list = (struct temp_name_list *) temp_name_list->q.q_forw;
116 }
117}
118
119/* Create a temporary file. */
120static int
121__attribute__ ((unused))
122create_temp_file (const char *base, char **filename)
123{
124 char *fname;
125 int fd;
126
127 fname = (char *) malloc (strlen (test_dir) + 1 + strlen (base)
128 + sizeof ("XXXXXX"));
129 if (fname == NULL)
130 {
131 puts ("out of memory");
132 return -1;
133 }
134 strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX");
135
136 fd = mkstemp (fname);
137 if (fd == -1)
138 {
139 printf ("cannot open temporary file '%s': %m\n", fname);
140 free (fname);
141 return -1;
142 }
143
144 add_temp_file (fname);
145 if (filename != NULL)
146 *filename = fname;
147
148 return fd;
149}
150
151/* Timeout handler. We kill the child and exit with an error. */
152static void
153__attribute__ ((noreturn))
154timeout_handler (int sig __attribute__ ((unused)))
155{
156 int killed;
157 int status;
158
159 /* Send signal. */
160 kill (pid, SIGKILL);
161
162 /* Wait for it to terminate. */
163 int i;
164 for (i = 0; i < 5; ++i)
165 {
166 killed = waitpid (pid, &status, WNOHANG|WUNTRACED);
167 if (killed != 0)
168 break;
169
170 /* Delay, give the system time to process the kill. If the
171 nanosleep() call return prematurely, all the better. We
172 won't restart it since this probably means the child process
173 finally died. */
174 struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
175 nanosleep (&ts, NULL);
176 }
177 if (killed != 0 && killed != pid)
178 {
179 perror ("Failed to kill test process");
180 exit (1);
181 }
182
183#ifdef CLEANUP_HANDLER
184 CLEANUP_HANDLER;
185#endif
186
187 /* If we expected this signal: good! */
188#ifdef EXPECTED_SIGNAL
189 if (EXPECTED_SIGNAL == SIGALRM)
190 exit (0);
191#endif
192
193 if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL))
194 fputs ("Timed out: killed the child process\n", stderr);
195 else if (WIFSTOPPED (status))
196 fprintf (stderr, "Timed out: the child process was %s\n",
197 strsignal (WSTOPSIG (status)));
198 else if (WIFSIGNALED (status))
199 fprintf (stderr, "Timed out: the child process got signal %s\n",
200 strsignal (WTERMSIG (status)));
201 else
202 fprintf (stderr, "Timed out: killed the child process but it exited %d\n",
203 WEXITSTATUS (status));
204
205 /* Exit with an error. */
206 exit (1);
207}
208
209/* We provide the entry point here. */
210int
211main (int argc, char *argv[])
212{
213 int direct = 0; /* Directly call the test function? */
214 int status;
215 int opt;
216 unsigned int timeoutfactor = 1;
217 pid_t termpid;
218
219 /* Make uses of freed and uninitialized memory known. */
220#if 0 /* bird */
221 mallopt (M_PERTURB, 42);
222#endif
223
224#ifdef STDOUT_UNBUFFERED
225 setbuf (stdout, NULL);
226#endif
227
228 while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1)
229 switch (opt)
230 {
231 case '?':
232 exit (1);
233 case OPT_DIRECT:
234 direct = 1;
235 break;
236 case OPT_TESTDIR:
237 test_dir = optarg;
238 break;
239#ifdef CMDLINE_PROCESS
240 CMDLINE_PROCESS
241#endif
242 }
243
244 /* If set, read the test TIMEOUTFACTOR value from the environment.
245 This value is used to scale the default test timeout values. */
246 char *envstr_timeoutfactor = getenv ("TIMEOUTFACTOR");
247 if (envstr_timeoutfactor != NULL)
248 {
249 char *envstr_conv = envstr_timeoutfactor;
250 unsigned long int env_fact;
251
252 env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0);
253 if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor)
254 timeoutfactor = MAX (env_fact, 1);
255 }
256
257 /* Set TMPDIR to specified test directory. */
258 if (test_dir != NULL)
259 {
260 setenv ("TMPDIR", test_dir, 1);
261
262 if (chdir (test_dir) < 0)
263 {
264 perror ("chdir");
265 exit (1);
266 }
267 }
268 else
269 {
270 test_dir = getenv ("TMPDIR");
271 if (test_dir == NULL || test_dir[0] == '\0')
272 test_dir = "/tmp";
273 }
274
275 /* Make sure we see all message, even those on stdout. */
276 setvbuf (stdout, NULL, _IONBF, 0);
277
278 /* make sure temporary files are deleted. */
279 atexit (delete_temp_files);
280
281 /* Correct for the possible parameters. */
282 argv[optind - 1] = argv[0];
283 argv += optind - 1;
284 argc -= optind - 1;
285
286 /* Call the initializing function, if one is available. */
287#ifdef PREPARE
288 PREPARE (argc, argv);
289#endif
290
291 /* If we are not expected to fork run the function immediately. */
292 if (direct)
293 return TEST_FUNCTION;
294
295 /* Set up the test environment:
296 - prevent core dumps
297 - set up the timer
298 - fork and execute the function. */
299
300 pid = fork ();
301 if (pid == 0)
302 {
303 /* This is the child. */
304#ifdef RLIMIT_CORE
305 /* Try to avoid dumping core. */
306 struct rlimit core_limit;
307 core_limit.rlim_cur = 0;
308 core_limit.rlim_max = 0;
309 setrlimit (RLIMIT_CORE, &core_limit);
310#endif
311
312#ifdef RLIMIT_DATA
313 /* Try to avoid eating all memory if a test leaks. */
314 struct rlimit data_limit;
315 if (getrlimit (RLIMIT_DATA, &data_limit) == 0)
316 {
317 if (TEST_DATA_LIMIT == RLIM_INFINITY)
318 data_limit.rlim_cur = data_limit.rlim_max;
319 else if (data_limit.rlim_cur > (rlim_t) TEST_DATA_LIMIT)
320 data_limit.rlim_cur = MIN ((rlim_t) TEST_DATA_LIMIT,
321 data_limit.rlim_max);
322 if (setrlimit (RLIMIT_DATA, &data_limit) < 0)
323#ifndef __EMX__
324 perror ("setrlimit: RLIMIT_DATA");
325#else
326 ;
327#endif
328 }
329 else
330 perror ("getrlimit: RLIMIT_DATA");
331#endif
332
333 /* We put the test process in its own pgrp so that if it bogusly
334 generates any job control signals, they won't hit the whole build. */
335 setpgid (0, 0);
336
337 /* Execute the test function and exit with the return value. */
338 exit (TEST_FUNCTION);
339 }
340 else if (pid < 0)
341 {
342 perror ("Cannot fork test program");
343 exit (1);
344 }
345
346 /* Set timeout. */
347#ifndef TIMEOUT
348 /* Default timeout is two seconds. */
349# define TIMEOUT 2
350#endif
351 signal (SIGALRM, timeout_handler);
352 alarm (TIMEOUT * timeoutfactor);
353
354 /* Wait for the regular termination. */
355 termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
356 if (termpid == -1)
357 {
358 printf ("Waiting for test program failed: %m\n");
359 exit (1);
360 }
361 if (termpid != pid)
362 {
363 printf ("Oops, wrong test program terminated: expected %ld, got %ld\n",
364 (long int) pid, (long int) termpid);
365 exit (1);
366 }
367
368#ifndef EXPECTED_SIGNAL
369 /* We don't expect any signal. */
370# define EXPECTED_SIGNAL 0
371#endif
372 if (WTERMSIG (status) != EXPECTED_SIGNAL)
373 {
374 if (EXPECTED_SIGNAL != 0)
375 {
376 if (WTERMSIG (status) == 0)
377 fprintf (stderr,
378 "Expected signal '%s' from child, got none\n",
379 strsignal (EXPECTED_SIGNAL));
380 else
381 fprintf (stderr,
382 "Incorrect signal from child: got `%s', need `%s'\n",
383 strsignal (WTERMSIG (status)),
384 strsignal (EXPECTED_SIGNAL));
385 }
386 else
387 fprintf (stderr, "Didn't expect signal from child: got `%s'\n",
388 strsignal (WTERMSIG (status)));
389 exit (1);
390 }
391
392 /* Simply exit with the return value of the test. */
393#ifndef EXPECTED_STATUS
394 return WEXITSTATUS (status);
395#else
396 if (WEXITSTATUS (status) != EXPECTED_STATUS)
397 {
398 fprintf (stderr, "Expected status %d, got %d\n",
399 EXPECTED_STATUS, WEXITSTATUS (status));
400 exit (1);
401 }
402
403 return 0;
404#endif
405}
Note: See TracBrowser for help on using the repository browser.