quote-stress-test: allow skipping some trials
[git] / t / helper / test-run-command.c
1 /*
2  * test-run-command.c: test run command API.
3  *
4  * (C) 2009 Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
5  *
6  * This code is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include "git-compat-util.h"
12 #include "run-command.h"
13 #include "argv-array.h"
14 #include "strbuf.h"
15 #include "gettext.h"
16 #include "parse-options.h"
17
18 static int number_callbacks;
19 static int parallel_next(struct child_process *cp,
20                          struct strbuf *err,
21                          void *cb,
22                          void **task_cb)
23 {
24         struct child_process *d = cb;
25         if (number_callbacks >= 4)
26                 return 0;
27
28         argv_array_pushv(&cp->args, d->argv);
29         strbuf_addstr(err, "preloaded output of a child\n");
30         number_callbacks++;
31         return 1;
32 }
33
34 static int no_job(struct child_process *cp,
35                   struct strbuf *err,
36                   void *cb,
37                   void **task_cb)
38 {
39         strbuf_addstr(err, "no further jobs available\n");
40         return 0;
41 }
42
43 static int task_finished(int result,
44                          struct strbuf *err,
45                          void *pp_cb,
46                          void *pp_task_cb)
47 {
48         strbuf_addstr(err, "asking for a quick stop\n");
49         return 1;
50 }
51
52 static uint64_t my_random_next = 1234;
53
54 static uint64_t my_random(void)
55 {
56         uint64_t res = my_random_next;
57         my_random_next = my_random_next * 1103515245 + 12345;
58         return res;
59 }
60
61 static int quote_stress_test(int argc, const char **argv)
62 {
63         /*
64          * We are running a quote-stress test.
65          * spawn a subprocess that runs quote-stress with a
66          * special option that echoes back the arguments that
67          * were passed in.
68          */
69         char special[] = ".?*\\^_\"'`{}()[]<>@~&+:;$%"; // \t\r\n\a";
70         int i, j, k, trials = 100, skip = 0;
71         struct strbuf out = STRBUF_INIT;
72         struct argv_array args = ARGV_ARRAY_INIT;
73         struct option options[] = {
74                 OPT_INTEGER('n', "trials", &trials, "Number of trials"),
75                 OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"),
76                 OPT_END()
77         };
78         const char * const usage[] = {
79                 "test-run-command quote-stress-test <options>",
80                 NULL
81         };
82
83         argc = parse_options(argc, argv, NULL, options, usage, 0);
84
85         for (i = 0; i < trials; i++) {
86                 struct child_process cp = CHILD_PROCESS_INIT;
87                 size_t arg_count, arg_offset;
88                 int ret = 0;
89
90                 argv_array_clear(&args);
91                 argv_array_pushl(&args, "test-run-command",
92                                  "quote-echo", NULL);
93                 arg_offset = args.argc;
94
95                 if (argc > 0) {
96                         trials = 1;
97                         arg_count = argc;
98                         for (j = 0; j < arg_count; j++)
99                                 argv_array_push(&args, argv[j]);
100                 } else {
101                         arg_count = 1 + (my_random() % 5);
102                         for (j = 0; j < arg_count; j++) {
103                                 char buf[20];
104                                 size_t min_len = 1;
105                                 size_t arg_len = min_len +
106                                         (my_random() % (ARRAY_SIZE(buf) - min_len));
107
108                                 for (k = 0; k < arg_len; k++)
109                                         buf[k] = special[my_random() %
110                                                 ARRAY_SIZE(special)];
111                                 buf[arg_len] = '\0';
112
113                                 argv_array_push(&args, buf);
114                         }
115                 }
116
117                 if (i < skip)
118                         continue;
119
120                 cp.argv = args.argv;
121                 strbuf_reset(&out);
122                 if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0)
123                         return error("Failed to spawn child process");
124
125                 for (j = 0, k = 0; j < arg_count; j++) {
126                         const char *arg = args.argv[j + arg_offset];
127
128                         if (strcmp(arg, out.buf + k))
129                                 ret = error("incorrectly quoted arg: '%s', "
130                                             "echoed back as '%s'",
131                                              arg, out.buf + k);
132                         k += strlen(out.buf + k) + 1;
133                 }
134
135                 if (k != out.len)
136                         ret = error("got %d bytes, but consumed only %d",
137                                      (int)out.len, (int)k);
138
139                 if (ret) {
140                         fprintf(stderr, "Trial #%d failed. Arguments:\n", i);
141                         for (j = 0; j < arg_count; j++)
142                                 fprintf(stderr, "arg #%d: '%s'\n",
143                                         (int)j, args.argv[j + arg_offset]);
144
145                         strbuf_release(&out);
146                         argv_array_clear(&args);
147
148                         return ret;
149                 }
150
151                 if (i && (i % 100) == 0)
152                         fprintf(stderr, "Trials completed: %d\n", (int)i);
153         }
154
155         strbuf_release(&out);
156         argv_array_clear(&args);
157
158         return 0;
159 }
160
161 static int quote_echo(int argc, const char **argv)
162 {
163         while (argc > 1) {
164                 fwrite(argv[1], strlen(argv[1]), 1, stdout);
165                 fputc('\0', stdout);
166                 argv++;
167                 argc--;
168         }
169
170         return 0;
171 }
172
173 int cmd_main(int argc, const char **argv)
174 {
175         struct child_process proc = CHILD_PROCESS_INIT;
176         int jobs;
177
178         if (argc >= 2 && !strcmp(argv[1], "quote-stress-test"))
179                 return !!quote_stress_test(argc - 1, argv + 1);
180
181         if (argc >= 2 && !strcmp(argv[1], "quote-echo"))
182                 return !!quote_echo(argc - 1, argv + 1);
183
184         if (argc < 3)
185                 return 1;
186         proc.argv = (const char **)argv + 2;
187
188         if (!strcmp(argv[1], "start-command-ENOENT")) {
189                 if (start_command(&proc) < 0 && errno == ENOENT)
190                         return 0;
191                 fprintf(stderr, "FAIL %s\n", argv[1]);
192                 return 1;
193         }
194         if (!strcmp(argv[1], "run-command"))
195                 exit(run_command(&proc));
196
197         jobs = atoi(argv[2]);
198         proc.argv = (const char **)argv + 3;
199
200         if (!strcmp(argv[1], "run-command-parallel"))
201                 exit(run_processes_parallel(jobs, parallel_next,
202                                             NULL, NULL, &proc));
203
204         if (!strcmp(argv[1], "run-command-abort"))
205                 exit(run_processes_parallel(jobs, parallel_next,
206                                             NULL, task_finished, &proc));
207
208         if (!strcmp(argv[1], "run-command-no-jobs"))
209                 exit(run_processes_parallel(jobs, no_job,
210                                             NULL, task_finished, &proc));
211
212         fprintf(stderr, "check usage\n");
213         return 1;
214 }