static bool run_test(struct ccanlint *i,
bool quiet,
- unsigned int *score,
- unsigned int *total_score,
+ unsigned int *running_score,
+ unsigned int *running_total,
struct manifest *m)
{
- void *result;
- unsigned int this_score, max_score, timeleft;
+ unsigned int timeleft;
const struct dependent *d;
const char *skip;
- bool bad, good;
+ struct score *score;
//one less test to run through
list_for_each(&i->dependencies, d, node)
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);
if (skip) {
skip:
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)
- *total_score += i->total_score;
+ (*running_total)++;
list_del(&i->list);
list_add_tail(&finished_tests, &i->list);
d->dependent->skip = "dependency was skipped";
d->dependent->skip_fail = i->skip_fail;
}
- return true;
+ return i->skip_fail ? false : true;
}
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) {
skip = "timeout";
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");
}
- 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)
- 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_add_tail(&finished_tests, &i->list);
- if (bad) {
+ if (!score->pass) {
/* Skip any tests which depend on this one. */
list_for_each(&i->dependencies, d, node) {
if (d->dependent->skip)
d->dependent->skip_fail = true;
}
}
- return good;
+ return score->pass;
}
static void register_test(struct list_head *h, struct ccanlint *test, ...)
}
/* 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) {
if (!run_test(i, summary, &score, &total_score, m)) {
printf("%sTotal score: 0/%u\n", prefix, total_score);
}
add_info_fails(m->info_file);
-
- if (verbose)
- printf("\nNormal tests:\n");
while ((i = get_next_test(&normal_tests)) != NULL)
run_test(i, summary, &score, &total_score, m);
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 list_node list;
/* Unique name of test */
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. */
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) */
- void (*handle)(struct manifest *m, void *check_result);
+ void (*handle)(struct manifest *m, struct score *score);
/* Internal use fields: */
/* Who depends on us? */
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. */
extern struct ccanlint trailing_whitespace;
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)) {
/* 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);
/* We leave this object file around, all built. */
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 = {
.key = "build",
.name = "Module can be built from object files",
- .total_score = 1,
.check = do_build,
- .describe = describe_build,
.can_run = can_build,
};
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;
+ if (list_empty(&m->c_files))
+ score->total = 0;
+
list_for_each(&m->c_files, i, list) {
char *err;
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);
err = compile_object(m, fullfile, ccan_dir, "", i->compiled);
if (err) {
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 = {
.key = "build-objects",
.name = "Module object files can be built",
.check = check_objs_build,
- .describe = describe_objs_build,
.can_run = can_build,
};
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 *tmpfile;
fd = open(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0)
- return talloc_asprintf(m, "Creating temporary file: %s",
- strerror(errno));
+ err(1, "Creating temporary file %s", tmpfile);
contents = talloc_asprintf(tmpfile,
"#include <ccan/%s/%s.h>\n"
" return 0;\n"
"}\n",
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);
- 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 = {
.key = "check-link",
.name = "Module can be linked against trivial program",
- .total_score = 1,
.check = check_use_build,
- .describe = describe_use_build,
.can_run = can_build,
};
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 stat st;
- char *report = NULL;
list_for_each(&m->dep_dirs, i, list) {
if (!expect_obj_file(i->fullname))
i->compiled = talloc_asprintf(i, "%s.o", i->fullname);
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;
}
}
&& (!list_empty(&m->run_tests) || !list_empty(&m->api_tests))) {
char *tapobj = talloc_asprintf(m, "%s/ccan/tap.o", ccan_dir);
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 = {
.key = "depends-built",
.name = "Module's CCAN dependencies are already built",
- .total_score = 1,
.check = check_depends_built,
- .describe = describe_depends_built,
.can_run = can_build,
};
#include <string.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 ccan_file *f;
f = new_ccan_file(m, ccan_dir, talloc_strdup(m, dep));
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;
- char *report = NULL;
char **deps;
char *updir = talloc_strdup(m, m->dir);
if (!strstarts(deps[i], "ccan/"))
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 = {
.key = "depends-exist",
.name = "Module's CCAN dependencies are present",
- .total_score = 1,
.check = check_depends_exist,
- .describe = describe_depends_exist,
};
REGISTER_TEST(depends_exist, NULL);
return f;
}
/* 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 *tmpsrc, *tmpobj;
fd = open(tmpsrc, O_WRONLY | O_CREAT | O_EXCL, 0600);
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",
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);
- 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 = {
.key = "include-main",
.name = "Modules main header compiles",
- .total_score = 1,
.check = check_includes_build,
- .describe = describe_includes_build,
.can_run = can_build,
};
#include <ccan/noerr/noerr.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"
"like the dependencies, the documentation for the package as a whole\n"
"and license information.\n";
+ }
}
static const char template[] =
" return 1;\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;
const char *filename;
.key = "info",
.name = "Module has _info file",
.check = check_has_info,
- .describe = describe_has_info,
.handle = create_info_template,
};
#include <ccan/talloc/talloc.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;
list_for_each(&m->h_files, f, list) {
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"
"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",
- m->basename, m->basename);
+ m->basename, m->basename);
}
struct ccanlint has_main_header = {
.key = "has-main-header",
.name = "Module has main header file",
.check = check_has_main_header,
- .describe = describe_has_main_header,
};
REGISTER_TEST(has_main_header, NULL);
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 *m = talloc(ctx, struct manifest);
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);
+}
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;
err = compile_object(m, fullfile, ccan_dir, "",
i->cov_compiled);
if (err) {
+ score_file_error(score, i, 0, err);
talloc_free(i->cov_compiled);
- return err;
+ i->cov_compiled = NULL;
+ return false;
}
*modobjs = talloc_asprintf_append(*modobjs,
" %s", i->cov_compiled);
}
- return NULL;
+ return true;
}
static char *obj_list(const struct manifest *m, const char *modobjs)
lib_list(m), file->cov_compiled);
if (errmsg) {
talloc_free(file->cov_compiled);
+ file->cov_compiled = NULL;
return errmsg;
}
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;
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) {
- compile_tests.total_score++;
cmdout = cov_compile(m, m, i, NULL, keep);
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) {
- compile_tests.total_score++;
cmdout = cov_compile(m, m, i, modobjs, keep);
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 = {
.key = "compile-coverage-tests",
.name = "Module tests compile with profiling",
.check = do_compile_coverage_tests,
- .total_score = 1,
- .describe = describe_compile_coverage_tests,
.can_run = can_run_coverage,
};
#include <string.h>
#include <ctype.h>
-static const char *can_build(struct manifest *m)
+static const char *can_run(struct manifest *m)
{
if (safe_mode)
return "Safe mode enabled";
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;
- 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) {
- 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 = {
.key = "compile-helpers",
.name = "Module test helper objects compile",
- .total_score = 1,
.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);
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;
struct ccan_file *i;
- struct compile_tests_result *res;
- list_head_init(list);
-
- compile_tests.total_score = 0;
list_for_each(&m->compile_ok_tests, i, list) {
- compile_tests.total_score++;
- cmdout = compile(list, m, i, false, false, keep);
+ cmdout = compile(score, m, i, false, false, keep);
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) {
- compile_tests.total_score++;
- cmdout = compile(m, m, i, false, false, keep);
+ cmdout = compile(score, m, i, false, false, keep);
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) {
- compile_tests.total_score++;
- cmdout = compile(m, m, i, false, true, keep);
+ cmdout = compile(score, m, i, false, true, keep);
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) {
- compile_tests.total_score++;
- cmdout = compile(list, m, i, false, false, false);
+ cmdout = compile(score, m, i, false, false, false);
if (cmdout) {
- res = talloc(list, struct compile_tests_result);
- res->filename = i->name;
- res->description = "failed to compile without -DFAIL";
- res->output = talloc_steal(res, cmdout);
- list_add_tail(list, &res->list);
- } else {
- cmdout = compile(list, m, i, 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 = {
.key = "compile-tests",
.name = "Module tests compile",
- .score = score_compile_tests,
- .total_score = 1,
.check = do_compile_tests,
- .describe = describe_compile_tests,
.can_run = can_build,
};
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;
- char *report = talloc_strdup(m, "");
foreach_ptr(list, &m->c_files, &m->h_files,
&m->run_tests, &m->api_tests,
bool tap_ok;
/* 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) {
unsigned int i;
if (!strchr(strchr(p, '/') + 1, '/'))
continue;
*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 = {
.key = "depends-accurate",
.name = "Module's CCAN dependencies are the only ccan files #included",
- .total_score = 1,
.check = check_depends_accurate,
- .describe = describe_depends_accurage,
};
REGISTER_TEST(depends_accurate, &depends_exist, NULL);
{
if (safe_mode)
return "Safe mode enabled";
+ if (list_empty(&m->examples))
+ return "No examples to compile";
return NULL;
}
return NULL;
}
-struct score {
- unsigned int score;
- char *errors;
-};
-
static char *start_main(char *ret, const char *why)
{
return talloc_asprintf_append(ret,
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 score *score = talloc(m, struct score);
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) {
char *ret, *ret1, *ret2 = NULL;
struct ccan_file *mangle1, *mangle2 = NULL;
+ char *err;
- examples_compile.total_score++;
+ score->total++;
/* Simplify our dumb parsing. */
strip_leading_whitespace(get_ccan_file_lines(i));
ret = compile(i, m, i, 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"
"Errors: %s\n\n",
- i->name,
get_ccan_file_contents(mangle2),
ret2);
}
+ score_file_error(score, i, 0, err);
/* This didn't work, so not a candidate for combining. */
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 = {
.key = "examples-compile",
.name = "Module examples compile",
- .score = score_examples,
- .total_score = 3, /* This gets changed to # examples, if they exist */
.check = build_examples,
- .describe = describe,
.can_run = can_run,
};
static const char *can_run(struct manifest *m)
{
+ struct list_head *list;
+
if (safe_mode)
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. */
static bool scan_forv(const void *ctx,
const char *input, const char *fmt, const va_list *args)
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 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) {
list_for_each(list, i, list) {
char **lines, *expect, *input, *output;
unsigned int linenum = 0;
bool exact;
- if (i->compiled == NULL)
- continue;
-
lines = get_ccan_file_lines(i);
for (expect = find_expect(i, lines, &input, &exact,
linenum++,
expect = find_expect(i, lines, &input,
&exact, &linenum)) {
- examples_run.total_score++;
+ char *err;
+ score->total++;
+ if (i->compiled == NULL)
+ continue;
+
output = unexpected(i, input, expect, exact);
- if (!output)
+ if (!output) {
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 = {
.key = "examples-run",
.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,
- .describe = describe,
.can_run = can_run,
};
}
/* 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 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) {
if (streq(d->type, "example")) {
score->error = add_example(m, m->info_file, keep, d);
if (score->error)
- return score;
- score->info_example = true;
+ return;
+ have_info_example = true;
}
}
if (streq(d->type, "example")) {
score->error = add_example(m, f, keep, d);
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 = {
.key = "has-examples",
.name = "_info and header files have examples",
- .score = score_examples,
.check = extract_examples,
- .describe = describe_examples,
- .total_score = 2,
};
REGISTER_TEST(has_examples, &has_info, NULL);
#include <ccan/noerr/noerr.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);
FILE *new;
}
}
-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"
- "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"
"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 = {
.key = "info-documentation",
.name = "Module has documentation in _info",
- .total_score = 2,
- .score = has_info_documentation_score,
.check = check_has_info_documentation,
- .describe = describe_has_info_documentation,
};
REGISTER_TEST(has_info_documentation, NULL);
#include <err.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);
- 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"
"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"
"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"
"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"
"when it's not defined: this helps ensure unrelated errors don't make\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?"))
return;
fprintf(run, "#include <ccan/%s/%s>\n",
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);
}
+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 = {
.key = "has-tests",
.name = "Module has tests",
.check = check_has_tests,
- .describe = describe_has_tests,
- .total_score = 1,
- .handle = handle_no_tests,
};
REGISTER_TEST(has_tests, NULL);
static const char explain[]
= "Headers usually start with the C preprocessor lines to prevent multiple\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"
- "#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;
unsigned int i, first_preproc_line;
line_info = get_ccan_line_info(f);
if (f->num_lines < 3)
/* 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++) {
if (line_info[i].type == DOC_LINE
|| line_info[i].type == COMMENT_LINE)
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;
}
/* No code at all? Don't complain. */
if (i == f->num_lines)
- return sofar;
+ return true;
first_preproc_line = i;
for (i = first_preproc_line+1; i < f->num_lines; i++) {
if (line_info[i].type == DOC_LINE
|| line_info[i].type == COMMENT_LINE)
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;
}
/* No code at all? Weird. */
if (i == f->num_lines)
- return sofar;
+ return true;
/* We expect a condition on this line. */
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];
/* We expect the condition to be ! IFDEF <symbol>. */
if (line_info[i].cond->type != PP_COND_IFDEF
|| !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> */
if (!get_token(&line, "#"))
abort();
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);
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. */
|| line_info[i].type == COMMENT_LINE)
continue;
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;
- 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 = {
.key = "idempotent",
.name = "Module headers are #ifndef/#define wrapped",
- .total_score = 1,
.check = check_idempotent,
- .describe = describe_idempotent,
+ .handle = handle_idem,
};
REGISTER_TEST(idempotent, NULL);
#include <ccan/str_talloc/str_talloc.h>
#include <ccan/grab_file/grab_file.h>
#include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.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) {
if (streq(i->fullname, filename))
return false;
}
+/* 1 point for 50%, 2 points for 75%, 3 points for 87.5%... */
+static unsigned int score_coverage(float covered, unsigned total)
+{
+ float thresh, uncovered = 1.0 - covered;
+ unsigned int i;
+
+ for (i = 0, thresh = 0.5; i < total; i++, thresh /= 2) {
+ if (uncovered > thresh)
+ break;
+ }
+ return i;
+}
+
+
/* FIXME: Don't know how stable this is. Read cov files directly? */
-static void analyze_coverage(struct manifest *m,
- struct coverage_result *res, const char *output,
- 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;
unsigned int i, total_lines = 0;
bool lines_matter = false;
apostrophe = strchr(filename, '\'');
*apostrophe = '\0';
if (lines_matter) {
- file = grab_file(res, filename, NULL);
+ file = grab_file(score, filename, NULL);
if (!file) {
- res->what = talloc_asprintf(res,
+ score->error = talloc_asprintf(score,
"Reading %s",
filename);
- res->output = talloc_strdup(res,
- strerror(errno));
return;
}
- res->output = talloc_append_string(res->output,
- file);
+ printf("%s", file);
}
if (tools_verbose)
printf("Unlinking %s", filename);
}
}
+ score->pass = true;
+
/* Nothing covered? We can't tell if there's a source file which
* was never executed, or there really is no code to execute, so
* assume the latter: this test deserves no score. */
- if (total_lines == 0) {
- res->uncovered = 1.0;
- 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;
char *cmdout;
char *covcmd;
bool ok;
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. */
covcmd = talloc_asprintf(m, "gcov %s -o %s",
full_gcov ? "" : "-n",
- talloc_dirname(res, m->info_file->compiled));
+ talloc_dirname(score, m->info_file->compiled));
/* Run them all. */
- list_for_each(&m->run_tests, i, list) {
- cmdout = run_command(m, timeleft, i->cov_compiled);
- if (cmdout) {
- res->what = i->fullname;
- res->output = talloc_steal(res, cmdout);
- return res;
- }
- 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. */
cmdout = run_with_timeout(m, covcmd, &ok, timeleft);
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 = {
.key = "test-coverage",
.name = "Code coverage of module tests",
- .total_score = 5,
- .score = score_coverage,
.check = do_run_coverage_tests,
- .describe = describe_run_coverage_tests,
};
REGISTER_TEST(run_coverage_tests, &compile_coverage_tests, &run_tests, NULL);
#include <tools/tools.h>
#include <ccan/talloc/talloc.h>
#include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
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;
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. */
#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;
- 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?"))
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",
first->file->compiled);
if (system(command))
struct ccanlint run_tests = {
.key = "run",
.name = "Module's run and api tests pass",
- .score = score_run_tests,
- .total_score = 1,
.check = do_run_tests,
- .describe = describe_run_tests,
+ .handle = run_under_debugger,
.can_run = can_run,
- .handle = run_under_debugger
};
REGISTER_TEST(run_tests, &compile_tests, NULL);
#include <tools/tools.h>
#include <ccan/talloc/talloc.h>
#include <ccan/str/str.h>
+#include <ccan/foreach/foreach.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static const char *can_run_vg(struct manifest *m)
{
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)
return talloc_asprintf(m, "No valgrind support: %s", output);
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,
- 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 list_head *list;
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",
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. */
#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;
if (!ask("Should I run the first failing test under the debugger?"))
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",
first->file->compiled);
if (system(command))
struct ccanlint run_tests_vg = {
.key = "valgrind-tests",
.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,
+ .check = do_run_tests_vg,
.handle = run_under_debugger_vg
};
/* Trailing whitespace test. Almost embarrassing, but trivial. */
#include <tools/ccanlint/ccanlint.h>
#include <ccan/talloc/talloc.h>
+#include <ccan/foreach/foreach.h>
#include <ccan/str/str.h>
/* 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);
while (e>line && (e[-1]==' ' || e[-1]=='\t'))
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 = {
.key = "trailing-whitespace",
.name = "Module's source code has no trailing whitespace",
- .total_score = 1,
.check = check_trailing_whitespace,
- .describe = describe_trailing_whitespace,
};