]> git.ipfire.org Git - thirdparty/git.git/commitdiff
tests: add a helper to stress test argument quoting
authorGarima Singh <garima.singh@microsoft.com>
Wed, 18 Sep 2019 20:03:59 +0000 (16:03 -0400)
committerJohannes Schindelin <johannes.schindelin@gmx.de>
Thu, 5 Dec 2019 14:36:52 +0000 (15:36 +0100)
On Windows, we have to do all the command-line argument quoting
ourselves. Worse: we have to have two versions of said quoting, one for
MSYS2 programs (which have their own dequoting rules) and the rest.

We care mostly about the rest, and to make sure that that works, let's
have a stress test that comes up with all kinds of awkward arguments,
verifying that a spawned sub-process receives those unharmed.

Signed-off-by: Garima Singh <garima.singh@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
t/helper/test-run-command.c

index d24d157379f30cafdeeb772e82bf5ee4c862272c..6c801cb529763721171f5ad8360cc58a803bc46c 100644 (file)
@@ -12,8 +12,8 @@
 #include "run-command.h"
 #include "argv-array.h"
 #include "strbuf.h"
-#include <string.h>
-#include <errno.h>
+#include "gettext.h"
+#include "parse-options.h"
 
 static int number_callbacks;
 static int parallel_next(struct child_process *cp,
@@ -49,11 +49,125 @@ static int task_finished(int result,
        return 1;
 }
 
+static uint64_t my_random_next = 1234;
+
+static uint64_t my_random(void)
+{
+       uint64_t res = my_random_next;
+       my_random_next = my_random_next * 1103515245 + 12345;
+       return res;
+}
+
+static int quote_stress_test(int argc, const char **argv)
+{
+       /*
+        * We are running a quote-stress test.
+        * spawn a subprocess that runs quote-stress with a
+        * special option that echoes back the arguments that
+        * were passed in.
+        */
+       char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
+       int i, j, k, trials = 100;
+       struct strbuf out = STRBUF_INIT;
+       struct argv_array args = ARGV_ARRAY_INIT;
+       struct option options[] = {
+               OPT_INTEGER('n', "trials", &trials, "Number of trials"),
+               OPT_END()
+       };
+       const char * const usage[] = {
+               "test-run-command quote-stress-test <options>",
+               NULL
+       };
+
+       argc = parse_options(argc, argv, NULL, options, usage, 0);
+
+       for (i = 0; i < trials; i++) {
+               struct child_process cp = CHILD_PROCESS_INIT;
+               size_t arg_count = 1 + (my_random() % 5), arg_offset;
+               int ret = 0;
+
+               argv_array_clear(&args);
+               argv_array_pushl(&args, "test-run-command",
+                                "quote-echo", NULL);
+               arg_offset = args.argc;
+               for (j = 0; j < arg_count; j++) {
+                       char buf[20];
+                       size_t min_len = 1;
+                       size_t arg_len = min_len +
+                               (my_random() % (ARRAY_SIZE(buf) - min_len));
+
+                       for (k = 0; k < arg_len; k++)
+                               buf[k] = special[my_random() %
+                                       ARRAY_SIZE(special)];
+                       buf[arg_len] = '\0';
+
+                       argv_array_push(&args, buf);
+               }
+
+               cp.argv = args.argv;
+               strbuf_reset(&out);
+               if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
+                       return error("Failed to spawn child process");
+
+               for (j = 0, k = 0; j < arg_count; j++) {
+                       const char *arg = args.argv[j + arg_offset];
+
+                       if (strcmp(arg, out.buf + k))
+                               ret = error("incorrectly quoted arg: '%s', "
+                                           "echoed back as '%s'",
+                                            arg, out.buf + k);
+                       k += strlen(out.buf + k) + 1;
+               }
+
+               if (k != out.len)
+                       ret = error("got %d bytes, but consumed only %d",
+                                    (int)out.len, (int)k);
+
+               if (ret) {
+                       fprintf(stderr, "Trial #%d failed. Arguments:\n", i);
+                       for (j = 0; j < arg_count; j++)
+                               fprintf(stderr, "arg #%d: '%s'\n",
+                                       (int)j, args.argv[j + arg_offset]);
+
+                       strbuf_release(&out);
+                       argv_array_clear(&args);
+
+                       return ret;
+               }
+
+               if (i && (i % 100) == 0)
+                       fprintf(stderr, "Trials completed: %d\n", (int)i);
+       }
+
+       strbuf_release(&out);
+       argv_array_clear(&args);
+
+       return 0;
+}
+
+static int quote_echo(int argc, const char **argv)
+{
+       while (argc > 1) {
+               fwrite(argv[1], strlen(argv[1]), 1, stdout);
+               fputc('\0', stdout);
+               argv++;
+               argc--;
+       }
+
+       return 0;
+}
+
 int cmd_main(int argc, const char **argv)
 {
        struct child_process proc = CHILD_PROCESS_INIT;
        int jobs;
 
+       if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
+               return !!quote_stress_test(argc - 1, argv + 1);
+
+       if (argc >= 2 && !strcmp(argv[1], "quote-echo"))
+               return !!quote_echo(argc - 1, argv + 1);
+
        if (argc < 3)
                return 1;
        proc.argv = (const char **)argv + 2;