From: Rusty Russell Date: Fri, 25 Sep 2009 05:03:08 +0000 (+0930) Subject: ccanlint: compile and run tests. X-Git-Url: https://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=4e0dfdadf206c74dc9e5f302545b2419cc4798f4;ds=sidebyside ccanlint: compile and run tests. This means we should skip building if there are no C files in module: running tests requires building the module, but not necessarily that it has any C files. --- diff --git a/tools/ccanlint/ccanlint.h b/tools/ccanlint/ccanlint.h index d832332d..271fba95 100644 --- a/tools/ccanlint/ccanlint.h +++ b/tools/ccanlint/ccanlint.h @@ -22,6 +22,7 @@ struct manifest { struct list_head api_tests; struct list_head compile_ok_tests; struct list_head compile_fail_tests; + struct list_head other_test_c_files; struct list_head other_test_files; struct list_head other_files; @@ -124,6 +125,9 @@ struct ccan_file { struct line_info *line_info; struct list_head *doc_sections; + + /* If this file gets compiled (eg. .C file to .o file), result here. */ + const char *compiled; }; /* A new ccan_file, with the given name (talloc_steal onto returned value). */ diff --git a/tools/ccanlint/file_analysis.c b/tools/ccanlint/file_analysis.c index 44ea5c2d..3152cd1e 100644 --- a/tools/ccanlint/file_analysis.c +++ b/tools/ccanlint/file_analysis.c @@ -40,6 +40,7 @@ struct ccan_file *new_ccan_file(const void *ctx, char *name) f->lines = NULL; f->line_info = NULL; f->doc_sections = NULL; + f->compiled = NULL; f->name = talloc_steal(f, name); return f; } @@ -115,7 +116,7 @@ static void add_files(struct manifest *m, const char *dir) else if (strstarts(f->name, "test/compile_fail")) dest = &m->compile_fail_tests; else - dest = &m->other_test_files; + dest = &m->other_test_c_files; } else dest = &m->other_test_files; } else @@ -162,6 +163,7 @@ struct manifest *get_manifest(const void *ctx) list_head_init(&m->run_tests); list_head_init(&m->compile_ok_tests); list_head_init(&m->compile_fail_tests); + list_head_init(&m->other_test_c_files); list_head_init(&m->other_test_files); list_head_init(&m->other_files); list_head_init(&m->dep_dirs); diff --git a/tools/ccanlint/tests/build.c b/tools/ccanlint/tests/build.c index c3b570e4..0a5e11e6 100644 --- a/tools/ccanlint/tests/build.c +++ b/tools/ccanlint/tests/build.c @@ -16,8 +16,6 @@ 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; @@ -39,6 +37,11 @@ static char *obj_list(const struct manifest *m) /* We leave this object file around after ccanlint runs, all built. */ static void *do_build(struct manifest *m) { + if (list_empty(&m->c_files)) { + /* No files? No score, but we "pass". */ + build.total_score = 0; + return NULL; + } return run_command(m, "ld -r -o ../%s.o %s", m->basename, obj_list(m)); } diff --git a/tools/ccanlint/tests/build_objs.c b/tools/ccanlint/tests/build_objs.c index 199772ab..dc6e4d35 100644 --- a/tools/ccanlint/tests/build_objs.c +++ b/tools/ccanlint/tests/build_objs.c @@ -16,8 +16,6 @@ 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; diff --git a/tools/ccanlint/tests/check_build.c b/tools/ccanlint/tests/check_build.c index 03e3233c..dcba0e49 100644 --- a/tools/ccanlint/tests/check_build.c +++ b/tools/ccanlint/tests/check_build.c @@ -46,7 +46,7 @@ static char *lib_list(const struct manifest *m) char *ret = talloc_strdup(m, ""); for (i = 0; i < num; i++) - ret = talloc_asprintf_append(ret, "-l %s ", libs[i]); + ret = talloc_asprintf_append(ret, "-l%s ", libs[i]); return ret; } diff --git a/tools/ccanlint/tests/check_depends_built.c b/tools/ccanlint/tests/check_depends_built.c index 02fa83b8..1392be91 100644 --- a/tools/ccanlint/tests/check_depends_built.c +++ b/tools/ccanlint/tests/check_depends_built.c @@ -50,11 +50,11 @@ static bool expect_obj_file(const char *dir) static void *check_depends_built(struct manifest *m) { struct ccan_file *i; + struct stat st; char *report = NULL; list_for_each(&m->dep_dirs, i, list) { char *objfile; - struct stat st; if (!expect_obj_file(i->name)) continue; @@ -70,6 +70,17 @@ static void *check_depends_built(struct manifest *m) } } + + /* We may need libtap for testing, unless we're "tap" */ + if (!streq(m->basename, "tap") + && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) { + if (stat("../tap.o", &st) != 0) { + report = talloc_asprintf_append(report, + "object file ../tap.o" + " (for tests)\n"); + } + } + return talloc_steal(m, report); } diff --git a/tools/ccanlint/tests/compile_test_helpers.c b/tools/ccanlint/tests/compile_test_helpers.c new file mode 100644 index 00000000..017a5308 --- /dev/null +++ b/tools/ccanlint/tests/compile_test_helpers.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 *objname(const void *ctx, const char *cfile) +{ + return talloc_asprintf(ctx, "%.*s.o ", strlen(cfile) - 2, cfile); +} + +static char *compile(struct manifest *m, const char *cfile) +{ + char *obj; + + obj = objname(m, cfile); + talloc_set_destructor(obj, cleanup_testfile); + return run_command(m, "cc " CFLAGS " -c -o %s %s", obj, cfile); +} + +static void *do_compile_test_helpers(struct manifest *m) +{ + char *cmdout = NULL; + struct ccan_file *i; + + list_for_each(&m->other_test_c_files, i, list) { + compile_tests.total_score++; + cmdout = compile(m, i->name); + if (cmdout) + return talloc_asprintf(m, + "Failed to compile helper C" + " code file %s:\n%s", + i->name, cmdout); + } + return NULL; +} + +static const char *describe_compile_test_helpers(struct manifest *m, + void *check_result) +{ + return check_result; +} + +struct ccanlint compile_test_helpers = { + .name = "Compiling test helper files", + .total_score = 1, + .check = do_compile_test_helpers, + .describe = describe_compile_test_helpers, + .can_run = can_build, +}; + +REGISTER_TEST(compile_test_helpers, &depends_built); diff --git a/tools/ccanlint/tests/compile_tests.c b/tools/ccanlint/tests/compile_tests.c new file mode 100644 index 00000000..c9ace30d --- /dev/null +++ b/tools/ccanlint/tests/compile_tests.c @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *can_build(struct manifest *m) +{ + if (safe_mode) + return "Safe mode enabled"; + return NULL; +} + +static char *obj_list(const struct manifest *m, bool link_with_module) +{ + char *list = talloc_strdup(m, "../tap.o"); + struct ccan_file *i; + + /* Objects from any other C files. */ + list_for_each(&m->other_test_c_files, i, list) + list = talloc_asprintf_append(list, " %.*s.o", + strlen(i->name) - 2, i->name); + + if (link_with_module) + list = talloc_asprintf_append(list, " ../%s.o", m->basename); + + 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 int cleanup_testfile(const char *testfile) +{ + unlink(testfile); + return 0; +} + +static char *compile(const void *ctx, + struct manifest *m, struct ccan_file *file, bool fail, + bool link_with_module) +{ + file->compiled = talloc_strdup(ctx, tempnam("/tmp", "ccanlint")); + talloc_set_destructor(file->compiled, cleanup_testfile); + + return run_command(m, "cc " CFLAGS " %s -o %s %s %s %s", + fail ? "-DFAIL" : "", + file->compiled, file->name, + obj_list(m, link_with_module), lib_list(m)); +} + +struct compile_tests_result { + struct list_node list; + const char *filename; + const char *description; + const char *output; +}; + +static void *do_compile_tests(struct manifest *m) +{ + struct list_head *list = talloc(m, struct list_head); + char *cmdout; + struct ccan_file *i; + struct compile_tests_result *res; + + list_head_init(list); + + compile_tests.total_score = 0; + list_for_each(&m->compile_ok_tests, i, list) { + compile_tests.total_score++; + cmdout = compile(list, m, i, false, false); + if (cmdout) { + res = talloc(list, struct compile_tests_result); + res->filename = i->name; + res->description = "failed to compile"; + res->output = talloc_steal(res, cmdout); + list_add_tail(list, &res->list); + } + } + + list_for_each(&m->run_tests, i, list) { + compile_tests.total_score++; + cmdout = compile(m, m, i, false, false); + if (cmdout) { + res = talloc(list, struct compile_tests_result); + res->filename = i->name; + res->description = "failed to compile"; + res->output = talloc_steal(res, cmdout); + list_add_tail(list, &res->list); + } + } + + list_for_each(&m->api_tests, i, list) { + compile_tests.total_score++; + cmdout = compile(m, m, i, false, true); + if (cmdout) { + res = talloc(list, struct compile_tests_result); + res->filename = i->name; + res->description = "failed to compile"; + res->output = talloc_steal(res, cmdout); + list_add_tail(list, &res->list); + } + } + + list_for_each(&m->compile_fail_tests, i, list) { + compile_tests.total_score++; + cmdout = compile(list, m, i, true, false); + if (cmdout) { + res = talloc(list, struct compile_tests_result); + res->filename = i->name; + res->description = "failed to compile without -DFAIL"; + res->output = talloc_steal(res, cmdout); + list_add_tail(list, &res->list); + } else { + cmdout = compile(list, m, i, false, false); + if (!cmdout) { + res = talloc(list, struct compile_tests_result); + res->filename = i->name; + res->description = "compiled successfully" + " with -DFAIL"; + res->output = ""; + list_add_tail(list, &res->list); + } + } + } + + if (list_empty(list)) { + talloc_free(list); + list = NULL; + } + + return list; +} + +static unsigned int score_compile_tests(struct manifest *m, + void *check_result) +{ + struct list_head *list = check_result; + struct compile_tests_result *i; + unsigned int score = compile_tests.total_score; + + list_for_each(list, i, list) + score--; + return score; +} + +static const char *describe_compile_tests(struct manifest *m, + void *check_result) +{ + struct list_head *list = check_result; + struct compile_tests_result *i; + char *descrip = talloc_strdup(list, "Compilation tests failed:\n"); + + list_for_each(list, i, list) + descrip = talloc_asprintf_append(descrip, "%s %s\n%s", + i->filename, i->description, + i->output); + return descrip; +} + +struct ccanlint compile_tests = { + .name = "Compile tests succeed", + .total_score = 1, + .score = score_compile_tests, + .check = do_compile_tests, + .describe = describe_compile_tests, + .can_run = can_build, +}; + +REGISTER_TEST(compile_tests, &compile_test_helpers, NULL); diff --git a/tools/ccanlint/tests/run_tests.c b/tools/ccanlint/tests/run_tests.c new file mode 100644 index 00000000..e996c4e2 --- /dev/null +++ b/tools/ccanlint/tests/run_tests.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *can_run(struct manifest *m) +{ + if (safe_mode) + return "Safe mode enabled"; + return NULL; +} + +static void *do_run_tests(struct manifest *m) +{ + struct list_head *list = talloc(m, struct list_head); + char *failures = talloc_strdup(m, ""); + struct ccan_file *i; + + list_head_init(list); + + run_tests.total_score = 0; + list_for_each(&m->run_tests, i, list) { + char *testout; + run_tests.total_score++; + /* FIXME: timeout here */ + testout = run_command(m, i->compiled); + if (!testout) + continue; + failures = talloc_asprintf_append(failures, + "Running %s failed:\n", + i->name); + failures = talloc_append_string(failures, testout); + } + + list_for_each(&m->api_tests, i, list) { + char *testout; + run_tests.total_score++; + /* FIXME: timeout here */ + testout = run_command(m, i->compiled); + if (!testout) + continue; + failures = talloc_asprintf_append(failures, + "Running %s failed:\n", + i->name); + failures = talloc_append_string(failures, testout); + } + + if (streq(failures, "")) { + talloc_free(failures); + failures = NULL; + } + + return failures; +} + +static unsigned int score_run_tests(struct manifest *m, void *check_result) +{ + /* FIXME: be cleverer here */ + return 0; +} + +static const char *describe_run_tests(struct manifest *m, + void *check_result) +{ + char *descrip = talloc_strdup(check_result, "Running tests failed:\n"); + + return talloc_append_string(descrip, check_result); +} + +struct ccanlint run_tests = { + .name = "run and api tests run successfully", + .total_score = 1, + .score = score_run_tests, + .check = do_run_tests, + .describe = describe_run_tests, + .can_run = can_run, +}; + +REGISTER_TEST(run_tests, &compile_tests, NULL);