1 /*
2 * Copyright © 2020 Valve Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24 #include <map>
25 #include <set>
26 #include <string>
27 #include <vector>
28 #include <stdio.h>
29 #include <string.h>
30 #include <getopt.h>
31 #include <unistd.h>
32 #include <stdarg.h>
33 #include <llvm-c/Target.h>
34 #include "aco_ir.h"
35 #include "framework.h"
36
37 #include "util/u_cpu_detect.h"
38
39 static const char *help_message =
40 "Usage: %s [-h] [-l --list] [--no-check] [TEST [TEST ...]]\n"
41 "\n"
42 "Run ACO unit test(s). If TEST is not provided, all tests are run.\n"
43 "\n"
44 "positional arguments:\n"
45 " TEST Run TEST. If TEST ends with a '.', run tests with names\n"
46 " starting with TEST. The test variant (after the '/') can\n"
47 " be omitted to run all variants\n"
48 "\n"
49 "optional arguments:\n"
50 " -h, --help Show this help message and exit.\n"
51 " -l --list List unit tests.\n"
52 " --no-check Print test output instead of checking it.\n";
53
54 std::map<std::string, TestDef> tests;
55 FILE *output = NULL;
56
57 static TestDef current_test;
58 static unsigned tests_written = 0;
59 static FILE *checker_stdin = NULL;
60 static char *checker_stdin_data = NULL;
61 static size_t checker_stdin_size = 0;
62
63 static char *output_data = NULL;
64 static size_t output_size = 0;
65 static size_t output_offset = 0;
66
67 static char current_variant[64] = {0};
68 static std::set<std::string> *variant_filter = NULL;
69
70 bool test_failed = false;
71 bool test_skipped = false;
72 static char fail_message[256] = {0};
73
write_test()74 void write_test()
75 {
76 if (!checker_stdin) {
77 /* not entirely correct, but shouldn't matter */
78 tests_written++;
79 return;
80 }
81
82 fflush(output);
83 if (output_offset == output_size && !test_skipped && !test_failed)
84 return;
85
86 char *data = output_data + output_offset;
87 uint32_t size = output_size - output_offset;
88
89 fwrite("test", 1, 4, checker_stdin);
90 fwrite(current_test.name, 1, strlen(current_test.name)+1, checker_stdin);
91 fwrite(current_variant, 1, strlen(current_variant)+1, checker_stdin);
92 fwrite(current_test.source_file, 1, strlen(current_test.source_file)+1, checker_stdin);
93 if (test_failed || test_skipped) {
94 const char *res = test_failed ? "failed" : "skipped";
95 fwrite("\x01", 1, 1, checker_stdin);
96 fwrite(res, 1, strlen(res)+1, checker_stdin);
97 fwrite(fail_message, 1, strlen(fail_message)+1, checker_stdin);
98 } else {
99 fwrite("\x00", 1, 1, checker_stdin);
100 }
101 fwrite(&size, 4, 1, checker_stdin);
102 fwrite(data, 1, size, checker_stdin);
103
104 tests_written++;
105 output_offset += size;
106 }
107
set_variant(const char * name)108 bool set_variant(const char *name)
109 {
110 if (variant_filter && !variant_filter->count(name))
111 return false;
112
113 write_test();
114 test_failed = false;
115 test_skipped = false;
116 strncpy(current_variant, name, sizeof(current_variant) - 1);
117
118 printf("Running '%s/%s'\n", current_test.name, name);
119
120 return true;
121 }
122
fail_test(const char * fmt,...)123 void fail_test(const char *fmt, ...)
124 {
125 va_list args;
126 va_start(args, fmt);
127
128 test_failed = true;
129 vsnprintf(fail_message, sizeof(fail_message), fmt, args);
130
131 va_end(args);
132 }
133
skip_test(const char * fmt,...)134 void skip_test(const char *fmt, ...)
135 {
136 va_list args;
137 va_start(args, fmt);
138
139 test_skipped = true;
140 vsnprintf(fail_message, sizeof(fail_message), fmt, args);
141
142 va_end(args);
143 }
144
run_test(TestDef def)145 void run_test(TestDef def)
146 {
147 current_test = def;
148 output_data = NULL;
149 output_size = 0;
150 output_offset = 0;
151 test_failed = false;
152 test_skipped = false;
153 memset(current_variant, 0, sizeof(current_variant));
154
155 if (checker_stdin)
156 output = open_memstream(&output_data, &output_size);
157 else
158 output = stdout;
159
160 current_test.func();
161 write_test();
162
163 if (checker_stdin)
164 fclose(output);
165 free(output_data);
166 }
167
check_output(char ** argv)168 int check_output(char **argv)
169 {
170 fflush(stdout);
171 fflush(stderr);
172
173 fclose(checker_stdin);
174
175 int stdin_pipe[2];
176 pipe(stdin_pipe);
177
178 pid_t child_pid = fork();
179 if (child_pid == -1) {
180 fprintf(stderr, "%s: fork() failed: %s\n", argv[0], strerror(errno));
181 return 99;
182 } else if (child_pid != 0) {
183 /* Evaluate test output externally using Python */
184 dup2(stdin_pipe[0], STDIN_FILENO);
185 close(stdin_pipe[0]);
186 close(stdin_pipe[1]);
187
188 execlp(ACO_TEST_PYTHON_BIN, ACO_TEST_PYTHON_BIN, ACO_TEST_SOURCE_DIR "/check_output.py", NULL);
189 fprintf(stderr, "%s: execlp() failed: %s\n", argv[0], strerror(errno));
190 return 99;
191 } else {
192 /* Feed input data to the Python process. Writing large streams to
193 * stdin will block eventually, so this is done in a forked process
194 * to let the test checker process chunks of data as they arrive */
195 write(stdin_pipe[1], checker_stdin_data, checker_stdin_size);
196 close(stdin_pipe[0]);
197 close(stdin_pipe[1]);
198 _exit(0);
199 }
200 }
201
match_test(std::string name,std::string pattern)202 bool match_test(std::string name, std::string pattern)
203 {
204 if (name.length() < pattern.length())
205 return false;
206 if (pattern.back() == '.')
207 name.resize(pattern.length());
208 return name == pattern;
209 }
210
main(int argc,char ** argv)211 int main(int argc, char **argv)
212 {
213 int print_help = 0;
214 int do_list = 0;
215 int do_check = 1;
216 const struct option opts[] = {
217 { "help", no_argument, &print_help, 1 },
218 { "list", no_argument, &do_list, 1 },
219 { "no-check", no_argument, &do_check, 0 },
220 { NULL, 0, NULL, 0 }
221 };
222
223 int c;
224 while ((c = getopt_long(argc, argv, "hl", opts, NULL)) != -1) {
225 switch (c) {
226 case 'h':
227 print_help = 1;
228 break;
229 case 'l':
230 do_list = 1;
231 break;
232 case 0:
233 break;
234 case '?':
235 default:
236 fprintf(stderr, "%s: Invalid argument\n", argv[0]);
237 return 99;
238 }
239 }
240
241 if (print_help) {
242 fprintf(stderr, help_message, argv[0]);
243 return 99;
244 }
245
246 util_cpu_detect();
247
248 if (do_list) {
249 for (auto test : tests)
250 printf("%s\n", test.first.c_str());
251 return 99;
252 }
253
254 std::vector<std::pair<std::string, std::string>> names;
255 for (int i = optind; i < argc; i++) {
256 std::string name = argv[i];
257 std::string variant;
258 size_t pos = name.find('/');
259 if (pos != std::string::npos) {
260 variant = name.substr(pos + 1);
261 name = name.substr(0, pos);
262 }
263 names.emplace_back(std::pair<std::string, std::string>(name, variant));
264 }
265
266 if (do_check)
267 checker_stdin = open_memstream(&checker_stdin_data, &checker_stdin_size);
268
269 LLVMInitializeAMDGPUTargetInfo();
270 LLVMInitializeAMDGPUTarget();
271 LLVMInitializeAMDGPUTargetMC();
272 LLVMInitializeAMDGPUDisassembler();
273
274 aco::init();
275
276 for (auto pair : tests) {
277 bool found = names.empty();
278 bool all_variants = names.empty();
279 std::set<std::string> variants;
280 for (const std::pair<std::string, std::string>& name : names) {
281 if (match_test(pair.first, name.first)) {
282 found = true;
283 if (name.second.empty())
284 all_variants = true;
285 else
286 variants.insert(name.second);
287 }
288 }
289
290 if (found) {
291 variant_filter = all_variants ? NULL : &variants;
292 printf("Running '%s'\n", pair.first.c_str());
293 run_test(pair.second);
294 }
295 }
296 if (!tests_written) {
297 fprintf(stderr, "%s: No matching tests\n", argv[0]);
298 return 99;
299 }
300
301 if (checker_stdin) {
302 printf("\n");
303 return check_output(argv);
304 } else {
305 printf("Tests ran\n");
306 return 99;
307 }
308 }
309