2 * Copyright 2023 Giovanni Mascellani for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #define TIMEOUT_MS (10 * 1000)
28 #define MAX_TIMEOUT_COUNT 3
32 PROGRAM_RESULT_SUCCESS
,
33 PROGRAM_RESULT_TIMEOUT
,
34 PROGRAM_RESULT_FAILURE
,
37 static enum program_result
run_program(const char *cmdline
, const char *log_filename
)
39 char cmdline2
[1024], log_dirname
[1024], *file_part
;
40 enum program_result ret
= PROGRAM_RESULT_SUCCESS
;
41 HANDLE log
= INVALID_HANDLE_VALUE
;
42 SECURITY_ATTRIBUTES attrs
= {0};
43 PROCESS_INFORMATION info
= {0};
44 DWORD exit_code
, wait_result
;
45 STARTUPINFOA startup
= {0};
48 strcpy(cmdline2
, cmdline
);
50 if (GetFullPathNameA(log_filename
, sizeof(log_dirname
), log_dirname
, &file_part
) == 0)
52 fprintf(stderr
, "Cannot extract the directory name for path %s, last error %ld.\n", log_filename
, GetLastError());
53 ret
= PROGRAM_RESULT_FAILURE
;
58 res
= SHCreateDirectoryExA(NULL
, log_dirname
, NULL
);
59 if (res
!= ERROR_SUCCESS
&& res
!= ERROR_ALREADY_EXISTS
)
61 fprintf(stderr
, "Cannot create log directory %s, error %d.\n", log_dirname
, res
);
62 ret
= PROGRAM_RESULT_FAILURE
;
66 attrs
.nLength
= sizeof(attrs
);
67 attrs
.bInheritHandle
= TRUE
;
69 log
= CreateFileA(log_filename
, GENERIC_WRITE
, 0, &attrs
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
70 if (log
== INVALID_HANDLE_VALUE
)
72 fprintf(stderr
, "Cannot create log file %s, last error %ld.\n", log_filename
, GetLastError());
73 ret
= PROGRAM_RESULT_FAILURE
;
77 startup
.cb
= sizeof(startup
);
78 startup
.dwFlags
= STARTF_USESTDHANDLES
;
79 startup
.hStdInput
= INVALID_HANDLE_VALUE
;
80 startup
.hStdOutput
= log
;
81 startup
.hStdError
= log
;
83 if (!CreateProcessA(NULL
, cmdline2
, NULL
, NULL
, TRUE
, 0, NULL
, NULL
, &startup
, &info
))
85 fprintf(stderr
, "Cannot create process %s, last error %ld.\n", cmdline2
, GetLastError());
86 ret
= PROGRAM_RESULT_FAILURE
;
90 wait_result
= WaitForSingleObject(info
.hProcess
, TIMEOUT_MS
);
91 if (wait_result
== WAIT_TIMEOUT
)
93 fprintf(stderr
, "Process timed out, terminating it.\n");
94 ret
= PROGRAM_RESULT_TIMEOUT
;
96 if (!TerminateProcess(info
.hProcess
, 1))
98 fprintf(stderr
, "Cannot terminate process, last error %ld.\n", GetLastError());
102 wait_result
= WaitForSingleObject(info
.hProcess
, INFINITE
);
105 if (wait_result
!= WAIT_OBJECT_0
)
107 fprintf(stderr
, "Cannot wait for process termination, last error %ld.\n", GetLastError());
108 ret
= PROGRAM_RESULT_FAILURE
;
112 if (!GetExitCodeProcess(info
.hProcess
, &exit_code
))
114 fprintf(stderr
, "Cannot retrieve the process exit code, last error %ld.\n", GetLastError());
115 ret
= PROGRAM_RESULT_FAILURE
;
119 ret
= exit_code
== 0 ? PROGRAM_RESULT_SUCCESS
: PROGRAM_RESULT_FAILURE
;
121 printf("%s: %s\n", ret
== PROGRAM_RESULT_SUCCESS
? "PASS" : "FAIL", cmdline
);
124 if (info
.hProcess
&& !CloseHandle(info
.hProcess
))
125 fprintf(stderr
, "Cannot close process, last error %ld.\n", GetLastError());
126 if (info
.hThread
&& !CloseHandle(info
.hThread
))
127 fprintf(stderr
, "Cannot close thread, last error %ld.\n", GetLastError());
128 if (log
!= INVALID_HANDLE_VALUE
&& !CloseHandle(log
))
129 fprintf(stderr
, "Cannot close log file, last error %ld.\n", GetLastError());
134 static bool run_tests_for_directory(const char *commit_dir
)
136 char cmdline
[1024], log_filename
[1024], list_filename
[1024], line
[1024];
137 unsigned int success_count
= 0, test_count
= 0, timeout_count
= 0;
138 const char *test_arch
= getenv("TEST_ARCH");
139 enum program_result result
;
146 printf("\e[0Ksection_start:%I64d:commit_%s\r\e[0KBuilding commit %s\n",
147 (uint64_t)time(NULL
), commit_dir
, commit_dir
);
149 sprintf(list_filename
, "artifacts/%s/tests/shader_tests.txt", commit_dir
);
150 list_file
= fopen(list_filename
, "r");
154 fprintf(stderr
, "Cannot open list file %s, errno %d.\n", list_filename
, errno
);
159 while (fgets(line
, sizeof(line
), list_file
) && timeout_count
< MAX_TIMEOUT_COUNT
)
161 size_t len
= strlen(line
);
163 if (line
[len
- 1] == '\n')
166 sprintf(cmdline
, "artifacts/%s/tests/shader_runner.cross%s.exe %s", commit_dir
, test_arch
, line
);
168 /* Remove the .shader_test suffix. */
169 line
[len
- 12] = '\0';
170 sprintf(log_filename
, "artifacts/%s/%s.log", commit_dir
, line
);
173 result
= run_program(cmdline
, log_filename
);
174 success_count
+= result
== PROGRAM_RESULT_SUCCESS
;
175 timeout_count
+= result
== PROGRAM_RESULT_TIMEOUT
;
181 sprintf(list_filename
, "artifacts/%s/tests/crosstests.txt", commit_dir
);
182 list_file
= fopen(list_filename
, "r");
186 fprintf(stderr
, "Cannot open list file %s, errno %d.\n", list_filename
, errno
);
191 while (fgets(line
, sizeof(line
), list_file
) && timeout_count
< MAX_TIMEOUT_COUNT
)
193 size_t len
= strlen(line
);
195 if (line
[len
- 1] == '\n')
196 line
[len
- 1] = '\0';
198 sprintf(cmdline
, "artifacts/%s/%s.cross%s.exe", commit_dir
, line
, test_arch
);
199 sprintf(log_filename
, "artifacts/%s/%s.log", commit_dir
, line
);
201 result
= run_program(cmdline
, log_filename
);
202 success_count
+= result
== PROGRAM_RESULT_SUCCESS
;
203 timeout_count
+= result
== PROGRAM_RESULT_TIMEOUT
;
209 if (timeout_count
>= MAX_TIMEOUT_COUNT
)
210 fprintf(stderr
, "Too many timeouts, aborting tests.\n");
216 printf("# TOTAL: %u\n", test_count
);
217 printf("# PASS: %u\n", success_count
);
218 printf("# FAIL: %u\n", test_count
- success_count
);
220 if (test_count
!= success_count
)
224 handle
= CreateFileA("pipeline_failed", GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
225 if (handle
== INVALID_HANDLE_VALUE
)
227 fprintf(stderr
, "Cannot create failure file, last error %ld.\n", GetLastError());
232 if (!CloseHandle(handle
))
233 fprintf(stderr
, "Cannot close failure file, last error %ld.\n", GetLastError());
237 printf("\e[0Ksection_end:%I64d:commit_%s\r\e[0K\n",
238 (uint64_t)time(NULL
), commit_dir
);
243 int wmain(int argc
, WCHAR
**wargv
)
245 char commit_num
[16], commit_hash
[16], commit_dir
[16];
247 SetErrorMode(SEM_FAILCRITICALERRORS
| SEM_NOGPFAULTERRORBOX
| SEM_NOOPENFILEERRORBOX
);
251 fprintf(stderr
, "Call with commit number and hash.\n");
255 WideCharToMultiByte(CP_ACP
, 0, wargv
[1], -1, commit_num
, sizeof(commit_num
), NULL
, NULL
);
256 WideCharToMultiByte(CP_ACP
, 0, wargv
[2], -1, commit_hash
, sizeof(commit_hash
), NULL
, NULL
);
257 commit_num
[sizeof(commit_num
) - 1] = '\0';
258 commit_hash
[sizeof(commit_hash
) - 1] = '\0';
259 snprintf(commit_dir
, sizeof(commit_dir
), "%03d-%s", atoi(commit_num
), commit_hash
);
260 commit_dir
[sizeof(commit_dir
) - 1] = '\0';
262 return !run_tests_for_directory(commit_dir
);