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