Commit 61088f5c authored by Rusty Russell's avatar Rusty Russell

Build tests for ccan.

More sophisticated skipping: skip dependencies when one fails as well.
Allow tests to change their total_score; only access it after running (other than to check it's non-zero).
parent 3460418c
......@@ -3,13 +3,13 @@ ALL_TOOLS = tools/ccan_depends tools/run_tests tools/doc_extract tools/namespaci
.PHONY: tools
tools: $(ALL_TOOLS)
tools/ccan_depends: tools/ccan_depends.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/talloc/talloc.o ccan/noerr/noerr.o
tools/ccan_depends: tools/ccan_depends.o tools/depends.o tools/tools.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/talloc/talloc.o ccan/noerr/noerr.o
tools/run_tests: tools/run_tests.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/tap/tap.o ccan/noerr/noerr.o ccan/talloc/talloc.o
tools/doc_extract: tools/doc_extract.o tools/doc_extract-core.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
tools/namespacize: tools/namespacize.o tools/depends.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
tools/namespacize: tools/namespacize.o tools/depends.o tools/tools.o ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o ccan/noerr/noerr.o ccan/talloc/talloc.o
tools/run_tests.o tools/namespacize.o tools/depends.o: tools/tools.h
......
......@@ -6,6 +6,7 @@ CORE_OBJS := tools/ccanlint/ccanlint.o \
tools/ccanlint/file_analysis.o \
tools/doc_extract-core.o \
tools/depends.o \
tools/tools.o \
ccan/str_talloc/str_talloc.o ccan/grab_file/grab_file.o \
ccan/talloc/talloc.o ccan/noerr/noerr.o
......
......@@ -68,6 +68,19 @@ bool ask(const char *question)
&& toupper(reply[0]) == 'Y';
}
static const char *should_skip(struct manifest *m, struct ccanlint *i)
{
if (i->skip_fail)
return "dependency failed";
if (i->skip)
return "dependency was skipped";
if (i->can_run)
return i->can_run(m);
return NULL;
}
static bool run_test(struct ccanlint *i,
bool summary,
unsigned int *score,
......@@ -77,18 +90,38 @@ static bool run_test(struct ccanlint *i,
void *result;
unsigned int this_score;
const struct dependent *d;
const char *skip;
if (i->total_score)
*total_score += i->total_score;
//one less test to run through
list_for_each(&i->dependencies, d, node)
d->dependent->num_depends--;
skip = should_skip(m, i);
if (skip) {
if (verbose)
printf(" %s: skipped (%s)\n", i->name, skip);
/* If we're skipping this because a prereq failed, we fail. */
if (i->skip_fail)
*total_score += i->total_score;
list_del(&i->list);
list_add_tail(&finished_tests, &i->list);
list_for_each(&i->dependencies, d, node) {
d->dependent->skip = true;
d->dependent->skip_fail = i->skip_fail;
}
return true;
}
result = i->check(m);
if (!result) {
if (verbose)
printf(" %s: OK\n", i->name);
if (i->total_score)
if (i->total_score) {
*score += i->total_score;
*total_score += i->total_score;
}
list_del(&i->list);
list_add_tail(&finished_tests, &i->list);
......@@ -103,6 +136,7 @@ static bool run_test(struct ccanlint *i,
list_del(&i->list);
list_add_tail(&finished_tests, &i->list);
*total_score += i->total_score;
*score += this_score;
if (summary) {
printf("%s FAILED (%u/%u)\n",
......@@ -119,11 +153,8 @@ static bool run_test(struct ccanlint *i,
/* Skip any tests which depend on this one. */
list_for_each(&i->dependencies, d, node) {
list_del(&d->dependent->list);
list_add(&finished_tests, &d->dependent->list);
if (verbose)
printf(" -> skipping %s\n", d->dependent->name);
*total_score += d->dependent->total_score;
d->dependent->skip = true;
d->dependent->skip_fail = true;
}
return false;
......
......@@ -26,8 +26,10 @@ struct manifest {
struct list_head other_files;
/* From tests/check_depends.c */
struct list_head dep_obj_files;
/* From tests/check_depends_exist.c */
struct list_head dep_dirs;
/* From tests/check_depends_built.c */
struct list_head dep_objs;
};
struct manifest *get_manifest(const void *ctx);
......@@ -41,6 +43,9 @@ struct ccanlint {
/* Total score that this test is worth. 0 means compulsory tests. */
unsigned int total_score;
/* Can we run this test? Return string explaining why, if not. */
const char *(*can_run)(struct manifest *m);
/* If this returns non-NULL, it means the check failed. */
void *(*check)(struct manifest *m);
......@@ -60,6 +65,10 @@ struct ccanlint {
struct list_head dependencies;
/* How many things do we (still) depend on? */
unsigned int num_depends;
/* Did we skip a dependency? If so, must skip this, too. */
bool skip;
/* Did we fail a dependency? If so, skip and mark as fail. */
bool skip_fail;
};
/* Ask the user a yes/no question: the answer is NO if there's an error. */
......
......@@ -164,6 +164,8 @@ struct manifest *get_manifest(const void *ctx)
list_head_init(&m->compile_fail_tests);
list_head_init(&m->other_test_files);
list_head_init(&m->other_files);
list_head_init(&m->dep_dirs);
list_head_init(&m->dep_objs);
m->basename = talloc_getcwd(m);
if (!m->basename)
......
#include <tools/ccanlint/ccanlint.h>
#include <tools/tools.h>
#include <ccan/talloc/talloc.h>
#include <ccan/str/str.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <string.h>
#include <ctype.h>
static const char *can_build(struct manifest *m)
{
if (list_empty(&m->c_files))
return "No C files in module";
if (safe_mode)
return "Safe mode enabled";
return NULL;
}
static char *obj_list(const struct manifest *m)
{
char *list = talloc_strdup(m, "");
struct ccan_file *i;
/* Object from all the C files. */
list_for_each(&m->c_files, i, list)
list = talloc_asprintf_append(list, "%.*s.o ",
strlen(i->name) - 2, i->name);
return list;
}
/* We leave this object file around after ccanlint runs, all built. */
static void *do_build(struct manifest *m)
{
return run_command(m, "ld -r -o ../%s.o %s", m->basename, obj_list(m));
}
static const char *describe_build(struct manifest *m, void *check_result)
{
return talloc_asprintf(check_result,
"The object file for the module didn't build:\n"
"%s", (char *)check_result);
}
struct ccanlint build = {
.name = "Module can be built",
.total_score = 1,
.check = do_build,
.describe = describe_build,
.can_run = can_build,
};
REGISTER_TEST(build, &depends_built, NULL);
#include <tools/ccanlint/ccanlint.h>
#include <tools/tools.h>
#include <ccan/talloc/talloc.h>
#include <ccan/str/str.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <string.h>
#include <ctype.h>
static const char *can_build(struct manifest *m)
{
if (list_empty(&m->c_files))
return "No C files in module";
if (safe_mode)
return "Safe mode enabled";
return NULL;
}
static bool compile_obj(struct ccan_file *c_file, char *objfile, char **report)
{
char *contents;
contents = run_command(objfile, "cc " CFLAGS " -o %s -c %s",
objfile, c_file->name);
if (contents) {
if (*report)
*report = talloc_append_string(*report, contents);
else
*report = contents;
return false;
}
return true;
}
static int cleanup_obj(char *objfile)
{
unlink(objfile);
return 0;
}
static void *check_objs_build(struct manifest *m)
{
char *report = NULL;
struct ccan_file *i;
/* One point for each obj file. */
build_objs.total_score = 0;
list_for_each(&m->c_files, i, list)
build_objs.total_score++;
list_for_each(&m->c_files, i, list) {
char *objfile = talloc_strdup(m, i->name);
objfile[strlen(objfile)-1] = 'o';
if (compile_obj(i, objfile, &report))
talloc_set_destructor(objfile, cleanup_obj);
}
return report;
}
static const char *describe_objs_build(struct manifest *m, void *check_result)
{
return talloc_asprintf(check_result,
"%s", (char *)check_result);
}
struct ccanlint build_objs = {
.name = "Module object files can be built",
.total_score = 1,
.check = check_objs_build,
.describe = describe_objs_build,
.can_run = can_build,
};
REGISTER_TEST(build_objs, &depends_exist, NULL);
#include <tools/ccanlint/ccanlint.h>
#include <tools/tools.h>
#include <ccan/talloc/talloc.h>
#include <ccan/str/str.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <string.h>
#include <ctype.h>
static const char *can_build(struct manifest *m)
{
if (safe_mode)
return "Safe mode enabled";
return NULL;
}
static int cleanup_testfile(char *testfile)
{
unlink(testfile);
return 0;
}
static char *obj_list(const struct manifest *m)
{
char *list = talloc_strdup(m, "");
struct ccan_file *i;
/* Other CCAN deps. */
list_for_each(&m->dep_objs, i, list)
list = talloc_asprintf_append(list, "%s ", i->name);
return list;
}
static char *lib_list(const struct manifest *m)
{
unsigned int i, num;
char **libs = get_libs(m, ".", ".", &num);
char *ret = talloc_strdup(m, "");
for (i = 0; i < num; i++)
ret = talloc_asprintf_append(ret, "-l %s ", libs[i]);
return ret;
}
static void *check_use_build(struct manifest *m)
{
char *contents;
char *tmpfile, *outfile;
int fd;
tmpfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
talloc_set_destructor(tmpfile, cleanup_testfile);
outfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
talloc_set_destructor(outfile, cleanup_testfile);
fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0)
return talloc_asprintf(m, "Creating temporary file: %s",
strerror(errno));
contents = talloc_asprintf(tmpfile,
"#include <ccan/%s/%s.h>\n"
"int main(void)\n"
"{\n"
" return 0;\n"
"}\n",
m->basename, m->basename);
if (write(fd, contents, strlen(contents)) != strlen(contents)) {
close(fd);
return "Failure writing to temporary file";
}
close(fd);
return run_command(m, "cc " CFLAGS " -o %s -x c %s -x none %s %s",
outfile, tmpfile, obj_list(m), lib_list(m));
}
static const char *describe_use_build(struct manifest *m, void *check_result)
{
return talloc_asprintf(check_result,
"Linking against module:\n"
"%s", (char *)check_result);
}
struct ccanlint check_build = {
.name = "Module can be used",
.total_score = 1,
.check = check_use_build,
.describe = describe_use_build,
.can_run = can_build,
};
REGISTER_TEST(check_build, &build, NULL);
......@@ -14,6 +14,14 @@
#include <string.h>
#include <ctype.h>
static const char *can_build(struct manifest *m)
{
if (safe_mode)
return "Safe mode enabled";
return NULL;
}
/* FIXME: recursive ccanlint if they ask for it. */
static bool expect_obj_file(const char *dir)
{
char *olddir;
......@@ -39,71 +47,46 @@ static bool expect_obj_file(const char *dir)
return has_c_files;
}
/* FIXME: recursive ccanlint if they ask for it. */
static char *add_dep(char *sofar, struct manifest *m, const char *dep)
static void *check_depends_built(struct manifest *m)
{
char *file, *dir;
struct stat st;
bool need_obj;
dir = talloc_asprintf(m, "../%s", dep);
need_obj = expect_obj_file(dir);
if (need_obj) {
file = talloc_asprintf(m, "../%s.o", dep);
if (stat(file, &st) == 0) {
struct ccan_file *f = new_ccan_file(m, file);
list_add_tail(&m->dep_obj_files, &f->list);
return sofar;
}
}
if (stat(dir, &st) == 0) {
if (!need_obj)
return sofar;
return talloc_asprintf_append(sofar,
"ccan/%s: isn't built (no %s)\n",
dep, file);
}
return talloc_asprintf_append(sofar,
"ccan/%s: could not find directory %s\n",
dep, dir);
}
static void *check_depends(struct manifest *m)
{
unsigned int i;
struct ccan_file *i;
char *report = NULL;
char **deps;
if (safe_mode)
deps = get_safe_ccan_deps(m, "..", m->basename, true);
else
deps = get_deps(m, "..", m->basename, true);
list_for_each(&m->dep_dirs, i, list) {
char *objfile;
struct stat st;
for (i = 0; deps[i]; i++) {
if (!strstarts(deps[i], "ccan/"))
if (!expect_obj_file(i->name))
continue;
report = add_dep(report, m, deps[i] + strlen("ccan/"));
objfile = talloc_asprintf(m, "%s.o", i->name);
if (stat(objfile, &st) != 0) {
report = talloc_asprintf_append(report,
"object file %s\n",
objfile);
} else {
struct ccan_file *f = new_ccan_file(m, objfile);
list_add_tail(&m->dep_objs, &f->list);
}
}
return report;
return talloc_steal(m, report);
}
static const char *describe_depends(struct manifest *m, void *check_result)
static const char *describe_depends_built(struct manifest *m,
void *check_result)
{
return talloc_asprintf(check_result,
"The following dependencies are needed:\n"
"%s\n", (char *)check_result);
"The following dependencies are not built:\n"
"%s", (char *)check_result);
}
struct ccanlint depends = {
struct ccanlint depends_built = {
.name = "CCAN dependencies are built",
.total_score = 1,
.check = check_depends,
.describe = describe_depends,
.check = check_depends_built,
.describe = describe_depends_built,
.can_run = can_build,
};
REGISTER_TEST(depends, NULL);
REGISTER_TEST(depends_built, &depends_exist, NULL);
#include <tools/ccanlint/ccanlint.h>
#include <tools/tools.h>
#include <ccan/talloc/talloc.h>
#include <ccan/str/str.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <string.h>
#include <ctype.h>
static char *add_dep(char *sofar, struct manifest *m, const char *dep)
{
char *dir;
struct stat st;
struct ccan_file *f;
dir = talloc_asprintf(m, "../%s", dep);
if (stat(dir, &st) != 0) {
return talloc_asprintf_append(sofar,
"ccan/%s: expected it in"
" directory %s\n",
dep, dir);
}
f = new_ccan_file(m, dir);
list_add_tail(&m->dep_dirs, &f->list);
return sofar;
}
static void *check_depends_exist(struct manifest *m)
{
unsigned int i;
char *report = NULL;
char **deps;
if (safe_mode)
deps = get_safe_ccan_deps(m, "..", m->basename, true);
else
deps = get_deps(m, "..", m->basename, true);
for (i = 0; deps[i]; i++) {
if (!strstarts(deps[i], "ccan/"))
continue;
report = add_dep(report, m, deps[i] + strlen("ccan/"));
}
return report;
}
static const char *describe_depends_exist(struct manifest *m,
void *check_result)
{
return talloc_asprintf(check_result,
"The following dependencies are are expected:\n"
"%s", (char *)check_result);
}
struct ccanlint depends_exist = {
.name = "CCAN dependencies are present",
.total_score = 1,
.check = check_depends_exist,
.describe = describe_depends_exist,
};
REGISTER_TEST(depends_exist, NULL);
#include <tools/ccanlint/ccanlint.h>
#include <tools/tools.h>
#include <ccan/talloc/talloc.h>
#include <ccan/grab_file/grab_file.h>
#include <ccan/str/str.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <string.h>
#include <ctype.h>
static const char *can_build(struct manifest *m)
{
if (safe_mode)
return "Safe mode enabled";
return NULL;
}
static int cleanup_testfile(char *testfile)
{
unlink(testfile);
return 0;
}
static void *check_includes_build(struct manifest *m)
{
char *contents;
char *tmpfile, *objfile;
int fd;
tmpfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
talloc_set_destructor(tmpfile, cleanup_testfile);
objfile = talloc_strdup(m, tempnam("/tmp", "ccanlint"));
talloc_set_destructor(objfile, cleanup_testfile);
fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0)
return talloc_asprintf(m, "Creating temporary file: %s",
strerror(errno));
contents = talloc_asprintf(tmpfile, "#include <ccan/%s/%s.h>\n",
m->basename, m->basename);
if (write(fd, contents, strlen(contents)) != strlen(contents)) {
close(fd);
return "Failure writing to temporary file";
}
close(fd);
return run_command(m, "cc " CFLAGS " -o %s -c -x c %s",
objfile, tmpfile);
}
static const char *describe_includes_build(struct manifest *m,
void *check_result)
{
return talloc_asprintf(check_result,
"#include of the main header file:\n"
"%s", (char *)check_result);
}
struct ccanlint includes_build = {
.name = "Can compile against main header",
.total_score = 1,
.check = check_includes_build,
.describe = describe_includes_build,
.can_run = can_build,
};
REGISTER_TEST(includes_build, &depends_exist, NULL);
......@@ -185,6 +185,22 @@ get_all_deps(const void *ctx, const char *dir, const char *name,
return deps;
}
char **get_libs(const void *ctx, const char *dir,
const char *name, unsigned int *num)
{
char **libs, *cmd, *infofile;
infofile = compile_info(ctx, dir, name);
if (!infofile)
errx(1, "Could not compile _info for '%s'", name);
cmd = talloc_asprintf(ctx, "%s libs", infofile);
libs = lines_from_cmd(cmd, num, "%s", cmd);
if (!libs)
err(1, "Could not run '%s'", cmd);
return libs;
}
char **get_deps(const void *ctx, const char *dir, const char *name,
bool recurse)
{
......@@ -204,40 +220,3 @@ char **get_safe_ccan_deps(const void *ctx, const char *dir,
}
return get_all_deps(ctx, dir, name, get_one_safe_deps);
}
char *talloc_basename(const void *ctx, const char *dir)
{
char *p = strrchr(dir, '/');
if (!p)
return (char *)dir;
return talloc_strdup(ctx, p+1);
}
char *talloc_dirname(const void *ctx, const char *dir)
{
char *p = strrchr(dir, '/');
if (!p)
return talloc_strdup(ctx, ".");
return talloc_strndup(ctx, dir, p - dir);
}
char *talloc_getcwd(const void *ctx)
{
unsigned int len;
char *cwd;
/* *This* is why people hate C. */
len = 32;
cwd = talloc_array(ctx, char, len);
while (!getcwd(cwd, len)) {
if (errno != ERANGE) {
talloc_free(cwd);
return NULL;
}
cwd = talloc_realloc(ctx, cwd, char, len *= 2);
}
return cwd;
}
#include <ccan/talloc/talloc.h>
#include <ccan/grab_file/grab_file.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include "tools.h"
char *talloc_basename(const void *ctx, const char *dir)
{
char *p = strrchr(dir, '/');
if (!p)
return (char *)dir;
return talloc_strdup(ctx, p+1);
}
char *talloc_dirname(const void *ctx, const char *dir)
{
char *p = strrchr(dir, '/');
if (!p)
return talloc_strdup(ctx, ".");
return talloc_strndup(ctx, dir, p - dir);
}
char *talloc_getcwd(const void *ctx)
{
unsigned int len;
char *cwd;
/* *This* is why people hate C. */
len = 32;
cwd = talloc_array(ctx, char, len);
while (!getcwd(cwd, len)) {
if (errno != ERANGE) {
talloc_free(cwd);
return NULL;
}
cwd = talloc_realloc(ctx, cwd, char, len *= 2);
}
return cwd;
}
char *run_command(const void *ctx, const char *fmt, ...)
{
va_list ap;
char *cmd, *contents;
FILE *pipe;
va_start(ap, fmt);
cmd = talloc_vasprintf(ctx, fmt, ap);
va_end(ap);
/* Ensure stderr gets to us too. */
cmd = talloc_asprintf_append(cmd, " 2>&1");
pipe = popen(cmd, "r");
if (!pipe)
return talloc_asprintf(ctx, "Failed to run '%s'", cmd);
contents = grab_fd(cmd, fileno(pipe), NULL);
if (pclose(pipe) != 0)
return talloc_asprintf(ctx, "Running '%s':\n%s",
cmd, contents);
talloc_free(cmd);
return NULL;
}
......@@ -19,7 +19,13 @@ char **get_deps(const void *ctx, const char *dir, const char *name,
char **get_safe_ccan_deps(const void *ctx, const char *dir, const char *name,
bool recurse);
/* This also needs to compile the info file. */
char **get_libs(const void *ctx, const char *dir,
const char *name, unsigned int *num);
/* From tools.c */
char *talloc_basename(const void *ctx, const char *dir);
char *talloc_dirname(const void *ctx, const char *dir);
char *talloc_getcwd(const void *ctx);
char *run_command(const void *ctx, const char *fmt, ...);
#endif /* CCAN_TOOLS_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment