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/grab_file/grab_file.h>
6 #include <ccan/str/str.h>
19 struct coverage_result {
25 static bool find_source_file(struct manifest *m, const char *filename)
29 list_for_each(&m->c_files, i, list) {
30 if (streq(i->fullname, filename))
33 list_for_each(&m->h_files, i, list) {
34 if (streq(i->fullname, filename))
40 /* FIXME: Don't know how stable this is. Read cov files directly? */
41 static void analyze_coverage(struct manifest *m,
42 struct coverage_result *res, const char *output,
45 char **lines = strsplit(res, output, "\n", NULL);
46 float covered_lines = 0.0;
47 unsigned int i, total_lines = 0;
48 bool lines_matter = false;
50 /* FIXME: We assume GCOV mentions all files!
52 File '../../../ccan/tdb2/private.h'
53 Lines executed:0.00% of 8
54 /home/ccan/ccan/tdb2/test/run-simple-delete.c:creating 'run-simple-delete.c.gcov'
56 File '../../../ccan/tdb2/tdb.c'
57 Lines executed:0.00% of 450
60 for (i = 0; lines[i]; i++) {
61 if (strstarts(lines[i], "File '")) {
62 char *file = lines[i] + strlen("File '");
63 file[strcspn(file, "'")] = '\0';
64 if (find_source_file(m, file))
68 } else if (lines_matter
69 && strstarts(lines[i], "Lines executed:")) {
72 if (sscanf(lines[i], "Lines executed:%f%% of %u",
74 errx(1, "Could not parse line '%s'", lines[i]);
76 covered_lines += ex / 100.0 * of;
77 } else if (full_gcov && strstr(lines[i], ":creating '")) {
78 char *file, *filename, *apostrophe;
79 apostrophe = strchr(lines[i], '\'');
80 filename = apostrophe + 1;
81 apostrophe = strchr(filename, '\'');
84 file = grab_file(res, filename, NULL);
86 res->what = talloc_asprintf(res,
89 res->output = talloc_strdup(res,
93 res->output = talloc_append_string(res->output,
97 printf("Unlinking %s", filename);
102 /* Nothing covered? */
103 if (total_lines == 0)
104 res->uncovered = 1.0;
106 res->uncovered = 1.0 - covered_lines / total_lines;
109 static void *do_run_coverage_tests(struct manifest *m,
111 unsigned int *timeleft)
113 struct coverage_result *res;
118 bool full_gcov = (verbose > 1);
120 res = talloc(m, struct coverage_result);
122 res->output = talloc_strdup(res, "");
123 res->uncovered = 1.0;
125 /* This tells gcov where we put those .gcno files. */
126 covcmd = talloc_asprintf(m, "gcov %s -o %s",
127 full_gcov ? "" : "-n",
128 talloc_dirname(res, m->info_file->compiled));
131 list_for_each(&m->run_tests, i, list) {
132 cmdout = run_command(m, timeleft, i->cov_compiled);
134 res->what = i->fullname;
135 res->output = talloc_steal(res, cmdout);
138 covcmd = talloc_asprintf_append(covcmd, " %s", i->fullname);
141 list_for_each(&m->api_tests, i, list) {
142 cmdout = run_command(m, timeleft, i->cov_compiled);
144 res->what = i->fullname;
145 res->output = talloc_steal(res, cmdout);
148 covcmd = talloc_asprintf_append(covcmd, " %s", i->fullname);
151 /* Now run gcov: we want output even if it succeeds. */
152 cmdout = run_with_timeout(m, covcmd, &ok, timeleft);
154 res->what = "Running gcov";
155 res->output = talloc_steal(res, cmdout);
159 analyze_coverage(m, res, cmdout, full_gcov);
164 /* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... */
165 static unsigned int score_coverage(struct manifest *m, void *check_result)
167 struct coverage_result *res = check_result;
171 for (i = 0, thresh = 0.5;
172 i < run_coverage_tests.total_score;
174 if (res->uncovered > thresh)
180 static const char *describe_run_coverage_tests(struct manifest *m,
183 struct coverage_result *res = check_result;
184 bool full_gcov = (verbose > 1);
188 return talloc_asprintf(m, "%s: %s", res->what, res->output);
193 ret = talloc_asprintf(m, "Tests achieved %0.2f%% coverage",
194 (1.0 - res->uncovered) * 100);
196 ret = talloc_asprintf_append(ret, "\n%s", res->output);
200 struct ccanlint run_coverage_tests = {
201 .key = "test-coverage",
202 .name = "Code coverage of module tests",
204 .score = score_coverage,
205 .check = do_run_coverage_tests,
206 .describe = describe_run_coverage_tests,
209 REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, NULL);