1 #include <tools/ccanlint/ccanlint.h>
2 #include <tools/tools.h>
3 #include <ccan/talloc/talloc.h>
4 #include <ccan/str_talloc/str_talloc.h>
5 #include <ccan/str/str.h>
18 struct coverage_result {
24 static bool find_source_file(struct manifest *m, const char *filename)
28 list_for_each(&m->c_files, i, list) {
29 if (streq(i->fullname, filename))
32 list_for_each(&m->h_files, i, list) {
33 if (streq(i->fullname, filename))
39 /* FIXME: Don't know how stable this is. Read cov files directly? */
40 static void analyze_coverage(struct manifest *m,
41 struct coverage_result *res, const char *output)
43 char **lines = strsplit(res, output, "\n", NULL);
44 float covered_lines = 0.0;
45 unsigned int i, total_lines = 0;
46 bool lines_matter = false;
48 /* FIXME: We assume GCOV mentions all files!
50 File '../../../ccan/tdb2/private.h'
51 Lines executed:0.00% of 8
53 File '../../../ccan/tdb2/tdb.c'
54 Lines executed:0.00% of 450
57 for (i = 0; lines[i]; i++) {
58 if (strstarts(lines[i], "File '")) {
59 char *file = lines[i] + strlen("File '");
60 file[strcspn(file, "'")] = '\0';
61 if (find_source_file(m, file))
65 } else if (lines_matter
66 && strstarts(lines[i], "Lines executed:")) {
69 if (sscanf(lines[i], "Lines executed:%f%% of %u",
71 errx(1, "Could not parse line '%s'", lines[i]);
73 covered_lines += ex / 100.0 * of;
77 /* Nothing covered? */
81 res->uncovered = 1.0 - covered_lines / total_lines;
84 static void *do_run_coverage_tests(struct manifest *m,
86 unsigned int *timeleft)
88 struct coverage_result *res;
95 /* We run tests in the module directory, so any paths
96 * referenced can all be module-local. */
97 olddir = talloc_getcwd(m);
99 err(1, "Could not save cwd");
100 if (chdir(m->dir) != 0)
101 err(1, "Could not chdir to %s", m->dir);
103 res = talloc(m, struct coverage_result);
105 res->uncovered = 1.0;
107 /* This tells gcov where we put those .gcno files. */
108 covcmd = talloc_asprintf(m, "gcov -n -o %s",
109 talloc_dirname(res, m->info_file->compiled));
112 list_for_each(&m->run_tests, i, list) {
113 cmdout = run_command(m, timeleft, i->cov_compiled);
115 res->what = i->fullname;
116 res->output = talloc_steal(res, cmdout);
119 covcmd = talloc_asprintf_append(covcmd, " %s", i->name);
122 list_for_each(&m->api_tests, i, list) {
123 cmdout = run_command(m, timeleft, i->cov_compiled);
125 res->what = i->fullname;
126 res->output = talloc_steal(res, cmdout);
129 covcmd = talloc_asprintf_append(covcmd, " %s", i->name);
132 /* Now run gcov: we want output even if it succeeds. */
133 cmdout = run_with_timeout(m, covcmd, &ok, timeleft);
135 res->what = "Running gcov";
136 res->output = talloc_steal(res, cmdout);
140 analyze_coverage(m, res, cmdout);
144 /* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... */
145 static unsigned int score_coverage(struct manifest *m, void *check_result)
147 struct coverage_result *res = check_result;
151 for (i = 0, thresh = 0.5;
152 i < run_coverage_tests.total_score;
154 if (res->uncovered > thresh)
160 static const char *describe_run_coverage_tests(struct manifest *m,
163 struct coverage_result *res = check_result;
166 return talloc_asprintf(m, "%s: %s", res->what, res->output);
168 return talloc_asprintf(m, "Tests achieved %0.2f%% coverage",
169 (1.0 - res->uncovered) * 100);
172 struct ccanlint run_coverage_tests = {
173 .key = "test-coverage",
174 .name = "Code coverage of module tests",
176 .score = score_coverage,
177 .check = do_run_coverage_tests,
178 .describe = describe_run_coverage_tests,
181 REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, NULL);