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 static const char *help_message =
38    "Usage: %s [-h] [-l --list] [--no-check] [TEST [TEST ...]]\n"
39    "\n"
40    "Run ACO unit test(s). If TEST is not provided, all tests are run.\n"
41    "\n"
42    "positional arguments:\n"
43    "  TEST        Run TEST. If TEST ends with a '.', run tests with names\n"
44    "              starting with TEST. The test variant (after the '/') can\n"
45    "              be omitted to run all variants\n"
46    "\n"
47    "optional arguments:\n"
48    "  -h, --help  Show this help message and exit.\n"
49    "  -l --list   List unit tests.\n"
50    "  --no-check  Print test output instead of checking it.\n";
51 
52 std::map<std::string, TestDef> tests;
53 FILE *output = NULL;
54 
55 static TestDef current_test;
56 static unsigned tests_written = 0;
57 static FILE *checker_stdin = NULL;
58 static char *checker_stdin_data = NULL;
59 static size_t checker_stdin_size = 0;
60 
61 static char *output_data = NULL;
62 static size_t output_size = 0;
63 static size_t output_offset = 0;
64 
65 static char current_variant[64] = {0};
66 static std::set<std::string> *variant_filter = NULL;
67 
68 bool test_failed = false;
69 bool test_skipped = false;
70 static char fail_message[256] = {0};
71 
write_test()72 void write_test()
73 {
74    if (!checker_stdin) {
75       /* not entirely correct, but shouldn't matter */
76       tests_written++;
77       return;
78    }
79 
80    fflush(output);
81    if (output_offset == output_size && !test_skipped && !test_failed)
82       return;
83 
84    char *data = output_data + output_offset;
85    uint32_t size = output_size - output_offset;
86 
87    fwrite("test", 1, 4, checker_stdin);
88    fwrite(current_test.name, 1, strlen(current_test.name)+1, checker_stdin);
89    fwrite(current_variant, 1, strlen(current_variant)+1, checker_stdin);
90    fwrite(current_test.source_file, 1, strlen(current_test.source_file)+1, checker_stdin);
91    if (test_failed || test_skipped) {
92       const char *res = test_failed ? "failed" : "skipped";
93       fwrite("\x01", 1, 1, checker_stdin);
94       fwrite(res, 1, strlen(res)+1, checker_stdin);
95       fwrite(fail_message, 1, strlen(fail_message)+1, checker_stdin);
96    } else {
97       fwrite("\x00", 1, 1, checker_stdin);
98    }
99    fwrite(&size, 4, 1, checker_stdin);
100    fwrite(data, 1, size, checker_stdin);
101 
102    tests_written++;
103    output_offset += size;
104 }
105 
set_variant(const char * name)106 bool set_variant(const char *name)
107 {
108    if (variant_filter && !variant_filter->count(name))
109       return false;
110 
111    write_test();
112    test_failed = false;
113    test_skipped = false;
114    strncpy(current_variant, name, sizeof(current_variant) - 1);
115 
116    printf("Running '%s/%s'\n", current_test.name, name);
117 
118    return true;
119 }
120 
fail_test(const char * fmt,...)121 void fail_test(const char *fmt, ...)
122 {
123    va_list args;
124    va_start(args, fmt);
125 
126    test_failed = true;
127    vsnprintf(fail_message, sizeof(fail_message), fmt, args);
128 
129    va_end(args);
130 }
131 
skip_test(const char * fmt,...)132 void skip_test(const char *fmt, ...)
133 {
134    va_list args;
135    va_start(args, fmt);
136 
137    test_skipped = true;
138    vsnprintf(fail_message, sizeof(fail_message), fmt, args);
139 
140    va_end(args);
141 }
142 
run_test(TestDef def)143 void run_test(TestDef def)
144 {
145    current_test = def;
146    output_data = NULL;
147    output_size = 0;
148    output_offset = 0;
149    test_failed = false;
150    test_skipped = false;
151    memset(current_variant, 0, sizeof(current_variant));
152 
153    if (checker_stdin)
154       output = open_memstream(&output_data, &output_size);
155    else
156       output = stdout;
157 
158    current_test.func();
159    write_test();
160 
161    if (checker_stdin)
162       fclose(output);
163    free(output_data);
164 }
165 
check_output(char ** argv)166 int check_output(char **argv)
167 {
168    fflush(stdout);
169    fflush(stderr);
170 
171    fclose(checker_stdin);
172 
173    int stdin_pipe[2];
174    pipe(stdin_pipe);
175 
176    write(stdin_pipe[1], checker_stdin_data, checker_stdin_size);
177    close(stdin_pipe[1]);
178    dup2(stdin_pipe[0], STDIN_FILENO);
179 
180    execlp(ACO_TEST_PYTHON_BIN, ACO_TEST_PYTHON_BIN, ACO_TEST_SOURCE_DIR "/check_output.py", NULL);
181 
182    fprintf(stderr, "%s: execl() failed: %s\n", argv[0], strerror(errno));
183    return 99;
184 }
185 
match_test(std::string name,std::string pattern)186 bool match_test(std::string name, std::string pattern)
187 {
188    if (name.length() < pattern.length())
189       return false;
190    if (pattern.back() == '.')
191       name.resize(pattern.length());
192    return name == pattern;
193 }
194 
main(int argc,char ** argv)195 int main(int argc, char **argv)
196 {
197    int print_help = 0;
198    int do_list = 0;
199    int do_check = 1;
200    const struct option opts[] = {
201       { "help",     no_argument, &print_help, 1 },
202       { "list",     no_argument, &do_list,    1 },
203       { "no-check", no_argument, &do_check,   0 },
204       { NULL,       0,           NULL,        0 }
205    };
206 
207    int c;
208    while ((c = getopt_long(argc, argv, "hl", opts, NULL)) != -1) {
209       switch (c) {
210       case 'h':
211          print_help = 1;
212          break;
213       case 'l':
214          do_list = 1;
215          break;
216       case 0:
217          break;
218       case '?':
219       default:
220          fprintf(stderr, "%s: Invalid argument\n", argv[0]);
221          return 99;
222       }
223    }
224 
225    if (print_help) {
226       fprintf(stderr, help_message, argv[0]);
227       return 99;
228    }
229 
230    if (do_list) {
231       for (auto test : tests)
232          printf("%s\n", test.first.c_str());
233       return 99;
234    }
235 
236    std::vector<std::pair<std::string, std::string>> names;
237    for (int i = optind; i < argc; i++) {
238       std::string name = argv[i];
239       std::string variant;
240       size_t pos = name.find('/');
241       if (pos != std::string::npos) {
242          variant = name.substr(pos + 1);
243          name = name.substr(0, pos);
244       }
245       names.emplace_back(std::pair<std::string, std::string>(name, variant));
246    }
247 
248    if (do_check)
249       checker_stdin = open_memstream(&checker_stdin_data, &checker_stdin_size);
250 
251 	LLVMInitializeAMDGPUTargetInfo();
252 	LLVMInitializeAMDGPUTarget();
253 	LLVMInitializeAMDGPUTargetMC();
254 	LLVMInitializeAMDGPUDisassembler();
255 
256    aco::init();
257 
258    for (auto pair : tests) {
259       bool found = names.empty();
260       bool all_variants = names.empty();
261       std::set<std::string> variants;
262       for (const std::pair<std::string, std::string>& name : names) {
263          if (match_test(pair.first, name.first)) {
264             found = true;
265             if (name.second.empty())
266                all_variants = true;
267             else
268                variants.insert(name.second);
269          }
270       }
271 
272       if (found) {
273          variant_filter = all_variants ? NULL : &variants;
274          printf("Running '%s'\n", pair.first.c_str());
275          run_test(pair.second);
276       }
277    }
278    if (!tests_written) {
279       fprintf(stderr, "%s: No matching tests\n", argv[0]);
280       return 99;
281    }
282 
283    if (checker_stdin) {
284       printf("\n");
285       return check_output(argv);
286    } else {
287       printf("Tests ran\n");
288       return 99;
289    }
290 }
291