You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
565 lines
12 KiB
565 lines
12 KiB
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "job_list.h"
|
|
#include "igt_core.h"
|
|
|
|
static bool matches_any(const char *str, struct regex_list *list)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < list->size; i++) {
|
|
if (g_regex_match(list->regexes[i], str, 0, NULL))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void add_job_list_entry(struct job_list *job_list,
|
|
char *binary,
|
|
char **subtests,
|
|
size_t subtest_count)
|
|
{
|
|
struct job_list_entry *entry;
|
|
|
|
job_list->size++;
|
|
job_list->entries = realloc(job_list->entries, job_list->size * sizeof(*job_list->entries));
|
|
entry = &job_list->entries[job_list->size - 1];
|
|
|
|
entry->binary = binary;
|
|
entry->subtests = subtests;
|
|
entry->subtest_count = subtest_count;
|
|
}
|
|
|
|
static void add_subtests(struct job_list *job_list, struct settings *settings,
|
|
char *binary,
|
|
struct regex_list *include, struct regex_list *exclude)
|
|
{
|
|
FILE *p;
|
|
char cmd[256] = {};
|
|
char *subtestname;
|
|
char **subtests = NULL;
|
|
size_t num_subtests = 0;
|
|
int s;
|
|
|
|
s = snprintf(cmd, sizeof(cmd), "%s/%s --list-subtests",
|
|
settings->test_root, binary);
|
|
if (s < 0) {
|
|
fprintf(stderr, "Failure generating command string, this shouldn't happen.\n");
|
|
return;
|
|
}
|
|
|
|
if (s >= sizeof(cmd)) {
|
|
fprintf(stderr, "Path to binary too long, ignoring: %s/%s\n",
|
|
settings->test_root, binary);
|
|
return;
|
|
}
|
|
|
|
p = popen(cmd, "r");
|
|
if (!p) {
|
|
fprintf(stderr, "popen failed when executing %s: %s\n",
|
|
cmd,
|
|
strerror(errno));
|
|
return;
|
|
}
|
|
|
|
while (fscanf(p, "%ms", &subtestname) == 1) {
|
|
char piglitname[256];
|
|
|
|
generate_piglit_name(binary, subtestname, piglitname, sizeof(piglitname));
|
|
|
|
if (exclude && exclude->size && matches_any(piglitname, exclude)) {
|
|
free(subtestname);
|
|
continue;
|
|
}
|
|
|
|
if (include && include->size && !matches_any(piglitname, include)) {
|
|
free(subtestname);
|
|
continue;
|
|
}
|
|
|
|
if (settings->multiple_mode) {
|
|
num_subtests++;
|
|
subtests = realloc(subtests, num_subtests * sizeof(*subtests));
|
|
subtests[num_subtests - 1] = strdup(subtestname);
|
|
} else {
|
|
subtests = malloc(sizeof(*subtests));
|
|
*subtests = strdup(subtestname);
|
|
add_job_list_entry(job_list, strdup(binary), subtests, 1);
|
|
subtests = NULL;
|
|
}
|
|
|
|
free(subtestname);
|
|
}
|
|
|
|
if (num_subtests)
|
|
add_job_list_entry(job_list, strdup(binary), subtests, num_subtests);
|
|
|
|
s = pclose(p);
|
|
if (s == 0) {
|
|
return;
|
|
} else if (s == -1) {
|
|
fprintf(stderr, "popen error when executing %s: %s\n", binary, strerror(errno));
|
|
} else if (WIFEXITED(s)) {
|
|
if (WEXITSTATUS(s) == IGT_EXIT_INVALID) {
|
|
char piglitname[256];
|
|
|
|
generate_piglit_name(binary, NULL,
|
|
piglitname, sizeof(piglitname));
|
|
/* No subtests on this one */
|
|
if (exclude && exclude->size &&
|
|
matches_any(piglitname, exclude)) {
|
|
return;
|
|
}
|
|
if (!include || !include->size ||
|
|
matches_any(piglitname, include)) {
|
|
add_job_list_entry(job_list, strdup(binary), NULL, 0);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Test binary %s died unexpectedly\n", binary);
|
|
}
|
|
}
|
|
|
|
static bool filtered_job_list(struct job_list *job_list,
|
|
struct settings *settings,
|
|
int fd)
|
|
{
|
|
FILE *f;
|
|
char buf[128];
|
|
bool ok;
|
|
|
|
if (job_list->entries != NULL) {
|
|
fprintf(stderr, "Caller didn't clear the job list, this shouldn't happen\n");
|
|
exit(1);
|
|
}
|
|
|
|
f = fdopen(fd, "r");
|
|
|
|
while (fscanf(f, "%127s", buf) == 1) {
|
|
if (!strcmp(buf, "TESTLIST") || !(strcmp(buf, "END")))
|
|
continue;
|
|
|
|
/*
|
|
* If the binary name matches exclude filters, no
|
|
* subtests are added.
|
|
*/
|
|
if (settings->exclude_regexes.size && matches_any(buf, &settings->exclude_regexes))
|
|
continue;
|
|
|
|
/*
|
|
* If the binary name matches include filters (or include filters not present),
|
|
* all subtests except those matching exclude filters are added.
|
|
*/
|
|
if (!settings->include_regexes.size || matches_any(buf, &settings->include_regexes)) {
|
|
if (settings->multiple_mode && !settings->exclude_regexes.size)
|
|
/*
|
|
* Optimization; we know that all
|
|
* subtests will be included, so we
|
|
* get to omit executing
|
|
* --list-subtests.
|
|
*/
|
|
add_job_list_entry(job_list, strdup(buf), NULL, 0);
|
|
else
|
|
add_subtests(job_list, settings, buf,
|
|
NULL, &settings->exclude_regexes);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Binary name doesn't match exclude or include filters.
|
|
*/
|
|
add_subtests(job_list, settings, buf,
|
|
&settings->include_regexes,
|
|
&settings->exclude_regexes);
|
|
}
|
|
|
|
ok = job_list->size != 0;
|
|
if (!ok)
|
|
fprintf(stderr, "Filter didn't match any job name\n");
|
|
|
|
return ok;
|
|
}
|
|
|
|
static bool job_list_from_test_list(struct job_list *job_list,
|
|
struct settings *settings)
|
|
{
|
|
FILE *f;
|
|
char *line = NULL;
|
|
size_t line_len = 0;
|
|
struct job_list_entry entry = {};
|
|
bool any = false;
|
|
|
|
if ((f = fopen(settings->test_list, "r")) == NULL) {
|
|
fprintf(stderr, "Cannot open test list file %s\n", settings->test_list);
|
|
return false;
|
|
}
|
|
|
|
while (1) {
|
|
char *binary;
|
|
char *delim;
|
|
|
|
if (getline(&line, &line_len, f) == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
|
|
/* # starts a comment */
|
|
if ((delim = strchr(line, '#')) != NULL)
|
|
*delim = '\0';
|
|
|
|
if (settings->exclude_regexes.size && matches_any(line, &settings->exclude_regexes))
|
|
continue;
|
|
|
|
if (settings->include_regexes.size && !matches_any(line, &settings->include_regexes))
|
|
continue;
|
|
|
|
if (sscanf(line, "igt@%ms", &binary) == 1) {
|
|
if ((delim = strchr(binary, '@')) != NULL)
|
|
*delim++ = '\0';
|
|
|
|
if (!settings->multiple_mode) {
|
|
char **subtests = NULL;
|
|
if (delim) {
|
|
subtests = malloc(sizeof(char*));
|
|
subtests[0] = strdup(delim);
|
|
}
|
|
add_job_list_entry(job_list, strdup(binary),
|
|
subtests, (size_t)(subtests != NULL));
|
|
any = true;
|
|
free(binary);
|
|
binary = NULL;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* If the currently built entry has the same
|
|
* binary, add a subtest. Otherwise submit
|
|
* what's already built and start a new one.
|
|
*/
|
|
if (entry.binary && !strcmp(entry.binary, binary)) {
|
|
if (!delim) {
|
|
/* ... except we didn't get a subtest */
|
|
fprintf(stderr,
|
|
"Error: Unexpected test without subtests "
|
|
"after same test had subtests\n");
|
|
free(binary);
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
entry.subtest_count++;
|
|
entry.subtests = realloc(entry.subtests,
|
|
entry.subtest_count *
|
|
sizeof(*entry.subtests));
|
|
entry.subtests[entry.subtest_count - 1] = strdup(delim);
|
|
free(binary);
|
|
binary = NULL;
|
|
continue;
|
|
}
|
|
|
|
if (entry.binary) {
|
|
add_job_list_entry(job_list, entry.binary, entry.subtests, entry.subtest_count);
|
|
any = true;
|
|
}
|
|
|
|
memset(&entry, 0, sizeof(entry));
|
|
entry.binary = strdup(binary);
|
|
if (delim) {
|
|
entry.subtests = malloc(sizeof(*entry.subtests));
|
|
entry.subtests[0] = strdup(delim);
|
|
entry.subtest_count = 1;
|
|
}
|
|
|
|
free(binary);
|
|
binary = NULL;
|
|
}
|
|
}
|
|
|
|
if (entry.binary) {
|
|
add_job_list_entry(job_list, entry.binary, entry.subtests, entry.subtest_count);
|
|
any = true;
|
|
}
|
|
|
|
free(line);
|
|
fclose(f);
|
|
return any;
|
|
}
|
|
|
|
void list_all_tests(struct job_list *lst)
|
|
{
|
|
char piglit_name[256];
|
|
|
|
for (size_t test_idx = 0; test_idx < lst->size; ++test_idx) {
|
|
struct job_list_entry *current_entry = lst->entries + test_idx;
|
|
char *binary = current_entry->binary;
|
|
|
|
if (current_entry->subtest_count == 0) {
|
|
generate_piglit_name(binary, NULL,
|
|
piglit_name, sizeof(piglit_name));
|
|
printf("%s\n", piglit_name);
|
|
continue;
|
|
}
|
|
for (size_t subtest_idx = 0;
|
|
subtest_idx < current_entry->subtest_count;
|
|
++subtest_idx) {
|
|
generate_piglit_name(binary, current_entry->subtests[subtest_idx],
|
|
piglit_name, sizeof(piglit_name));
|
|
printf("%s\n", piglit_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *lowercase(const char *str)
|
|
{
|
|
char *ret = malloc(strlen(str) + 1);
|
|
char *q = ret;
|
|
|
|
while (*str) {
|
|
if (isspace(*str))
|
|
break;
|
|
|
|
*q++ = tolower(*str++);
|
|
}
|
|
*q = '\0';
|
|
|
|
return ret;
|
|
}
|
|
|
|
void generate_piglit_name(const char *binary, const char *subtest,
|
|
char *namebuf, size_t namebuf_size)
|
|
{
|
|
char *lc_binary = lowercase(binary);
|
|
char *lc_subtest = NULL;
|
|
|
|
if (!subtest) {
|
|
snprintf(namebuf, namebuf_size, "igt@%s", lc_binary);
|
|
free(lc_binary);
|
|
return;
|
|
}
|
|
|
|
lc_subtest = lowercase(subtest);
|
|
|
|
snprintf(namebuf, namebuf_size, "igt@%s@%s", lc_binary, lc_subtest);
|
|
|
|
free(lc_binary);
|
|
free(lc_subtest);
|
|
}
|
|
|
|
void init_job_list(struct job_list *job_list)
|
|
{
|
|
memset(job_list, 0, sizeof(*job_list));
|
|
}
|
|
|
|
void free_job_list(struct job_list *job_list)
|
|
{
|
|
int i, k;
|
|
|
|
for (i = 0; i < job_list->size; i++) {
|
|
struct job_list_entry *entry = &job_list->entries[i];
|
|
|
|
free(entry->binary);
|
|
for (k = 0; k < entry->subtest_count; k++) {
|
|
free(entry->subtests[k]);
|
|
}
|
|
free(entry->subtests);
|
|
}
|
|
free(job_list->entries);
|
|
init_job_list(job_list);
|
|
}
|
|
|
|
bool create_job_list(struct job_list *job_list,
|
|
struct settings *settings)
|
|
{
|
|
int dirfd, fd;
|
|
bool result;
|
|
|
|
if (!settings->test_root) {
|
|
fprintf(stderr, "No test root set; this shouldn't happen\n");
|
|
return false;
|
|
}
|
|
|
|
free_job_list(job_list);
|
|
|
|
dirfd = open(settings->test_root, O_DIRECTORY | O_RDONLY);
|
|
if (dirfd < 0) {
|
|
fprintf(stderr, "Test directory %s cannot be opened\n", settings->test_root);
|
|
return false;
|
|
}
|
|
|
|
fd = openat(dirfd, "test-list.txt", O_RDONLY);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "Cannot open %s/test-list.txt\n", settings->test_root);
|
|
close(dirfd);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* If a test_list is given (not to be confused with
|
|
* test-list.txt), we use it directly without making tests
|
|
* list their subtests. If include/exclude filters are given
|
|
* we filter them directly from the test_list.
|
|
*/
|
|
if (settings->test_list)
|
|
result = job_list_from_test_list(job_list, settings);
|
|
else
|
|
result = filtered_job_list(job_list, settings, fd);
|
|
|
|
close(fd);
|
|
close(dirfd);
|
|
|
|
return result;
|
|
}
|
|
|
|
static char joblist_filename[] = "joblist.txt";
|
|
bool serialize_job_list(struct job_list *job_list, struct settings *settings)
|
|
{
|
|
int dirfd, fd;
|
|
size_t i, k;
|
|
FILE *f;
|
|
|
|
if (!settings->results_path) {
|
|
fprintf(stderr, "No results-path set; this shouldn't happen\n");
|
|
return false;
|
|
}
|
|
|
|
if ((dirfd = open(settings->results_path, O_DIRECTORY | O_RDONLY)) < 0) {
|
|
mkdir(settings->results_path, 0777);
|
|
if ((dirfd = open(settings->results_path, O_DIRECTORY | O_RDONLY)) < 0) {
|
|
fprintf(stderr, "Creating results-path failed\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!settings->overwrite &&
|
|
faccessat(dirfd, joblist_filename, F_OK, 0) == 0) {
|
|
fprintf(stderr, "Job list file already exists and not overwriting\n");
|
|
close(dirfd);
|
|
return false;
|
|
}
|
|
|
|
if (settings->overwrite &&
|
|
unlinkat(dirfd, joblist_filename, 0) != 0 &&
|
|
errno != ENOENT) {
|
|
fprintf(stderr, "Error removing old job list\n");
|
|
close(dirfd);
|
|
return false;
|
|
}
|
|
|
|
if ((fd = openat(dirfd, joblist_filename, O_CREAT | O_EXCL | O_WRONLY, 0666)) < 0) {
|
|
fprintf(stderr, "Creating job list serialization file failed: %s\n", strerror(errno));
|
|
close(dirfd);
|
|
return false;
|
|
}
|
|
|
|
f = fdopen(fd, "w");
|
|
if (!f) {
|
|
close(fd);
|
|
close(dirfd);
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < job_list->size; i++) {
|
|
struct job_list_entry *entry = &job_list->entries[i];
|
|
fputs(entry->binary, f);
|
|
|
|
if (entry->subtest_count) {
|
|
const char *delim = "";
|
|
|
|
fprintf(f, " ");
|
|
|
|
for (k = 0; k < entry->subtest_count; k++) {
|
|
fprintf(f, "%s%s", delim, entry->subtests[k]);
|
|
delim = ",";
|
|
}
|
|
}
|
|
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
if (settings->sync) {
|
|
fsync(fd);
|
|
fsync(dirfd);
|
|
}
|
|
|
|
fclose(f);
|
|
close(dirfd);
|
|
return true;
|
|
}
|
|
|
|
bool read_job_list(struct job_list *job_list, int dirfd)
|
|
{
|
|
int fd;
|
|
FILE *f;
|
|
ssize_t read;
|
|
char *line = NULL;
|
|
size_t line_len = 0;
|
|
|
|
free_job_list(job_list);
|
|
|
|
if ((fd = openat(dirfd, joblist_filename, O_RDONLY)) < 0)
|
|
return false;
|
|
|
|
f = fdopen(fd, "r");
|
|
if (!f) {
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
while ((read = getline(&line, &line_len, f))) {
|
|
char *binary, *sublist, *comma;
|
|
char **subtests = NULL;
|
|
size_t num_subtests = 0, len;
|
|
|
|
if (read < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
|
|
len = strlen(line);
|
|
if (len > 0 && line[len - 1] == '\n')
|
|
line[len - 1] = '\0';
|
|
|
|
sublist = strchr(line, ' ');
|
|
if (!sublist) {
|
|
add_job_list_entry(job_list, strdup(line), NULL, 0);
|
|
continue;
|
|
}
|
|
|
|
*sublist++ = '\0';
|
|
binary = strdup(line);
|
|
|
|
do {
|
|
comma = strchr(sublist, ',');
|
|
if (comma) {
|
|
*comma++ = '\0';
|
|
}
|
|
|
|
++num_subtests;
|
|
subtests = realloc(subtests, num_subtests * sizeof(*subtests));
|
|
subtests[num_subtests - 1] = strdup(sublist);
|
|
sublist = comma;
|
|
} while (comma != NULL);
|
|
|
|
add_job_list_entry(job_list, binary, subtests, num_subtests);
|
|
}
|
|
|
|
free(line);
|
|
fclose(f);
|
|
|
|
return true;
|
|
}
|