Browse Source

ccanlint: rework so checks have more structure.

Previously each check returned a void *, but in fact most of them fell into
similar patterns.  So define 'struct score' and a helper to add files to it,
and use that.

Under these rules, you get 0/1 if you skip a test because a dependency failed
which in theory means your score (as a percentage) could drop if you fix
a test.
Rusty Russell 15 years ago
parent
commit
7a163ea2dc

+ 44 - 46
tools/ccanlint/ccanlint.c

@@ -88,30 +88,37 @@ static const char *should_skip(struct manifest *m, struct ccanlint *i)
 
 
 static bool run_test(struct ccanlint *i,
 static bool run_test(struct ccanlint *i,
 		     bool quiet,
 		     bool quiet,
-		     unsigned int *score,
-		     unsigned int *total_score,
+		     unsigned int *running_score,
+		     unsigned int *running_total,
 		     struct manifest *m)
 		     struct manifest *m)
 {
 {
-	void *result;
-	unsigned int this_score, max_score, timeleft;
+	unsigned int timeleft;
 	const struct dependent *d;
 	const struct dependent *d;
 	const char *skip;
 	const char *skip;
-	bool bad, good;
+	struct score *score;
 
 
 	//one less test to run through
 	//one less test to run through
 	list_for_each(&i->dependencies, d, node)
 	list_for_each(&i->dependencies, d, node)
 		d->dependent->num_depends--;
 		d->dependent->num_depends--;
 
 
+	score = talloc(m, struct score);
+	list_head_init(&score->per_file_errors);
+	score->error = NULL;
+	score->pass = false;
+	score->score = 0;
+	score->total = 1;
+
 	skip = should_skip(m, i);
 	skip = should_skip(m, i);
 
 
 	if (skip) {
 	if (skip) {
 	skip:
 	skip:
 		if (verbose && !streq(skip, "not relevant to target"))
 		if (verbose && !streq(skip, "not relevant to target"))
-			printf("  %s: skipped (%s)\n", i->name, skip);
+			printf("%s: skipped (%s)\n", i->name, skip);
 
 
-		/* If we're skipping this because a prereq failed, we fail. */
+		/* If we're skipping this because a prereq failed, we fail:
+		 * count it as a score of 1. */
 		if (i->skip_fail)
 		if (i->skip_fail)
-			*total_score += i->total_score;
+			(*running_total)++;
 			
 			
 		list_del(&i->list);
 		list_del(&i->list);
 		list_add_tail(&finished_tests, &i->list);
 		list_add_tail(&finished_tests, &i->list);
@@ -121,55 +128,52 @@ static bool run_test(struct ccanlint *i,
 			d->dependent->skip = "dependency was skipped";
 			d->dependent->skip = "dependency was skipped";
 			d->dependent->skip_fail = i->skip_fail;
 			d->dependent->skip_fail = i->skip_fail;
 		}
 		}
-		return true;
+		return i->skip_fail ? false : true;
 	}
 	}
 
 
 	timeleft = timeout ? timeout : default_timeout_ms;
 	timeleft = timeout ? timeout : default_timeout_ms;
-	result = i->check(m, i->keep_results, &timeleft);
+	i->check(m, i->keep_results, &timeleft, score);
 	if (timeout && timeleft == 0) {
 	if (timeout && timeleft == 0) {
 		skip = "timeout";
 		skip = "timeout";
 		goto skip;
 		goto skip;
 	}
 	}
 
 
-	max_score = i->total_score;
-	if (!max_score)
-		max_score = 1;
-
-	if (!result)
-		this_score = max_score;
-	else if (i->score)
-		this_score = i->score(m, result);
-	else
-		this_score = 0;
-
-	bad = (this_score == 0);
-	good = (this_score >= max_score);
-
-	if (verbose || (!good && !quiet)) {
-		printf("  %s: %s", i->name,
-		       bad ? "FAIL" : good ? "PASS" : "PARTIAL");
-		if (max_score > 1)
-			printf(" (+%u/%u)", this_score, max_score);
+	assert(score->score <= score->total);
+	if ((!score->pass && !quiet)
+	    || (score->score < score->total && verbose)
+	    || verbose > 1) {
+		printf("%s: %s", i->name, score->pass ? "PASS" : "FAIL");
+		if (score->total > 1)
+			printf(" (+%u/%u)", score->score, score->total);
 		printf("\n");
 		printf("\n");
 	}
 	}
 
 
-	if (!quiet && result) {
-		const char *desc;
-		if (i->describe && (desc = i->describe(m, result)) != NULL) 
-			printf("    %s\n", desc);
+	if (!quiet && !score->pass) {
+		struct file_error *f;
+
+		if (score->error)
+			printf("%s:\n", score->error);
+
+		list_for_each(&score->per_file_errors, f, list) {
+			if (f->line)
+				printf("%s:%u:%s\n",
+				       f->file->fullname, f->line, f->error);
+			else if (f->file)
+				printf("%s:%s\n", f->file->fullname, f->error);
+			else
+				printf("%s\n", f->error);
+		}
 		if (i->handle)
 		if (i->handle)
-			i->handle(m, result);
+			i->handle(m, score);
 	}
 	}
 
 
-	if (i->total_score) {
-		*score += this_score;
-		*total_score += i->total_score;
-	}
+	*running_score += score->score;
+	*running_total += score->total;
 
 
 	list_del(&i->list);
 	list_del(&i->list);
 	list_add_tail(&finished_tests, &i->list);
 	list_add_tail(&finished_tests, &i->list);
 
 
-	if (bad) {
+	if (!score->pass) {
 		/* Skip any tests which depend on this one. */
 		/* Skip any tests which depend on this one. */
 		list_for_each(&i->dependencies, d, node) {
 		list_for_each(&i->dependencies, d, node) {
 			if (d->dependent->skip)
 			if (d->dependent->skip)
@@ -178,7 +182,7 @@ static bool run_test(struct ccanlint *i,
 			d->dependent->skip_fail = true;
 			d->dependent->skip_fail = true;
 		}
 		}
 	}
 	}
-	return good;
+	return score->pass;
 }
 }
 
 
 static void register_test(struct list_head *h, struct ccanlint *test, ...)
 static void register_test(struct list_head *h, struct ccanlint *test, ...)
@@ -451,9 +455,6 @@ int main(int argc, char *argv[])
 	}
 	}
 
 
 	/* If you don't pass the compulsory tests, you get a score of 0. */
 	/* If you don't pass the compulsory tests, you get a score of 0. */
-	if (verbose)
-		printf("Compulsory tests:\n");
-
 	while ((i = get_next_test(&compulsory_tests)) != NULL) {
 	while ((i = get_next_test(&compulsory_tests)) != NULL) {
 		if (!run_test(i, summary, &score, &total_score, m)) {
 		if (!run_test(i, summary, &score, &total_score, m)) {
 			printf("%sTotal score: 0/%u\n", prefix, total_score);
 			printf("%sTotal score: 0/%u\n", prefix, total_score);
@@ -462,9 +463,6 @@ int main(int argc, char *argv[])
 	}
 	}
 
 
 	add_info_fails(m->info_file);
 	add_info_fails(m->info_file);
-
-	if (verbose)
-		printf("\nNormal tests:\n");
 	while ((i = get_next_test(&normal_tests)) != NULL)
 	while ((i = get_next_test(&normal_tests)) != NULL)
 		run_test(i, summary, &score, &total_score, m);
 		run_test(i, summary, &score, &total_score, m);
 
 

+ 24 - 21
tools/ccanlint/ccanlint.h

@@ -42,6 +42,20 @@ struct manifest {
 
 
 struct manifest *get_manifest(const void *ctx, const char *dir);
 struct manifest *get_manifest(const void *ctx, const char *dir);
 
 
+struct file_error {
+	struct list_node list;
+	struct ccan_file *file;
+	unsigned int line; /* 0 not to print */
+	const char *error;
+};
+
+struct score {
+	bool pass;
+	unsigned int score, total;
+	const char *error;
+	struct list_head per_file_errors;
+};
+
 struct ccanlint {
 struct ccanlint {
 	struct list_node list;
 	struct list_node list;
 
 
@@ -51,27 +65,18 @@ struct ccanlint {
 	/* Unique name of test */
 	/* Unique name of test */
 	const char *name;
 	const char *name;
 
 
-	/* Total score that this test is worth. */
-	unsigned int total_score;
-
 	/* Can we run this test?  Return string explaining why, if not. */
 	/* Can we run this test?  Return string explaining why, if not. */
 	const char *(*can_run)(struct manifest *m);
 	const char *(*can_run)(struct manifest *m);
 
 
-	/* If this returns non-NULL, it means the check failed.
-	 * keep is set if you should keep the results.
-	 * If timeleft is set to 0, means it timed out. */
-	void *(*check)(struct manifest *m, bool keep, unsigned int *timeleft);
-
-	/* The non-NULL return from check is passed to one of these: */
-
-	/* So, what did this get out of the total_score?  (NULL means 0). */
-	unsigned int (*score)(struct manifest *m, void *check_result);
-
-	/* Verbose description of what was wrong. */
-	const char *(*describe)(struct manifest *m, void *check_result);
+	/* keep is set if you should keep the results.
+	 * If timeleft is set to 0, means it timed out.
+	 * score is the result, and a talloc context freed after all our
+	 * depends are done. */
+	void (*check)(struct manifest *m,
+		      bool keep, unsigned int *timeleft, struct score *score);
 
 
 	/* Can we do something about it? (NULL if not) */
 	/* Can we do something about it? (NULL if not) */
-	void (*handle)(struct manifest *m, void *check_result);
+	void (*handle)(struct manifest *m, struct score *score);
 
 
 	/* Internal use fields: */
 	/* Internal use fields: */
 	/* Who depends on us? */
 	/* Who depends on us? */
@@ -184,11 +189,9 @@ char *get_symbol_token(void *ctx, const char **line);
 struct list_head *get_ccan_file_docs(struct ccan_file *f);
 struct list_head *get_ccan_file_docs(struct ccan_file *f);
 
 
 
 
-/* Call the reporting on every line in the file.  sofar contains
- * previous results. */
-char *report_on_lines(struct list_head *files,
-		      char *(*report)(const char *),
-		      char *sofar);
+/* Add an error about this file (and line, if non-zero) to the score struct */
+void score_file_error(struct score *, struct ccan_file *f, unsigned line,
+		      const char *error);
 
 
 /* Normal tests. */
 /* Normal tests. */
 extern struct ccanlint trailing_whitespace;
 extern struct ccanlint trailing_whitespace;

+ 20 - 21
tools/ccanlint/compulsory_tests/build.c

@@ -33,42 +33,41 @@ static char *obj_list(const struct manifest *m)
 	return list;
 	return list;
 }
 }
 
 
-static void *do_build(struct manifest *m,
-		      bool keep,
-		      unsigned int *timeleft)
+static void do_build(struct manifest *m,
+		     bool keep,
+		     unsigned int *timeleft,
+		     struct score *score)
 {
 {
-	char *filename, *err;
+	char *filename, *errstr;
 
 
 	if (list_empty(&m->c_files)) {
 	if (list_empty(&m->c_files)) {
 		/* No files?  No score, but we "pass". */
 		/* No files?  No score, but we "pass". */
-		build.total_score = 0;
-		return NULL;
+		score->total = 0;
+		score->pass = true;
+		return;
 	}
 	}
-	filename = link_objects(m, m->basename, false, obj_list(m), &err);
-	if (filename && keep) {
+
+	filename = link_objects(m, m->basename, false, obj_list(m), &errstr);
+	if (!filename) {
+		score->error = "The object file didn't build";
+		score_file_error(score, NULL, 0, errstr);
+		return;
+	}
+
+	if (keep) {
 		char *realname = talloc_asprintf(m, "%s.o", m->dir);
 		char *realname = talloc_asprintf(m, "%s.o", m->dir);
 		/* We leave this object file around, all built. */
 		/* We leave this object file around, all built. */
 		if (!move_file(filename, realname))
 		if (!move_file(filename, realname))
-			return talloc_asprintf(m, "Failed to rename %s to %s",
-					       filename, realname);
-		return NULL;
+			err(1, "Renaming %s to %s", filename, realname);
 	}
 	}
-	return err;
-}
-
-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);
+	score->pass = true;
+	score->score = score->total;
 }
 }
 
 
 struct ccanlint build = {
 struct ccanlint build = {
 	.key = "build",
 	.key = "build",
 	.name = "Module can be built from object files",
 	.name = "Module can be built from object files",
-	.total_score = 1,
 	.check = do_build,
 	.check = do_build,
-	.describe = describe_build,
 	.can_run = can_build,
 	.can_run = can_build,
 };
 };
 
 

+ 12 - 17
tools/ccanlint/compulsory_tests/build_objs.c

@@ -21,42 +21,37 @@ static const char *can_build(struct manifest *m)
 	return NULL;
 	return NULL;
 }
 }
 
 
-static void *check_objs_build(struct manifest *m,
-			      bool keep, unsigned int *timeleft)
+static void check_objs_build(struct manifest *m,
+			     bool keep,
+			     unsigned int *timeleft, struct score *score)
 {
 {
-	char *report = NULL;
 	struct ccan_file *i;
 	struct ccan_file *i;
 
 
+	if (list_empty(&m->c_files))
+		score->total = 0;
+
 	list_for_each(&m->c_files, i, list) {
 	list_for_each(&m->c_files, i, list) {
 		char *err;
 		char *err;
 		char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
 		char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
 
 
-		/* One point for each obj file. */
-		build_objs.total_score++;
-
 		i->compiled = maybe_temp_file(m, "", keep, fullfile);
 		i->compiled = maybe_temp_file(m, "", keep, fullfile);
 		err = compile_object(m, fullfile, ccan_dir, "", i->compiled);
 		err = compile_object(m, fullfile, ccan_dir, "", i->compiled);
 		if (err) {
 		if (err) {
 			talloc_free(i->compiled);
 			talloc_free(i->compiled);
-			if (report)
-				report = talloc_append_string(report, err);
-			else
-				report = err;
+			score->error = "Compiling object files";
+			score_file_error(score, i, 0, err);
 		}
 		}
 	}
 	}
-	return report;
-}
-
-static const char *describe_objs_build(struct manifest *m, void *check_result)
-{
-	return check_result;
+	if (!score->error) {
+		score->pass = true;
+		score->score = score->total;
+	}
 }
 }
 
 
 struct ccanlint build_objs = {
 struct ccanlint build_objs = {
 	.key = "build-objects",
 	.key = "build-objects",
 	.name = "Module object files can be built",
 	.name = "Module object files can be built",
 	.check = check_objs_build,
 	.check = check_objs_build,
-	.describe = describe_objs_build,
 	.can_run = can_build,
 	.can_run = can_build,
 };
 };
 
 

+ 13 - 21
tools/ccanlint/compulsory_tests/check_build.c

@@ -45,9 +45,9 @@ static char *lib_list(const struct manifest *m)
 	return ret;
 	return ret;
 }
 }
 
 
-static void *check_use_build(struct manifest *m,
-			     bool keep,
-			     unsigned int *timeleft)
+static void check_use_build(struct manifest *m,
+			    bool keep,
+			    unsigned int *timeleft, struct score *score)
 {
 {
 	char *contents;
 	char *contents;
 	char *tmpfile;
 	char *tmpfile;
@@ -58,8 +58,7 @@ static void *check_use_build(struct manifest *m,
 
 
 	fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
 	fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
 	if (fd < 0)
 	if (fd < 0)
-		return talloc_asprintf(m, "Creating temporary file: %s",
-				       strerror(errno));
+		err(1, "Creating temporary file %s", tmpfile);
 
 
 	contents = talloc_asprintf(tmpfile,
 	contents = talloc_asprintf(tmpfile,
 				   "#include <ccan/%s/%s.h>\n"
 				   "#include <ccan/%s/%s.h>\n"
@@ -68,30 +67,23 @@ static void *check_use_build(struct manifest *m,
 				   "	return 0;\n"
 				   "	return 0;\n"
 				   "}\n",
 				   "}\n",
 				   m->basename, m->basename);
 				   m->basename, m->basename);
-	if (write(fd, contents, strlen(contents)) != strlen(contents)) {
-		close(fd);
-		return "Failure writing to temporary file";
-	}
+	if (write(fd, contents, strlen(contents)) != strlen(contents))
+		err(1, "Failure writing to temporary file %s", tmpfile);
 	close(fd);
 	close(fd);
 
 
-	return compile_and_link(m, tmpfile, ccan_dir, obj_list(m), "",
-				lib_list(m),
-				maybe_temp_file(m, "", keep, tmpfile));
-}
-
-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);
+	score->error = compile_and_link(m, tmpfile, ccan_dir, obj_list(m), "",
+					lib_list(m),
+					maybe_temp_file(m, "", keep, tmpfile));
+	if (!score->error) {
+		score->pass = true;
+		score->score = score->total;
+	}
 }
 }
 
 
 struct ccanlint check_build = {
 struct ccanlint check_build = {
 	.key = "check-link",
 	.key = "check-link",
 	.name = "Module can be linked against trivial program",
 	.name = "Module can be linked against trivial program",
-	.total_score = 1,
 	.check = check_use_build,
 	.check = check_use_build,
-	.describe = describe_use_build,
 	.can_run = can_build,
 	.can_run = can_build,
 };
 };
 
 

+ 14 - 22
tools/ccanlint/compulsory_tests/check_depends_built.c

@@ -35,13 +35,12 @@ static bool expect_obj_file(const char *dir)
 	return has_c_files;
 	return has_c_files;
 }
 }
 
 
-static void *check_depends_built(struct manifest *m,
-				 bool keep,
-				 unsigned int *timeleft)
+static void check_depends_built(struct manifest *m,
+				bool keep,
+				unsigned int *timeleft, struct score *score)
 {
 {
 	struct ccan_file *i;
 	struct ccan_file *i;
 	struct stat st;
 	struct stat st;
-	char *report = NULL;
 
 
 	list_for_each(&m->dep_dirs, i, list) {
 	list_for_each(&m->dep_dirs, i, list) {
 		if (!expect_obj_file(i->fullname))
 		if (!expect_obj_file(i->fullname))
@@ -49,9 +48,11 @@ static void *check_depends_built(struct manifest *m,
 
 
 		i->compiled = talloc_asprintf(i, "%s.o", i->fullname);
 		i->compiled = talloc_asprintf(i, "%s.o", i->fullname);
 		if (stat(i->compiled, &st) != 0) {
 		if (stat(i->compiled, &st) != 0) {
-			report = talloc_asprintf_append(report,
-							"object file %s\n",
-							i->compiled);
+			score->error = "Dependencies are not built";
+			score_file_error(score, i, 0,
+					 talloc_asprintf(score,
+							"object file %s",
+							 i->compiled));
 			i->compiled = NULL;
 			i->compiled = NULL;
 		}			
 		}			
 	}
 	}
@@ -61,30 +62,21 @@ static void *check_depends_built(struct manifest *m,
 	    && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
 	    && (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
 		char *tapobj = talloc_asprintf(m, "%s/ccan/tap.o", ccan_dir);
 		char *tapobj = talloc_asprintf(m, "%s/ccan/tap.o", ccan_dir);
 		if (stat(tapobj, &st) != 0) {
 		if (stat(tapobj, &st) != 0) {
-			report = talloc_asprintf_append(report,
-							"object file %s"
-							" (for tests)\n",
-							tapobj);
+			score->error = talloc_asprintf(score,
+					       "tap object file not built");
 		}
 		}
 	}
 	}
 
 
-	return talloc_steal(m, report);
-}
-
-static const char *describe_depends_built(struct manifest *m,
-					  void *check_result)
-{
-	return talloc_asprintf(check_result, 
-			       "The following dependencies are not built:\n"
-			       "%s", (char *)check_result);
+	if (!score->error) {
+		score->pass = true;
+		score->score = score->total;
+	}
 }
 }
 
 
 struct ccanlint depends_built = {
 struct ccanlint depends_built = {
 	.key = "depends-built",
 	.key = "depends-built",
 	.name = "Module's CCAN dependencies are already built",
 	.name = "Module's CCAN dependencies are already built",
-	.total_score = 1,
 	.check = check_depends_built,
 	.check = check_depends_built,
-	.describe = describe_depends_built,
 	.can_run = can_build,
 	.can_run = can_build,
 };
 };
 
 

+ 13 - 25
tools/ccanlint/compulsory_tests/check_depends_exist.c

@@ -14,29 +14,24 @@
 #include <string.h>
 #include <string.h>
 #include <ctype.h>
 #include <ctype.h>
 
 
-static char *add_dep(char *sofar, struct manifest *m, const char *dep)
+static void add_dep(struct manifest *m, const char *dep, struct score *score)
 {
 {
 	struct stat st;
 	struct stat st;
 	struct ccan_file *f;
 	struct ccan_file *f;
 
 
 	f = new_ccan_file(m, ccan_dir, talloc_strdup(m, dep));
 	f = new_ccan_file(m, ccan_dir, talloc_strdup(m, dep));
 	if (stat(f->fullname, &st) != 0) {
 	if (stat(f->fullname, &st) != 0) {
-		return talloc_asprintf_append(sofar,
-					      "ccan/%s: expected it in"
-					      " directory %s\n",
-					      dep, f->fullname);
-	}
-
-	list_add_tail(&m->dep_dirs, &f->list);
-	return sofar;
+		score->error = "Depends don't exist";
+		score_file_error(score, f, 0, "could not stat");
+	} else
+		list_add_tail(&m->dep_dirs, &f->list);
 }
 }
 
 
-static void *check_depends_exist(struct manifest *m,
-				 bool keep,
-				 unsigned int *timeleft)
+static void check_depends_exist(struct manifest *m,
+				bool keep,
+				unsigned int *timeleft, struct score *score)
 {
 {
 	unsigned int i;
 	unsigned int i;
-	char *report = NULL;
 	char **deps;
 	char **deps;
 	char *updir = talloc_strdup(m, m->dir);
 	char *updir = talloc_strdup(m, m->dir);
 
 
@@ -52,25 +47,18 @@ static void *check_depends_exist(struct manifest *m,
 		if (!strstarts(deps[i], "ccan/"))
 		if (!strstarts(deps[i], "ccan/"))
 			continue;
 			continue;
 
 
-		report = add_dep(report, m, deps[i]);
+		add_dep(m, deps[i], score);
+	}
+	if (!score->error) {
+		score->pass = true;
+		score->score = score->total;
 	}
 	}
-	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 = {
 struct ccanlint depends_exist = {
 	.key = "depends-exist",
 	.key = "depends-exist",
 	.name = "Module's CCAN dependencies are present",
 	.name = "Module's CCAN dependencies are present",
-	.total_score = 1,
 	.check = check_depends_exist,
 	.check = check_depends_exist,
-	.describe = describe_depends_exist,
 };
 };
 
 
 REGISTER_TEST(depends_exist, NULL);
 REGISTER_TEST(depends_exist, NULL);

+ 16 - 21
tools/ccanlint/compulsory_tests/check_includes_build.c

@@ -32,12 +32,12 @@ static struct ccan_file *main_header(struct manifest *m)
 			return f;
 			return f;
 	}
 	}
 	/* Should not happen: we depend on has_main_header */
 	/* Should not happen: we depend on has_main_header */
-	return NULL;
+	abort();
 }
 }
 
 
-static void *check_includes_build(struct manifest *m,
-				  bool keep,
-				  unsigned int *timeleft)
+static void check_includes_build(struct manifest *m,
+				 bool keep,
+				 unsigned int *timeleft, struct score *score)
 {
 {
 	char *contents;
 	char *contents;
 	char *tmpsrc, *tmpobj;
 	char *tmpsrc, *tmpobj;
@@ -49,34 +49,29 @@ static void *check_includes_build(struct manifest *m,
 
 
 	fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
 	fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
 	if (fd < 0)
 	if (fd < 0)
-		return talloc_asprintf(m, "Creating temporary file %s: %s",
-				       tmpsrc, strerror(errno));
+		err(1, "Creating temporary file %s", tmpsrc);
 
 
 	contents = talloc_asprintf(tmpsrc, "#include <ccan/%s/%s.h>\n",
 	contents = talloc_asprintf(tmpsrc, "#include <ccan/%s/%s.h>\n",
 				   m->basename, m->basename);
 				   m->basename, m->basename);
-	if (write(fd, contents, strlen(contents)) != strlen(contents)) {
-		close(fd);
-		return "Failure writing to temporary file";
-	}
+	if (write(fd, contents, strlen(contents)) != strlen(contents))
+		err(1, "writing to temporary file %s", tmpsrc);
 	close(fd);
 	close(fd);
 
 
-	return compile_object(m, tmpsrc, ccan_dir, "", tmpobj);
-}
-
-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);
+	score->error = compile_object(m, tmpsrc, ccan_dir, "", tmpobj);
+	if (score->error) {
+		score->error = talloc_asprintf(score,
+				       "#include of the main header file:\n%s",
+				       score->error);
+	} else {
+		score->pass = true;
+		score->score = score->total;
+	}
 }
 }
 
 
 struct ccanlint includes_build = {
 struct ccanlint includes_build = {
 	.key = "include-main",
 	.key = "include-main",
 	.name = "Modules main header compiles",
 	.name = "Modules main header compiles",
-	.total_score = 1,
 	.check = check_includes_build,
 	.check = check_includes_build,
-	.describe = describe_includes_build,
 	.can_run = can_build,
 	.can_run = can_build,
 };
 };
 
 

+ 11 - 13
tools/ccanlint/compulsory_tests/has_info.c

@@ -12,21 +12,20 @@
 #include <ccan/noerr/noerr.h>
 #include <ccan/noerr/noerr.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/talloc/talloc.h>
 
 
-static void *check_has_info(struct manifest *m,
-			    bool keep,
-			    unsigned int *timeleft)
+static void check_has_info(struct manifest *m,
+			   bool keep,
+			   unsigned int *timeleft,
+			   struct score *score)
 {
 {
-	if (m->info_file)
-		return NULL;
-	return m;
-}
-
-static const char *describe_has_info(struct manifest *m, void *check_result)
-{
-	return "You have no _info file.\n\n"
+	if (m->info_file) {
+		score->pass = true;
+		score->score = score->total;
+	} else {
+		score->error = "You have no _info file.\n\n"
 	"The file _info contains the metadata for a ccan package: things\n"
 	"The file _info contains the metadata for a ccan package: things\n"
 	"like the dependencies, the documentation for the package as a whole\n"
 	"like the dependencies, the documentation for the package as a whole\n"
 	"and license information.\n";
 	"and license information.\n";
+	}
 }
 }
 
 
 static const char template[] = 
 static const char template[] = 
@@ -55,7 +54,7 @@ static const char template[] =
 	"	return 1;\n"
 	"	return 1;\n"
 	"}\n";
 	"}\n";
 
 
-static void create_info_template(struct manifest *m, void *check_result)
+static void create_info_template(struct manifest *m, struct score *score)
 {
 {
 	FILE *info;
 	FILE *info;
 	const char *filename;
 	const char *filename;
@@ -79,7 +78,6 @@ struct ccanlint has_info = {
 	.key = "info",
 	.key = "info",
 	.name = "Module has _info file",
 	.name = "Module has _info file",
 	.check = check_has_info,
 	.check = check_has_info,
-	.describe = describe_has_info,
 	.handle = create_info_template,
 	.handle = create_info_template,
 };
 };
 
 

+ 10 - 14
tools/ccanlint/compulsory_tests/has_main_header.c

@@ -12,35 +12,31 @@
 #include <ccan/talloc/talloc.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/noerr/noerr.h>
 #include <ccan/noerr/noerr.h>
 
 
-static void *check_has_main_header(struct manifest *m,
-				   bool keep,
-				   unsigned int *timeleft)
+static void check_has_main_header(struct manifest *m,
+				  bool keep,
+				  unsigned int *timeleft, struct score *score)
 {
 {
 	struct ccan_file *f;
 	struct ccan_file *f;
 
 
 	list_for_each(&m->h_files, f, list) {
 	list_for_each(&m->h_files, f, list) {
 		if (strstarts(f->name, m->basename)
 		if (strstarts(f->name, m->basename)
-		    && strlen(f->name) == strlen(m->basename) + 2)
-			return NULL;
+		    && strlen(f->name) == strlen(m->basename) + 2) {
+			score->pass = true;
+			score->score = score->total;
+			return;
+		} 
 	}
 	}
-	return m;
-}
-
-static const char *describe_has_main_header(struct manifest *m,
-					    void *check_result)
-{
-	return talloc_asprintf(m,
+	score->error = talloc_asprintf(score,
 	"You have no %s/%s.h header file.\n\n"
 	"You have no %s/%s.h header file.\n\n"
 	"CCAN modules have a name, the same as the directory name.  They're\n"
 	"CCAN modules have a name, the same as the directory name.  They're\n"
 	"expected to have an interface in the header of the same name.\n",
 	"expected to have an interface in the header of the same name.\n",
-			       m->basename, m->basename);
+				       m->basename, m->basename);
 }
 }
 
 
 struct ccanlint has_main_header = {
 struct ccanlint has_main_header = {
 	.key = "has-main-header",
 	.key = "has-main-header",
 	.name = "Module has main header file",
 	.name = "Module has main header file",
 	.check = check_has_main_header,
 	.check = check_has_main_header,
-	.describe = describe_has_main_header,
 };
 };
 
 
 REGISTER_TEST(has_main_header, NULL);
 REGISTER_TEST(has_main_header, NULL);

+ 9 - 24
tools/ccanlint/file_analysis.c

@@ -139,30 +139,6 @@ static void add_files(struct manifest *m, const char *dir)
 	closedir(d);
 	closedir(d);
 }
 }
 
 
-char *report_on_lines(struct list_head *files,
-		      char *(*report)(const char *),
-		      char *sofar)
-{
-	struct ccan_file *f;
-
-	list_for_each(files, f, list) {
-		unsigned int i;
-		char **lines = get_ccan_file_lines(f);
-
-		for (i = 0; i < f->num_lines; i++) {
-			char *r = report(lines[i]);
-			if (!r)
-				continue;
-
-			sofar = talloc_asprintf_append(sofar,
-						       "%s:%u:%s\n",
-						       f->name, i+1, r);
-			talloc_free(r);
-		}
-	}
-	return sofar;
-}
-
 struct manifest *get_manifest(const void *ctx, const char *dir)
 struct manifest *get_manifest(const void *ctx, const char *dir)
 {
 {
 	struct manifest *m = talloc(ctx, struct manifest);
 	struct manifest *m = talloc(ctx, struct manifest);
@@ -574,3 +550,12 @@ enum line_compiled get_ccan_line_pp(struct pp_conditions *cond,
 	return ret;
 	return ret;
 }
 }
 
 
+void score_file_error(struct score *score, struct ccan_file *f, unsigned line,
+		      const char *error)
+{
+	struct file_error *fe = talloc(score, struct file_error);
+	fe->file = f;
+	fe->line = line;
+	fe->error = error;
+	list_add(&score->per_file_errors, &fe->list);
+}

+ 24 - 65
tools/ccanlint/tests/build-coverage.c

@@ -25,8 +25,9 @@ static const char *can_run_coverage(struct manifest *m)
 	return NULL;
 	return NULL;
 }
 }
 
 
-static char *build_module_objs_with_coverage(struct manifest *m, bool keep,
-					     char **modobjs)
+static bool build_module_objs_with_coverage(struct manifest *m, bool keep,
+					    struct score *score,
+					    char **modobjs)
 {
 {
 	struct ccan_file *i;
 	struct ccan_file *i;
 
 
@@ -39,13 +40,15 @@ static char *build_module_objs_with_coverage(struct manifest *m, bool keep,
 		err = compile_object(m, fullfile, ccan_dir, "",
 		err = compile_object(m, fullfile, ccan_dir, "",
 				     i->cov_compiled);
 				     i->cov_compiled);
 		if (err) {
 		if (err) {
+			score_file_error(score, i, 0, err);
 			talloc_free(i->cov_compiled);
 			talloc_free(i->cov_compiled);
-			return err;
+			i->cov_compiled = NULL;
+			return false;
 		}
 		}
 		*modobjs = talloc_asprintf_append(*modobjs,
 		*modobjs = talloc_asprintf_append(*modobjs,
 						  " %s", i->cov_compiled);
 						  " %s", i->cov_compiled);
 	}
 	}
-	return NULL;
+	return true;
 }
 }
 
 
 static char *obj_list(const struct manifest *m, const char *modobjs)
 static char *obj_list(const struct manifest *m, const char *modobjs)
@@ -101,97 +104,53 @@ static char *cov_compile(const void *ctx,
 				  lib_list(m), file->cov_compiled);
 				  lib_list(m), file->cov_compiled);
 	if (errmsg) {
 	if (errmsg) {
 		talloc_free(file->cov_compiled);
 		talloc_free(file->cov_compiled);
+		file->cov_compiled = NULL;
 		return errmsg;
 		return errmsg;
 	}
 	}
 
 
 	return NULL;
 	return NULL;
 }
 }
 
 
-struct compile_tests_result {
-	struct list_node list;
-	const char *filename;
-	const char *description;
-	const char *output;
-};
-
-static void *do_compile_coverage_tests(struct manifest *m,
-				       bool keep,
-				       unsigned int *timeleft)
+/* FIXME: Coverage from testable examples as well. */
+static void do_compile_coverage_tests(struct manifest *m,
+				      bool keep,
+				      unsigned int *timeleft,
+				      struct score *score)
 {
 {
-	struct list_head *list = talloc(m, struct list_head);
 	char *cmdout, *modobjs = NULL;
 	char *cmdout, *modobjs = NULL;
 	struct ccan_file *i;
 	struct ccan_file *i;
-	struct compile_tests_result *res;
 
 
-	list_head_init(list);
-
-	if (!list_empty(&m->api_tests)) {
-		cmdout = build_module_objs_with_coverage(m, keep, &modobjs);
-		if (cmdout) {
-			res = talloc(list, struct compile_tests_result);
-			res->filename = "Module objects with coverage";
-			res->description = "failed to compile";
-			res->output = talloc_steal(res, cmdout);
-			list_add_tail(list, &res->list);
-			return list;
-		}
+	if (!list_empty(&m->api_tests)
+	    && !build_module_objs_with_coverage(m, keep, score, &modobjs)) {
+		score->error = "Failed to compile module objects with coverage";
+		return;
 	}
 	}
 
 
 	list_for_each(&m->run_tests, i, list) {
 	list_for_each(&m->run_tests, i, list) {
-		compile_tests.total_score++;
 		cmdout = cov_compile(m, m, i, NULL, keep);
 		cmdout = cov_compile(m, m, i, NULL, keep);
 		if (cmdout) {
 		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);
+			score->error = "Failed to compile test with coverage";
+			score_file_error(score, i, 0, cmdout);
 		}
 		}
 	}
 	}
 
 
 	list_for_each(&m->api_tests, i, list) {
 	list_for_each(&m->api_tests, i, list) {
-		compile_tests.total_score++;
 		cmdout = cov_compile(m, m, i, modobjs, keep);
 		cmdout = cov_compile(m, m, i, modobjs, keep);
 		if (cmdout) {
 		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);
+			score->error = "Failed to compile test with coverage";
+			score_file_error(score, i, 0, cmdout);
 		}
 		}
 	}
 	}
-
-	if (list_empty(list)) {
-		talloc_free(list);
-		list = NULL;
+	if (!score->error) {
+		score->pass = true;
+		score->score = score->total;
 	}
 	}
-
-	return list;
-}
-
-static const char *describe_compile_coverage_tests(struct manifest *m,
-						   void *check_result)
-{
-	struct list_head *list = check_result;
-	struct compile_tests_result *i;
-	char *descrip;
-
-	descrip = talloc_strdup(list,
-				"Compilation of tests for coverage 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_coverage_tests = {
 struct ccanlint compile_coverage_tests = {
 	.key = "compile-coverage-tests",
 	.key = "compile-coverage-tests",
 	.name = "Module tests compile with profiling",
 	.name = "Module tests compile with profiling",
 	.check = do_compile_coverage_tests,
 	.check = do_compile_coverage_tests,
-	.total_score = 1,
-	.describe = describe_compile_coverage_tests,
 	.can_run = can_run_coverage,
 	.can_run = can_run_coverage,
 };
 };
 
 

+ 18 - 22
tools/ccanlint/tests/compile_test_helpers.c

@@ -14,7 +14,7 @@
 #include <string.h>
 #include <string.h>
 #include <ctype.h>
 #include <ctype.h>
 
 
-static const char *can_build(struct manifest *m)
+static const char *can_run(struct manifest *m)
 {
 {
 	if (safe_mode)
 	if (safe_mode)
 		return "Safe mode enabled";
 		return "Safe mode enabled";
@@ -30,39 +30,35 @@ static char *compile(struct manifest *m,
 			      cfile->compiled);
 			      cfile->compiled);
 }
 }
 
 
-static void *do_compile_test_helpers(struct manifest *m,
-				     bool keep,
-				     unsigned int *timeleft)
+static void do_compile_test_helpers(struct manifest *m,
+				    bool keep,
+				    unsigned int *timeleft,
+				    struct score *score)
 {
 {
-	char *cmdout = NULL;
 	struct ccan_file *i;
 	struct ccan_file *i;
 
 
-	compile_tests.total_score = 0;
+	if (list_empty(&m->other_test_c_files))
+		score->total = 0;
+
 	list_for_each(&m->other_test_c_files, i, list) {
 	list_for_each(&m->other_test_c_files, i, list) {
-		compile_tests.total_score++;
-		cmdout = compile(m, keep, i);
-		if (cmdout)
-			return talloc_asprintf(m,
-					       "Failed to compile helper C"
-					       " code file %s:\n%s",
-					       i->name, cmdout);
+		char *cmdout = compile(m, keep, i);
+		if (cmdout) {
+			score->error = "Failed to compile helper C files";
+			score_file_error(score, i, 0, cmdout);
+		}
 	}
 	}
-	return NULL;
-}
 
 
-static const char *describe_compile_test_helpers(struct manifest *m,
-						 void *check_result)
-{
-	return check_result;
+	if (!score->error) {
+		score->pass = true;
+		score->score = score->total;
+	}
 }
 }
 
 
 struct ccanlint compile_test_helpers = {
 struct ccanlint compile_test_helpers = {
 	.key = "compile-helpers",
 	.key = "compile-helpers",
 	.name = "Module test helper objects compile",
 	.name = "Module test helper objects compile",
-	.total_score = 1,
 	.check = do_compile_test_helpers,
 	.check = do_compile_test_helpers,
-	.describe = describe_compile_test_helpers,
-	.can_run = can_build,
+	.can_run = can_run,
 };
 };
 
 
 REGISTER_TEST(compile_test_helpers, &depends_built, &has_tests, NULL);
 REGISTER_TEST(compile_test_helpers, &depends_built, &has_tests, NULL);

+ 28 - 88
tools/ccanlint/tests/compile_tests.c

@@ -82,124 +82,64 @@ static char *compile(const void *ctx,
 	return NULL;
 	return NULL;
 }
 }
 
 
-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,
-			      bool keep,
-			      unsigned int *timeleft)
+static void do_compile_tests(struct manifest *m,
+			     bool keep,
+			     unsigned int *timeleft, struct score *score)
 {
 {
-	struct list_head *list = talloc(m, struct list_head);
 	char *cmdout;
 	char *cmdout;
 	struct ccan_file *i;
 	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) {
 	list_for_each(&m->compile_ok_tests, i, list) {
-		compile_tests.total_score++;
-		cmdout = compile(list, m, i, false, false, keep);
+		cmdout = compile(score, m, i, false, false, keep);
 		if (cmdout) {
 		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);
+			score->error = "Failed to compile tests";
+			score_file_error(score, i, 0, cmdout);
 		}
 		}
 	}
 	}
 
 
 	list_for_each(&m->run_tests, i, list) {
 	list_for_each(&m->run_tests, i, list) {
-		compile_tests.total_score++;
-		cmdout = compile(m, m, i, false, false, keep);
+		cmdout = compile(score, m, i, false, false, keep);
 		if (cmdout) {
 		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);
+			score->error = "Failed to compile tests";
+			score_file_error(score, i, 0, cmdout);
 		}
 		}
 	}
 	}
 
 
 	list_for_each(&m->api_tests, i, list) {
 	list_for_each(&m->api_tests, i, list) {
-		compile_tests.total_score++;
-		cmdout = compile(m, m, i, false, true, keep);
+		cmdout = compile(score, m, i, false, true, keep);
 		if (cmdout) {
 		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);
+			score->error = "Failed to compile tests";
+			score_file_error(score, i, 0, cmdout);
 		}
 		}
 	}
 	}
 
 
+	/* The compile fail tests are a bit weird, handle them separately */
+	if (score->error)
+		return;
+
 	list_for_each(&m->compile_fail_tests, i, list) {
 	list_for_each(&m->compile_fail_tests, i, list) {
-		compile_tests.total_score++;
-		cmdout = compile(list, m, i, false, false, false);
+		cmdout = compile(score, m, i, false, false, false);
 		if (cmdout) {
 		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, true, 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);
-			}
+			score->error = "Failed to compile without -DFAIL";
+			score_file_error(score, i, 0, cmdout);
+			return;
+		}
+		cmdout = compile(score, m, i, true, false, false);
+		if (!cmdout) {
+			score->error = "Compiled successfully with -DFAIL?";
+			score_file_error(score, i, 0, NULL);
+			return;
 		}
 		}
 	}
 	}
 
 
-	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;
+	score->pass = true;
+	score->score = score->total;
 }
 }
 
 
 struct ccanlint compile_tests = {
 struct ccanlint compile_tests = {
 	.key = "compile-tests",
 	.key = "compile-tests",
 	.name = "Module tests compile",
 	.name = "Module tests compile",
-	.score = score_compile_tests,
-	.total_score = 1,
 	.check = do_compile_tests,
 	.check = do_compile_tests,
-	.describe = describe_compile_tests,
 	.can_run = can_build,
 	.can_run = can_build,
 };
 };
 
 

+ 12 - 23
tools/ccanlint/tests/depends_accurate.c

@@ -46,12 +46,11 @@ static bool has_dep(struct manifest *m, const char *depname, bool tap_ok)
 	return false;
 	return false;
 }
 }
 
 
-static void *check_depends_accurate(struct manifest *m,
-				    bool keep,
-				    unsigned int *timeleft)
+static void check_depends_accurate(struct manifest *m,
+				   bool keep,
+				   unsigned int *timeleft, struct score *score)
 {
 {
 	struct list_head *list;
 	struct list_head *list;
-	char *report = talloc_strdup(m, "");
 
 
 	foreach_ptr(list, &m->c_files, &m->h_files,
 	foreach_ptr(list, &m->c_files, &m->h_files,
 		    &m->run_tests, &m->api_tests,
 		    &m->run_tests, &m->api_tests,
@@ -61,7 +60,7 @@ static void *check_depends_accurate(struct manifest *m,
 		bool tap_ok;
 		bool tap_ok;
 
 
 		/* Including ccan/tap is fine for tests. */
 		/* Including ccan/tap is fine for tests. */
-		tap_ok =  (list != &m->c_files && list != &m->h_files);
+		tap_ok = (list != &m->c_files && list != &m->h_files);
 
 
 		list_for_each(list, f, list) {
 		list_for_each(list, f, list) {
 			unsigned int i;
 			unsigned int i;
@@ -79,35 +78,25 @@ static void *check_depends_accurate(struct manifest *m,
 				if (!strchr(strchr(p, '/') + 1, '/'))
 				if (!strchr(strchr(p, '/') + 1, '/'))
 					continue;
 					continue;
 				*strchr(strchr(p, '/') + 1, '/') = '\0';
 				*strchr(strchr(p, '/') + 1, '/') = '\0';
-				if (!has_dep(m, p, tap_ok))
-					report = talloc_asprintf_append(report,
-					       "%s:%u:%s\n",
-					       f->name, i+1, lines[i]);
+				if (has_dep(m, p, tap_ok))
+					continue;
+				score->error = "Includes a ccan module"
+					" not listed in _info";
+				score_file_error(score, f, i+1, lines[i]);
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	if (streq(report, "")) {
-		talloc_free(report);
-		report = NULL;
+	if (!score->error) {
+		score->pass = true;
+		score->score = score->total;
 	}
 	}
-	return report;
-}
-
-static const char *describe_depends_accurage(struct manifest *m,
-					     void *check_result)
-{
-	return talloc_asprintf(check_result, 
-			       "You include ccan modules you don't list as dependencies:\n"
-			       "%s", (char *)check_result);
 }
 }
 
 
 struct ccanlint depends_accurate = {
 struct ccanlint depends_accurate = {
 	.key = "depends-accurate",
 	.key = "depends-accurate",
 	.name = "Module's CCAN dependencies are the only ccan files #included",
 	.name = "Module's CCAN dependencies are the only ccan files #included",
-	.total_score = 1,
 	.check = check_depends_accurate,
 	.check = check_depends_accurate,
-	.describe = describe_depends_accurage,
 };
 };
 
 
 REGISTER_TEST(depends_accurate, &depends_exist, NULL);
 REGISTER_TEST(depends_accurate, &depends_exist, NULL);

+ 34 - 56
tools/ccanlint/tests/examples_compile.c

@@ -15,6 +15,8 @@ static const char *can_run(struct manifest *m)
 {
 {
 	if (safe_mode)
 	if (safe_mode)
 		return "Safe mode enabled";
 		return "Safe mode enabled";
+	if (list_empty(&m->examples))
+		return "No examples to compile";
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -128,11 +130,6 @@ static char *compile(const void *ctx,
 	return NULL;
 	return NULL;
 }
 }
 
 
-struct score {
-	unsigned int score;
-	char *errors;
-};
-
 static char *start_main(char *ret, const char *why)
 static char *start_main(char *ret, const char *why)
 {
 {
 	return talloc_asprintf_append(ret,
 	return talloc_asprintf_append(ret,
@@ -434,22 +431,21 @@ static struct ccan_file *mangle_example(struct manifest *m,
 	return f;
 	return f;
 }
 }
 
 
-static void *build_examples(struct manifest *m, bool keep,
-			    unsigned int *timeleft)
+static void build_examples(struct manifest *m, bool keep,
+			   unsigned int *timeleft, struct score *score)
 {
 {
 	struct ccan_file *i;
 	struct ccan_file *i;
-	struct score *score = talloc(m, struct score);
 	char **prev = NULL;
 	char **prev = NULL;
 
 
-	score->score = 0;
-	score->errors = talloc_strdup(score, "");
+	score->total = 0;
+	score->pass = true;
 
 
-	examples_compile.total_score = 0;
 	list_for_each(&m->examples, i, list) {
 	list_for_each(&m->examples, i, list) {
 		char *ret, *ret1, *ret2 = NULL;
 		char *ret, *ret1, *ret2 = NULL;
 		struct ccan_file *mangle1, *mangle2 = NULL;
 		struct ccan_file *mangle1, *mangle2 = NULL;
+		char *err;
 
 
-		examples_compile.total_score++;
+		score->total++;
 		/* Simplify our dumb parsing. */
 		/* Simplify our dumb parsing. */
 		strip_leading_whitespace(get_ccan_file_lines(i));
 		strip_leading_whitespace(get_ccan_file_lines(i));
 		ret = compile(i, m, i, keep);
 		ret = compile(i, m, i, keep);
@@ -481,64 +477,46 @@ static void *build_examples(struct manifest *m, bool keep,
 			}
 			}
 		}
 		}
 
 
-		score->errors = talloc_asprintf_append(score->errors,
-				       "%s: tried standalone example:\n"
-				       "%s\n"
-				       "Errors: %s\n\n",
-				       i->name,
-				       get_ccan_file_contents(i),
-				       ret);
-		score->errors = talloc_asprintf_append(score->errors,
-				       "%s: tried adding headers, wrappers:\n"
-				       "%s\n"
-				       "Errors: %s\n\n",
-				       i->name,
-				       get_ccan_file_contents(mangle1),
-				       ret1);
-
-		if (mangle2) {
-			score->errors = talloc_asprintf_append(score->errors,
-				       "%s: tried combining with"
-				       " previous example:\n"
+		score->pass = false;
+		score->error = "Compiling extracted examples failed";
+		if (!verbose) {
+			if (mangle2) 
+				err = "Standalone, adding headers, "
+					"and including previous "
+					"example all failed";
+			else
+				err = "Standalone compile and"
+					" adding headers both failed";
+		} else {
+			err = talloc_asprintf("Standalone example:\n"
+					      "%s\n"
+					      "Errors: %s\n\n"
+					      "Adding headers, wrappers:\n"
+					      "%s\n"
+					      "Errors: %s\n\n",
+					      get_ccan_file_contents(i),
+					      ret,
+					      get_ccan_file_contents(mangle1),
+					      ret1);
+
+			if (mangle2)
+				err = talloc_asprintf_append(err, 
+				       "Combining with previous example:\n"
 				       "%s\n"
 				       "%s\n"
 				       "Errors: %s\n\n",
 				       "Errors: %s\n\n",
-				       i->name,
 				       get_ccan_file_contents(mangle2),
 				       get_ccan_file_contents(mangle2),
 				       ret2);
 				       ret2);
 		}
 		}
+		score_file_error(score, i, 0, err);
 		/* This didn't work, so not a candidate for combining. */
 		/* This didn't work, so not a candidate for combining. */
 		prev = NULL;
 		prev = NULL;
 	}
 	}
-
-	if (strcmp(score->errors, "") == 0) {
-		talloc_free(score);
-		return NULL;
-	}
-	return score;
-}
-
-static unsigned int score_examples(struct manifest *m, void *check_result)
-{
-	struct score *score = check_result;
-	return score->score;
-}
-
-static const char *describe(struct manifest *m, void *check_result)
-{
-	struct score *score = check_result;
-	if (verbose >= 2 && score->errors)
-		return talloc_asprintf(m, "Compile errors building examples:\n"
-				       "%s", score->errors);
-	return NULL;
 }
 }
 
 
 struct ccanlint examples_compile = {
 struct ccanlint examples_compile = {
 	.key = "examples-compile",
 	.key = "examples-compile",
 	.name = "Module examples compile",
 	.name = "Module examples compile",
-	.score = score_examples,
-	.total_score = 3, /* This gets changed to # examples, if they exist */
 	.check = build_examples,
 	.check = build_examples,
-	.describe = describe,
 	.can_run = can_run,
 	.can_run = can_run,
 };
 };
 
 

+ 26 - 49
tools/ccanlint/tests/examples_run.c

@@ -14,16 +14,16 @@
 
 
 static const char *can_run(struct manifest *m)
 static const char *can_run(struct manifest *m)
 {
 {
+	struct list_head *list;
+
 	if (safe_mode)
 	if (safe_mode)
 		return "Safe mode enabled";
 		return "Safe mode enabled";
-	return NULL;
+	foreach_ptr(list, &m->examples, &m->mangled_examples)
+		if (!list_empty(list))
+			return NULL;
+	return "No examples";
 }
 }
 
 
-struct score {
-	unsigned int score;
-	char *errors;
-};
-
 /* Very dumb scanner, allocates %s-strings. */
 /* Very dumb scanner, allocates %s-strings. */
 static bool scan_forv(const void *ctx,
 static bool scan_forv(const void *ctx,
 		      const char *input, const char *fmt, const va_list *args)
 		      const char *input, const char *fmt, const va_list *args)
@@ -224,26 +224,21 @@ static char *unexpected(struct ccan_file *i, const char *input,
 	return output;
 	return output;
 }
 }
 
 
-static void *run_examples(struct manifest *m, bool keep,
-			  unsigned int *timeleft)
+static void run_examples(struct manifest *m, bool keep,
+			 unsigned int *timeleft, struct score *score)
 {
 {
 	struct ccan_file *i;
 	struct ccan_file *i;
 	struct list_head *list;
 	struct list_head *list;
-	struct score *score = talloc(m, struct score);
 
 
-	score->score = 0;
-	score->errors = talloc_strdup(score, "");
+	score->total = 0;
+	score->pass = true;
 
 
-	examples_run.total_score = 0;
 	foreach_ptr(list, &m->examples, &m->mangled_examples) {
 	foreach_ptr(list, &m->examples, &m->mangled_examples) {
 		list_for_each(list, i, list) {
 		list_for_each(list, i, list) {
 			char **lines, *expect, *input, *output;
 			char **lines, *expect, *input, *output;
 			unsigned int linenum = 0;
 			unsigned int linenum = 0;
 			bool exact;
 			bool exact;
 
 
-			if (i->compiled == NULL)
-				continue;
-
 			lines = get_ccan_file_lines(i);
 			lines = get_ccan_file_lines(i);
 
 
 			for (expect = find_expect(i, lines, &input, &exact,
 			for (expect = find_expect(i, lines, &input, &exact,
@@ -252,52 +247,34 @@ static void *run_examples(struct manifest *m, bool keep,
 			     linenum++,
 			     linenum++,
 				     expect = find_expect(i, lines, &input,
 				     expect = find_expect(i, lines, &input,
 							  &exact, &linenum)) {
 							  &exact, &linenum)) {
-				examples_run.total_score++;
+				char *err;
+				score->total++;
+				if (i->compiled == NULL)
+					continue;
+
 				output = unexpected(i, input, expect, exact);
 				output = unexpected(i, input, expect, exact);
-				if (!output)
+				if (!output) {
 					score->score++;
 					score->score++;
-				else {
-					score->errors = talloc_asprintf_append(
-						score->errors,
-						"%s: output '%s' didn't"
-						" %s '%s'\n",
-						i->name, output,
-						exact ? "match" : "contain",
-						expect);
+					continue;
 				}
 				}
+				err = talloc_asprintf(score,
+						      "output '%s' didn't"
+						      " %s '%s'\n",
+						      output,
+						      exact
+						      ? "match" : "contain",
+						      expect);
+				score_file_error(score, i, linenum+1, err);
+				score->pass = false;
 			}
 			}
 		}
 		}
 	}
 	}
-
-	if (strcmp(score->errors, "") == 0) {
-		talloc_free(score);
-		return NULL;
-	}
-	return score;
-}
-
-static unsigned int score_examples(struct manifest *m, void *check_result)
-{
-	struct score *score = check_result;
-	return score->score;
-}
-
-static const char *describe(struct manifest *m, void *check_result)
-{
-	struct score *score = check_result;
-	if (verbose)
-		return talloc_asprintf(m, "Wrong output running examples:\n"
-				       "%s", score->errors);
-	return NULL;
 }
 }
 
 
 struct ccanlint examples_run = {
 struct ccanlint examples_run = {
 	.key = "examples-run",
 	.key = "examples-run",
 	.name = "Module examples with expected output give that output",
 	.name = "Module examples with expected output give that output",
-	.score = score_examples,
-	.total_score = 3, /* This gets changed to # testable, if we run. */
 	.check = run_examples,
 	.check = run_examples,
-	.describe = describe,
 	.can_run = can_run,
 	.can_run = can_run,
 };
 };
 
 

+ 27 - 55
tools/ccanlint/tests/has_examples.c

@@ -56,28 +56,22 @@ static char *add_example(struct manifest *m, struct ccan_file *source,
 }
 }
 
 
 /* FIXME: We should have one example per function in header. */
 /* FIXME: We should have one example per function in header. */
-struct score {
-	bool info_example, header_example;
-	char *error;
-};
-
-static void *extract_examples(struct manifest *m,
-			      bool keep,
-			      unsigned int *timeleft)
+static void extract_examples(struct manifest *m,
+			     bool keep,
+			     unsigned int *timeleft,
+			     struct score *score)
 {
 {
 	struct ccan_file *f;
 	struct ccan_file *f;
 	struct doc_section *d;
 	struct doc_section *d;
-	struct score *score = talloc(m, struct score);
-
-	score->info_example = score->header_example = false;
-	score->error = NULL;
+	bool have_info_example = false, have_header_example = false;
 
 
+	score->total = 2;
 	list_for_each(get_ccan_file_docs(m->info_file), d, list) {
 	list_for_each(get_ccan_file_docs(m->info_file), d, list) {
 		if (streq(d->type, "example")) {
 		if (streq(d->type, "example")) {
 			score->error = add_example(m, m->info_file, keep, d);
 			score->error = add_example(m, m->info_file, keep, d);
 			if (score->error)
 			if (score->error)
-				return score;
-			score->info_example = true;
+				return;
+			have_info_example = true;
 		}
 		}
 	}
 	}
 
 
@@ -91,57 +85,35 @@ static void *extract_examples(struct manifest *m,
 			if (streq(d->type, "example")) {
 			if (streq(d->type, "example")) {
 				score->error = add_example(m, f, keep, d);
 				score->error = add_example(m, f, keep, d);
 				if (score->error)
 				if (score->error)
-					return score;
-				score->header_example = true;
+					return;
+				have_header_example = true;
 			}
 			}
 		}
 		}
 	}
 	}
-	return score;
-}
-
-static unsigned int score_examples(struct manifest *m, void *check_result)
-{
-	struct score *score = check_result;
-	int total = 0;
-
-	if (score->error)
-		return 0;
-	total += score->info_example;
-	total += score->header_example;
-	return total;
-}
-
-static const char *describe_examples(struct manifest *m,
-				     void *check_result)
-{
-	struct score *score = check_result;
-	char *descrip = NULL;
-
-	if (score->error)
-		return score->error;
 
 
-	if (!score->info_example)
-		descrip = talloc_asprintf(score,
-		"Your _info file has no module example.\n\n"
-		"There should be an Example: section of the _info documentation\n"
-		"which provides a concise toy program which uses your module\n");
-
-	if (!score->header_example)
-		descrip = talloc_asprintf(score,
-		 "%sMain header file file has no examples\n\n"
-		 "There should be an Example: section for each public function\n"
-		  "demonstrating its use\n", descrip ? descrip : "");
-
-	return descrip;
+	if (!have_info_example && !have_header_example) {
+		score->error = "You don't have any Example: sections";
+		score->score = 0;
+	} else if (!have_info_example) {
+		score->error = "You don't have an Example: section in _info";
+		score->score = 1;
+		score->pass = true;
+	} else if (!have_header_example) {
+		score->error = talloc_asprintf(score,
+			       "You don't have an Example: section in %s.h",
+			       m->basename);
+		score->score = 1;
+		score->pass = true;
+	} else {
+		score->score = score->total;
+		score->pass = true;
+	}
 }
 }
 
 
 struct ccanlint has_examples = {
 struct ccanlint has_examples = {
 	.key = "has-examples",
 	.key = "has-examples",
 	.name = "_info and header files have examples",
 	.name = "_info and header files have examples",
-	.score = score_examples,
 	.check = extract_examples,
 	.check = extract_examples,
-	.describe = describe_examples,
-	.total_score = 2,
 };
 };
 
 
 REGISTER_TEST(has_examples, &has_info, NULL);
 REGISTER_TEST(has_examples, &has_info, NULL);

+ 27 - 56
tools/ccanlint/tests/has_info_documentation.c

@@ -15,38 +15,7 @@
 #include <ccan/noerr/noerr.h>
 #include <ccan/noerr/noerr.h>
 #include <ccan/grab_file/grab_file.h>
 #include <ccan/grab_file/grab_file.h>
 
 
-struct info_docs
-{
-	bool summary;
-	bool description;
-};
-
-static void *check_has_info_documentation(struct manifest *m,
-					  bool keep,
-					  unsigned int *timeleft)
-{
-	struct list_head *infodocs = get_ccan_file_docs(m->info_file);
-	struct doc_section *d;
-	struct info_docs id = { false, false };
-
-	list_for_each(infodocs, d, list) {
-		if (!streq(d->function, m->basename))
-			continue;
-		if (streq(d->type, "summary"))
-			id.summary = true;
-		if (streq(d->type, "description"))
-			id.description = true;
-	}
-
-	if (id.summary && id.description)
-		return NULL;
-	return talloc_memdup(m, &id, sizeof(id));
-}
-
-/* This is defined below. */
-extern struct ccanlint has_info_documentation;
-
-static void create_info_template_doc(struct manifest *m, void *check_result)
+static void create_info_template_doc(struct manifest *m, struct score *score)
 {
 {
 	int fd = open("_info.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
 	int fd = open("_info.new", O_WRONLY|O_CREAT|O_EXCL, 0666);
 	FILE *new;
 	FILE *new;
@@ -87,42 +56,44 @@ static void create_info_template_doc(struct manifest *m, void *check_result)
 	}
 	}
 }
 }
 
 
-static const char *describe_has_info_documentation(struct manifest *m,
-						   void *check_result)
+static void check_has_info_documentation(struct manifest *m,
+					 bool keep,
+					 unsigned int *timeleft,
+					 struct score *score)
 {
 {
-	struct info_docs *id = check_result;
-	char *reason = talloc_strdup(m, "");
+	struct list_head *infodocs = get_ccan_file_docs(m->info_file);
+	struct doc_section *d;
+	bool summary = false, description = false;
 
 
-	if (!id->summary) {
-		has_info_documentation.handle = create_info_template_doc;
-		reason = talloc_asprintf_append(reason,
-		"Your _info file has no module documentation.\n\n"
+	list_for_each(infodocs, d, list) {
+		if (!streq(d->function, m->basename))
+			continue;
+		if (streq(d->type, "summary"))
+			summary = true;
+		if (streq(d->type, "description"))
+			description = true;
+	}
+
+	if (summary && description) {
+		score->score = score->total;
+		score->pass = true;
+	} else if (!summary) {
+		score->error = "_info file has no module documentation.\n\n"
 		"CCAN modules use /**-style comments for documentation: the\n"
 		"CCAN modules use /**-style comments for documentation: the\n"
-	        "overall documentation belongs in the _info metafile.\n");
+		"overall documentation belongs in the _info metafile.\n";
+		has_info_documentation.handle = create_info_template_doc;
 	}
 	}
-	if (!id->description)
-		reason = talloc_asprintf_append(reason,
-		"Your _info file has no module description.\n\n"
+	else if (!description) 
+		score->error = "_info file has no module description.\n\n"
 		"The lines after the first summary line in the _info file\n"
 		"The lines after the first summary line in the _info file\n"
 		"documentation should describe the purpose and use of the\n"
 		"documentation should describe the purpose and use of the\n"
-		"overall package\n");
-	return reason;
-}
-
-static unsigned int has_info_documentation_score(struct manifest *m,
-						 void *check_result)
-{
-	struct info_docs *id = check_result;
-	return (unsigned int)id->summary + id->description;
+		"overall package\n";
 }
 }
 
 
 struct ccanlint has_info_documentation = {
 struct ccanlint has_info_documentation = {
 	.key = "info-documentation",
 	.key = "info-documentation",
 	.name = "Module has documentation in _info",
 	.name = "Module has documentation in _info",
-	.total_score = 2,
-	.score = has_info_documentation_score,
 	.check = check_has_info_documentation,
 	.check = check_has_info_documentation,
-	.describe = describe_has_info_documentation,
 };
 };
 
 
 REGISTER_TEST(has_info_documentation, NULL);
 REGISTER_TEST(has_info_documentation, NULL);

+ 65 - 73
tools/ccanlint/tests/has_tests.c

@@ -10,45 +10,20 @@
 #include <err.h>
 #include <err.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/talloc/talloc.h>
 
 
-static char test_is_not_dir[] = "test is not a directory";
-
-static void *check_has_tests(struct manifest *m,
-			     bool keep,
-			     unsigned int *timeleft)
+static void handle_no_tests(struct manifest *m, struct score *score)
 {
 {
-	struct stat st;
+	FILE *run;
+	struct ccan_file *i;
 	char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
 	char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
 
 
-	if (lstat(test_dir, &st) != 0) {
-		if (errno != ENOENT)
-			err(1, "statting %s", test_dir);
-		return "You have no test directory";
-	}
-
-	if (!S_ISDIR(st.st_mode))
-		return test_is_not_dir;
-
-	if (list_empty(&m->api_tests)
-	    && list_empty(&m->run_tests)
-	    && list_empty(&m->compile_ok_tests)) {
-		if (list_empty(&m->compile_fail_tests))
-			return "You have no tests in the test directory";
-		else
-			return "You have no positive tests in the test directory";
-	}
-	return NULL;
-}
-
-static const char *describe_has_tests(struct manifest *m, void *check_result)
-{
-	return talloc_asprintf(m, "%s\n\n"
-        "CCAN modules have a directory called test/ which contains tests.\n"
+	printf(
+	"CCAN modules have a directory called test/ which contains tests.\n"
 	"There are four kinds of tests: api, run, compile_ok and compile_fail:\n"
 	"There are four kinds of tests: api, run, compile_ok and compile_fail:\n"
 	"you can tell which type of test a C file is by its name, eg 'run.c'\n"
 	"you can tell which type of test a C file is by its name, eg 'run.c'\n"
 	"and 'run-simple.c' are both run tests.\n\n"
 	"and 'run-simple.c' are both run tests.\n\n"
 
 
 	"The simplest kind of test is a run test, which must compile with no\n"
 	"The simplest kind of test is a run test, which must compile with no\n"
-	"warnings, and then run: it is expected to use libtap to report its\n"
+	"warnings, and then run: it is expected to use ccan/tap to report its\n"
 	"results in a simple and portable format.  It should #include the C\n"
 	"results in a simple and portable format.  It should #include the C\n"
 	"files from the module directly (so it can probe the internals): the\n"
 	"files from the module directly (so it can probe the internals): the\n"
 	"module will not be linked in.  The test will be run in a temporary\n"
 	"module will not be linked in.  The test will be run in a temporary\n"
@@ -67,20 +42,8 @@ static const char *describe_has_tests(struct manifest *m, void *check_result)
 	"when it's not defined: this helps ensure unrelated errors don't make\n"
 	"when it's not defined: this helps ensure unrelated errors don't make\n"
 	"compilation fail.\n\n"
 	"compilation fail.\n\n"
 
 
-	"Note that the tests are not linked against the files in the\n"
-	"above: you should directly #include those C files you want.  This\n"
-	"allows access to static functions and use special effects inside\n"
-	"test files\n", (char *)check_result);
-}
-
-static void handle_no_tests(struct manifest *m, void *check_result)
-{
-	FILE *run;
-	struct ccan_file *i;
-	char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
-
-	if (check_result == test_is_not_dir)
-		return;
+	"Note that only API tests are linked against the files in the module!\n"
+		);
 
 
 	if (!ask("Should I create a template test/run.c file for you?"))
 	if (!ask("Should I create a template test/run.c file for you?"))
 		return;
 		return;
@@ -101,41 +64,70 @@ static void handle_no_tests(struct manifest *m, void *check_result)
 			fprintf(run, "#include <ccan/%s/%s>\n",
 			fprintf(run, "#include <ccan/%s/%s>\n",
 				m->basename, i->name);
 				m->basename, i->name);
 	}
 	}
-	fputs("#include <ccan/tap/tap.h>\n", run);
-	fputs("\n", run);
-
-	fputs("int main(void)\n", run);
-	fputs("{\n", run);
-	fputs("\t/* This is how many tests you plan to run */\n", run);
-	fputs("\tplan_tests(3);\n", run);
-	fputs("\n", run);
-	fputs("\t/* Simple thing we expect to succeed */\n", run);
-	fputs("\tok1(some_test())\n", run);
-	fputs("\t/* Same, with an explicit description of the test. */\n", run);
-	fputs("\tok(some_test(), \"%s with no args should return 1\", \"some_test\")\n", run);
-	fputs("\t/* How to print out messages for debugging. */\n", run);
-	fputs("\tdiag(\"Address of some_test is %p\", &some_test)\n", run);
-	fputs("\t/* Conditional tests must be explicitly skipped. */\n", run);
-	fputs("#if HAVE_SOME_FEATURE\n", run);
-	fputs("\tok1(test_some_feature())\n", run);
-	fputs("#else\n", run);
-	fputs("\tskip(1, \"Don\'t have SOME_FEATURE\")\n", run);
-	fputs("#endif\n", run);
-	fputs("\n", run);
-	fputs("\t/* This exits depending on whether all tests passed */\n", run);
-	fputs("\treturn exit_status();\n", run);
-	fputs("}\n", run);
-
+	fprintf(run, "%s",
+		"#include <ccan/tap/tap.h>\n\n"
+		"int main(void)\n"
+		"{\n"
+		"	/* This is how many tests you plan to run */\n"
+		"	plan_tests(3);\n"
+		"\n"
+		"	/* Simple thing we expect to succeed */\n"
+		"	ok1(some_test())\n"
+		"	/* Same, with an explicit description of the test. */\n"
+		"	ok(some_test(), \"%s with no args should return 1\", \"some_test\")\n"
+		"	/* How to print out messages for debugging. */\n"
+		"	diag(\"Address of some_test is %p\", &some_test)\n"
+		"	/* Conditional tests must be explicitly skipped. */\n"
+		"#if HAVE_SOME_FEATURE\n"
+		"	ok1(test_some_feature())\n"
+		"#else\n"
+		"	skip(1, \"Don\'t have SOME_FEATURE\")\n"
+		"#endif\n"
+		"\n"
+		"	/* This exits depending on whether all tests passed */\n"
+		"	return exit_status();\n"
+		"}\n");
 	fclose(run);
 	fclose(run);
 }
 }
 
 
+static void check_has_tests(struct manifest *m,
+			    bool keep,
+			    unsigned int *timeleft, struct score *score)
+{
+	struct stat st;
+	char *test_dir = talloc_asprintf(m, "%s/test", m->dir);
+
+	if (lstat(test_dir, &st) != 0) {
+		score->error = "No test directory";
+		if (errno != ENOENT)
+			err(1, "statting %s", test_dir);
+		has_tests.handle = handle_no_tests;
+		return;
+	}
+
+	if (!S_ISDIR(st.st_mode)) {
+		score->error = "test is not a directory";
+		return;
+	}
+
+	if (list_empty(&m->api_tests)
+	    && list_empty(&m->run_tests)
+	    && list_empty(&m->compile_ok_tests)) {
+		if (list_empty(&m->compile_fail_tests)) {
+			score->error = "No tests in test directory";
+			has_tests.handle = handle_no_tests;
+		} else
+			score->error = "No positive tests in test directory";
+		return;
+	}
+	score->pass = true;
+	score->score = score->total;
+}
+
 struct ccanlint has_tests = {
 struct ccanlint has_tests = {
 	.key = "has-tests",
 	.key = "has-tests",
 	.name = "Module has tests",
 	.name = "Module has tests",
 	.check = check_has_tests,
 	.check = check_has_tests,
-	.describe = describe_has_tests,
-	.total_score = 1,
-	.handle = handle_no_tests,
 };
 };
 
 
 REGISTER_TEST(has_tests, NULL);
 REGISTER_TEST(has_tests, NULL);

+ 107 - 50
tools/ccanlint/tests/idempotent.c

@@ -17,12 +17,69 @@
 static const char explain[] 
 static const char explain[] 
 = "Headers usually start with the C preprocessor lines to prevent multiple\n"
 = "Headers usually start with the C preprocessor lines to prevent multiple\n"
   "inclusions.  These look like the following:\n"
   "inclusions.  These look like the following:\n"
-  "#ifndef MY_HEADER_H\n"
-  "#define MY_HEADER_H\n"
+  "#ifndef CCAN_<MODNAME>_H\n"
+  "#define CCAN_<MODNAME>_H\n"
   "...\n"
   "...\n"
-  "#endif /* MY_HEADER_H */\n";
+  "#endif /* CCAN_<MODNAME>_H */\n";
 
 
-static char *report_idem(struct ccan_file *f, char *sofar)
+static void fix_name(char *name)
+{
+	unsigned int i, j;
+
+	for (i = j = 0; name[i]; i++) {
+		if (isalnum(name[i]) || name[i] == '_')
+			name[j++] = toupper(name[i]);
+	}
+	name[j] = '\0';
+}
+
+static void handle_idem(struct manifest *m, struct score *score)
+{
+	struct file_error *e;
+
+	list_for_each(&score->per_file_errors, e, list) {
+		char *name, *q, *tmpname;
+		FILE *out;
+		unsigned int i;
+
+		/* Main header gets CCAN_FOO_H, others CCAN_FOO_XXX_H */
+		if (strstarts(e->file->name, m->basename)
+		    || strlen(e->file->name) != strlen(m->basename) + 2)
+			name = talloc_asprintf(score, "CCAN_%s_H", m->basename);
+		else
+			name = talloc_asprintf(score, "CCAN_%s_%s_H",
+					       m->basename, e->file->name);
+		fix_name(name);
+
+		q = talloc_asprintf(score,
+			    "Should I wrap %s in #ifndef/#define %s for you?",
+			    e->file->name, name);
+		if (!ask(q))
+			continue;
+
+		tmpname = maybe_temp_file(score, ".h", false, e->file->name);
+		out = fopen(tmpname, "w");
+		if (!out)
+			err(1, "Opening %s", tmpname);
+		if (fprintf(out, "#ifndef %s\n#define %s\n", name, name) < 0)
+			err(1, "Writing %s", tmpname);
+
+		for (i = 0; i < e->file->num_lines; i++)
+			if (fprintf(out, "%s\n", e->file->lines[i]) < 0)
+				err(1, "Writing %s", tmpname);
+
+		if (fprintf(out, "#endif /* %s */\n", name) < 0)
+			err(1, "Writing %s", tmpname);
+		
+		if (fclose(out) != 0)
+			err(1, "Closing %s", tmpname);
+
+		if (!move_file(tmpname, e->file->fullname))
+			err(1, "Moving %s to %s", tmpname, e->file->fullname);
+	}
+}
+
+static bool check_idem(struct ccan_file *f, struct score *score)
 {
 {
 	struct line_info *line_info;
 	struct line_info *line_info;
 	unsigned int i, first_preproc_line;
 	unsigned int i, first_preproc_line;
@@ -31,44 +88,48 @@ static char *report_idem(struct ccan_file *f, char *sofar)
 	line_info = get_ccan_line_info(f);
 	line_info = get_ccan_line_info(f);
 	if (f->num_lines < 3)
 	if (f->num_lines < 3)
 		/* FIXME: We assume small headers probably uninteresting. */
 		/* FIXME: We assume small headers probably uninteresting. */
-		return sofar;
+		return true;
 
 
+	score->error = "Headers are not idempotent";
 	for (i = 0; i < f->num_lines; i++) {
 	for (i = 0; i < f->num_lines; i++) {
 		if (line_info[i].type == DOC_LINE
 		if (line_info[i].type == DOC_LINE
 		    || line_info[i].type == COMMENT_LINE)
 		    || line_info[i].type == COMMENT_LINE)
 			continue;
 			continue;
-		if (line_info[i].type == CODE_LINE)
-			return talloc_asprintf_append(sofar,
-			      "%s:%u:expect first non-comment line to be #ifndef.\n", f->name, i+1);
-		else if (line_info[i].type == PREPROC_LINE)
+		if (line_info[i].type == CODE_LINE) {
+			score_file_error(score, f, i+1,
+					 "Expect first non-comment line to be"
+					 " #ifndef.");
+			return false;
+		} else if (line_info[i].type == PREPROC_LINE)
 			break;
 			break;
 	}
 	}
 
 
 	/* No code at all?  Don't complain. */
 	/* No code at all?  Don't complain. */
 	if (i == f->num_lines)
 	if (i == f->num_lines)
-		return sofar;
+		return true;
 
 
 	first_preproc_line = i;
 	first_preproc_line = i;
 	for (i = first_preproc_line+1; i < f->num_lines; i++) {
 	for (i = first_preproc_line+1; i < f->num_lines; i++) {
 		if (line_info[i].type == DOC_LINE
 		if (line_info[i].type == DOC_LINE
 		    || line_info[i].type == COMMENT_LINE)
 		    || line_info[i].type == COMMENT_LINE)
 			continue;
 			continue;
-		if (line_info[i].type == CODE_LINE)
-			return talloc_asprintf_append(sofar,
-			      "%s:%u:expect second line to be #define.\n", f->name, i+1);
-		else if (line_info[i].type == PREPROC_LINE)
+		if (line_info[i].type == CODE_LINE) {
+			score_file_error(score, f, i+1,
+					 "Expect second non-comment line to be"
+					 " #define.");
+			return false;
+		} else if (line_info[i].type == PREPROC_LINE)
 			break;
 			break;
 	}
 	}
 
 
 	/* No code at all?  Weird. */
 	/* No code at all?  Weird. */
 	if (i == f->num_lines)
 	if (i == f->num_lines)
-		return sofar;
+		return true;
 
 
 	/* We expect a condition on this line. */
 	/* We expect a condition on this line. */
 	if (!line_info[i].cond) {
 	if (!line_info[i].cond) {
-		return talloc_asprintf_append(sofar,
-					      "%s:%u:expected #ifndef.\n",
-					      f->name, first_preproc_line+1);
+		score_file_error(score, f, i+1, "Expected #ifndef");
+		return false;
 	}
 	}
 
 
 	line = f->lines[i];
 	line = f->lines[i];
@@ -76,24 +137,27 @@ static char *report_idem(struct ccan_file *f, char *sofar)
 	/* We expect the condition to be ! IFDEF <symbol>. */
 	/* We expect the condition to be ! IFDEF <symbol>. */
 	if (line_info[i].cond->type != PP_COND_IFDEF
 	if (line_info[i].cond->type != PP_COND_IFDEF
 	    || !line_info[i].cond->inverse) {
 	    || !line_info[i].cond->inverse) {
-		return talloc_asprintf_append(sofar,
-					      "%s:%u:expected #ifndef.\n",
-					      f->name, first_preproc_line+1);
+		score_file_error(score, f, i+1, "Expected #ifndef");
+		return false;
 	}
 	}
 
 
 	/* And this to be #define <symbol> */
 	/* And this to be #define <symbol> */
 	if (!get_token(&line, "#"))
 	if (!get_token(&line, "#"))
 		abort();
 		abort();
 	if (!get_token(&line, "define")) {
 	if (!get_token(&line, "define")) {
-		return talloc_asprintf_append(sofar,
-			      "%s:%u:expected '#define %s'.\n",
-			      f->name, i+1, line_info[i].cond->symbol);
+		char *str = talloc_asprintf(score,
+					    "expected '#define %s'",
+					    line_info[i].cond->symbol);
+		score_file_error(score, f, i+1, str);
+		return false;
 	}
 	}
 	sym = get_symbol_token(f, &line);
 	sym = get_symbol_token(f, &line);
 	if (!sym || !streq(sym, line_info[i].cond->symbol)) {
 	if (!sym || !streq(sym, line_info[i].cond->symbol)) {
-		return talloc_asprintf_append(sofar,
-			      "%s:%u:expected '#define %s'.\n",
-			      f->name, i+1, line_info[i].cond->symbol);
+		char *str = talloc_asprintf(score,
+					    "expected '#define %s'",
+					    line_info[i].cond->symbol);
+		score_file_error(score, f, i+1, str);
+		return false;
 	}
 	}
 
 
 	/* Rest of code should all be covered by that conditional. */
 	/* Rest of code should all be covered by that conditional. */
@@ -103,42 +167,35 @@ static char *report_idem(struct ccan_file *f, char *sofar)
 		    || line_info[i].type == COMMENT_LINE)
 		    || line_info[i].type == COMMENT_LINE)
 			continue;
 			continue;
 		if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
 		if (get_ccan_line_pp(line_info[i].cond, sym, &val, NULL)
-		    != NOT_COMPILED)
-			return talloc_asprintf_append(sofar,
-			      "%s:%u:code outside idempotent region.\n",
-			      f->name, i+1);
+		    != NOT_COMPILED) {
+			score_file_error(score, f, i+1, "code outside"
+					 " idempotent region");
+			return false;
+		}
 	}
 	}
 
 
-	return sofar;
+	return true;
 }
 }
 
 
-static void *check_idempotent(struct manifest *m,
-			      bool keep,
-			      unsigned int *timeleft)
+static void check_idempotent(struct manifest *m,
+			     bool keep,
+			     unsigned int *timeleft, struct score *score)
 {
 {
 	struct ccan_file *f;
 	struct ccan_file *f;
-	char *report = NULL;
-
-	list_for_each(&m->h_files, f, list)
-		report = report_idem(f, report);
 
 
-	return report;
-}
-
-static const char *describe_idempotent(struct manifest *m, void *check_result)
-{
-	return talloc_asprintf(check_result, 
-			       "Some headers not idempotent:\n"
-			       "%s\n%s", (char *)check_result,
-			       explain);
+	list_for_each(&m->h_files, f, list) {
+		if (!check_idem(f, score))
+			return;
+	}
+	score->pass = true;
+	score->score = score->total;
 }
 }
 
 
 struct ccanlint idempotent = {
 struct ccanlint idempotent = {
 	.key = "idempotent",
 	.key = "idempotent",
 	.name = "Module headers are #ifndef/#define wrapped",
 	.name = "Module headers are #ifndef/#define wrapped",
-	.total_score = 1,
 	.check = check_idempotent,
 	.check = check_idempotent,
-	.describe = describe_idempotent,
+	.handle = handle_idem,
 };
 };
 
 
 REGISTER_TEST(idempotent, NULL);
 REGISTER_TEST(idempotent, NULL);

+ 51 - 95
tools/ccanlint/tests/run-coverage.c

@@ -4,6 +4,7 @@
 #include <ccan/str_talloc/str_talloc.h>
 #include <ccan/str_talloc/str_talloc.h>
 #include <ccan/grab_file/grab_file.h>
 #include <ccan/grab_file/grab_file.h>
 #include <ccan/str/str.h>
 #include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
 #include <sys/types.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <fcntl.h>
@@ -16,15 +17,9 @@
 #include <string.h>
 #include <string.h>
 #include <ctype.h>
 #include <ctype.h>
 
 
-struct coverage_result {
-	float uncovered;
-	const char *what;
-	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) {
 	list_for_each(&m->c_files, i, list) {
 		if (streq(i->fullname, filename))
 		if (streq(i->fullname, filename))
@@ -37,12 +32,25 @@ static bool find_source_file(struct manifest *m, const char *filename)
 	return false;
 	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? */
 /* 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,
-			     bool full_gcov)
+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;
 	float covered_lines = 0.0;
 	unsigned int i, total_lines = 0;
 	unsigned int i, total_lines = 0;
 	bool lines_matter = false;
 	bool lines_matter = false;
@@ -81,17 +89,14 @@ static void analyze_coverage(struct manifest *m,
 			apostrophe = strchr(filename, '\'');
 			apostrophe = strchr(filename, '\'');
 			*apostrophe = '\0';
 			*apostrophe = '\0';
 			if (lines_matter) {
 			if (lines_matter) {
-				file = grab_file(res, filename, NULL);
+				file = grab_file(score, filename, NULL);
 				if (!file) {
 				if (!file) {
-					res->what = talloc_asprintf(res,
+					score->error = talloc_asprintf(score,
 							    "Reading %s",
 							    "Reading %s",
 							    filename);
 							    filename);
-					res->output = talloc_strdup(res,
-							    strerror(errno));
 					return;
 					return;
 				}
 				}
-				res->output = talloc_append_string(res->output,
-								   file);
+				printf("%s", file);
 			}
 			}
 			if (tools_verbose)
 			if (tools_verbose)
 				printf("Unlinking %s", filename);
 				printf("Unlinking %s", filename);
@@ -99,114 +104,65 @@ static void analyze_coverage(struct manifest *m,
 		}
 		}
 	}
 	}
 
 
+	score->pass = true;
+
 	/* Nothing covered?  We can't tell if there's a source file which
 	/* 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
 	 * was never executed, or there really is no code to execute, so
 	 * assume the latter: this test deserves no score. */
 	 * assume the latter: this test deserves no score. */
-	if (total_lines == 0) {
-		res->uncovered = 1.0;
-		run_coverage_tests.total_score = 0;
-	} else
-		res->uncovered = 1.0 - covered_lines / total_lines;
+	if (total_lines == 0)
+		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;
 	struct ccan_file *i;
 	char *cmdout;
 	char *cmdout;
 	char *covcmd;
 	char *covcmd;
 	bool ok;
 	bool ok;
 	bool full_gcov = (verbose > 1);
 	bool full_gcov = (verbose > 1);
-
-	res = talloc(m, struct coverage_result);
-	res->what = NULL;
-	res->output = talloc_strdup(res, "");
-	res->uncovered = 1.0;
+	struct list_head *list;
 
 
 	/* This tells gcov where we put those .gcno files. */
 	/* This tells gcov where we put those .gcno files. */
 	covcmd = talloc_asprintf(m, "gcov %s -o %s",
 	covcmd = talloc_asprintf(m, "gcov %s -o %s",
 				 full_gcov ? "" : "-n",
 				 full_gcov ? "" : "-n",
-				 talloc_dirname(res, m->info_file->compiled));
+				 talloc_dirname(score, m->info_file->compiled));
 
 
 	/* Run them all. */
 	/* 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;
-		}
-		covcmd = talloc_asprintf_append(covcmd, " %s", i->fullname);
-	}
-
-	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;
+	foreach_ptr(list, &m->run_tests, &m->api_tests) {
+		list_for_each(list, i, list) {
+			cmdout = run_command(m, timeleft, i->cov_compiled);
+			if (cmdout) {
+				score->error = "Running test with coverage";
+				score_file_error(score, i, 0, cmdout);
+				return;
+			}
+			covcmd = talloc_asprintf_append(covcmd, " %s",
+							i->fullname);
 		}
 		}
-		covcmd = talloc_asprintf_append(covcmd, " %s", i->fullname);
 	}
 	}
 
 
 	/* Now run gcov: we want output even if it succeeds. */
 	/* Now run gcov: we want output even if it succeeds. */
 	cmdout = run_with_timeout(m, covcmd, &ok, timeleft);
 	cmdout = run_with_timeout(m, covcmd, &ok, timeleft);
 	if (!ok) {
 	if (!ok) {
-		res->what = "Running gcov";
-		res->output = talloc_steal(res, cmdout);
-		return res;
-	}
-
-	analyze_coverage(m, res, cmdout, full_gcov);
-
-	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;
+		score->error = talloc_asprintf(score, "Running gcov: %s",
+					       cmdout);
+		return;
 	}
 	}
-	return i;
-}
-
-static const char *describe_run_coverage_tests(struct manifest *m,
-					       void *check_result)
-{
-	struct coverage_result *res = check_result;
-	bool full_gcov = (verbose > 1);
-	char *ret;
-
-	if (res->what)
-		return talloc_asprintf(m, "%s: %s", res->what, res->output);
-
-	if (!verbose)
-		return NULL;
 
 
-	ret = talloc_asprintf(m, "Tests achieved %0.2f%% coverage",
-			      (1.0 - res->uncovered) * 100);
-	if (full_gcov)
-		ret = talloc_asprintf_append(ret, "\n%s", res->output);
-	return ret;
+	analyze_coverage(m, full_gcov, cmdout, score);
 }
 }
 
 
 struct ccanlint run_coverage_tests = {
 struct ccanlint run_coverage_tests = {
 	.key = "test-coverage",
 	.key = "test-coverage",
 	.name = "Code coverage of module tests",
 	.name = "Code coverage of module tests",
-	.total_score = 5,
-	.score = score_coverage,
 	.check = do_run_coverage_tests,
 	.check = do_run_coverage_tests,
-	.describe = describe_run_coverage_tests,
 };
 };
 
 
 REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, &run_tests, NULL);
 REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, &run_tests, NULL);

+ 21 - 71
tools/ccanlint/tests/run_tests.c

@@ -2,6 +2,7 @@
 #include <tools/tools.h>
 #include <tools/tools.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/str/str.h>
 #include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
 #include <sys/types.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <fcntl.h>
@@ -21,91 +22,43 @@ static const char *can_run(struct manifest *m)
 	return NULL;
 	return NULL;
 }
 }
 
 
-struct run_tests_result {
-	struct list_node list;
-	struct ccan_file *file;
-	const char *output;
-};
-
-static void *do_run_tests(struct manifest *m,
-			  bool keep,
-			  unsigned int *timeleft)
+static void do_run_tests(struct manifest *m,
+			 bool keep,
+			 unsigned int *timeleft,
+			 struct score *score)
 {
 {
-	struct list_head *list = talloc(m, struct list_head);
-	struct run_tests_result *res;
+	struct list_head *list;
 	struct ccan_file *i;
 	struct ccan_file *i;
 	char *cmdout;
 	char *cmdout;
 
 
-	list_head_init(list);
-	run_tests.total_score = 0;
-
-	list_for_each(&m->run_tests, i, list) {
-		run_tests.total_score++;
-		cmdout = run_command(m, timeleft, i->compiled);
-		if (cmdout) {
-			res = talloc(list, struct run_tests_result);
-			res->file = i;
-			res->output = talloc_steal(res, cmdout);
-			list_add_tail(list, &res->list);
-		}
-	}
-
-	list_for_each(&m->api_tests, i, list) {
-		run_tests.total_score++;
-		cmdout = run_command(m, timeleft, i->compiled);
-		if (cmdout) {
-			res = talloc(list, struct run_tests_result);
-			res->file = i;
-			res->output = talloc_steal(res, cmdout);
-			list_add_tail(list, &res->list);
+	score->total = 0;
+	foreach_ptr(list, &m->run_tests, &m->api_tests) {
+		list_for_each(list, i, list) {
+			score->total++;
+			cmdout = run_command(m, timeleft, i->compiled);
+			if (cmdout)
+				score_file_error(score, i, 0, cmdout);
+			else
+				score->score++;
 		}
 		}
 	}
 	}
 
 
-	if (list_empty(list)) {
-		talloc_free(list);
-		list = NULL;
-	}
-
-	return list;
-}
-
-static unsigned int score_run_tests(struct manifest *m, void *check_result)
-{
-	struct list_head *list = check_result;
-	struct run_tests_result *i;
-	unsigned int score = run_tests.total_score;
-
-	list_for_each(list, i, list)
-		score--;
-	return score;
-}
-
-static const char *describe_run_tests(struct manifest *m,
-					  void *check_result)
-{
-	struct list_head *list = check_result;
-	char *descrip = talloc_strdup(check_result, "Running tests failed:\n");
-	struct run_tests_result *i;
-
-	list_for_each(list, i, list)
-		descrip = talloc_asprintf_append(descrip, "Running %s:\n%s",
-						 i->file->name, i->output);
-	return descrip;
+	if (score->score == score->total)
+		score->pass = true;
 }
 }
 
 
 /* Gcc's warn_unused_result is fascist bullshit. */
 /* Gcc's warn_unused_result is fascist bullshit. */
 #define doesnt_matter()
 #define doesnt_matter()
 
 
-static void run_under_debugger(struct manifest *m, void *check_result)
+static void run_under_debugger(struct manifest *m, struct score *score)
 {
 {
 	char *command;
 	char *command;
-	struct list_head *list = check_result;
-	struct run_tests_result *first;
+	struct file_error *first;
 
 
 	if (!ask("Should I run the first failing test under the debugger?"))
 	if (!ask("Should I run the first failing test under the debugger?"))
 		return;
 		return;
 
 
-	first = list_top(list, struct run_tests_result, list);
+	first = list_top(&score->per_file_errors, struct file_error, list);
 	command = talloc_asprintf(m, "gdb -ex 'break tap.c:136' -ex 'run' %s",
 	command = talloc_asprintf(m, "gdb -ex 'break tap.c:136' -ex 'run' %s",
 				  first->file->compiled);
 				  first->file->compiled);
 	if (system(command))
 	if (system(command))
@@ -115,12 +68,9 @@ static void run_under_debugger(struct manifest *m, void *check_result)
 struct ccanlint run_tests = {
 struct ccanlint run_tests = {
 	.key = "run",
 	.key = "run",
 	.name = "Module's run and api tests pass",
 	.name = "Module's run and api tests pass",
-	.score = score_run_tests,
-	.total_score = 1,
 	.check = do_run_tests,
 	.check = do_run_tests,
-	.describe = describe_run_tests,
+	.handle = run_under_debugger,
 	.can_run = can_run,
 	.can_run = can_run,
-	.handle = run_under_debugger
 };
 };
 
 
 REGISTER_TEST(run_tests, &compile_tests, NULL);
 REGISTER_TEST(run_tests, &compile_tests, NULL);

+ 24 - 73
tools/ccanlint/tests/run_tests_valgrind.c

@@ -2,6 +2,7 @@
 #include <tools/tools.h>
 #include <tools/tools.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/str/str.h>
 #include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
 #include <sys/types.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <fcntl.h>
@@ -18,102 +19,55 @@
 static const char *can_run_vg(struct manifest *m)
 static const char *can_run_vg(struct manifest *m)
 {
 {
 	unsigned int timeleft = default_timeout_ms;
 	unsigned int timeleft = default_timeout_ms;
-	char *output = run_command(m, &timeleft, "valgrind -q --error-exitcode=0 true");
+	char *output = run_command(m, &timeleft,
+				   "valgrind -q --error-exitcode=0 true");
 
 
 	if (output)
 	if (output)
 		return talloc_asprintf(m, "No valgrind support: %s", output);
 		return talloc_asprintf(m, "No valgrind support: %s", output);
 	return NULL;
 	return NULL;
 }
 }
 
 
-struct run_tests_result {
-	struct list_node list;
-	struct ccan_file *file;
-	const char *output;
-};
-
-static void *do_run_tests_vg(struct manifest *m,
+/* FIXME: Run examples, too! */
+static void do_run_tests_vg(struct manifest *m,
 			     bool keep,
 			     bool keep,
-			     unsigned int *timeleft)
+			    unsigned int *timeleft,
+			    struct score *score)
 {
 {
-	struct list_head *list = talloc(m, struct list_head);
-	struct run_tests_result *res;
 	struct ccan_file *i;
 	struct ccan_file *i;
+	struct list_head *list;
 	char *cmdout;
 	char *cmdout;
 
 
-	list_head_init(list);
-	run_tests_vg.total_score = 0;
-
-	list_for_each(&m->run_tests, i, list) {
-		run_tests_vg.total_score++;
-		cmdout = run_command(m, timeleft,
-				     "valgrind -q --error-exitcode=100 %s",
-				     i->compiled);
-		if (cmdout) {
-			res = talloc(list, struct run_tests_result);
-			res->file = i;
-			res->output = talloc_steal(res, cmdout);
-			list_add_tail(list, &res->list);
-		}
-	}
-
-	list_for_each(&m->api_tests, i, list) {
-		run_tests_vg.total_score++;
-		cmdout = run_command(m, timeleft,
+	score->total = 0;
+	foreach_ptr(list, &m->run_tests, &m->api_tests) {
+		list_for_each(list, i, list) {
+			score->total++;
+			cmdout = run_command(m, timeleft,
 				     "valgrind -q --error-exitcode=100 %s",
 				     "valgrind -q --error-exitcode=100 %s",
 				     i->compiled);
 				     i->compiled);
-		if (cmdout) {
-			res = talloc(list, struct run_tests_result);
-			res->file = i;
-			res->output = talloc_steal(res, cmdout);
-			list_add_tail(list, &res->list);
+			if (cmdout) {
+				score->error = "Running under valgrind";
+				score_file_error(score, i, 0, cmdout);
+			} else
+				score->score++;
 		}
 		}
 	}
 	}
 
 
-	if (list_empty(list)) {
-		talloc_free(list);
-		list = NULL;
-	}
-
-	return list;
-}
-
-static unsigned int score_run_tests_vg(struct manifest *m, void *check_result)
-{
-	struct list_head *list = check_result;
-	struct run_tests_result *i;
-	unsigned int score = run_tests_vg.total_score;
-
-	list_for_each(list, i, list)
-		score--;
-	return score;
-}
-
-static const char *describe_run_tests_vg(struct manifest *m,
-					 void *check_result)
-{
-	struct list_head *list = check_result;
-	char *descrip = talloc_strdup(check_result, "Running tests under valgrind failed:\n");
-	struct run_tests_result *i;
-
-	list_for_each(list, i, list)
-		descrip = talloc_asprintf_append(descrip, "Running %s:\n%s",
-						 i->file->name, i->output);
-	return descrip;
+	if (score->score == score->total)
+		score->pass = true;
 }
 }
 
 
 /* Gcc's warn_unused_result is fascist bullshit. */
 /* Gcc's warn_unused_result is fascist bullshit. */
 #define doesnt_matter()
 #define doesnt_matter()
 
 
-static void run_under_debugger_vg(struct manifest *m, void *check_result)
+static void run_under_debugger_vg(struct manifest *m, struct score *score)
 {
 {
-	struct list_head *list = check_result;
-	struct run_tests_result *first;
+	struct file_error *first;
 	char *command;
 	char *command;
 
 
 	if (!ask("Should I run the first failing test under the debugger?"))
 	if (!ask("Should I run the first failing test under the debugger?"))
 		return;
 		return;
 
 
-	first = list_top(list, struct run_tests_result, list);
+	first = list_top(&score->per_file_errors, struct file_error, list);
 	command = talloc_asprintf(m, "valgrind --db-attach=yes %s",
 	command = talloc_asprintf(m, "valgrind --db-attach=yes %s",
 				  first->file->compiled);
 				  first->file->compiled);
 	if (system(command))
 	if (system(command))
@@ -123,11 +77,8 @@ static void run_under_debugger_vg(struct manifest *m, void *check_result)
 struct ccanlint run_tests_vg = {
 struct ccanlint run_tests_vg = {
 	.key = "valgrind-tests",
 	.key = "valgrind-tests",
 	.name = "Module's run and api tests succeed under valgrind",
 	.name = "Module's run and api tests succeed under valgrind",
-	.score = score_run_tests_vg,
-	.total_score = 1,
-	.check = do_run_tests_vg,
-	.describe = describe_run_tests_vg,
 	.can_run = can_run_vg,
 	.can_run = can_run_vg,
+	.check = do_run_tests_vg,
 	.handle = run_under_debugger_vg
 	.handle = run_under_debugger_vg
 };
 };
 
 

+ 27 - 24
tools/ccanlint/tests/trailing_whitespace.c

@@ -1,10 +1,11 @@
 /* Trailing whitespace test.  Almost embarrassing, but trivial. */
 /* Trailing whitespace test.  Almost embarrassing, but trivial. */
 #include <tools/ccanlint/ccanlint.h>
 #include <tools/ccanlint/ccanlint.h>
 #include <ccan/talloc/talloc.h>
 #include <ccan/talloc/talloc.h>
+#include <ccan/foreach/foreach.h>
 #include <ccan/str/str.h>
 #include <ccan/str/str.h>
 
 
 /* FIXME: only print full analysis if verbose >= 2.  */
 /* FIXME: only print full analysis if verbose >= 2.  */
-static char *report_on_trailing_whitespace(const char *line)
+static char *get_trailing_whitespace(const char *line)
 {
 {
 	const char *e = strchr(line, 0);
 	const char *e = strchr(line, 0);
 	while (e>line && (e[-1]==' ' || e[-1]=='\t'))
 	while (e>line && (e[-1]==' ' || e[-1]=='\t'))
@@ -20,36 +21,38 @@ static char *report_on_trailing_whitespace(const char *line)
 	return talloc_asprintf(line, "'%s'", line);
 	return talloc_asprintf(line, "'%s'", line);
 }
 }
 
 
-static void *check_trailing_whitespace(struct manifest *m,
-				       bool keep,
-				       unsigned int *timeleft)
+static void check_trailing_whitespace(struct manifest *m,
+				      bool keep,
+				      unsigned int *timeleft,
+				      struct score *score)
 {
 {
-	char *report;
-
-	report = report_on_lines(&m->c_files, report_on_trailing_whitespace,
-				 NULL);
-	report = report_on_lines(&m->h_files, report_on_trailing_whitespace,
-				 report);
-
-	return report;
-}
-
-static const char *describe_trailing_whitespace(struct manifest *m,
-						void *check_result)
-{
-	if (!verbose)
-		return NULL;
-	return talloc_asprintf(check_result, 
-			       "Some source files have trailing whitespace:\n"
-			       "%s", (char *)check_result);
+	struct list_head *list;
+	struct ccan_file *f;
+	unsigned int i;
+
+	foreach_ptr(list, &m->c_files, &m->h_files) {
+		list_for_each(list, f, list) {
+			char **lines = get_ccan_file_lines(f);
+			for (i = 0; i < f->num_lines; i++) {
+				char *err = get_trailing_whitespace(lines[i]);
+				if (err) {
+					score->error = "Trailing whitespace"
+						"found";
+					score_file_error(score, f, i+1, err);
+				}
+			}
+		}
+	}
+	if (!score->error) {
+		score->pass = true;
+		score->score = score->total;
+	}
 }
 }
 
 
 struct ccanlint trailing_whitespace = {
 struct ccanlint trailing_whitespace = {
 	.key = "trailing-whitespace",
 	.key = "trailing-whitespace",
 	.name = "Module's source code has no trailing whitespace",
 	.name = "Module's source code has no trailing whitespace",
-	.total_score = 1,
 	.check = check_trailing_whitespace,
 	.check = check_trailing_whitespace,
-	.describe = describe_trailing_whitespace,
 };
 };