Raw
1 #include <stdarg.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/stat.h>
5
6 #include "selftest.h"
7
8 #ifdef _WIN32
9 # define WIN32_LEAN_AND_MEAN
10 # include <windows.h>
11
12 static char *read_full(HANDLE h, int is_pipe)
13 {
14 char *data = NULL;
15 size_t data_size = 0;
16
17 while (1) {
18 CHAR buf[4096];
19 DWORD bytes_read;
20
21 if (!ReadFile(h, buf, sizeof(buf), &bytes_read, NULL)) {
22 if (!is_pipe)
23 cl_fail("Failed reading file handle.");
24 cl_assert_equal_i(GetLastError(), ERROR_BROKEN_PIPE);
25 break;
26 }
27 if (!bytes_read)
28 break;
29
30 data = realloc(data, data_size + bytes_read);
31 cl_assert(data);
32 memcpy(data + data_size, buf, bytes_read);
33 data_size += bytes_read;
34 }
35
36 data = realloc(data, data_size + 1);
37 cl_assert(data);
38 data[data_size] = '\0';
39
40 while (strstr(data, "\r\n")) {
41 char *ptr = strstr(data, "\r\n");
42 memmove(ptr, ptr + 1, strlen(ptr));
43 }
44
45 return data;
46 }
47
48 static char *read_file(const char *path)
49 {
50 char *content;
51 HANDLE file;
52
53 file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL,
54 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
55 cl_assert(file != INVALID_HANDLE_VALUE);
56 content = read_full(file, 0);
57 cl_assert_equal_b(1, CloseHandle(file));
58
59 return content;
60 }
61
62 static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs)
63 {
64 SECURITY_ATTRIBUTES security_attributes = { 0 };
65 PROCESS_INFORMATION process_info = { 0 };
66 STARTUPINFO startup_info = { 0 };
67 char binary_path[4096] = { 0 };
68 char cmdline[4096] = { 0 };
69 char *output = NULL;
70 HANDLE stdout_write;
71 HANDLE stdout_read;
72 DWORD exit_code;
73 size_t i;
74
75 snprintf(binary_path, sizeof(binary_path), "%s/%s_suite.exe",
76 selftest_suite_directory, suite);
77
78 /*
79 * Assemble command line arguments. In theory we'd have to properly
80 * quote them. In practice none of our tests actually care.
81 */
82 snprintf(cmdline, sizeof(cmdline), suite);
83 for (i = 0; i < nargs; i++) {
84 size_t cmdline_len = strlen(cmdline);
85 const char *arg = args[i];
86 cl_assert(cmdline_len + strlen(arg) < sizeof(cmdline));
87 snprintf(cmdline + cmdline_len, sizeof(cmdline) - cmdline_len,
88 " %s", arg);
89 }
90
91 /*
92 * Create a pipe that we will use to read data from the child process.
93 * The writing side needs to be inheritable such that the child can use
94 * it as stdout and stderr. The reading side should only be used by the
95 * parent.
96 */
97 security_attributes.nLength = sizeof(security_attributes);
98 security_attributes.bInheritHandle = TRUE;
99 cl_assert_equal_b(1, CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0));
100 cl_assert_equal_b(1, SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0));
101
102 /*
103 * Create the child process with our pipe.
104 */
105 startup_info.cb = sizeof(startup_info);
106 startup_info.hStdError = stdout_write;
107 startup_info.hStdOutput = stdout_write;
108 startup_info.dwFlags |= STARTF_USESTDHANDLES;
109 cl_assert_equal_b(1, CreateProcess(binary_path, cmdline, NULL, NULL, TRUE,
110 0, NULL, NULL, &startup_info, &process_info));
111 cl_assert_equal_b(1, CloseHandle(stdout_write));
112
113 output = read_full(stdout_read, 1);
114 cl_assert_equal_b(1, CloseHandle(stdout_read));
115 cl_assert_equal_b(1, GetExitCodeProcess(process_info.hProcess, &exit_code));
116 cl_assert_equal_i(exit_code, expected_error_code);
117
118 return output;
119 }
120
121 static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...)
122 {
123 char *expected_output = NULL;
124 char *output = NULL;
125 const char *args[16];
126 va_list ap;
127 size_t i;
128
129 va_start(ap, expected_error_code);
130 for (i = 0; ; i++) {
131 const char *arg = va_arg(ap, const char *);
132 if (!arg)
133 break;
134 cl_assert(i < sizeof(args) / sizeof(*args));
135 args[i] = arg;
136 }
137 va_end(ap);
138
139 output = execute(suite, expected_error_code, args, i);
140 expected_output = read_file(cl_fixture(expected_output_file));
141 cl_assert_equal_s(output, expected_output);
142
143 free(expected_output);
144 free(output);
145 }
146
147 #else
148 # include <errno.h>
149 # include <fcntl.h>
150 # include <limits.h>
151 # include <unistd.h>
152 # include <sys/wait.h>
153
154 static char *read_full(int fd)
155 {
156 size_t data_bytes = 0;
157 char *data = NULL;
158
159 while (1) {
160 char buf[4096];
161 ssize_t n;
162
163 n = read(fd, buf, sizeof(buf));
164 if (n < 0) {
165 if (errno == EAGAIN || errno == EINTR)
166 continue;
167 cl_fail("Failed reading from child process.");
168 }
169 if (!n)
170 break;
171
172 data = realloc(data, data_bytes + n);
173 cl_assert(data);
174
175 memcpy(data + data_bytes, buf, n);
176 data_bytes += n;
177 }
178
179 data = realloc(data, data_bytes + 1);
180 cl_assert(data);
181 data[data_bytes] = '\0';
182
183 return data;
184 }
185
186 static char *read_file(const char *path)
187 {
188 char *data;
189 int fd;
190
191 fd = open(path, O_RDONLY);
192 if (fd < 0)
193 cl_fail("Failed reading expected file.");
194
195 data = read_full(fd);
196 cl_must_pass(close(fd));
197
198 return data;
199 }
200
201 static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs)
202 {
203 int pipe_fds[2];
204 pid_t pid;
205
206 cl_must_pass(pipe(pipe_fds));
207
208 pid = fork();
209 if (!pid) {
210 const char *final_args[17] = { NULL };
211 char binary_path[4096];
212 size_t len = 0;
213 size_t i;
214
215 cl_assert(nargs < sizeof(final_args) / sizeof(*final_args));
216 final_args[0] = suite;
217 for (i = 0; i < nargs; i++)
218 final_args[i + 1] = args[i];
219
220 if (dup2(pipe_fds[1], STDOUT_FILENO) < 0 ||
221 dup2(pipe_fds[1], STDERR_FILENO) < 0 ||
222 close(0) < 0 ||
223 close(pipe_fds[0]) < 0 ||
224 close(pipe_fds[1]) < 0)
225 exit(1);
226
227 cl_assert(len + strlen(selftest_suite_directory) < sizeof(binary_path));
228 strcpy(binary_path, selftest_suite_directory);
229 len += strlen(selftest_suite_directory);
230
231 cl_assert(len + 1 < sizeof(binary_path));
232 binary_path[len] = '/';
233 len += 1;
234
235 cl_assert(len + strlen(suite) < sizeof(binary_path));
236 strcpy(binary_path + len, suite);
237 len += strlen(suite);
238
239 cl_assert(len + strlen("_suite") < sizeof(binary_path));
240 strcpy(binary_path + len, "_suite");
241 len += strlen("_suite");
242
243 binary_path[len] = '\0';
244
245 execv(binary_path, (char **) final_args);
246 exit(1);
247 } else if (pid > 0) {
248 pid_t waited_pid;
249 char *output;
250 int stat;
251
252 cl_must_pass(close(pipe_fds[1]));
253
254 output = read_full(pipe_fds[0]);
255
256 waited_pid = waitpid(pid, &stat, 0);
257 cl_assert_equal_i(pid, waited_pid);
258 cl_assert(WIFEXITED(stat));
259 cl_assert_equal_i(WEXITSTATUS(stat), expected_error_code);
260
261 return output;
262 } else {
263 cl_fail("Fork failed.");
264 }
265
266 return NULL;
267 }
268
269 static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...)
270 {
271 char *expected_output, *output;
272 const char *args[16];
273 va_list ap;
274 size_t i;
275
276 va_start(ap, expected_error_code);
277 for (i = 0; ; i++) {
278 cl_assert(i < sizeof(args) / sizeof(*args));
279 args[i] = va_arg(ap, const char *);
280 if (!args[i])
281 break;
282 }
283 va_end(ap);
284
285 output = execute(suite, expected_error_code, args, i);
286 expected_output = read_file(cl_fixture(expected_output_file));
287 cl_assert_equal_s(output, expected_output);
288
289 free(expected_output);
290 free(output);
291 }
292 #endif
293
294 void test_selftest__help(void)
295 {
296 cl_invoke(assert_output("combined", "help", 1, "-h", NULL));
297 }
298
299 void test_selftest__without_arguments(void)
300 {
301 cl_invoke(assert_output("combined", "without_arguments", 15, NULL));
302 }
303
304 void test_selftest__specific_test(void)
305 {
306 cl_invoke(assert_output("combined", "specific_test", 1, "-scombined::bool", NULL));
307 }
308
309 void test_selftest__stop_on_failure(void)
310 {
311 cl_invoke(assert_output("combined", "stop_on_failure", 1, "-Q", NULL));
312 }
313
314 void test_selftest__quiet(void)
315 {
316 cl_invoke(assert_output("combined", "quiet", 15, "-q", NULL));
317 }
318
319 void test_selftest__tap(void)
320 {
321 cl_invoke(assert_output("combined", "tap", 15, "-t", NULL));
322 }
323
324 void test_selftest__suite_names(void)
325 {
326 cl_invoke(assert_output("combined", "suite_names", 0, "-l", NULL));
327 }
328
329 void test_selftest__summary_without_filename(void)
330 {
331 struct stat st;
332 cl_invoke(assert_output("combined", "summary_without_filename", 15, "-r", NULL));
333 /* The summary contains timestamps, so we cannot verify its contents. */
334 cl_must_pass(stat("summary.xml", &st));
335 }
336
337 void test_selftest__summary_with_filename(void)
338 {
339 struct stat st;
340 cl_invoke(assert_output("combined", "summary_with_filename", 15, "-rdifferent.xml", NULL));
341 /* The summary contains timestamps, so we cannot verify its contents. */
342 cl_must_pass(stat("different.xml", &st));
343 }
344
345 void test_selftest__pointer_equal(void)
346 {
347 const char *args[] = {
348 "-spointer::equal",
349 "-t"
350 };
351 char *output = execute("pointer", 0, args, 2);
352 cl_assert_equal_s(output,
353 "TAP version 13\n"
354 "# start of suite 1: pointer\n"
355 "ok 1 - pointer::equal\n"
356 "1..1\n"
357 );
358 free(output);
359 }
360
361 void test_selftest__pointer_unequal(void)
362 {
363 const char *args[] = {
364 "-spointer::unequal",
365 };
366 char *output = execute("pointer", 1, args, 1);
367 cl_assert(output);
368 cl_assert(strstr(output, "Pointer mismatch: "));
369 free(output);
370 }