X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=tools%2Fccanlint%2Ftests%2Frun-coverage.c;h=a526d55e8237cf4ed58afac49cc30d208a1a5d20;hb=9d511cc5b12021b6c6c079fc5b08487fdf361a26;hp=74247e32ac83f4c685551222adf6bd1688935d99;hpb=a200e1ad1cf5a4828ea4e4e222838ddad5a4a9a3;p=ccan diff --git a/tools/ccanlint/tests/run-coverage.c b/tools/ccanlint/tests/run-coverage.c index 74247e32..a526d55e 100644 --- a/tools/ccanlint/tests/run-coverage.c +++ b/tools/ccanlint/tests/run-coverage.c @@ -2,7 +2,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -14,17 +16,10 @@ #include #include #include -#include "build-coverage.h" -struct coverage_result { - float uncovered; - const char *what; - const char *output; -}; - -static bool find_source_file(struct manifest *m, const char *filename) +static bool find_source_file(const struct manifest *m, const char *filename) { - struct ccan_file *i; + const struct ccan_file *i; list_for_each(&m->c_files, i, list) { if (streq(i->fullname, filename)) @@ -37,19 +32,34 @@ static bool find_source_file(struct manifest *m, const char *filename) return false; } +/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... */ +static unsigned int score_coverage(float covered, unsigned total) +{ + float thresh, uncovered = 1.0 - covered; + unsigned int i; + + for (i = 0, thresh = 0.5; i < total; i++, thresh /= 2) { + if (uncovered > thresh) + break; + } + return i; +} + + /* FIXME: Don't know how stable this is. Read cov files directly? */ -static void analyze_coverage(struct manifest *m, - struct coverage_result *res, const char *output) +static void analyze_coverage(struct manifest *m, bool full_gcov, + const char *output, struct score *score) { - char **lines = strsplit(res, output, "\n", NULL); + char **lines = strsplit(score, output, "\n", NULL); float covered_lines = 0.0; unsigned int i, total_lines = 0; bool lines_matter = false; - /* FIXME: We assume GCOV mentions all files! + /* Output looks like: File '../../../ccan/tdb2/private.h' Lines executed:0.00% of 8 + /home/ccan/ccan/tdb2/test/run-simple-delete.c:creating 'run-simple-delete.c.gcov' File '../../../ccan/tdb2/tdb.c' Lines executed:0.00% of 450 @@ -72,113 +82,86 @@ static void analyze_coverage(struct manifest *m, errx(1, "Could not parse line '%s'", lines[i]); total_lines += of; covered_lines += ex / 100.0 * of; + } else if (full_gcov && strstr(lines[i], ":creating '")) { + char *file, *filename, *apostrophe; + apostrophe = strchr(lines[i], '\''); + filename = apostrophe + 1; + apostrophe = strchr(filename, '\''); + *apostrophe = '\0'; + if (lines_matter) { + file = grab_file(score, filename, NULL); + if (!file) { + score->error = talloc_asprintf(score, + "Reading %s", + filename); + return; + } + printf("%s", file); + } + if (tools_verbose) + printf("Unlinking %s", filename); + unlink(filename); } } - /* Nothing covered? */ + score->pass = true; + + /* Nothing covered? We can't tell if there's a source file which + * was never executed, or there really is no code to execute, so + * assume the latter: this test deserves no score. */ if (total_lines == 0) - res->uncovered = 1.0; - else - res->uncovered = 1.0 - covered_lines / total_lines; + score->total = score->score = 0; + else { + score->total = 5; + score->score = score_coverage(covered_lines / total_lines, + score->total); + } } -static void *do_run_coverage_tests(struct manifest *m, - bool keep, - unsigned int *timeleft) +static void do_run_coverage_tests(struct manifest *m, + bool keep, + unsigned int *timeleft, struct score *score) { - struct coverage_result *res; struct ccan_file *i; char *cmdout; - char *olddir; char *covcmd; - bool ok; - - /* We run tests in the module directory, so any paths - * referenced can all be module-local. */ - olddir = talloc_getcwd(m); - if (!olddir) - err(1, "Could not save cwd"); - if (chdir(m->dir) != 0) - err(1, "Could not chdir to %s", m->dir); - - res = talloc(m, struct coverage_result); - res->what = NULL; - res->uncovered = 1.0; + bool full_gcov = (verbose > 1); + struct list_head *list; /* This tells gcov where we put those .gcno files. */ - covcmd = talloc_asprintf(m, "gcov -n -o %s", - talloc_dirname(res, m->info_file->compiled)); + covcmd = talloc_asprintf(m, "gcov %s -o %s", + full_gcov ? "" : "-n", + talloc_dirname(score, m->info_file->compiled)); /* Run them all. */ - list_for_each(&m->run_tests, i, list) { - cmdout = run_command(m, timeleft, i->cov_compiled); - if (cmdout) { - res->what = i->fullname; - res->output = talloc_steal(res, cmdout); - return res; + foreach_ptr(list, &m->run_tests, &m->api_tests) { + list_for_each(list, i, list) { + if (run_command(score, timeleft, &cmdout, + "%s", i->cov_compiled)) { + covcmd = talloc_asprintf_append(covcmd, " %s", + i->fullname); + } else { + score->error = "Running test with coverage"; + score_file_error(score, i, 0, cmdout); + return; + } } - covcmd = talloc_asprintf_append(covcmd, " %s", i->name); - move_gcov_turd(olddir, i, ".gcda"); - } - - list_for_each(&m->api_tests, i, list) { - cmdout = run_command(m, timeleft, i->cov_compiled); - if (cmdout) { - res->what = i->fullname; - res->output = talloc_steal(res, cmdout); - return res; - } - covcmd = talloc_asprintf_append(covcmd, " %s", i->name); - move_gcov_turd(olddir, i, ".gcda"); } /* Now run gcov: we want output even if it succeeds. */ - cmdout = run_with_timeout(m, covcmd, &ok, timeleft); - if (!ok) { - res->what = "Running gcov"; - res->output = talloc_steal(res, cmdout); - return res; + if (!run_command(score, timeleft, &cmdout, "%s", covcmd)) { + score->error = talloc_asprintf(score, "Running gcov: %s", + cmdout); + return; } - analyze_coverage(m, res, cmdout); - return res; -} - -/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... */ -static unsigned int score_coverage(struct manifest *m, void *check_result) -{ - struct coverage_result *res = check_result; - float thresh; - unsigned int i; - - for (i = 0, thresh = 0.5; - i < run_coverage_tests.total_score; - i++, thresh /= 2) { - if (res->uncovered > thresh) - break; - } - return i; -} - -static const char *describe_run_coverage_tests(struct manifest *m, - void *check_result) -{ - struct coverage_result *res = check_result; - - if (res->what) - return talloc_asprintf(m, "%s: %s", res->what, res->output); - - return talloc_asprintf(m, "Tests achieved %0.2f%% coverage", - (1.0 - res->uncovered) * 100); + analyze_coverage(m, full_gcov, cmdout, score); } struct ccanlint run_coverage_tests = { .key = "test-coverage", .name = "Code coverage of module tests", - .total_score = 5, - .score = score_coverage, .check = do_run_coverage_tests, - .describe = describe_run_coverage_tests, }; -REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, NULL); +REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, &run_tests, NULL);