mirror of
https://github.com/danog/ir.git
synced 2024-12-12 09:19:47 +01:00
8668550427
Currently we may just load LLVM module and convert all functions into IR (print it). For better LLVM support IR framework needs support for modules. Some LLVM features are not supported yet (see TODOs in ir_load_llvm.c) .
660 lines
17 KiB
C
660 lines
17 KiB
C
/*
|
|
* IR - Lightweight JIT Compilation Framework
|
|
* (Test runner implementation)
|
|
* Copyright (C) 2023 by IR project.
|
|
* Authors: Dmitry Stogov <dmitry@php.net>, Anatol Belski <anbelski@linux.microsoft.com>
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef _WIN32
|
|
# include <io.h>
|
|
# include <windows.h>
|
|
# pragma warning(disable : 4996)
|
|
# define PATH_SEP '\\'
|
|
# define DEFAULT_DIFF_CMD "fc"
|
|
# define S_IRUSR _S_IREAD
|
|
# define S_IWUSR _S_IWRITE
|
|
#else
|
|
# include <dirent.h>
|
|
# include <unistd.h>
|
|
# include <alloca.h>
|
|
# define PATH_SEP '/'
|
|
# define DEFAULT_DIFF_CMD "diff --strip-trailing-cr -u"
|
|
# define O_BINARY 0
|
|
#endif
|
|
|
|
typedef enum _color {GREEN, YELLOW, RED} color;
|
|
|
|
typedef struct _test {
|
|
int id;
|
|
char *name;
|
|
char *target;
|
|
char *args;
|
|
char *code;
|
|
char *expect;
|
|
char *xfail;
|
|
} test;
|
|
|
|
static int colorize = 1;
|
|
|
|
static const char *test_cmd = NULL;
|
|
static const char *target = NULL;
|
|
static const char *default_args = NULL;
|
|
static const char *additional_args = NULL;
|
|
static const char *diff_cmd = NULL;
|
|
|
|
static const char *test_extension = NULL;
|
|
static int test_extension_len = 0;
|
|
|
|
static const char *code_extension = NULL;
|
|
static int code_extension_len = 0;
|
|
|
|
static char **files = NULL;
|
|
static int files_count = 0;
|
|
static int files_limit = 0;
|
|
|
|
static void print_color(const char *s, color c)
|
|
{
|
|
if (colorize) {
|
|
switch (c) {
|
|
case GREEN:
|
|
printf("\x1b[1;32m%s\x1b[0m", s);
|
|
return;
|
|
case YELLOW:
|
|
printf("\x1b[1;33m%s\x1b[0m", s);
|
|
return;
|
|
case RED:
|
|
printf("\x1b[1;31m%s\x1b[0m", s);
|
|
return;
|
|
}
|
|
}
|
|
printf("%s", s);
|
|
}
|
|
|
|
static void init_console()
|
|
{
|
|
#ifdef _WIN32
|
|
if (colorize) {
|
|
DWORD mode, new_mode = ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
|
|
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
GetConsoleMode(h, &mode);
|
|
SetConsoleMode(h, mode | new_mode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static test *parse_file(const char *filename, int id)
|
|
{
|
|
test *t;
|
|
char *buf, **section;
|
|
int fd;
|
|
size_t start, i, size;
|
|
struct stat stat_buf;
|
|
|
|
if (stat(filename, &stat_buf) != 0) {
|
|
return NULL;
|
|
}
|
|
size = stat_buf.st_size;
|
|
fd = open(filename, O_RDONLY | O_BINARY, 0);
|
|
if (fd < 0) {
|
|
return NULL;
|
|
}
|
|
t = malloc(sizeof(test) + size + 1);
|
|
if (!t) {
|
|
return NULL;
|
|
}
|
|
buf = (char*)t + sizeof(test);
|
|
if ((size_t)read(fd, buf, size) != size) {
|
|
free(t);
|
|
return NULL;
|
|
}
|
|
close(fd);
|
|
memset(t, 0, sizeof(test));
|
|
buf[size] = 0;
|
|
i = 0;
|
|
while (i < size) {
|
|
start = i;
|
|
while (i < size && buf[i] != '\r' && buf[i] != '\n') i++;
|
|
if (i - start == strlen("--TEST--") && memcmp(buf + start, "--TEST--", strlen("--TEST--")) == 0) {
|
|
section = &t->name;
|
|
} else if (i - start == strlen("--ARGS--") && memcmp(buf + start, "--ARGS--", strlen("--ARGS--")) == 0) {
|
|
section = &t->args;
|
|
} else if (i - start == strlen("--CODE--") && memcmp(buf + start, "--CODE--", strlen("--CODE--")) == 0) {
|
|
section = &t->code;
|
|
} else if (i - start == strlen("--EXPECT--") && memcmp(buf + start, "--EXPECT--", strlen("--EXPECT--")) == 0) {
|
|
section = &t->expect;
|
|
} else if (i - start == strlen("--XFAIL--") && memcmp(buf + start, "--XFAIL--", strlen("--XFAIL--")) == 0) {
|
|
section = &t->xfail;
|
|
} else if (i - start == strlen("--TARGET--") && memcmp(buf + start, "--TARGET--", strlen("--TARGET--")) == 0) {
|
|
section = &t->target;
|
|
} else {
|
|
section = NULL;
|
|
while (i < size && (buf[i] == '\r' || buf[i] == '\n')) i++;
|
|
}
|
|
if (section) {
|
|
while (start > 0 && (buf[start - 1] == '\r' || buf[start - 1] == '\n')) start--;
|
|
buf[start] = 0;
|
|
if (*section) {
|
|
free(t);
|
|
return NULL;
|
|
}
|
|
while (i < size && (buf[i] == '\r' || buf[i] == '\n')) i++;
|
|
*section = buf + i;
|
|
}
|
|
}
|
|
if (!t->name || !t->code || !t->expect) {
|
|
free(t);
|
|
return NULL;
|
|
}
|
|
t->id = id;
|
|
return t;
|
|
}
|
|
|
|
static int skip_test(test *t)
|
|
{
|
|
if (target && (t->target && strcmp(t->target, target) != 0)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int same_text(const char *exp, const char *out)
|
|
{
|
|
int i, j;
|
|
|
|
while (*exp == '\r' || *exp == '\n') exp++;
|
|
while (*out == '\r' || *out == '\n') out++;
|
|
while (*exp != 0 && *out != 0) {
|
|
i = j = 0;
|
|
while (exp[i] != 0 && exp[i] != '\r' && exp[i] != '\n') i++;
|
|
while (out[j] != 0 && out[j] != '\r' && out[j] != '\n') j++;
|
|
if (i != j || memcmp(exp, out, i) != 0) {
|
|
return 0;
|
|
}
|
|
exp += i;
|
|
out += j;
|
|
if (*exp == '\r') exp++;
|
|
if (*exp == '\n') exp++;
|
|
if (*out == '\r') out++;
|
|
if (*out == '\n') out++;
|
|
}
|
|
while (*exp == '\r' || *exp == '\n') exp++;
|
|
while (*out == '\r' || *out == '\n') out++;
|
|
return *exp == 0 && *out == 0;
|
|
}
|
|
|
|
static char *read_file(const char *filename)
|
|
{
|
|
struct stat stat_buf;
|
|
size_t size;
|
|
char *buf;
|
|
int fd;
|
|
|
|
if (stat(filename, &stat_buf) != 0) {
|
|
return NULL;
|
|
}
|
|
size = stat_buf.st_size;
|
|
buf = malloc(size + 1);
|
|
if (!buf) {
|
|
return NULL;
|
|
}
|
|
fd = open(filename, O_RDONLY | O_BINARY, 0);
|
|
if (fd < 0) {
|
|
free(buf);
|
|
return NULL;
|
|
}
|
|
if ((size_t)read(fd, buf, size) != size) {
|
|
free(buf);
|
|
return NULL;
|
|
}
|
|
close(fd);
|
|
buf[size] = 0;
|
|
return buf;
|
|
}
|
|
|
|
static int write_file(const char *filename, const char *buf, size_t size)
|
|
{
|
|
int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, S_IRUSR | S_IWUSR);
|
|
if (fd < 0) {
|
|
return 0;
|
|
}
|
|
if ((size_t)write(fd, buf, size) != size) {
|
|
return 0;
|
|
}
|
|
close(fd);
|
|
return 1;
|
|
}
|
|
|
|
static char *replace_extension(const char *filename, size_t len, const char *ext, size_t ext_len)
|
|
{
|
|
char *ret;
|
|
|
|
if (test_extension) {
|
|
ret = malloc(len - test_extension_len + ext_len + 1);
|
|
memcpy(ret, filename, len - test_extension_len);
|
|
memcpy(ret + len - test_extension_len, ext, ext_len + 1);
|
|
} else {
|
|
ret = malloc(len + ext_len + 1);
|
|
memcpy(ret, filename, len);
|
|
memcpy(ret + len, ext, ext_len + 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int run_test(const char *filename, test *t, int show_diff)
|
|
{
|
|
size_t len;
|
|
int ret;
|
|
char cmd[4096];
|
|
char *code_filename, *out_filename, *exp_filename, *diff_filename, *out;
|
|
|
|
len = strlen(filename);
|
|
|
|
code_filename = replace_extension(filename, len, code_extension, code_extension_len);
|
|
out_filename = replace_extension(filename, len, ".out", strlen(".out"));
|
|
exp_filename = replace_extension(filename, len, ".exp", strlen(".exp"));
|
|
diff_filename = replace_extension(filename, len, ".diff", strlen(".diff"));
|
|
|
|
unlink(code_filename);
|
|
unlink(out_filename);
|
|
unlink(exp_filename);
|
|
unlink(diff_filename);
|
|
|
|
if (!write_file(code_filename, t->code, strlen(t->code))) {
|
|
free(code_filename);
|
|
free(out_filename);
|
|
free(exp_filename);
|
|
free(diff_filename);
|
|
return 0;
|
|
}
|
|
|
|
if ((size_t)snprintf(cmd, sizeof(cmd), "%s %s %s %s > %s 2>&1",
|
|
test_cmd, code_filename,
|
|
t->args ? t->args : default_args, additional_args,
|
|
out_filename) > sizeof(cmd)) {
|
|
free(code_filename);
|
|
free(out_filename);
|
|
free(exp_filename);
|
|
free(diff_filename);
|
|
return 0;
|
|
}
|
|
|
|
ret = system(cmd) >> 8;
|
|
|
|
out = read_file(out_filename);
|
|
if (!out) {
|
|
out = malloc(1);
|
|
out[0] = 0;
|
|
}
|
|
|
|
ret = ret == 0 && same_text(t->expect, out);
|
|
if (ret) {
|
|
unlink(code_filename);
|
|
unlink(out_filename);
|
|
} else {
|
|
if (write_file(exp_filename, t->expect, strlen(t->expect))) {
|
|
if ((size_t)snprintf(cmd, sizeof(cmd), "%s %s %s > %s",
|
|
diff_cmd, exp_filename, out_filename, diff_filename) < sizeof(cmd)) {
|
|
system(cmd);
|
|
if (show_diff && !t->xfail) {
|
|
char *diff = read_file(diff_filename);
|
|
printf("\n");
|
|
printf("%s", diff);
|
|
free(diff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free(out);
|
|
free(code_filename);
|
|
free(out_filename);
|
|
free(exp_filename);
|
|
free(diff_filename);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void add_file(char *name)
|
|
{
|
|
if (files_count >= files_limit) {
|
|
files_limit += 1024;
|
|
files = realloc(files, sizeof(char*) * files_limit);
|
|
}
|
|
files[files_count++] = name;
|
|
}
|
|
|
|
static void find_files_in_dir(const char *dir_name, size_t dir_name_len)
|
|
{
|
|
char *name;
|
|
size_t len;
|
|
#ifdef _WIN32
|
|
char buf[MAX_PATH];
|
|
HANDLE dir;
|
|
WIN32_FIND_DATA info;
|
|
|
|
memcpy(buf, dir_name, dir_name_len);
|
|
buf[dir_name_len] = '\\';
|
|
buf[dir_name_len + 1] = '*';
|
|
buf[dir_name_len + 2] = 0;
|
|
|
|
dir = FindFirstFile(buf, &info);
|
|
if (dir == INVALID_HANDLE_VALUE) {
|
|
fprintf(stderr, "ERROR: Cannot read directory [%s]\n", dir_name);
|
|
return;
|
|
}
|
|
do {
|
|
len = strlen(info.cFileName);
|
|
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
if ((len == 1 && info.cFileName[0] == '.')
|
|
|| (len == 2 && info.cFileName[0] == '.' && info.cFileName[1] == '.')) {
|
|
/* skip */
|
|
} else {
|
|
name = malloc(dir_name_len + len + 2);
|
|
memcpy(name, dir_name, dir_name_len);
|
|
name[dir_name_len] = PATH_SEP;
|
|
memcpy(name + dir_name_len + 1, info.cFileName, len + 1);
|
|
find_files_in_dir(name, dir_name_len + len + 1);
|
|
free(name);
|
|
}
|
|
} else /*if (info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)*/ {
|
|
if (!test_extension
|
|
|| stricmp(info.cFileName + len - test_extension_len, test_extension) == 0) {
|
|
name = malloc(dir_name_len + len + 2);
|
|
memcpy(name, dir_name, dir_name_len);
|
|
name[dir_name_len] = PATH_SEP;
|
|
memcpy(name + dir_name_len + 1, info.cFileName, len + 1);
|
|
add_file(name);
|
|
}
|
|
}
|
|
} while (FindNextFile(dir, &info));
|
|
FindClose(dir);
|
|
#else
|
|
DIR *dir = opendir(dir_name);
|
|
struct dirent *info;
|
|
|
|
if (!dir) {
|
|
fprintf(stderr, "ERROR: Cannot read directory [%s]\n", dir_name);
|
|
return;
|
|
}
|
|
while ((info = readdir(dir)) != 0) {
|
|
len = strlen(info->d_name);
|
|
if (info->d_type == DT_DIR) {
|
|
if ((len == 1 && info->d_name[0] == '.')
|
|
|| (len == 2 && info->d_name[0] == '.' && info->d_name[1] == '.')) {
|
|
/* skip */
|
|
} else {
|
|
name = malloc(dir_name_len + len + 2);
|
|
memcpy(name, dir_name, dir_name_len);
|
|
name[dir_name_len] = PATH_SEP;
|
|
memcpy(name + dir_name_len + 1, info->d_name, len + 1);
|
|
find_files_in_dir(name, dir_name_len + len + 1);
|
|
free(name);
|
|
}
|
|
} else if (info->d_type == DT_REG) {
|
|
if (!test_extension
|
|
|| strcasecmp(info->d_name + len - test_extension_len, test_extension) == 0) {
|
|
name = malloc(dir_name_len + len + 2);
|
|
memcpy(name, dir_name, dir_name_len);
|
|
name[dir_name_len] = PATH_SEP;
|
|
memcpy(name + dir_name_len + 1, info->d_name, len + 1);
|
|
add_file(name);
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
#endif
|
|
}
|
|
|
|
static int cmp_files(const void *s1, const void *s2)
|
|
{
|
|
return strcmp(*(const char**)s1, *(const char**)s2);
|
|
}
|
|
|
|
static void find_files(char **tests, int tests_count)
|
|
{
|
|
int i;
|
|
struct stat stat_buf;
|
|
|
|
for (i = 0; i < tests_count; i++) {
|
|
if (stat(tests[i], &stat_buf) != 0) {
|
|
fprintf(stderr, "ERROR: Bad File or Folder [%s]\n", tests[i]);
|
|
}
|
|
if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
|
|
find_files_in_dir(tests[i], strlen(tests[i]));
|
|
} else {
|
|
add_file(strdup(tests[i]));
|
|
}
|
|
}
|
|
qsort(files, files_count, sizeof(char*), cmp_files);
|
|
}
|
|
|
|
static void print_help(const char *exe_name)
|
|
{
|
|
printf(
|
|
"Run IR uint tests\n"
|
|
"Usage:\n"
|
|
" %s --test-cmd <cmd> {options} <test folders or files...>\n"
|
|
" Run the \"--CODE--\" section of specified test files using <cmd>\n"
|
|
"Options:\n"
|
|
" --target <target> - skip tests that specifies different --TARGET--\n"
|
|
" --default-args <args> - default <cmd> arguments (if --ARGS-- is missed)\n"
|
|
" --additional-args <args> - additional <cmd> arguments (always added at the end)\n"
|
|
" --diff-cmd <cmd> - diff command\n"
|
|
" --test-extension <ext> - search test files with the given extension\n"
|
|
" --code-extension <ext> - produce code files with the given extension\n"
|
|
" --show-diff - show diff of the failed tests\n"
|
|
" --no-color - disable color output\n"
|
|
, exe_name);
|
|
}
|
|
|
|
static int check_arg(const char *name, const char **value, int argc, char **argv, int *pos, int *bad_opt)
|
|
{
|
|
if (strcmp(argv[*pos], name) == 0) {
|
|
if (*value) {
|
|
*bad_opt = 1;
|
|
fprintf(stderr, "ERROR: Duplicate %s\n", name);
|
|
} else if (*pos + 1 == argc) {
|
|
*bad_opt = 1;
|
|
fprintf(stderr, "ERROR: Missing %s value\n", name);
|
|
} else {
|
|
*value = argv[++(*pos)];
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int bad_opt = 0; // unsupported option given
|
|
int bad_files = 0; // bad file or folder given
|
|
int show_diff = 0;
|
|
struct stat stat_buf;
|
|
#ifdef _WIN32
|
|
char **tests = _alloca(sizeof(char*) * argc);
|
|
#else
|
|
char **tests = alloca(sizeof(char*) * argc);
|
|
#endif
|
|
int i, tests_count = 0;
|
|
int skipped = 0;
|
|
int passed = 0;
|
|
int xfailed = 0, xfailed_limit = 0;
|
|
int failed = 0, failed_limit = 0;
|
|
int broken = 0, broken_limit = 0;
|
|
test *t, **xfailed_tests = NULL, **failed_tests = NULL;
|
|
char **broken_tests = NULL;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
|
print_help(argv[0]);
|
|
return 0;
|
|
} else if (check_arg("--test-cmd", &test_cmd, argc, argv, &i, &bad_opt)) {
|
|
} else if (check_arg("--target", &target, argc, argv, &i, &bad_opt)) {
|
|
} else if (check_arg("--default-args", &default_args, argc, argv, &i, &bad_opt)) {
|
|
} else if (check_arg("--additional-args", &additional_args, argc, argv, &i, &bad_opt)) {
|
|
} else if (check_arg("--diff-cmd", &diff_cmd, argc, argv, &i, &bad_opt)) {
|
|
} else if (check_arg("--test-extension", &test_extension, argc, argv, &i, &bad_opt)) {
|
|
} else if (check_arg("--code-extension", &code_extension, argc, argv, &i, &bad_opt)) {
|
|
} else if (strcmp(argv[i], "--show-diff") == 0) {
|
|
show_diff = 1;
|
|
} else if (strcmp(argv[i], "--no-color") == 0) {
|
|
colorize = 0;
|
|
} else if (argv[i][0] == '-') {
|
|
bad_opt = 1;
|
|
fprintf(stderr, "ERROR: Unsupported Option [%s]\n", argv[i]);
|
|
} else {
|
|
// User specified test folders/files
|
|
if (stat(argv[i], &stat_buf) == 0
|
|
&& ((stat_buf.st_mode & S_IFMT) == S_IFDIR
|
|
|| (stat_buf.st_mode & S_IFMT) == S_IFREG)) {
|
|
tests[tests_count++] = argv[i];
|
|
} else {
|
|
bad_files = 1;
|
|
fprintf(stderr, "ERROR: Bad File or Folder [%s]\n", argv[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bad_opt || bad_files || !tests_count || !test_cmd) {
|
|
if (bad_opt || !tests_count || !test_cmd) {
|
|
print_help(argv[0]);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (!default_args) {
|
|
default_args = "";
|
|
}
|
|
if (!additional_args) {
|
|
additional_args = "";
|
|
}
|
|
|
|
if (!code_extension) {
|
|
code_extension = ".code";
|
|
}
|
|
code_extension_len = (int)strlen(code_extension);
|
|
if (test_extension) {
|
|
test_extension_len = (int)strlen(test_extension);
|
|
if (strcmp(test_extension, code_extension) == 0) {
|
|
fprintf(stderr, "ERROR: --test-extension and --code-extension can't be the same\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!diff_cmd) {
|
|
diff_cmd = DEFAULT_DIFF_CMD;
|
|
}
|
|
|
|
init_console();
|
|
|
|
find_files(tests, tests_count);
|
|
|
|
// Run each test
|
|
for (i = 0; i < files_count; i++) {
|
|
t = parse_file(files[i], i);
|
|
if (!t) {
|
|
printf("\r");
|
|
print_color("BROK", RED);
|
|
printf(": [%s]\n", files[i]);
|
|
if (broken >= broken_limit) {
|
|
broken_limit += 1024;
|
|
broken_tests = realloc(broken_tests, sizeof(char*) * broken_limit);
|
|
}
|
|
broken_tests[broken++] = files[i];
|
|
continue;
|
|
}
|
|
printf("TEST: %s [%s]", t->name, files[i]);
|
|
fflush(stdout);
|
|
if (skip_test(t)) {
|
|
printf("\r");
|
|
print_color("SKIP", YELLOW);
|
|
printf(": %s [%s]\n", t->name, files[i]);
|
|
skipped++;
|
|
free(t);
|
|
} else if (run_test(files[i], t, show_diff)) {
|
|
printf("\r");
|
|
print_color("PASS", GREEN);
|
|
printf(": %s [%s]\n", t->name, files[i]);
|
|
passed++;
|
|
free(t);
|
|
} else if (t->xfail) {
|
|
printf("\r");
|
|
print_color("XFAIL", RED);
|
|
printf(": %s [%s]\n", t->name, files[i]);
|
|
if (xfailed >= xfailed_limit) {
|
|
xfailed_limit += 1024;
|
|
xfailed_tests = realloc(xfailed_tests, sizeof(test*) * xfailed_limit);
|
|
}
|
|
xfailed_tests[xfailed++] = t;
|
|
} else {
|
|
printf("\r");
|
|
print_color("FAIL", RED);
|
|
printf(": %s [%s]\n", t->name, files[i]);
|
|
if (failed >= failed_limit) {
|
|
failed_limit += 1024;
|
|
failed_tests = realloc(failed_tests, sizeof(test*) * failed_limit);
|
|
}
|
|
failed_tests[failed++] = t;
|
|
}
|
|
}
|
|
|
|
// Produce the summary
|
|
printf("-------------------------------\n");
|
|
printf("Test Sumary\n");
|
|
printf("-------------------------------\n");
|
|
printf("Total: %d\n", files_count);
|
|
printf("Passed: %d\n", passed);
|
|
printf("Skipped: %d\n", skipped);
|
|
printf("Expected fail: %d\n", xfailed);
|
|
printf("Failed: %d\n", failed);
|
|
if (broken) {
|
|
printf("Broken: %d\n", broken);
|
|
}
|
|
if (xfailed > 0) {
|
|
printf("-------------------------------\n");
|
|
printf("EXPECTED FAILED TESTS\n");
|
|
printf("-------------------------------\n");
|
|
for (i = 0; i < xfailed; i++) {
|
|
t = xfailed_tests[i];
|
|
printf("%s [%s] XFAIL REASON: %s\n", t->name, files[t->id], t->xfail);
|
|
free(t);
|
|
}
|
|
free(xfailed_tests);
|
|
}
|
|
if (failed > 0) {
|
|
printf("-------------------------------\n");
|
|
printf("FAILED TESTS\n");
|
|
printf("-------------------------------\n");
|
|
for (i = 0; i < failed; i++) {
|
|
t = failed_tests[i];
|
|
printf("%s [%s]\n", t->name, files[t->id]);
|
|
free(t);
|
|
}
|
|
free(failed_tests);
|
|
}
|
|
if (broken > 0) {
|
|
printf("-------------------------------\n");
|
|
printf("BROKEN TESTS\n");
|
|
printf("-------------------------------\n");
|
|
for (i = 0; i < broken; i++) {
|
|
printf("%s\n", broken_tests[i]);
|
|
}
|
|
free(broken_tests);
|
|
}
|
|
printf("-------------------------------\n");
|
|
|
|
for (i = 0; i < files_count; i++) {
|
|
free(files[i]);
|
|
}
|
|
free(files);
|
|
|
|
return failed > 0 ? 1 : 0;
|
|
}
|