test-expandargv.c revision 1.11 1 /* expandargv test program,
2 Copyright (C) 2006-2024 Free Software Foundation, Inc.
3 Written by Carlos O'Donell <carlos (at) codesourcery.com>
4
5 This file is part of the libiberty library, which is part of GCC.
6
7 This file is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 In addition to the permissions in the GNU General Public License, the
13 Free Software Foundation gives you unlimited permission to link the
14 compiled version of this file into combinations with other programs,
15 and to distribute those combinations without any restriction coming
16 from the use of this file. (The General Public License restrictions
17 do apply in other respects; for example, they cover modification of
18 the file, and distribution when not linked into a combined
19 executable.)
20
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 #include "libiberty.h"
35 #include <stdio.h>
36 #include <errno.h>
37 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #endif
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #ifndef EXIT_SUCCESS
48 #define EXIT_SUCCESS 0
49 #endif
50
51 #ifndef EXIT_FAILURE
52 #define EXIT_FAILURE 1
53 #endif
54
55 static void fatal_error (int, const char *, int) ATTRIBUTE_NORETURN;
56 void writeout_test (int, const char *);
57 void run_replaces (char *);
58 void hook_char_replace (char *, size_t, char, char);
59 int run_tests (const char **);
60 void erase_test (int);
61
62 /* Test input data, argv before, and argv after:
63
64 The \n is an important part of test_data since expandargv
65 may have to work in environments where \n is translated
66 as \r\n. Thus \n is included in the test data for the file.
67
68 We use \b to indicate that the test data is the null character.
69 This is because we use \0 normally to represent the end of the
70 file data, so we need something else for this. */
71
72 #define FILENAME_PATTERN "test-expandargv-%d.lst"
73 #define ARGV0 "test-expandargv"
74
75 const char *test_data[] = {
76 /* Test 0 - Check for expansion with \r\n */
77 "a\r\nb", /* Test 0 data */
78 ARGV0,
79 "@test-expandargv-0.lst",
80 0, /* End of argv[] before expansion */
81 ARGV0,
82 "a",
83 "b",
84 0, /* End of argv[] after expansion */
85
86 /* Test 1 - Check for expansion with \n */
87 "a\nb", /* Test 1 data */
88 ARGV0,
89 "@test-expandargv-1.lst",
90 0,
91 ARGV0,
92 "a",
93 "b",
94 0,
95
96 /* Test 2 - Check for expansion with \0 */
97 "a\bb", /* Test 2 data */
98 ARGV0,
99 "@test-expandargv-2.lst",
100 0,
101 ARGV0,
102 "a",
103 0,
104
105 /* Test 3 - Check for expansion with only \0 */
106 "\b", /* Test 3 data */
107 ARGV0,
108 "@test-expandargv-3.lst",
109 0,
110 ARGV0,
111 0,
112
113 /* Test 4 - Check for options beginning with an empty line. */
114 "\na\nb", /* Test 4 data */
115 ARGV0,
116 "@test-expandargv-4.lst",
117 0,
118 ARGV0,
119 "a",
120 "b",
121 0,
122
123 /* Test 5 - Check for options containing an empty argument. */
124 "a\n''\nb", /* Test 5 data */
125 ARGV0,
126 "@test-expandargv-5.lst",
127 0,
128 ARGV0,
129 "a",
130 "",
131 "b",
132 0,
133
134 /* Test 6 - Check for options containing a quoted newline. */
135 "a\n'a\n\nb'\nb", /* Test 6 data */
136 ARGV0,
137 "@test-expandargv-6.lst",
138 0,
139 ARGV0,
140 "a",
141 "a\n\nb",
142 "b",
143 0,
144
145 /* Test 7 - No backslash removal within single quotes. */
146 "'a\\$VAR' '\\\"'", /* Test 7 data */
147 ARGV0,
148 "@test-expandargv-7.lst",
149 0,
150 ARGV0,
151 "a\\$VAR",
152 "\\\"",
153 0,
154
155 /* Test 8 - Remove backslash / newline pairs. */
156 "\"ab\\\ncd\" ef\\\ngh", /* Test 8 data */
157 ARGV0,
158 "@test-expandargv-8.lst",
159 0,
160 ARGV0,
161 "abcd",
162 "efgh",
163 0,
164
165 /* Test 9 - Backslash within double quotes. */
166 "\"\\$VAR\" \"\\`\" \"\\\"\" \"\\\\\" \"\\n\" \"\\t\"", /* Test 9 data */
167 ARGV0,
168 "@test-expandargv-9.lst",
169 0,
170 ARGV0,
171 "$VAR",
172 "`",
173 "\"",
174 "\\",
175 "\\n",
176 "\\t",
177 0,
178
179 /* Test 10 - Mixed white space characters. */
180 "\t \n \t ", /* Test 10 data */
181 ARGV0,
182 "@test-expandargv-10.lst",
183 0,
184 ARGV0,
185 0,
186
187 /* Test 11 - Single ' ' character. */
188 " ", /* Test 11 data */
189 ARGV0,
190 "@test-expandargv-11.lst",
191 0,
192 ARGV0,
193 0,
194
195 /* Test 12 - Multiple ' ' characters. */
196 " ", /* Test 12 data */
197 ARGV0,
198 "@test-expandargv-12.lst",
199 0,
200 ARGV0,
201 0,
202
203 0 /* Test done marker, don't remove. */
204 };
205
206 /* Print a fatal error and exit. LINE is the line number where we
207 detected the error, ERRMSG is the error message to print, and ERR
208 is 0 or an errno value to print. */
209
210 static void
211 fatal_error (int line, const char *errmsg, int err)
212 {
213 fprintf (stderr, "test-expandargv:%d: %s", line, errmsg);
214 if (errno != 0)
215 fprintf (stderr, ": %s", xstrerror (err));
216 fprintf (stderr, "\n");
217 exit (EXIT_FAILURE);
218 }
219
220 /* hook_char_replace:
221 Replace 'replacethis' with 'withthis' */
222
223 void
224 hook_char_replace (char *string, size_t len, char replacethis, char withthis)
225 {
226 int i = 0;
227 for (i = 0; i < len; i++)
228 if (string[i] == replacethis)
229 string[i] = withthis;
230 }
231
232 /* run_replaces:
233 Hook here all the character for character replaces.
234 Be warned that expanding the string or contracting the string
235 should be handled with care. */
236
237 void
238 run_replaces (char * string)
239 {
240 /* Store original string size */
241 size_t len = strlen (string);
242 hook_char_replace (string, len, '\b', '\0');
243 }
244
245 /* write_test:
246 Write test datafile */
247
248 void
249 writeout_test (int test, const char * test_data)
250 {
251 char filename[256];
252 FILE *fd;
253 size_t len, sys_fwrite;
254 char * parse;
255
256 /* Unique filename per test */
257 sprintf (filename, FILENAME_PATTERN, test);
258 fd = fopen (filename, "w");
259 if (fd == NULL)
260 fatal_error (__LINE__, "Failed to create test file.", errno);
261
262 /* Generate RW copy of data for replaces */
263 len = strlen (test_data);
264 parse = malloc (sizeof (char) * (len + 1));
265 if (parse == NULL)
266 fatal_error (__LINE__, "Failed to malloc parse.", errno);
267
268 memcpy (parse, test_data, sizeof (char) * (len + 1));
269 /* Run all possible replaces */
270 run_replaces (parse);
271
272 sys_fwrite = fwrite (parse, sizeof (char), len, fd);
273 if (sys_fwrite != len)
274 fatal_error (__LINE__, "Failed to write to test file.", errno);
275
276 free (parse);
277 fclose (fd);
278 }
279
280 /* erase_test:
281 Erase the test file */
282
283 void
284 erase_test (int test)
285 {
286 char filename[256];
287 sprintf (filename, FILENAME_PATTERN, test);
288 if (unlink (filename) != 0)
289 fatal_error (__LINE__, "Failed to erase test file.", errno);
290 }
291
292 /* compare_argv:
293 TEST is the current test number, and NAME is a short string to identify
294 which libibery function is being tested. ARGC_A and ARGV_A describe an
295 argument array, and this is compared to ARGC_B and ARGV_B, return 0 if
296 the two arrays match, otherwise return 1. */
297
298 static int
299 compare_argv (int test, const char *name, int argc_a, char *argv_a[],
300 int argc_b, char *argv_b[])
301 {
302 int failed = 0, k;
303
304 if (argc_a != argc_b)
305 {
306 printf ("FAIL: test-%s-%d. Argument count didn't match\n", name, test);
307 failed = 1;
308 }
309 /* Compare each of the argv's ... */
310 else
311 for (k = 0; k < argc_a; k++)
312 if (strcmp (argv_a[k], argv_b[k]) != 0)
313 {
314 printf ("FAIL: test-%s-%d. Arguments don't match.\n", name, test);
315 failed = 1;
316 break;
317 }
318
319 if (!failed)
320 printf ("PASS: test-%s-%d.\n", name, test);
321
322 return failed;
323 }
324
325 /* test_buildargv
326 Test the buildargv function from libiberty. TEST is the current test
327 number and TEST_INPUT is the string to pass to buildargv (after calling
328 run_replaces on it). ARGC_AFTER and ARGV_AFTER are the expected
329 results. Return 0 if the test passes, otherwise return 1. */
330
331 static int
332 test_buildargv (int test, const char * test_input, int argc_after,
333 char *argv_after[])
334 {
335 char * input, ** argv;
336 size_t len;
337 int argc, failed;
338
339 /* Generate RW copy of data for replaces */
340 len = strlen (test_input);
341 input = malloc (sizeof (char) * (len + 1));
342 if (input == NULL)
343 fatal_error (__LINE__, "Failed to malloc buildargv input buffer.", errno);
344
345 memcpy (input, test_input, sizeof (char) * (len + 1));
346 /* Run all possible replaces */
347 run_replaces (input);
348
349 /* Split INPUT into separate arguments. */
350 argv = buildargv (input);
351
352 /* Count the arguments we got back. */
353 argc = 0;
354 while (argv[argc])
355 ++argc;
356
357 failed = compare_argv (test, "buildargv", argc_after, argv_after, argc, argv);
358
359 free (input);
360 freeargv (argv);
361
362 return failed;
363 }
364
365 /* run_tests:
366 Run expandargv
367 Compare argv before and after.
368 Return number of fails */
369
370 int
371 run_tests (const char **test_data)
372 {
373 int argc_after, argc_before;
374 char ** argv_before, ** argv_after;
375 int i, j, k, fails;
376 const char * input_str;
377
378 i = j = fails = 0;
379 /* Loop over all the tests */
380 while (test_data[j])
381 {
382 /* Save original input in case we run a buildargv test. */
383 input_str = test_data[j];
384
385 /* Write test data */
386 writeout_test (i, test_data[j++]);
387 /* Copy argv before */
388 argv_before = dupargv ((char **) &test_data[j]);
389
390 /* Count argc before/after */
391 argc_before = 0;
392 argc_after = 0;
393 while (test_data[j + argc_before])
394 argc_before++;
395 j += argc_before + 1; /* Skip null */
396 while (test_data[j + argc_after])
397 argc_after++;
398
399 /* Copy argv after */
400 argv_after = dupargv ((char **) &test_data[j]);
401
402 /* Run all possible replaces */
403 for (k = 0; k < argc_before; k++)
404 run_replaces (argv_before[k]);
405 for (k = 0; k < argc_after; k++)
406 run_replaces (argv_after[k]);
407
408 /* If the test input is just a file to expand then we can also test
409 calling buildargv directly as the expected output is equivalent to
410 calling buildargv on the contents of the file.
411
412 The results of calling buildargv will not include the ARGV0 constant,
413 which is why we pass 'argc_after - 1' and 'argv_after + 1', this skips
414 over the ARGV0 in the expected results. */
415 if (argc_before == 2)
416 fails += test_buildargv (i, input_str, argc_after - 1, argv_after + 1);
417 else
418 printf ("SKIP: test-buildargv-%d. This test isn't for buildargv\n", i);
419
420 /* Run test: Expand arguments */
421 expandargv (&argc_before, &argv_before);
422
423 fails += compare_argv (i, "expandargv", argc_before, argv_before,
424 argc_after, argv_after);
425
426 freeargv (argv_before);
427 freeargv (argv_after);
428 /* Advance to next test */
429 j += argc_after + 1;
430 /* Erase test file */
431 erase_test (i);
432 i++;
433 }
434 return fails;
435 }
436
437 /* main:
438 Run tests.
439 Check result and exit with appropriate code. */
440
441 int
442 main(int argc, char **argv)
443 {
444 int fails;
445 /* Repeat for all the tests:
446 - Parse data array and write into file.
447 - Run replace hooks before writing to file.
448 - Parse data array and build argv before/after.
449 - Run replace hooks on argv before/after
450 - Run expandargv.
451 - Compare output of expandargv argv to after argv.
452 - If they compare the same then test passes
453 else the test fails.
454 - Erase test file. */
455
456 fails = run_tests (test_data);
457 if (!fails)
458 exit (EXIT_SUCCESS);
459 else
460 exit (EXIT_FAILURE);
461 }
462
463