Rizin
unix-like reverse engineering framework and cli tools
rz-test.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2020-2021 thestr4ng3r <info@florianmaerkl.de>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include "rz_test.h"
5 #include <assert.h>
6 #include <rz_cons.h>
7 #include <rz_main.h>
8 #include <rz_windows.h>
9 
10 #define Color_INSERT Color_BGREEN
11 #define Color_DELETE Color_BRED
12 #define Color_BGINSERT "\x1b[48;5;22m"
13 #define Color_BGDELETE "\x1b[48;5;52m"
14 #define Color_HLINSERT Color_BGINSERT Color_INSERT
15 #define Color_HLDELETE Color_BGDELETE Color_DELETE
16 
17 #define WORKERS_DEFAULT 8
18 #define RIZIN_CMD_DEFAULT "rizin"
19 #define RZ_ASM_CMD_DEFAULT "rz-asm"
20 #define JSON_TEST_FILE_DEFAULT "bins/elf/crackme0x00b"
21 #define TIMEOUT_DEFAULT 960
22 
23 #define STRV(x) #x
24 #define STR(x) STRV(x)
25 #define WORKERS_DEFAULT_STR STR(WORKERS_DEFAULT)
26 #define TIMEOUT_DEFAULT_STR STR(TIMEOUT_DEFAULT)
27 
28 typedef struct rz_testfile_counts_t {
29  ut64 tests_left; // count of remaining tests
35 
36 typedef struct rz_test_state_t {
38  bool verbose;
41 
42  RzThreadCond *cond; // signaled from workers to main thread to update status
43  RzThreadLock *lock; // protects everything below
44  HtPP *path_left; // char * (path to test file) => RzTestFileCounts *
53 
54 static void *worker_th(RzTestState *state);
55 static void print_state(RzTestState *state, ut64 prev_completed);
56 static void print_log(RzTestState *state, ut64 prev_completed, ut64 prev_paths_completed);
57 static void interact(RzTestState *state);
58 static bool interact_fix(RzTestResultInfo *result, RzPVector *fixup_results);
59 static void interact_break(RzTestResultInfo *result, RzPVector *fixup_results);
60 static void interact_commands(RzTestResultInfo *result, RzPVector *fixup_results);
61 
62 static int help(bool verbose) {
63  printf("Usage: rz-test [-qvVnL] [-j threads] [test file/dir | @test-type]\n");
64  if (verbose) {
65  printf(
66  " -h print this help\n"
67  " -v show version\n"
68  " -q quiet\n"
69  " -V verbose\n"
70  " -i interactive mode\n"
71  " -n do nothing (don't run any test, just load/parse them)\n"
72  " -L log mode (better printing for CI, logfiles, etc.)\n"
73  " -F [dir] run fuzz tests (open and default analysis) on all files in the given dir\n"
74  " -j [threads] how many threads to use for running tests concurrently (default is " WORKERS_DEFAULT_STR ")\n"
75  " -r [rizin] path to rizin executable (default is " RIZIN_CMD_DEFAULT ")\n"
76  " -m [rz-asm] path to rz-asm executable (default is " RZ_ASM_CMD_DEFAULT ")\n"
77  " -f [file] file to use for json tests (default is " JSON_TEST_FILE_DEFAULT ")\n"
78  " -C [dir] chdir before running rz-test (default follows executable symlink + test/new\n"
79  " -t [seconds] timeout per test (default is " TIMEOUT_DEFAULT_STR ")\n"
80  " -o [file] output test run information in JSON format to file\n"
81  " -e [dir] exclude a particular directory while testing (this option can appear many times)\n"
82  " -s [num] number of expected successful tests\n"
83  " -x [num] number of expected failed tests"
84  "\n"
85  "Supported test types: @json @unit @fuzz @cmds\n"
86  "OS/Arch for archos tests: " RZ_TEST_ARCH_OS "\n");
87  }
88  return 1;
89 }
90 
91 static void path_left_free_kv(HtPPKv *kv) {
92  free(kv->key);
93  free(kv->value);
94 }
95 
96 static bool rz_test_chdir(const char *argv0) {
97 #if __UNIX__
98  if (rz_file_is_directory("db")) {
99  return true;
100  }
101  char src_path[PATH_MAX];
102  char *rz_test_path = rz_file_path(argv0);
103  bool found = false;
104 
105  ssize_t linklen = readlink(rz_test_path, src_path, sizeof(src_path) - 1);
106  if (linklen != -1) {
107  src_path[linklen] = '\0';
108  char *p = strstr(src_path, RZ_SYS_DIR "binrz" RZ_SYS_DIR "rz-test" RZ_SYS_DIR "rz-test");
109  if (p) {
110  *p = 0;
111  strcat(src_path, RZ_SYS_DIR "test" RZ_SYS_DIR);
112  if (rz_file_is_directory(src_path)) {
113  if (chdir(src_path) != -1) {
114  eprintf("Running from %s\n", src_path);
115  found = true;
116  } else {
117  eprintf("Cannot find '%s' directory\n", src_path);
118  }
119  }
120  }
121  } else {
122  eprintf("Cannot follow the link %s\n", src_path);
123  }
124  free(rz_test_path);
125  return found;
126 #else
127  return false;
128 #endif
129 }
130 
131 static bool rz_test_test_run_unit(void) {
132  return rz_sys_system("make -C unit all run") == 0;
133 }
134 
135 static bool rz_test_chdir_fromtest(const char *test_path) {
136  if (!test_path || *test_path == '@') {
137  test_path = "";
138  }
139  char *abs_test_path = rz_file_abspath(test_path);
140  if (!rz_file_is_directory(abs_test_path)) {
141  char *last_slash = (char *)rz_str_lchr(abs_test_path, RZ_SYS_DIR[0]);
142  if (last_slash) {
143  *last_slash = 0;
144  }
145  }
146  if (chdir(abs_test_path) == -1) {
147  free(abs_test_path);
148  return false;
149  }
150  free(abs_test_path);
151  bool found = false;
152  char *cwd = NULL;
153  char *old_cwd = NULL;
154  while (true) {
155  cwd = rz_sys_getdir();
156  if (old_cwd && !strcmp(old_cwd, cwd)) {
157  break;
158  }
159  if (rz_file_is_directory("test")) {
160  rz_sys_chdir("test");
161  if (rz_file_is_directory("db")) {
162  found = true;
163  eprintf("Running from %s\n", cwd);
164  break;
165  }
166  rz_sys_chdir("..");
167  }
168  if (rz_file_is_directory("db")) {
169  found = true;
170  eprintf("Running from %s\n", cwd);
171  break;
172  }
173  free(old_cwd);
174  old_cwd = cwd;
175  cwd = NULL;
176  if (chdir("..") == -1) {
177  break;
178  }
179  }
180  free(old_cwd);
181  free(cwd);
182  return found;
183 }
184 
185 static bool log_mode = false;
186 
187 int rz_test_main(int argc, const char **argv) {
188  int workers_count = WORKERS_DEFAULT;
189  bool verbose = false;
190  bool nothing = false;
191  bool quiet = false;
192  bool interactive = false;
193  char *rizin_cmd = NULL;
194  char *rz_asm_cmd = NULL;
195  char *json_test_file = NULL;
196  char *output_file = NULL;
197  char *fuzz_dir = NULL;
198  RzPVector *except_dir = rz_pvector_new(free);
199  const char *rz_test_dir = NULL;
200  ut64 timeout_sec = TIMEOUT_DEFAULT;
201  st64 expect_succ = -1;
202  st64 expect_fail = -1;
203  int ret = 0;
204 
205  if (!except_dir) {
206  RZ_LOG_ERROR("Fail to create RzPVector\n");
207  ret = -1;
208  goto beach;
209  }
210 
211 #if __WINDOWS__
212  UINT old_cp = GetConsoleOutputCP();
213  {
214  HANDLE streams[] = { GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_ERROR_HANDLE) };
215  DWORD mode;
216  int i;
217  for (i = 0; i < RZ_ARRAY_SIZE(streams); i++) {
218  GetConsoleMode(streams[i], &mode);
219  SetConsoleMode(streams[i],
220  mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
221  }
222  }
223 #endif
224 
225  RzGetopt opt;
226  rz_getopt_init(&opt, argc, (const char **)argv, "hqvj:r:m:f:C:LnVt:F:io:e:s:x:");
227 
228  int c;
229  while ((c = rz_getopt_next(&opt)) != -1) {
230  switch (c) {
231  case 'h':
232  ret = help(true);
233  goto beach;
234  case 'q':
235  quiet = true;
236  break;
237  case 'v':
238  if (quiet) {
239  printf(RZ_VERSION "\n");
240  } else {
241  char *s = rz_str_version("rz-test");
242  printf("%s\n", s);
243  free(s);
244  }
245  ret = 0;
246  goto beach;
247  case 'V':
248  verbose = true;
249  break;
250  case 'i':
251  interactive = true;
252  break;
253  case 'L':
254  log_mode = true;
255  break;
256  case 'F':
257  free(fuzz_dir);
258  fuzz_dir = strdup(opt.arg);
259  break;
260  case 'j':
261  workers_count = atoi(opt.arg);
262  if (workers_count <= 0) {
263  eprintf("Invalid thread count\n");
264  ret = help(false);
265  goto beach;
266  }
267  break;
268  case 'r':
269  free(rizin_cmd);
270  rizin_cmd = strdup(opt.arg);
271  break;
272  case 'C':
273  rz_test_dir = opt.arg;
274  break;
275  case 'n':
276  nothing = true;
277  break;
278  case 'm':
279  free(rz_asm_cmd);
280  rz_asm_cmd = strdup(opt.arg);
281  break;
282  case 'f':
283  free(json_test_file);
284  json_test_file = strdup(opt.arg);
285  break;
286  case 't':
287  timeout_sec = strtoull(opt.arg, NULL, 0);
288  if (!timeout_sec) {
289  timeout_sec = UT64_MAX;
290  }
291  break;
292  case 'o':
293  free(output_file);
294  output_file = strdup(opt.arg);
295  break;
296  case 'e':
297  rz_pvector_push(except_dir, strdup(opt.arg));
298  break;
299  case 's':
300  // rz_num_math returns 0 for both '0' and invalid str
301  expect_succ = rz_num_math(NULL, opt.arg);
302  if (!rz_num_is_valid_input(NULL, opt.arg) || expect_succ < 0) {
303  RZ_LOG_ERROR("Number of expected successful tests is invalid\n");
304  goto beach;
305  }
306  break;
307  case 'x':
308  expect_fail = rz_num_math(NULL, opt.arg);
309  if (!rz_num_is_valid_input(NULL, opt.arg) || expect_fail < 0) {
310  RZ_LOG_ERROR("Number of expected failed tests is invalid\n");
311  goto beach;
312  }
313  break;
314  default:
315  ret = help(false);
316  goto beach;
317  }
318  }
319 
320  char *cwd = rz_sys_getdir();
321  if (rz_test_dir) {
322  if (chdir(rz_test_dir) == -1) {
323  eprintf("Cannot find %s directory.\n", rz_test_dir);
324  ret = -1;
325  goto beach;
326  }
327  } else {
328  bool dir_found = (opt.ind < argc && argv[opt.ind][0] != '.')
330  : rz_test_chdir(argv[0]);
331  if (!dir_found) {
332  eprintf("Cannot find db/ directory related to the given test.\n");
333  ret = -1;
334  goto beach;
335  }
336  }
337 
338  if (fuzz_dir) {
339  char *tmp = fuzz_dir;
340  fuzz_dir = rz_file_abspath_rel(cwd, fuzz_dir);
341  free(tmp);
342  }
343 
344  if (!rz_subprocess_init()) {
345  eprintf("Subprocess init failed\n");
346  ret = -1;
347  goto beach;
348  }
349  atexit(rz_subprocess_fini);
350 
351  rz_sys_setenv("TZ", "UTC");
352  ut64 time_start = rz_time_now_mono();
353  RzTestState state = { 0 };
354  // Avoid PATH search for each process launched
355  if (!rizin_cmd) {
356  rizin_cmd = rz_file_path(RIZIN_CMD_DEFAULT);
357  }
358  if (!rz_asm_cmd) {
359  rz_asm_cmd = rz_file_path(RZ_ASM_CMD_DEFAULT);
360  }
361  state.run_config.rz_cmd = rizin_cmd;
362  state.run_config.rz_asm_cmd = rz_asm_cmd;
363  state.run_config.json_test_file = json_test_file ? json_test_file : JSON_TEST_FILE_DEFAULT;
364  state.run_config.timeout_ms = timeout_sec > UT64_MAX / 1000 ? UT64_MAX : timeout_sec * 1000;
365  state.verbose = verbose;
367  if (!state.db) {
368  ret = -1;
369  goto beach;
370  }
371  rz_pvector_init(&state.queue, NULL);
373  rz_pvector_init(&state.completed_paths, NULL);
374  if (output_file) {
375  state.test_results = pj_new();
376  pj_a(state.test_results);
377  }
378  state.lock = rz_th_lock_new(false);
379  if (!state.lock) {
380  ret = -1;
381  goto beach;
382  }
383  state.cond = rz_th_cond_new();
384  if (!state.cond) {
385  ret = -1;
386  goto beach;
387  }
388 
389  if (opt.ind < argc) {
390  // Manually specified path(s)
391  int i;
392  for (i = opt.ind; i < argc; i++) {
393  const char *arg = argv[i];
394  char *alloc_arg = NULL;
395  if (*arg == '@') {
396  arg++;
397  eprintf("Category: %s\n", arg);
398  if (!strcmp(arg, "unit")) {
399  if (!rz_test_test_run_unit()) {
400  ret = -1;
401  goto beach;
402  }
403  continue;
404  } else if (!strcmp(arg, "fuzz")) {
405  if (!fuzz_dir) {
406  eprintf("No fuzz dir given. Use -F [dir]\n");
407  ret = -1;
408  goto beach;
409  }
410  if (!rz_test_test_database_load_fuzz(state.db, fuzz_dir)) {
411  eprintf("Failed to load fuzz tests from \"%s\"\n", fuzz_dir);
412  }
413  continue;
414  } else if (!strcmp(arg, "json")) {
415  arg = "db/json";
416  } else if (!strcmp(arg, "dasm")) {
417  arg = "db/asm";
418  } else if (!strcmp(arg, "cmds")) {
419  arg = "db";
420  } else {
421  arg = alloc_arg = rz_str_newf("db/%s", arg + 1);
422  }
423  }
424  char *tf = rz_file_abspath_rel(cwd, arg);
425  if (!tf || !rz_test_test_database_load(state.db, tf)) {
426  eprintf("Failed to load tests from \"%s\"\n", tf);
428  free(tf);
429  free(alloc_arg);
430  ret = -1;
431  goto beach;
432  }
433  RZ_FREE(alloc_arg);
434  free(tf);
435  }
436  } else {
437  // Default db path
438  if (!rz_test_test_database_load(state.db, "db")) {
439  eprintf("Failed to load tests from ./db\n");
441  ret = -1;
442  goto beach;
443  }
444  if (fuzz_dir && !rz_test_test_database_load_fuzz(state.db, fuzz_dir)) {
445  eprintf("Failed to load fuzz tests from \"%s\"\n", fuzz_dir);
446  }
447  }
448 
449  // filter out except_dir
450  if (!rz_pvector_empty(except_dir)) {
451  void **it;
452  rz_pvector_foreach (except_dir, it) {
453  const char *p = rz_file_abspath_rel(cwd, (char *)*it), *tp;
454  for (ut32 i = 0; i < rz_pvector_len(&state.db->tests); i++) {
455  RzTest *test = rz_pvector_at(&state.db->tests, i);
456  if (rz_file_is_abspath(test->path)) {
457  tp = strdup(test->path);
458  } else {
459  tp = rz_file_abspath_rel(cwd, test->path);
460  }
461  if (rz_str_startswith(tp, p)) {
463  rz_pvector_remove_at(&state.db->tests, i--);
464  }
465  RZ_FREE(tp);
466  }
467  RZ_FREE(p);
468  }
469  }
470 
471  RZ_FREE(cwd);
472  uint32_t loaded_tests = rz_pvector_len(&state.db->tests);
473  printf("Loaded %u tests.\n", loaded_tests);
474  if (nothing) {
475  goto coast;
476  }
477 
478  bool jq_available = rz_test_check_jq_available();
479  if (!jq_available) {
480  eprintf("Skipping json tests because jq is not available.\n");
481  size_t i;
482  for (i = 0; i < rz_pvector_len(&state.db->tests);) {
483  RzTest *test = rz_pvector_at(&state.db->tests, i);
484  if (test->type == RZ_TEST_TYPE_JSON) {
486  rz_pvector_remove_at(&state.db->tests, i);
487  continue;
488  }
489  i++;
490  }
491  }
492 
493  if (rz_pvector_len(&state.db->tests) != 0) {
494  rz_pvector_insert_range(&state.queue, 0, state.db->tests.v.a, rz_pvector_len(&state.db->tests));
495  } else {
496  eprintf("No tests discovered\n");
497  }
498 
499  if (log_mode) {
500  // Log mode prints the state after every completed file.
501  // The count of tests left per file is stored in a ht.
502  state.path_left = ht_pp_new(NULL, path_left_free_kv, NULL);
503  if (state.path_left) {
504  void **it;
505  rz_pvector_foreach (&state.queue, it) {
506  RzTest *test = *it;
507  RzTestFileCounts *counts = ht_pp_find(state.path_left, test->path, NULL);
508  if (!counts) {
509  counts = calloc(1, sizeof(RzTestFileCounts));
510  ht_pp_insert(state.path_left, test->path, counts);
511  }
512  counts->tests_left++;
513  }
514  }
515  }
516 
517  rz_th_lock_enter(state.lock);
518 
521  int i;
522  for (i = 0; i < workers_count; i++) {
524  if (!th) {
525  eprintf("Failed to start thread.\n");
526  rz_th_lock_leave(state.lock);
527  exit(-1);
528  }
529  rz_pvector_push(&workers, th);
530  }
531 
532  ut64 prev_completed = UT64_MAX;
533  ut64 prev_paths_completed = 0;
534  while (true) {
535  ut64 completed = (ut64)rz_pvector_len(&state.results);
536  if (log_mode) {
537  print_log(&state, prev_completed, prev_paths_completed);
538  } else if (completed != prev_completed) {
539  print_state(&state, prev_completed);
540  }
541  prev_completed = completed;
542  prev_paths_completed = (ut64)rz_pvector_len(&state.completed_paths);
543  if (completed == rz_pvector_len(&state.db->tests)) {
544  break;
545  }
546  rz_th_cond_wait(state.cond, state.lock);
547  }
548 
549  rz_th_lock_leave(state.lock);
550 
551  printf("\n");
552 
553  void **it;
554  rz_pvector_foreach (&workers, it) {
555  RzThread *th = *it;
556  rz_th_wait(th);
557  rz_th_free(th);
558  }
560 
561  ut64 seconds = (rz_time_now_mono() - time_start) / 1000000;
562  printf("Finished in");
563  if (seconds > 60) {
564  ut64 minutes = seconds / 60;
565  printf(" %" PFMT64u " minutes and", minutes);
566  seconds -= (minutes * 60);
567  }
568  printf(" %" PFMT64u " seconds.\n", seconds % 60);
569 
570  if (output_file) {
571  pj_end(state.test_results);
572  char *results = pj_drain(state.test_results);
573  rz_file_dump(output_file, (ut8 *)results, strlen(results), false);
574  free(results);
575  }
576 
577  if (interactive) {
578  interact(&state);
579  }
580 
581  if (expect_succ > 0 && expect_succ != state.ok_count) {
582  ret = 1;
583  }
584 
585  if (expect_fail > 0 && expect_fail != state.xx_count) {
586  ret = 1;
587  }
588 
589  if (expect_fail < 0 && expect_succ < 0 && state.xx_count) {
590  ret = 1;
591  }
592 
593 coast:
594  rz_pvector_clear(&state.queue);
595  rz_pvector_clear(&state.results);
596  rz_pvector_clear(&state.completed_paths);
598  rz_th_lock_free(state.lock);
599  rz_th_cond_free(state.cond);
600  ht_pp_free(state.path_left);
601 beach:
602  free(output_file);
603  free(rizin_cmd);
604  free(rz_asm_cmd);
605  free(json_test_file);
606  free(fuzz_dir);
607  rz_pvector_free(except_dir);
608 #if __WINDOWS__
609  if (old_cp) {
610  (void)SetConsoleOutputCP(old_cp);
611  // chcp doesn't pick up the code page switch for some reason
612  (void)rz_sys_cmdf("chcp %u > NUL", old_cp);
613  }
614 #endif
615  return ret;
616 }
617 
618 static void test_result_to_json(PJ *pj, RzTestResultInfo *result) {
619  rz_return_if_fail(pj && result);
620  pj_o(pj);
621  pj_k(pj, "type");
622  RzTest *test = result->test;
623  switch (test->type) {
624  case RZ_TEST_TYPE_CMD:
625  pj_s(pj, "cmd");
626  pj_ks(pj, "name", test->cmd_test->name.value);
627  break;
628  case RZ_TEST_TYPE_ASM:
629  pj_s(pj, "asm");
630  pj_ks(pj, "arch", test->asm_test->arch);
631  pj_ki(pj, "bits", test->asm_test->bits);
632  pj_kn(pj, "line", test->asm_test->line);
633  break;
634  case RZ_TEST_TYPE_JSON:
635  pj_s(pj, "json");
636  pj_ks(pj, "cmd", test->json_test->cmd);
637  break;
638  case RZ_TEST_TYPE_FUZZ:
639  pj_s(pj, "fuzz");
640  pj_ks(pj, "file", test->fuzz_test->file);
641  break;
642  }
643  pj_k(pj, "result");
644  switch (result->result) {
645  case RZ_TEST_RESULT_OK:
646  pj_s(pj, "ok");
647  break;
649  pj_s(pj, "failed");
650  break;
652  pj_s(pj, "broken");
653  break;
655  pj_s(pj, "fixed");
656  break;
657  }
658  pj_kb(pj, "run_failed", result->run_failed);
659  pj_kn(pj, "time_elapsed", result->time_elapsed);
660  pj_kb(pj, "timeout", result->timeout);
661  pj_end(pj);
662 }
663 
664 static void *worker_th(RzTestState *state) {
665  rz_th_lock_enter(state->lock);
666  while (true) {
667  if (rz_pvector_empty(&state->queue)) {
668  break;
669  }
670  RzTest *test = rz_pvector_pop(&state->queue);
671  rz_th_lock_leave(state->lock);
672 
673  RzTestResultInfo *result = rz_test_run_test(&state->run_config, test);
674 
675  rz_th_lock_enter(state->lock);
676  rz_pvector_push(&state->results, result);
677  if (!log_mode) {
678  switch (result->result) {
679  case RZ_TEST_RESULT_OK:
680  state->ok_count++;
681  break;
683  state->xx_count++;
684  break;
686  state->br_count++;
687  break;
689  state->fx_count++;
690  break;
691  }
692  }
693  if (state->path_left) {
694  RzTestFileCounts *counts = ht_pp_find(state->path_left, test->path, NULL);
695  if (counts) {
696  switch (result->result) {
697  case RZ_TEST_RESULT_OK:
698  counts->ok++;
699  break;
701  counts->xx++;
702  break;
704  counts->br++;
705  break;
707  counts->fx++;
708  break;
709  }
710  counts->tests_left--;
711  if (!counts->tests_left) {
712  rz_pvector_push(&state->completed_paths, (void *)test->path);
713  }
714  }
715  }
716  rz_th_cond_signal(state->cond);
717  }
718  rz_th_lock_leave(state->lock);
719  return NULL;
720 }
721 
722 static void print_diff(const char *actual, const char *expected, const char *regexp) {
723  RzDiff *d = NULL;
724  char *uni = NULL;
725  const char *output = actual;
726 
727  if (regexp) {
728  RzList *matches = rz_regex_get_match_list(regexp, "e", actual);
729  output = rz_list_to_str(matches, '\0');
730  rz_list_free(matches);
731  }
732 
733  d = rz_diff_lines_new(expected, output, NULL);
734  if (!d) {
735  goto cleanup;
736  }
737 
738  uni = rz_diff_unified_text(d, "expected", "actual", false, true);
739  if (!uni) {
740  goto cleanup;
741  }
742  puts(uni);
743  free(uni);
744 
745 cleanup:
746  rz_diff_free(d);
747  if (regexp) {
748  free((char *)output);
749  }
750 }
751 
752 static RzSubprocessOutput *print_runner(const char *file, const char *args[], size_t args_size,
753  const char *envvars[], const char *envvals[], size_t env_size, ut64 timeout_ms, void *user) {
754  size_t i;
755  for (i = 0; i < env_size; i++) {
756  printf("%s=%s ", envvars[i], envvals[i]);
757  }
758  printf("%s", file);
759  for (i = 0; i < args_size; i++) {
760  const char *str = args[i];
761  if (strpbrk(str, "\n \'\"")) {
762  printf(" '%s'", str); // TODO: escape
763  } else {
764  printf(" %s", str);
765  }
766  }
767  printf("\n");
768  return NULL;
769 }
770 
772  if (result->run_failed) {
773  printf(Color_RED "RUN FAILED (e.g. wrong rizin path)" Color_RESET "\n");
774  return;
775  }
776  switch (result->test->type) {
777  case RZ_TEST_TYPE_CMD: {
779  const char *expect = result->test->cmd_test->expect.value;
780  const char *out = (const char *)result->proc_out->out;
781  const char *regexp_out = result->test->cmd_test->regexp_out.value;
782  if (expect && !rz_test_cmp_cmd_output(out, expect, regexp_out)) {
783  printf("-- stdout\n");
784  print_diff(out, expect, regexp_out);
785  }
786  expect = result->test->cmd_test->expect_err.value;
787  const char *err = (const char *)result->proc_out->err;
788  const char *regexp_err = result->test->cmd_test->regexp_err.value;
789  if (expect && !rz_test_cmp_cmd_output(err, expect, regexp_err)) {
790  printf("-- stderr\n");
791  print_diff(err, expect, regexp_err);
792  } else if (*err) {
793  printf("-- stderr\n%s\n", err);
794  }
795  if (result->proc_out->ret != 0) {
796  printf("-- exit status: " Color_RED "%d" Color_RESET "\n", result->proc_out->ret);
797  }
798  break;
799  }
800  case RZ_TEST_TYPE_ASM: {
801  RzAsmTest *test = result->test->asm_test;
802  RzAsmTestOutput *out = result->asm_out;
803  char *expect_hex = rz_hex_bin2strdup(test->bytes, test->bytes_size);
804  printf("-- <asm> " Color_YELLOW "%s %c--%c %s%s" Color_RESET "\n",
805  test->disasm,
806  test->mode & RZ_ASM_TEST_MODE_DISASSEMBLE ? '<' : '-',
807  test->mode & RZ_ASM_TEST_MODE_ASSEMBLE ? '>' : '-',
808  expect_hex ? expect_hex : "",
809  test->il ? " ---> <IL>" : "");
810  if (test->mode & RZ_ASM_TEST_MODE_DISASSEMBLE) {
811  const char *expect = test->disasm;
812  const char *actual = out->disasm;
813  if (expect && actual && strcmp(actual, expect)) {
814  printf("-- disassembly\n");
815  print_diff(actual, expect, NULL);
816  }
817  }
818  if (test->mode & RZ_ASM_TEST_MODE_ASSEMBLE) {
819  printf("-- assembly\n");
820  if (out->bytes && (out->bytes_size != test->bytes_size || memcmp(out->bytes, test->bytes, out->bytes_size))) {
821  char *actual = rz_hex_bin2strdup(out->bytes, out->bytes_size);
822  print_diff(actual ? actual : "", expect_hex ? expect_hex : "", NULL);
823  free(actual);
824  }
825  }
826  if (test->il) {
827  const char *expect = test->il;
828  const char *actual = out->il;
829  const char *report = out->il_report;
830  bool il_printed = false;
831  const char *hdr = "-- IL\n";
832  if (expect && actual && strcmp(actual, expect)) {
833  printf("%s", hdr);
834  il_printed = true;
835  print_diff(actual, expect, NULL);
836  }
837  if (report) {
838  if (!il_printed) {
839  printf("%s", hdr);
840  if (actual) {
841  printf("%s\n", actual);
842  }
843  }
844  printf(Color_RED "%s" Color_RESET "\n", report);
845  }
846  }
847  free(expect_hex);
848  break;
849  }
850  case RZ_TEST_TYPE_JSON:
851  break;
852  case RZ_TEST_TYPE_FUZZ:
854  printf("-- stdout\n%s\n", (const char *)result->proc_out->out);
855  printf("-- stderr\n%s\n", (const char *)result->proc_out->err);
856  printf("-- exit status: " Color_RED "%d" Color_RESET "\n", result->proc_out->ret);
857  break;
858  }
859 }
860 
861 static void print_new_results(RzTestState *state, ut64 prev_completed) {
862  // Detailed test result (with diff if necessary)
863  ut64 completed = (ut64)rz_pvector_len(&state->results);
864  ut64 i;
865  for (i = prev_completed; i < completed; i++) {
866  RzTestResultInfo *result = rz_pvector_at(&state->results, (size_t)i);
867  if (state->test_results) {
868  test_result_to_json(state->test_results, result);
869  }
870  if (!state->verbose && (result->result == RZ_TEST_RESULT_OK || result->result == RZ_TEST_RESULT_FIXED || result->result == RZ_TEST_RESULT_BROKEN)) {
871  continue;
872  }
873  char *name = rz_test_test_name(result->test);
874  if (!name) {
875  continue;
876  }
878  switch (result->result) {
879  case RZ_TEST_RESULT_OK:
881  break;
883  printf(Color_RED "[XX]" Color_RESET);
884  break;
886  printf(Color_BLUE "[BR]" Color_RESET);
887  break;
889  printf(Color_CYAN "[FX]" Color_RESET);
890  break;
891  }
892  if (result->timeout) {
893  printf(Color_CYAN " TIMEOUT" Color_RESET);
894  }
895  printf(" %s " Color_YELLOW "%s" Color_RESET "\n", result->test->path, name);
896  if (result->result == RZ_TEST_RESULT_FAILED || (state->verbose && result->result == RZ_TEST_RESULT_BROKEN)) {
897  print_result_diff(&state->run_config, result);
898  }
899  free(name);
900  }
901 }
902 
904  printf("%8" PFMT64u " OK %8" PFMT64u " BR %8" PFMT64u " XX %8" PFMT64u " FX",
905  state->ok_count, state->br_count, state->xx_count, state->fx_count);
906 }
907 
908 static void print_state(RzTestState *state, ut64 prev_completed) {
909 #if __WINDOWS__
910  setvbuf(stdout, NULL, _IOFBF, 8192);
911 #endif
912  print_new_results(state, prev_completed);
913 
914  // [x/x] OK 42 BR 0 ...
916  int w = printf("[%" PFMT64u "/%" PFMT64u "]", (ut64)rz_pvector_len(&state->results), (ut64)rz_pvector_len(&state->db->tests));
917  while (w >= 0 && w < 20) {
918  printf(" ");
919  w++;
920  }
921  printf(" ");
923  fflush(stdout);
924 #if __WINDOWS__
925  setvbuf(stdout, NULL, _IONBF, 0);
926 #endif
927 }
928 
929 static void print_log(RzTestState *state, ut64 prev_completed, ut64 prev_paths_completed) {
930  print_new_results(state, prev_completed);
931  ut64 paths_completed = rz_pvector_len(&state->completed_paths);
932  for (; prev_paths_completed < paths_completed; prev_paths_completed++) {
933  const char *name = (const char *)rz_pvector_at(&state->completed_paths, prev_paths_completed);
934  if (!name) {
935  name = "unknown path. something is very wrong.";
936  }
937  printf("[**] %50s ", name);
938  if (state->path_left) {
939  RzTestFileCounts *counts = ht_pp_find(state->path_left, name, NULL);
940  if (counts) {
941  state->ok_count += counts->ok;
942  state->xx_count += counts->xx;
943  state->br_count += counts->br;
944  state->fx_count += counts->fx;
945  }
946  }
948  printf("\n");
949  fflush(stdout);
950  }
951 }
952 
953 static void interact(RzTestState *state) {
954  void **it;
955  RzPVector failed_results;
956  rz_pvector_init(&failed_results, NULL);
957  rz_pvector_foreach (&state->results, it) {
958  RzTestResultInfo *result = *it;
959  if (result->result == RZ_TEST_RESULT_FAILED) {
960  rz_pvector_push(&failed_results, result);
961  }
962  }
963  if (rz_pvector_empty(&failed_results)) {
964  goto beach;
965  }
966 
967 #if __WINDOWS__
968  (void)SetConsoleOutputCP(65001); // UTF-8
969 #endif
970  printf("\n");
971  printf("#####################\n");
972  printf(" %" PFMT64u " failed test(s) " UTF8_POLICE_CARS_REVOLVING_LIGHT "\n",
973  (ut64)rz_pvector_len(&failed_results));
974 
975  rz_pvector_foreach (&failed_results, it) {
976  RzTestResultInfo *result = *it;
977  if (result->test->type != RZ_TEST_TYPE_CMD && result->test->type != RZ_TEST_TYPE_ASM) {
978  continue;
979  }
980 
981  printf("#####################\n\n");
982  print_result_diff(&state->run_config, result);
983  bool have_commands = result->test->type == RZ_TEST_TYPE_CMD;
984  menu:
985  printf("Wat do? "
987  "(i)gnore " UTF8_SEE_NO_EVIL_MONKEY " "
989  "%s"
990  "(q)uit " UTF8_DOOR "\n",
991  have_commands ? "(c)ommands " UTF8_KEYBOARD UTF8_VS16 " " : "");
992  printf("> ");
993  char buf[0x30];
994  if (!fgets(buf, sizeof(buf), stdin)) {
995  break;
996  }
997  if (strlen(buf) != 2) {
998  goto menu;
999  }
1000  switch (buf[0]) {
1001  case 'f':
1002  if (!interact_fix(result, &failed_results)) {
1003  printf("This test has failed too hard to be fixed.\n");
1004  goto menu;
1005  }
1006  break;
1007  case 'i':
1008  break;
1009  case 'b':
1010  interact_break(result, &failed_results);
1011  break;
1012  case 'c':
1013  if (have_commands) {
1014  interact_commands(result, &failed_results);
1015  break;
1016  }
1017  goto menu;
1018  case 'q':
1019  goto beach;
1020  default:
1021  goto menu;
1022  }
1023  }
1024 
1025 beach:
1026  rz_pvector_clear(&failed_results);
1027 }
1028 
1029 static char *format_cmd_kv(const char *key, const char *val) {
1030  RzStrBuf buf;
1031  rz_strbuf_init(&buf);
1032  rz_strbuf_appendf(&buf, "%s=", key);
1033  if (strchr(val, '\n')) {
1034  rz_strbuf_appendf(&buf, "<<EOF\n%sEOF", val);
1035  } else {
1037  }
1038  return rz_strbuf_drain_nofree(&buf);
1039 }
1040 
1041 static char *replace_lines(const char *src, size_t from, size_t to, const char *news) {
1042  const char *begin = src;
1043  size_t line = 1;
1044  while (line < from) {
1045  begin = strchr(begin, '\n');
1046  if (!begin) {
1047  break;
1048  }
1049  begin++;
1050  line++;
1051  }
1052  if (!begin) {
1053  return NULL;
1054  }
1055 
1056  const char *end = begin;
1057  while (line < to) {
1058  end = strchr(end, '\n');
1059  if (!end) {
1060  break;
1061  }
1062  end++;
1063  line++;
1064  }
1065 
1066  RzStrBuf buf;
1067  rz_strbuf_init(&buf);
1068  rz_strbuf_append_n(&buf, src, begin - src);
1069  rz_strbuf_append(&buf, news);
1070  rz_strbuf_append(&buf, "\n");
1071  if (end) {
1073  }
1074  return rz_strbuf_drain_nofree(&buf);
1075 }
1076 
1077 // After editing a test, fix the line numbers previously saved for all the other tests
1078 static void fixup_tests(RzPVector *results, const char *edited_file, ut64 start_line, st64 delta) {
1079  void **it;
1080  rz_pvector_foreach (results, it) {
1081  RzTestResultInfo *result = *it;
1082  if (result->test->type != RZ_TEST_TYPE_CMD) {
1083  continue;
1084  }
1085  if (result->test->path != edited_file) { // this works because all the paths come from the string pool
1086  continue;
1087  }
1088  RzCmdTest *test = result->test->cmd_test;
1089  test->run_line += delta;
1090 
1091 #define DO_KEY_STR(key, field) \
1092  if (test->field.value) { \
1093  if (test->field.line_begin >= start_line) { \
1094  test->field.line_begin += delta; \
1095  } \
1096  if (test->field.line_end >= start_line) { \
1097  test->field.line_end += delta; \
1098  } \
1099  }
1100 
1101 #define DO_KEY_BOOL(key, field) \
1102  if (test->field.set && test->field.line >= start_line) { \
1103  test->field.line += delta; \
1104  }
1105 
1106 #define DO_KEY_NUM(key, field) \
1107  if (test->field.set && test->field.line >= start_line) { \
1108  test->field.line += delta; \
1109  }
1110 
1112 #undef DO_KEY_STR
1113 #undef DO_KEY_BOOL
1114 #undef DO_KEY_NUM
1115  }
1116 }
1117 
1118 static char *read_test_file_for_fix(const char *path) {
1119  char *content = rz_file_slurp(path, NULL);
1120  if (!content) {
1121  eprintf("Failed to read file \"%s\"\n", path);
1122  }
1123  return content;
1124 }
1125 
1126 static void save_test_file_for_fix(const char *path, const char *newc) {
1127  if (rz_file_dump(path, (const ut8 *)newc, -1, false)) {
1128 #if __UNIX__
1129  sync();
1130 #endif
1131  } else {
1132  eprintf("Failed to write file \"%s\"\n", path);
1133  }
1134 }
1135 
1136 static char *replace_cmd_kv(const char *path, const char *content, size_t line_begin, size_t line_end, const char *key, const char *value, RzPVector *fixup_results) {
1137  char *kv = format_cmd_kv(key, value);
1138  if (!kv) {
1139  return NULL;
1140  }
1141  size_t kv_lines = rz_str_char_count(kv, '\n') + 1;
1142  char *newc = replace_lines(content, line_begin, line_end, kv);
1143  free(kv);
1144  if (!newc) {
1145  return NULL;
1146  }
1147  size_t lines_before = line_end - line_begin;
1148  st64 delta = (st64)kv_lines - (st64)lines_before;
1149  if (line_end == line_begin) {
1150  delta++;
1151  }
1152  fixup_tests(fixup_results, path, line_end, delta);
1153  return newc;
1154 }
1155 
1156 static void replace_cmd_kv_file(const char *path, ut64 line_begin, ut64 line_end, const char *key, const char *value, RzPVector *fixup_results) {
1157  char *content = read_test_file_for_fix(path);
1158  if (!content) {
1159  return;
1160  }
1161  char *newc = replace_cmd_kv(path, content, line_begin, line_end, key, value, fixup_results);
1162  free(content);
1163  if (!newc) {
1164  return;
1165  }
1167  free(newc);
1168 }
1169 
1170 static bool interact_fix_cmd(RzTestResultInfo *result, RzPVector *fixup_results) {
1171  assert(result->test->type == RZ_TEST_TYPE_CMD);
1172  if (result->run_failed || result->proc_out->ret != 0) {
1173  return false;
1174  }
1175  RzCmdTest *test = result->test->cmd_test;
1176  RzSubprocessOutput *out = result->proc_out;
1177  if (test->expect.value && out->out) {
1178  replace_cmd_kv_file(result->test->path, test->expect.line_begin, test->expect.line_end, "EXPECT", (char *)out->out, fixup_results);
1179  }
1180  if (test->expect_err.value && out->err) {
1181  replace_cmd_kv_file(result->test->path, test->expect_err.line_begin, test->expect_err.line_end, "EXPECT_ERR", (char *)out->err, fixup_results);
1182  }
1183  return true;
1184 }
1185 
1186 static void replace_file_line(const char *path, ut64 line_idx, const char *line_new) {
1187  char *content = read_test_file_for_fix(path);
1188  if (!content) {
1189  return;
1190  }
1191  char *newc = replace_lines(content, line_idx, line_idx + 1, line_new);
1192  free(content);
1193  if (!newc) {
1194  return;
1195  }
1197  free(newc);
1198 }
1199 
1200 static void replace_asm_test(RZ_NONNULL const char *path, ut64 line_idx,
1201  int mode, RZ_NONNULL const char *disasm, RZ_NONNULL const ut8 *bytes, size_t bytes_sz, ut64 offset, RZ_NULLABLE const char *il) {
1202  char *hex = rz_hex_bin2strdup(bytes, bytes_sz);
1203  if (!hex) {
1204  return;
1205  }
1206  char offset_str[0x20];
1207  if ((!offset && !il) || snprintf(offset_str, sizeof(offset_str), " 0x%" PFMT64x, offset) < 0) {
1208  *offset_str = '\0';
1209  }
1210  char *line = rz_str_newf("%s%s%s%s \"%s\" %s%s%s%s",
1211  (mode & RZ_ASM_TEST_MODE_ASSEMBLE) ? "a" : "",
1212  (mode & RZ_ASM_TEST_MODE_DISASSEMBLE) ? "d" : "",
1213  (mode & RZ_ASM_TEST_MODE_BIG_ENDIAN) ? "E" : "",
1214  (mode & RZ_ASM_TEST_MODE_BROKEN) ? "B" : "",
1215  disasm, hex, offset_str, il ? " " : "", il ? il : "");
1216  free(hex);
1217  if (!line) {
1218  return;
1219  }
1220  replace_file_line(path, line_idx, line);
1221  free(line);
1222 }
1223 
1229  // check that both ways are requested
1230  if (!(test->mode & RZ_ASM_TEST_MODE_ASSEMBLE) || !(test->mode & RZ_ASM_TEST_MODE_ASSEMBLE)) {
1231  return false;
1232  }
1233  // check that disasm is wrong
1234  if (out->disasm && !strcmp(test->disasm, out->disasm)) {
1235  return false;
1236  }
1237  // check that asm is wrong too
1238  if (out->bytes && out->bytes_size == test->bytes_size && !memcmp(out->bytes, test->bytes, test->bytes_size)) {
1239  return false;
1240  }
1241  // determined that both ways are broken
1242  return true;
1243 }
1244 
1245 static bool interact_fix_asm(RzTestResultInfo *result) {
1246  assert(result->test->type == RZ_TEST_TYPE_ASM);
1247  RzAsmTest *test = result->test->asm_test;
1248  RzAsmTestOutput *out = result->asm_out;
1249 
1250  const char *disasm = test->mode & RZ_ASM_TEST_MODE_DISASSEMBLE ? out->disasm : test->disasm;
1251  if (!disasm) {
1252  return false;
1253  }
1254 
1255  const ut8 *bytes;
1256  size_t bytes_sz;
1257  if (test->mode & RZ_ASM_TEST_MODE_ASSEMBLE) {
1258  bytes = out->bytes;
1259  bytes_sz = out->bytes_size;
1260  } else {
1261  bytes = test->bytes;
1262  bytes_sz = test->bytes_size;
1263  }
1264  if (!bytes) {
1265  return false;
1266  }
1267 
1269  // both disasm and asm failed, so trying to fix here would likely only make things worse
1270  return false;
1271  }
1272 
1273  if (test->il && (out->il_failed || !out->il)) {
1274  // IL wasn't lifted or validation failed, this can only be fixed in code
1275  return false;
1276  }
1277 
1278  replace_asm_test(result->test->path, test->line, test->mode, disasm, bytes, bytes_sz, test->offset, out->il);
1279  return true;
1280 }
1281 
1282 static bool interact_fix(RzTestResultInfo *result, RzPVector *fixup_results) {
1283  switch (result->test->type) {
1284  case RZ_TEST_TYPE_CMD:
1285  return interact_fix_cmd(result, fixup_results);
1286  case RZ_TEST_TYPE_ASM:
1287  return interact_fix_asm(result);
1288  default:
1289  return false;
1290  }
1291 }
1292 
1293 static void interact_break_cmd(RzTestResultInfo *result, RzPVector *fixup_results) {
1294  assert(result->test->type == RZ_TEST_TYPE_CMD);
1295  RzCmdTest *test = result->test->cmd_test;
1296  ut64 line_begin;
1297  ut64 line_end;
1298  if (test->broken.set) {
1299  line_begin = test->broken.set;
1300  line_end = line_begin + 1;
1301  } else {
1302  line_begin = line_end = test->run_line;
1303  }
1304  replace_cmd_kv_file(result->test->path, line_begin, line_end, "BROKEN", "1", fixup_results);
1305 }
1306 
1307 static void interact_break_asm(RzTestResultInfo *result) {
1308  assert(result->test->type == RZ_TEST_TYPE_ASM);
1309  RzAsmTest *test = result->test->asm_test;
1310  replace_asm_test(result->test->path, test->line,
1311  test->mode | RZ_ASM_TEST_MODE_BROKEN, test->disasm, test->bytes, test->bytes_size, test->offset, test->il);
1312 }
1313 
1314 static void interact_break(RzTestResultInfo *result, RzPVector *fixup_results) {
1315  switch (result->test->type) {
1316  case RZ_TEST_TYPE_CMD:
1317  interact_break_cmd(result, fixup_results);
1318  break;
1319  case RZ_TEST_TYPE_ASM:
1320  interact_break_asm(result);
1321  break;
1322  default:
1323  break;
1324  }
1325 }
1326 
1327 static void interact_commands(RzTestResultInfo *result, RzPVector *fixup_results) {
1328  assert(result->test->type == RZ_TEST_TYPE_CMD);
1329  RzCmdTest *test = result->test->cmd_test;
1330  if (!test->cmds.value) {
1331  return;
1332  }
1333  char *name = NULL;
1334  int fd = rz_file_mkstemp("rz-test-cmds", &name);
1335  if (fd == -1) {
1336  free(name);
1337  eprintf("Failed to open tmp file\n");
1338  return;
1339  }
1340  size_t cmds_sz = strlen(test->cmds.value);
1341  if (write(fd, test->cmds.value, cmds_sz) != cmds_sz) {
1342  eprintf("Failed to write to tmp file\n");
1343  free(name);
1344  close(fd);
1345  return;
1346  }
1347  close(fd);
1348 
1349  char *editor = rz_sys_getenv("EDITOR");
1350  if (!editor || !*editor) {
1351  free(editor);
1352  editor = strdup("vim");
1353  if (!editor) {
1354  free(name);
1355  return;
1356  }
1357  }
1358  rz_sys_cmdf("%s '%s'", editor, name);
1359  free(editor);
1360 
1361  char *newcmds = rz_file_slurp(name, NULL);
1362  if (!newcmds) {
1363  eprintf("Failed to read edited command file\n");
1364  free(name);
1365  return;
1366  }
1367  rz_str_trim(newcmds);
1368 
1369  // if it's multiline we want exactly one trailing newline
1370  if (strchr(newcmds, '\n')) {
1371  char *tmp = newcmds;
1372  newcmds = rz_str_newf("%s\n", newcmds);
1373  free(tmp);
1374  if (!newcmds) {
1375  free(name);
1376  return;
1377  }
1378  }
1379 
1380  replace_cmd_kv_file(result->test->path, test->cmds.line_begin, test->cmds.line_end, "CMDS", newcmds, fixup_results);
1381  free(name);
1382  free(newcmds);
1383 }
1384 
1385 int MAIN_NAME(int argc, const ARGV_TYPE **argv) {
1386  char **utf8_argv = ARGV_TYPE_TO_UTF8(argc, argv);
1387  int ret = rz_test_main(argc, (const char **)utf8_argv);
1388  FREE_UTF8_ARGV(argc, utf8_argv);
1389  return ret;
1390 }
lzma_index ** i
Definition: index.h:629
lzma_index * src
Definition: index.h:567
ut16 val
Definition: armass64_const.h:6
static bool err
Definition: armass.c:435
static ut8 bytes[32]
Definition: asm_arc.c:23
RZ_API RzTestResultInfo * rz_test_run_test(RzTestRunConfig *config, RzTest *test)
Definition: run.c:482
RZ_API RzSubprocessOutput * rz_test_run_cmd_test(RzTestRunConfig *config, RzCmdTest *test, RzTestCmdRunner runner, void *user)
Definition: run.c:161
RZ_API bool rz_test_check_jq_available(void)
Definition: run.c:223
RZ_API bool rz_test_cmp_cmd_output(const char *output, const char *expect, const char *regexp)
Definition: run.c:192
RZ_API RzSubprocessOutput * rz_test_run_fuzz_test(RzTestRunConfig *config, RzFuzzTest *test, RzTestCmdRunner runner, void *user)
Definition: run.c:433
RZ_API void rz_test_test_result_info_free(RzTestResultInfo *result)
Definition: run.c:552
RZ_API char * rz_test_test_name(RzTest *test)
Definition: run.c:451
const lzma_allocator const uint8_t size_t uint8_t * out
Definition: block.h:528
static int value
Definition: cmd_api.c:93
#define NULL
Definition: cris-opc.c:27
#define w
Definition: crypto_rc6.c:13
_Use_decl_annotations_ int __cdecl printf(const char *const _Format,...)
Definition: cs_driver.c:93
static static fork const void static count static fd const char const char static newpath const char static path const char path
Definition: sflib.h:35
static static fork const void static count static fd const char const char static newpath chdir
Definition: sflib.h:33
static static fork write
Definition: sflib.h:33
static static fork const void static count close
Definition: sflib.h:33
static static sync static getppid static getegid const char static filename char static len readlink
Definition: sflib.h:65
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void static offset struct stat static buf void long static basep static whence static length const void static len key
Definition: sflib.h:118
uint32_t ut32
RZ_API RZ_OWN RzDiff * rz_diff_lines_new(RZ_BORROW const char *a, RZ_BORROW const char *b, RZ_NULLABLE RzDiffIgnoreLine ignore)
Returns the structure needed to diff lines.
Definition: diff.c:219
RZ_API void rz_diff_free(RZ_NULLABLE RzDiff *diff)
frees the diff structure
Definition: diff.c:295
void cleanup(void)
Definition: enough.c:244
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
RZ_API const KEY_TYPE bool * found
Definition: ht_inc.h:130
voidpf uLong offset
Definition: ioapi.h:144
const char int mode
Definition: ioapi.h:137
voidpf void * buf
Definition: ioapi.h:138
snprintf
Definition: kernel.h:364
uint8_t ut8
Definition: lh5801.h:11
void * p
Definition: libc.cpp:67
RZ_API RZ_OWN char * rz_list_to_str(RZ_NONNULL RzList *list, char ch)
Casts a RzList containg strings into a concatenated string.
Definition: list.c:785
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
RZ_API void rz_th_free(RZ_NULLABLE RzThread *th)
Frees a RzThread structure.
Definition: thread.c:246
RZ_API RZ_OWN RzThread * rz_th_new(RZ_NONNULL RzThreadFunction function, RZ_NULLABLE void *user)
Creates and starts a new thread.
Definition: thread.c:198
RZ_API bool rz_th_wait(RZ_NONNULL RzThread *th)
Awaits indefinetely for a thread to join.
Definition: thread.c:231
void * calloc(size_t number, size_t size)
Definition: malloc.c:102
static static fork const void static count static fd const char const char static newpath char char argv
Definition: sflib.h:40
return strdup("=SP r13\n" "=LR r14\n" "=PC r15\n" "=A0 r0\n" "=A1 r1\n" "=A2 r2\n" "=A3 r3\n" "=ZF zf\n" "=SF nf\n" "=OF vf\n" "=CF cf\n" "=SN or0\n" "gpr lr .32 56 0\n" "gpr pc .32 60 0\n" "gpr cpsr .32 64 0 ____tfiae_________________qvczn\n" "gpr or0 .32 68 0\n" "gpr tf .1 64.5 0 thumb\n" "gpr ef .1 64.9 0 endian\n" "gpr jf .1 64.24 0 java\n" "gpr qf .1 64.27 0 sticky_overflow\n" "gpr vf .1 64.28 0 overflow\n" "gpr cf .1 64.29 0 carry\n" "gpr zf .1 64.30 0 zero\n" "gpr nf .1 64.31 0 negative\n" "gpr itc .4 64.10 0 if_then_count\n" "gpr gef .4 64.16 0 great_or_equal\n" "gpr r0 .32 0 0\n" "gpr r1 .32 4 0\n" "gpr r2 .32 8 0\n" "gpr r3 .32 12 0\n" "gpr r4 .32 16 0\n" "gpr r5 .32 20 0\n" "gpr r6 .32 24 0\n" "gpr r7 .32 28 0\n" "gpr r8 .32 32 0\n" "gpr r9 .32 36 0\n" "gpr r10 .32 40 0\n" "gpr r11 .32 44 0\n" "gpr r12 .32 48 0\n" "gpr r13 .32 52 0\n" "gpr r14 .32 56 0\n" "gpr r15 .32 60 0\n" "gpr r16 .32 64 0\n" "gpr r17 .32 68 0\n")
RZ_API bool rz_test_test_database_load(RzTestDatabase *db, const char *path)
Definition: load.c:756
RZ_API RzTestDatabase * rz_test_test_database_new(void)
Definition: load.c:567
RZ_API void rz_test_test_database_free(RzTestDatabase *db)
Definition: load.c:577
RZ_API void rz_test_test_free(RzTest *test)
Definition: load.c:546
RZ_API bool rz_test_test_database_load_fuzz(RzTestDatabase *db, const char *path)
Definition: load.c:782
#define expect(expr, value)
Definition: lz4.c:170
assert(limit<=UINT32_MAX/2)
int args
Definition: mipsasm.c:18
line
Definition: setup.py:34
-lz4-versions
static const char hex[16]
Definition: print.c:21
#define eprintf(x, y...)
Definition: rlcc.c:7
static RzSocket * s
Definition: rtr.c:28
static char * replace_cmd_kv(const char *path, const char *content, size_t line_begin, size_t line_end, const char *key, const char *value, RzPVector *fixup_results)
Definition: rz-test.c:1136
#define WORKERS_DEFAULT_STR
Definition: rz-test.c:25
static void print_result_diff(RzTestRunConfig *config, RzTestResultInfo *result)
Definition: rz-test.c:771
static int help(bool verbose)
Definition: rz-test.c:62
static bool interact_fix_cmd(RzTestResultInfo *result, RzPVector *fixup_results)
Definition: rz-test.c:1170
static bool interact_fix_asm(RzTestResultInfo *result)
Definition: rz-test.c:1245
static void path_left_free_kv(HtPPKv *kv)
Definition: rz-test.c:91
#define RIZIN_CMD_DEFAULT
Definition: rz-test.c:18
static void interact_break(RzTestResultInfo *result, RzPVector *fixup_results)
Definition: rz-test.c:1314
#define RZ_ASM_CMD_DEFAULT
Definition: rz-test.c:19
static bool log_mode
Definition: rz-test.c:185
static void save_test_file_for_fix(const char *path, const char *newc)
Definition: rz-test.c:1126
struct rz_test_state_t RzTestState
struct rz_testfile_counts_t RzTestFileCounts
static char * format_cmd_kv(const char *key, const char *val)
Definition: rz-test.c:1029
static void test_result_to_json(PJ *pj, RzTestResultInfo *result)
Definition: rz-test.c:618
static void print_new_results(RzTestState *state, ut64 prev_completed)
Definition: rz-test.c:861
#define DO_KEY_NUM(key, field)
static void interact(RzTestState *state)
Definition: rz-test.c:953
static char * read_test_file_for_fix(const char *path)
Definition: rz-test.c:1118
static bool rz_test_chdir(const char *argv0)
Definition: rz-test.c:96
static void replace_file_line(const char *path, ut64 line_idx, const char *line_new)
Definition: rz-test.c:1186
static void print_log(RzTestState *state, ut64 prev_completed, ut64 prev_paths_completed)
Definition: rz-test.c:929
static void replace_asm_test(RZ_NONNULL const char *path, ut64 line_idx, int mode, RZ_NONNULL const char *disasm, RZ_NONNULL const ut8 *bytes, size_t bytes_sz, ut64 offset, RZ_NULLABLE const char *il)
Definition: rz-test.c:1200
static char * replace_lines(const char *src, size_t from, size_t to, const char *news)
Definition: rz-test.c:1041
static void print_diff(const char *actual, const char *expected, const char *regexp)
Definition: rz-test.c:722
static void replace_cmd_kv_file(const char *path, ut64 line_begin, ut64 line_end, const char *key, const char *value, RzPVector *fixup_results)
Definition: rz-test.c:1156
#define TIMEOUT_DEFAULT_STR
Definition: rz-test.c:26
int rz_test_main(int argc, const char **argv)
Definition: rz-test.c:187
static bool rz_test_test_run_unit(void)
Definition: rz-test.c:131
#define TIMEOUT_DEFAULT
Definition: rz-test.c:21
#define JSON_TEST_FILE_DEFAULT
Definition: rz-test.c:20
static void print_state(RzTestState *state, ut64 prev_completed)
Definition: rz-test.c:908
static void fixup_tests(RzPVector *results, const char *edited_file, ut64 start_line, st64 delta)
Definition: rz-test.c:1078
static void interact_commands(RzTestResultInfo *result, RzPVector *fixup_results)
Definition: rz-test.c:1327
static bool asm_test_failed_both_ways(RzAsmTest *test, RzAsmTestOutput *out)
Definition: rz-test.c:1228
static bool rz_test_chdir_fromtest(const char *test_path)
Definition: rz-test.c:135
static void interact_break_cmd(RzTestResultInfo *result, RzPVector *fixup_results)
Definition: rz-test.c:1293
static void interact_break_asm(RzTestResultInfo *result)
Definition: rz-test.c:1307
#define WORKERS_DEFAULT
Definition: rz-test.c:17
static RzSubprocessOutput * print_runner(const char *file, const char *args[], size_t args_size, const char *envvars[], const char *envvals[], size_t env_size, ut64 timeout_ms, void *user)
Definition: rz-test.c:752
static void * worker_th(RzTestState *state)
Definition: rz-test.c:664
#define DO_KEY_STR(key, field)
int MAIN_NAME(int argc, const ARGV_TYPE **argv)
Definition: rz-test.c:1385
#define DO_KEY_BOOL(key, field)
static bool interact_fix(RzTestResultInfo *result, RzPVector *fixup_results)
Definition: rz-test.c:1282
static void print_state_counts(RzTestState *state)
Definition: rz-test.c:903
#define rz_return_if_fail(expr)
Definition: rz_assert.h:100
#define RZ_CONS_CLEAR_LINE
Definition: rz_cons.h:593
#define UTF8_POLICE_CARS_REVOLVING_LIGHT
Definition: rz_cons.h:422
#define Color_RESET
Definition: rz_cons.h:617
#define Color_CYAN
Definition: rz_cons.h:633
#define RZ_CONS_CURSOR_UP
Definition: rz_cons.h:600
#define Color_GREEN
Definition: rz_cons.h:627
#define Color_RED
Definition: rz_cons.h:623
#define UTF8_SEE_NO_EVIL_MONKEY
Definition: rz_cons.h:424
#define Color_YELLOW
Definition: rz_cons.h:631
#define UTF8_DOOR
Definition: rz_cons.h:428
#define UTF8_WHITE_HEAVY_CHECK_MARK
Definition: rz_cons.h:423
#define Color_BLUE
Definition: rz_cons.h:635
#define UTF8_KEYBOARD
Definition: rz_cons.h:426
#define UTF8_VS16
Definition: rz_cons.h:431
#define UTF8_SKULL_AND_CROSSBONES
Definition: rz_cons.h:425
RZ_API bool rz_file_is_directory(const char *str)
Definition: file.c:167
RZ_API bool rz_file_is_abspath(const char *file)
Definition: file.c:214
RZ_API char * rz_file_abspath(const char *file)
Definition: file.c:267
RZ_API int rz_file_mkstemp(RZ_NULLABLE const char *prefix, char **oname)
Definition: file.c:1058
RZ_API RZ_OWN char * rz_file_slurp(const char *str, RZ_NULLABLE size_t *usz)
Definition: file.c:454
RZ_API bool rz_file_dump(const char *file, const ut8 *buf, int len, bool append)
Definition: file.c:838
RZ_API char * rz_file_path(const char *bin)
Definition: file.c:354
RZ_API char * rz_file_abspath_rel(const char *cwd, const char *file)
Definition: file.c:219
RZ_API void rz_getopt_init(RzGetopt *go, int argc, const char **argv, const char *ostr)
Definition: getopt.c:17
RZ_API int rz_getopt_next(RzGetopt *opt)
Definition: getopt.c:29
RZ_API char * rz_hex_bin2strdup(const ut8 *in, int len)
Definition: hex.c:415
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
#define ARGV_TYPE_TO_UTF8(argc, argv)
Definition: rz_main.h:30
#define FREE_UTF8_ARGV(argc, utf8_argv)
Definition: rz_main.h:31
#define ARGV_TYPE
Definition: rz_main.h:29
RZ_API ut64 rz_num_math(RzNum *num, const char *str)
Definition: unum.c:456
RZ_API int rz_num_is_valid_input(RzNum *num, const char *input_value)
Definition: unum.c:676
RZ_API PJ * pj_new(void)
Definition: pj.c:25
RZ_API PJ * pj_kb(PJ *j, const char *k, bool v)
Definition: pj.c:177
RZ_API char * pj_drain(PJ *j)
Definition: pj.c:50
RZ_API PJ * pj_ki(PJ *j, const char *k, int d)
Definition: pj.c:149
RZ_API PJ * pj_k(PJ *j, const char *k)
Definition: pj.c:104
RZ_API PJ * pj_end(PJ *j)
Definition: pj.c:87
RZ_API PJ * pj_o(PJ *j)
Definition: pj.c:75
RZ_API PJ * pj_s(PJ *j, const char *k)
Definition: pj.c:197
RZ_API PJ * pj_ks(PJ *j, const char *k, const char *v)
Definition: pj.c:170
RZ_API PJ * pj_kn(PJ *j, const char *k, ut64 n)
Definition: pj.c:121
RZ_API PJ * pj_a(PJ *j)
Definition: pj.c:81
RZ_API RzList * rz_regex_get_match_list(const char *pattern, const char *flags, const char *text)
Definition: regcomp.c:155
RZ_API int rz_str_char_count(const char *string, char ch)
Definition: str.c:611
RZ_API const char * rz_str_lchr(const char *str, char chr)
Definition: str.c:669
RZ_API char * rz_str_newf(const char *fmt,...) RZ_PRINTF_CHECK(1
RZ_API void rz_str_trim(RZ_NONNULL RZ_INOUT char *str)
Removes whitespace characters (space, tab, newline etc.) from the beginning and end of a string.
Definition: str_trim.c:190
RZ_API bool rz_str_startswith(RZ_NONNULL const char *str, RZ_NONNULL const char *needle)
Checks if a string starts with a specifc sequence of characters (case sensitive)
Definition: str.c:3286
RZ_API char * rz_str_version(const char *program)
Definition: str.c:4051
RZ_API RZ_OWN char * rz_strbuf_drain_nofree(RzStrBuf *sb)
Definition: strbuf.c:349
RZ_API bool rz_strbuf_append(RzStrBuf *sb, const char *s)
Definition: strbuf.c:222
RZ_API bool rz_strbuf_appendf(RzStrBuf *sb, const char *fmt,...) RZ_PRINTF_CHECK(2
RZ_API void rz_strbuf_init(RzStrBuf *sb)
Definition: strbuf.c:33
RZ_API bool rz_strbuf_append_n(RzStrBuf *sb, const char *s, size_t l)
Definition: strbuf.c:229
RZ_API void rz_subprocess_fini(void)
Definition: subprocess.c:814
RZ_API bool rz_subprocess_init(void)
Definition: subprocess.c:787
RZ_API int rz_sys_cmdf(const char *fmt,...) RZ_PRINTF_CHECK(1
RZ_API char * rz_sys_getenv(const char *key)
Get the value of an environment variable named key or NULL if none exists.
Definition: sys.c:483
RZ_API int rz_sys_setenv(const char *key, const char *value)
Set an environment variable in the calling process.
Definition: sys.c:405
RZ_API bool rz_sys_chdir(RZ_NONNULL const char *s)
Change current directory to s, taking care of home expansion ~.
Definition: sys.c:532
RZ_API int rz_sys_system(const char *command)
Definition: sys.c:1658
RZ_API char * rz_sys_getdir(void)
Get current working directory.
Definition: sys.c:521
@ RZ_TEST_RESULT_OK
Definition: rz_test.h:162
@ RZ_TEST_RESULT_FAILED
Definition: rz_test.h:163
@ RZ_TEST_RESULT_FIXED
Definition: rz_test.h:165
@ RZ_TEST_RESULT_BROKEN
Definition: rz_test.h:164
#define RZ_TEST_ARCH_OS
Definition: rz_test.h:33
@ RZ_TEST_TYPE_JSON
Definition: rz_test.h:122
@ RZ_TEST_TYPE_ASM
Definition: rz_test.h:121
@ RZ_TEST_TYPE_FUZZ
Definition: rz_test.h:123
@ RZ_TEST_TYPE_CMD
Definition: rz_test.h:120
#define RZ_CMD_TEST_FOREACH_RECORD(macro_str, macro_bool, macro_int)
Definition: rz_test.h:74
@ RZ_ASM_TEST_MODE_BIG_ENDIAN
Definition: rz_test.h:91
@ RZ_ASM_TEST_MODE_BROKEN
Definition: rz_test.h:92
@ RZ_ASM_TEST_MODE_ASSEMBLE
Definition: rz_test.h:89
@ RZ_ASM_TEST_MODE_DISASSEMBLE
Definition: rz_test.h:90
void *(* RzThreadFunction)(void *user)
Definition: rz_th.h:28
RZ_API ut64 rz_time_now_mono(void)
Returns the current time in microseconds, using the monotonic clock.
Definition: time.c:102
#define RZ_SYS_DIR
Definition: rz_types.h:218
#define RZ_NULLABLE
Definition: rz_types.h:65
#define RZ_NONNULL
Definition: rz_types.h:64
#define PFMT64u
Definition: rz_types.h:395
#define RZ_ARRAY_SIZE(x)
Definition: rz_types.h:300
#define RZ_FREE(x)
Definition: rz_types.h:369
#define PFMT64x
Definition: rz_types.h:393
#define st64
Definition: rz_types_base.h:10
#define UT64_MAX
Definition: rz_types_base.h:86
RZ_API void * rz_pvector_pop(RzPVector *vec)
Definition: vector.c:372
static void ** rz_pvector_insert_range(RzPVector *vec, size_t index, void **first, size_t count)
Definition: rz_vector.h:289
RZ_API void rz_pvector_init(RzPVector *vec, RzPVectorFree free)
Definition: vector.c:298
static size_t rz_pvector_len(const RzPVector *vec)
Definition: rz_vector.h:231
RZ_API RzPVector * rz_pvector_new(RzPVectorFree free)
Definition: vector.c:302
void(* RzPVectorFree)(void *e)
Definition: rz_vector.h:43
static bool rz_pvector_empty(RzPVector *vec)
Definition: rz_vector.h:246
RZ_API void * rz_pvector_remove_at(RzPVector *vec, size_t index)
Definition: vector.c:355
static void ** rz_pvector_push(RzPVector *vec, void *x)
Definition: rz_vector.h:300
RZ_API void rz_pvector_free(RzPVector *vec)
Definition: vector.c:336
RZ_API void rz_pvector_clear(RzPVector *vec)
Definition: vector.c:326
static void * rz_pvector_at(const RzPVector *vec, size_t index)
Definition: rz_vector.h:236
#define rz_pvector_foreach(vec, it)
Definition: rz_vector.h:334
#define RZ_VERSION
Definition: rz_version.h:8
static struct sockaddr static addrlen static backlog const void static flags void struct sockaddr from
Definition: sfsocketcall.h:123
static struct sockaddr static addrlen static backlog const void static flags void struct sockaddr socklen_t static fromlen const void const struct sockaddr to
Definition: sfsocketcall.h:125
unsigned int uint32_t
Definition: sftypes.h:29
int ssize_t
Definition: sftypes.h:39
#define d(i)
Definition: sha256.c:44
#define c(i)
Definition: sha256.c:43
Definition: gzappend.c:170
Definition: z80asm.h:102
Definition: rz_pj.h:12
Definition: diff.c:89
const char * arg
Definition: rz_getopt.h:15
ut8 * out
< Output generated by the process
Definition: rz_subprocess.h:41
int ret
True if the process has exited because of a timeout.
Definition: rz_subprocess.h:49
ut8 * err
Number of bytes in the err field.
Definition: rz_subprocess.h:45
RzCmdTestStringRecord expect
Definition: rz_test.h:59
RzCmdTestStringRecord regexp_out
Definition: rz_test.h:61
RzCmdTestStringRecord regexp_err
Definition: rz_test.h:62
RzCmdTestStringRecord expect_err
Definition: rz_test.h:60
HtPP * path_left
Definition: rz-test.c:44
ut64 ok_count
Definition: rz-test.c:46
RzPVector queue
Definition: rz-test.c:50
RzTestRunConfig run_config
Definition: rz-test.c:37
ut64 br_count
Definition: rz-test.c:48
ut64 xx_count
Definition: rz-test.c:47
bool verbose
Definition: rz-test.c:38
RzThreadCond * cond
Definition: rz-test.c:42
RzPVector completed_paths
Definition: rz-test.c:45
RzPVector results
Definition: rz-test.c:51
ut64 fx_count
Definition: rz-test.c:49
PJ * test_results
Definition: rz-test.c:40
RzTestDatabase * db
Definition: rz-test.c:39
RzThreadLock * lock
Definition: rz-test.c:43
RzAsmTestOutput * asm_out
Definition: rz_test.h:176
RzSubprocessOutput * proc_out
Definition: rz_test.h:175
RzCmdTest * cmd_test
Definition: rz_test.h:130
const char * path
Definition: rz_test.h:127
RzFuzzTest * fuzz_test
Definition: rz_test.h:133
RzAsmTest * asm_test
Definition: rz_test.h:131
RzTestType type
Definition: rz_test.h:128
Definition: thread.h:84
Definition: dis.h:43
struct child_worker * workers
uint64_t streams
Definition: list.c:103
RZ_API void rz_th_cond_free(RZ_NULLABLE RzThreadCond *cond)
Frees a RzThreadCond struct.
Definition: thread_cond.c:77
RZ_API void rz_th_cond_signal(RZ_NONNULL RzThreadCond *cond)
This function shall unblock at least one of the threads that are blocked on the specified condition.
Definition: thread_cond.c:34
RZ_API void rz_th_cond_wait(RZ_NONNULL RzThreadCond *cond, RZ_NONNULL RzThreadLock *lock)
The function shall block on a condition variable and shall be called with RzThreadLock locked by the ...
Definition: thread_cond.c:63
RZ_API RZ_OWN RzThreadCond * rz_th_cond_new(void)
Condition variables are intended to be used to communicate changes in the state of data shared betwee...
Definition: thread_cond.c:13
RZ_API void rz_th_lock_leave(RZ_NONNULL RzThreadLock *thl)
Releases a RzThreadLock structure.
Definition: thread_lock.c:75
RZ_API void rz_th_lock_free(RZ_NULLABLE RzThreadLock *thl)
Frees a RzThreadLock structure.
Definition: thread_lock.c:89
RZ_API RZ_OWN RzThreadLock * rz_th_lock_new(bool recursive)
Allocates and initialize a RzThreadLock structure.
Definition: thread_lock.c:14
RZ_API void rz_th_lock_enter(RZ_NONNULL RzThreadLock *thl)
Acquires a RzThreadLock structure.
Definition: thread_lock.c:45
RZ_API RZ_OWN char * rz_diff_unified_text(RZ_NONNULL RzDiff *diff, RZ_NULLABLE const char *from, RZ_NULLABLE const char *to, bool show_time, bool color)
Produces a diff output with A and B inputs presented immediately adjacent to each other.
Definition: unified_diff.c:333
static st64 delta
Definition: vmenus.c:2425
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING
Definition: tty.c:64
if(dbg->bits==RZ_SYS_BITS_64)
Definition: windows-arm64.h:4
DWORD * HANDLE
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
DWORD
static void nothing(void)
Definition: xtensa-dis.c:44
static const z80_opcode fd[]
Definition: z80_tab.h:997
static int verbose
Definition: z80asm.c:73
diff_output_t output
Definition: zipcmp.c:237