commit - deed5012af2072e6bec7db5318300f911cd96da5
commit + f6ad6195f31f30e9bf1aacef0f9a324d0d938a11
blob - 225c53597915a1bcf224cfa080cb71041f279eef
blob + f65d4c7000cafb27ae4f7ee00fc10629f65e4a23
--- tools/runtest/test_list.h
+++ tools/runtest/test_list.h
#include <sys/queue.h>
enum test_status {
- STATUS_OK, /* TestAnythingProtocol */
- STATUS_NOTOK, /* TestAnythingProtocol */
- STATUS_MISSING, /* TestAnythingProtocol */
- STATUS_TODO, /* TestAnythingProtocol */
- STATUS_SKIP, /* TestAnythingProtocol */
+ STATUS_OK, /* TestAnythingProtocol */
+ STATUS_NOTOK, /* TestAnythingProtocol */
+ STATUS_MISSING, /* TestAnythingProtocol */
+ STATUS_TODO, /* TestAnythingProtocol */
+ STATUS_SKIP, /* TestAnythingProtocol */
};
struct test {
blob - 11d5c4b677e9dd0ef5de584bf7a4cb34727baf98
blob + 5ffbc8a3e51ed7ea87a36b24e67b40852f9f91e1
--- tools/runtest/utils.c
+++ tools/runtest/utils.c
#define _GNU_SOURCE
-#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
-
+#include <err.h>
+#include <fnmatch.h>
+#include <fts.h>
#include <errno.h>
#include "test_list.h"
return strtime;
}
-struct test_list *test_discovery(const char *dir, const char *exec_file) {
+/* Compare files by name. */
+int entcmp(const FTSENT **a, const FTSENT **b)
+{
+ return strcmp((*a)->fts_name, (*b)->fts_name);
+}
+
+/*
+ * Print all files in the directory tree that match the glob pattern.
+ * Example: test_discovery("/usr/src/test", "Makefile");
+ */
+struct test_list *test_discovery(char *dir, const char *pattern) {
struct test_list *tests;
- struct stat st_buf;
-
- int n, i;
- struct dirent **namelist;
- int fail;
- int saved_errno;
-
tests = calloc(1, sizeof(struct test_list));
if (tests == NULL) {
fprintf(stderr, "malloc failed");
}
TAILQ_INIT(tests);
- do {
- if (stat(dir, &st_buf) == -1) {
- free_tests(tests);
- break;
- }
+ FTS *tree;
+ FTSENT *f;
+ char *argv[] = { dir, NULL };
- if (!S_ISDIR(st_buf.st_mode)) {
- free_tests(tests);
- errno = EINVAL;
- break;
+ /*
+ * FTS_LOGICAL follows symbolic links, including links to other
+ * directories. It detects cycles, so we never have an infinite
+ * loop. FTS_NOSTAT is because we never use f->statp. It uses
+ * our entcmp() to sort files by name.
+ */
+ tree = fts_open(argv, FTS_LOGICAL | FTS_NOSTAT, entcmp);
+ if (tree == NULL)
+ err(1, "fts_open");
+
+ while ((f = fts_read(tree))) {
+ switch (f->fts_info) {
+ case FTS_DNR: /* Cannot read directory */
+ case FTS_ERR: /* Miscellaneous error */
+ case FTS_NS: /* stat() error */
+ /* Show error, then continue to next files. */
+ warn("%s", f->fts_path);
+ continue;
+ case FTS_DP:
+ /* Ignore post-order visit to directory. */
+ continue;
}
- n = scandir(dir, &namelist, NULL, alphasort);
- if (n == -1) {
- free_tests(tests);
- break;
- }
-
- fail = 0;
- for (i = 0; i < n; i++) {
- char *run_test;
- char *d_name = strdup(namelist[i]->d_name);
- if (d_name == NULL) {
- fail = 1;
- saved_errno = errno;
- break;
- }
-
- if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) {
- free(d_name);
- continue;
- }
-
- if (asprintf(&run_test, "%s/%s/%s", dir, d_name, exec_file) == -1) {
- fail = 1;
- saved_errno = errno;
- free(d_name);
- break;
- }
-
- if (stat(run_test, &st_buf) == -1) {
- free(run_test);
- free(d_name);
- continue;
- }
-
- if (!S_ISREG(st_buf.st_mode)) {
- free(run_test);
- free(d_name);
- continue;
- }
-
+ if (fnmatch(pattern, f->fts_name, FNM_PERIOD) == 0) {
struct test *t = NULL;
t = calloc(1, sizeof(struct test));
if (t == NULL) {
fprintf(stderr, "malloc failed");
break;
}
- t->name = run_test;
- t->path = d_name;
+ t->name = dirname(strdup(f->fts_path));
+ t->path = strdup(f->fts_path);
TAILQ_INSERT_TAIL(tests, t, entries);
}
- for (i = 0; i < n; i++)
- free(namelist[i]);
- free(namelist);
-
- if (fail) {
- free_tests(tests);
- errno = saved_errno;
- break;
+ /*
+ * A cycle happens when a symbolic link (or perhaps a
+ * hard link) puts a directory inside itself. Tell user
+ * when this happens.
+ */
+ if (f->fts_info == FTS_DC) {
+ warnx("%s: cycle in directory tree", f->fts_path);
}
- } while (0);
+ }
+
+ /* fts_read() sets errno = 0 unless it has error. */
+ if (errno != 0)
+ err(1, "fts_read");
+ if (fts_close(tree) < 0)
+ err(1, "fts_close");
+
return tests;
}
exit(-1);
}
- char make[] = "/usr/bin/make";
+ char make_bin[] = "/usr/bin/make";
char make_opt[] = "-C";
- char *argv[] = {make, make_opt, dirname(strdup(run_test)), NULL};
- execv(argv[0], argv);
+ char *argv[] = {make_bin, make_opt, run_test, NULL};
+ int rc = execv(argv[0], argv);
dup2(fd_stdout, STDOUT_FILENO);
dup2(fd_stderr, STDERR_FILENO);
- exit(1);
+ exit(rc);
}
static inline int wait_child(const char *test_dir, const char *run_test,
rc = -1;
break;
}
- dirname(test_dir);
child = fork();
if (child == -1) {
if (status)
rc += 1;
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ t->status = STATUS_NOTOK;
+ } else {
+ t->status = STATUS_OK;
+ }
+ }
+
fprintf(fp_stdout, "%s END: %s\n", format_time(time, TIME_BUF_SIZE),
test_dir);
}
blob - ad0bf429bc5fc8000b36f01ae1830c10904087d6
blob + 1a605247ceecd97ead5f17d8db2cb789af17a88c
--- tools/runtest/utils.h
+++ tools/runtest/utils.h
char *exec_file;
};
-extern struct test_list *test_discovery(const char *, const char *);
+extern struct test_list *test_discovery(char *, const char *);
extern int print_tests(struct test_list *, FILE *);
extern int run_tests(struct test_list *, const struct test_options,
const char *, FILE *, FILE *);