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