Home | History | Annotate | Line # | Download | only in testsuite
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