]>
Commit | Line | Data |
---|---|---|
2b541bf8 JS |
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" | |
c553c72e SB |
13 | #include "argv-array.h" |
14 | #include "strbuf.h" | |
ad155925 GS |
15 | #include "gettext.h" |
16 | #include "parse-options.h" | |
2b541bf8 | 17 | |
c553c72e SB |
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); | |
02962d36 | 29 | strbuf_addstr(err, "preloaded output of a child\n"); |
c553c72e SB |
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 | { | |
02962d36 | 39 | strbuf_addstr(err, "no further jobs available\n"); |
c553c72e SB |
40 | return 0; |
41 | } | |
42 | ||
43 | static int task_finished(int result, | |
c553c72e SB |
44 | struct strbuf *err, |
45 | void *pp_cb, | |
46 | void *pp_task_cb) | |
47 | { | |
02962d36 | 48 | strbuf_addstr(err, "asking for a quick stop\n"); |
c553c72e SB |
49 | return 1; |
50 | } | |
51 | ||
ad155925 GS |
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"; | |
379e51d1 | 70 | int i, j, k, trials = 100, skip = 0, msys2 = 0; |
ad155925 GS |
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"), | |
7530a628 | 75 | OPT_INTEGER('s', "skip", &skip, "Skip <n> trials"), |
379e51d1 | 76 | OPT_BOOL('m', "msys2", &msys2, "Test quoting for MSYS2's sh"), |
ad155925 GS |
77 | OPT_END() |
78 | }; | |
79 | const char * const usage[] = { | |
80 | "test-run-command quote-stress-test <options>", | |
81 | NULL | |
82 | }; | |
83 | ||
84 | argc = parse_options(argc, argv, NULL, options, usage, 0); | |
85 | ||
379e51d1 JS |
86 | setenv("MSYS_NO_PATHCONV", "1", 0); |
87 | ||
ad155925 GS |
88 | for (i = 0; i < trials; i++) { |
89 | struct child_process cp = CHILD_PROCESS_INIT; | |
55953c77 | 90 | size_t arg_count, arg_offset; |
ad155925 GS |
91 | int ret = 0; |
92 | ||
93 | argv_array_clear(&args); | |
379e51d1 JS |
94 | if (msys2) |
95 | argv_array_pushl(&args, "sh", "-c", | |
96 | "printf %s\\\\0 \"$@\"", "skip", NULL); | |
97 | else | |
98 | argv_array_pushl(&args, "test-run-command", | |
99 | "quote-echo", NULL); | |
ad155925 | 100 | arg_offset = args.argc; |
55953c77 JS |
101 | |
102 | if (argc > 0) { | |
103 | trials = 1; | |
104 | arg_count = argc; | |
105 | for (j = 0; j < arg_count; j++) | |
106 | argv_array_push(&args, argv[j]); | |
107 | } else { | |
108 | arg_count = 1 + (my_random() % 5); | |
109 | for (j = 0; j < arg_count; j++) { | |
110 | char buf[20]; | |
111 | size_t min_len = 1; | |
112 | size_t arg_len = min_len + | |
113 | (my_random() % (ARRAY_SIZE(buf) - min_len)); | |
114 | ||
115 | for (k = 0; k < arg_len; k++) | |
116 | buf[k] = special[my_random() % | |
117 | ARRAY_SIZE(special)]; | |
118 | buf[arg_len] = '\0'; | |
119 | ||
120 | argv_array_push(&args, buf); | |
121 | } | |
ad155925 GS |
122 | } |
123 | ||
7530a628 JS |
124 | if (i < skip) |
125 | continue; | |
126 | ||
ad155925 GS |
127 | cp.argv = args.argv; |
128 | strbuf_reset(&out); | |
129 | if (pipe_command(&cp, NULL, 0, &out, 0, NULL, 0) < 0) | |
130 | return error("Failed to spawn child process"); | |
131 | ||
132 | for (j = 0, k = 0; j < arg_count; j++) { | |
133 | const char *arg = args.argv[j + arg_offset]; | |
134 | ||
135 | if (strcmp(arg, out.buf + k)) | |
136 | ret = error("incorrectly quoted arg: '%s', " | |
137 | "echoed back as '%s'", | |
138 | arg, out.buf + k); | |
139 | k += strlen(out.buf + k) + 1; | |
140 | } | |
141 | ||
142 | if (k != out.len) | |
143 | ret = error("got %d bytes, but consumed only %d", | |
144 | (int)out.len, (int)k); | |
145 | ||
146 | if (ret) { | |
147 | fprintf(stderr, "Trial #%d failed. Arguments:\n", i); | |
148 | for (j = 0; j < arg_count; j++) | |
149 | fprintf(stderr, "arg #%d: '%s'\n", | |
150 | (int)j, args.argv[j + arg_offset]); | |
151 | ||
152 | strbuf_release(&out); | |
153 | argv_array_clear(&args); | |
154 | ||
155 | return ret; | |
156 | } | |
157 | ||
158 | if (i && (i % 100) == 0) | |
159 | fprintf(stderr, "Trials completed: %d\n", (int)i); | |
160 | } | |
161 | ||
162 | strbuf_release(&out); | |
163 | argv_array_clear(&args); | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | static int quote_echo(int argc, const char **argv) | |
169 | { | |
170 | while (argc > 1) { | |
171 | fwrite(argv[1], strlen(argv[1]), 1, stdout); | |
172 | fputc('\0', stdout); | |
173 | argv++; | |
174 | argc--; | |
175 | } | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
3f2e2297 | 180 | int cmd_main(int argc, const char **argv) |
2b541bf8 | 181 | { |
d3180279 | 182 | struct child_process proc = CHILD_PROCESS_INIT; |
c553c72e | 183 | int jobs; |
2b541bf8 | 184 | |
ad155925 GS |
185 | if (argc >= 2 && !strcmp(argv[1], "quote-stress-test")) |
186 | return !!quote_stress_test(argc - 1, argv + 1); | |
187 | ||
188 | if (argc >= 2 && !strcmp(argv[1], "quote-echo")) | |
189 | return !!quote_echo(argc - 1, argv + 1); | |
190 | ||
2b541bf8 JS |
191 | if (argc < 3) |
192 | return 1; | |
c553c72e | 193 | proc.argv = (const char **)argv + 2; |
2b541bf8 JS |
194 | |
195 | if (!strcmp(argv[1], "start-command-ENOENT")) { | |
196 | if (start_command(&proc) < 0 && errno == ENOENT) | |
197 | return 0; | |
198 | fprintf(stderr, "FAIL %s\n", argv[1]); | |
199 | return 1; | |
200 | } | |
c0f19bf3 JN |
201 | if (!strcmp(argv[1], "run-command")) |
202 | exit(run_command(&proc)); | |
2b541bf8 | 203 | |
c553c72e SB |
204 | jobs = atoi(argv[2]); |
205 | proc.argv = (const char **)argv + 3; | |
206 | ||
207 | if (!strcmp(argv[1], "run-command-parallel")) | |
208 | exit(run_processes_parallel(jobs, parallel_next, | |
209 | NULL, NULL, &proc)); | |
210 | ||
211 | if (!strcmp(argv[1], "run-command-abort")) | |
212 | exit(run_processes_parallel(jobs, parallel_next, | |
213 | NULL, task_finished, &proc)); | |
214 | ||
215 | if (!strcmp(argv[1], "run-command-no-jobs")) | |
216 | exit(run_processes_parallel(jobs, no_job, | |
217 | NULL, task_finished, &proc)); | |
218 | ||
2b541bf8 JS |
219 | fprintf(stderr, "check usage\n"); |
220 | return 1; | |
221 | } |