4 == Describe every action. */
extern int verbose;
+enum compile_type {
+ COMPILE_NORMAL,
+ COMPILE_NOFEAT,
+ COMPILE_TYPES
+};
+
struct manifest {
char *dir;
/* The module name, ie. final element of dir name */
/* Linked off deps. */
struct list_node list;
/* Where our final compiled output is */
- char *compiled;
+ char *compiled[COMPILE_TYPES];
struct list_head c_files;
struct list_head h_files;
struct list_head *doc_sections;
/* If this file gets compiled (eg. .C file to .o file), result here. */
- char *compiled;
+ char *compiled[COMPILE_TYPES];
/* Compiled with coverage information. */
char *cov_compiled;
#ifndef CCANLINT_BUILD_H
#define CCANLINT_BUILD_H
-char *build_module(struct manifest *m, bool keep, char **errstr);
-char *build_submodule(struct manifest *m);
+char *build_module(struct manifest *m, bool keep, enum compile_type ctype,
+ char **errstr);
+char *build_submodule(struct manifest *m, const char *flags,
+ enum compile_type ctype);
void build_objects(struct manifest *m,
- bool keep, struct score *score, const char *flags);
+ bool keep, struct score *score, const char *flags,
+ enum compile_type ctype);
#endif /* CCANLINT_BUILD_H */
return !list_empty(&m->c_files);
}
-static char *build_subdir_objs(struct manifest *m)
+static char *build_subdir_objs(struct manifest *m,
+ const char *flags,
+ enum compile_type ctype)
{
struct ccan_file *i;
char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
char *output;
- i->compiled = maybe_temp_file(m, "", false, fullfile);
- if (!compile_object(m, fullfile, ccan_dir, compiler, cflags,
- i->compiled, &output)) {
- talloc_free(i->compiled);
- i->compiled = NULL;
+ i->compiled[ctype] = maybe_temp_file(m, "", false, fullfile);
+ if (!compile_object(m, fullfile, ccan_dir, compiler, flags,
+ i->compiled[ctype], &output)) {
+ talloc_free(i->compiled[ctype]);
+ i->compiled[ctype] = NULL;
return talloc_asprintf(m,
"Dependency %s"
" did not build:\n%s",
return NULL;
}
-char *build_submodule(struct manifest *m)
+char *build_submodule(struct manifest *m, const char *flags,
+ enum compile_type ctype)
{
char *errstr;
- if (m->compiled)
+ if (m->compiled[ctype])
return NULL;
if (!expect_obj_file(m))
if (verbose >= 2)
printf(" Building dependency %s\n", m->dir);
- errstr = build_subdir_objs(m);
+ errstr = build_subdir_objs(m, flags, ctype);
if (errstr)
return errstr;
- m->compiled = build_module(m, false, &errstr);
- if (!m->compiled)
+ m->compiled[ctype] = build_module(m, false, ctype, &errstr);
+ if (!m->compiled[ctype])
return errstr;
return NULL;
}
struct manifest *i;
list_for_each(&m->deps, i, list) {
- char *errstr = build_submodule(i);
+ char *errstr = build_submodule(i, cflags, COMPILE_NORMAL);
if (errstr) {
score->error = talloc_asprintf(score,
return true;
}
+/* FIXME: check this is still true once we reduce features. */
static void check_depends_exist(struct manifest *m,
bool keep,
unsigned int *timeleft, struct score *score)
if (safe_mode)
deps = get_safe_ccan_deps(m, m->dir, true);
else
- deps = get_deps(m, m->dir, true, &m->info_file->compiled);
+ deps = get_deps(m, m->dir, true,
+ &m->info_file->compiled[COMPILE_NORMAL]);
for (i = 0; deps[i]; i++) {
if (!strstarts(deps[i], "ccan/"))
return NULL;
}
-static char *obj_list(const struct manifest *m)
+static char *obj_list(const struct manifest *m, enum compile_type ctype)
{
char *list = talloc_strdup(m, "");
struct ccan_file *i;
/* Objects from all the C files. */
list_for_each(&m->c_files, i, list)
- list = talloc_asprintf_append(list, "%s ", i->compiled);
+ list = talloc_asprintf_append(list, "%s ",
+ i->compiled[ctype]);
return list;
}
-char *build_module(struct manifest *m, bool keep, char **errstr)
+char *build_module(struct manifest *m, bool keep,
+ enum compile_type ctype, char **errstr)
{
- char *name = link_objects(m, m->basename, false, obj_list(m), errstr);
+ char *name = link_objects(m, m->basename, false, obj_list(m, ctype),
+ errstr);
if (name) {
if (keep) {
char *realname = talloc_asprintf(m, "%s.o", m->dir);
+ assert(ctype == COMPILE_NORMAL);
/* We leave this object file around, all built. */
if (!move_file(name, realname))
err(1, "Renaming %s to %s", name, realname);
return;
}
- m->compiled = build_module(m, keep, &errstr);
- if (!m->compiled) {
+ m->compiled[COMPILE_NORMAL]
+ = build_module(m, keep, COMPILE_NORMAL, &errstr);
+ if (!m->compiled[COMPILE_NORMAL]) {
score_file_error(score, NULL, 0, "%s", errstr);
return;
}
/* Other CCAN deps. */
list_for_each(&m->deps, i, list) {
- if (i->compiled)
+ if (i->compiled[COMPILE_NORMAL])
list = talloc_asprintf_append(list, "%s ",
- i->compiled);
+ i->compiled
+ [COMPILE_NORMAL]);
}
return list;
}
static char *lib_list(const struct manifest *m)
{
unsigned int i, num;
- char **libs = get_libs(m, ".", &num, &m->info_file->compiled);
+ char **libs = get_libs(m, ".",
+ &num, &m->info_file->compiled[COMPILE_NORMAL]);
char *ret = talloc_strdup(m, "");
for (i = 0; i < num; i++)
}
void build_objects(struct manifest *m,
- bool keep, struct score *score, const char *flags)
+ bool keep, struct score *score, const char *flags,
+ enum compile_type ctype)
{
struct ccan_file *i;
bool errors = false, warnings = false;
char *output;
char *fullfile = talloc_asprintf(m, "%s/%s", m->dir, i->name);
- i->compiled = maybe_temp_file(m, "", keep, fullfile);
+ i->compiled[ctype] = maybe_temp_file(m, "", keep, fullfile);
if (!compile_object(score, fullfile, ccan_dir, compiler, flags,
- i->compiled, &output)) {
- talloc_free(i->compiled);
+ i->compiled[ctype], &output)) {
+ talloc_free(i->compiled[ctype]);
score_file_error(score, i, 0,
"Compiling object files:\n%s",
output);
bool keep,
unsigned int *timeleft, struct score *score)
{
- build_objects(m, keep, score, cflags);
+ build_objects(m, keep, score, cflags, COMPILE_NORMAL);
}
struct ccanlint objects_build = {
f->lines = NULL;
f->line_info = NULL;
f->doc_sections = NULL;
- f->compiled = NULL;
+ f->compiled[COMPILE_NORMAL] = f->compiled[COMPILE_NOFEAT] = NULL;
f->name = talloc_steal(f, name);
f->fullname = talloc_asprintf(f, "%s/%s", dir, f->name);
f->contents = NULL;
m = talloc_linked(ctx, talloc(NULL, struct manifest));
m->info_file = NULL;
- m->compiled = NULL;
+ m->compiled[COMPILE_NORMAL] = m->compiled[COMPILE_NOFEAT] = NULL;
m->dir = talloc_steal(m, canon_dir);
list_head_init(&m->c_files);
list_head_init(&m->h_files);
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#include <tools/tools.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+#include "../compulsory_tests/build.h"
+
+static const char *can_build(struct manifest *m)
+{
+ if (safe_mode)
+ return "Safe mode enabled";
+ return NULL;
+}
+
+static void check_depends_built_without_features(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct manifest *i;
+ char *flags;
+
+ flags = talloc_asprintf(score, "%s -I.", cflags);
+
+ list_for_each(&m->deps, i, list) {
+ char *errstr = build_submodule(i, flags, COMPILE_NOFEAT);
+
+ if (errstr) {
+ score->error = talloc_asprintf(score,
+ "Dependency %s"
+ " did not build:\n%s",
+ i->basename, errstr);
+ return;
+ }
+ }
+
+ score->pass = true;
+ score->score = score->total;
+}
+
+struct ccanlint depends_build_without_features = {
+ .key = "depends_build_without_features",
+ .name = "Module's CCAN dependencies can be found or built (reduced features)",
+ .check = check_depends_built_without_features,
+ .can_run = can_build,
+ .needs = "depends_exist"
+};
+
+REGISTER_TEST(depends_build_without_features);
m = get_manifest(*deps, talloc_asprintf(*deps, "%s/ccan/%s",
ccan_dir, basename));
- errstr = build_submodule(m);
+ errstr = build_submodule(m, cflags, COMPILE_NORMAL);
if (errstr)
errx(1, "%s", errstr);
if (m->info_file) {
char **infodeps;
- infodeps = get_deps(m, m->dir, false, &m->info_file->compiled);
+ infodeps = get_deps(m, m->dir, false,
+ &m->info_file->compiled[COMPILE_NORMAL]);
for (i = 0; infodeps[i]; i++) {
if (strstarts(infodeps[i], "ccan/"))
list = talloc_strdup(f, "");
for (i = 0; i < talloc_get_size(deps) / sizeof(*deps); i++) {
- if (deps[i]->compiled)
+ if (deps[i]->compiled[COMPILE_NORMAL])
list = talloc_asprintf_append(list, " %s",
- deps[i]->compiled);
+ deps[i]->compiled
+ [COMPILE_NORMAL]);
}
return list;
}
+/* FIXME: Test with reduced features! */
static char *lib_list(const struct manifest *m)
{
unsigned int i, num;
- char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
+ char **libs;
char *ret = talloc_strdup(m, "");
+ libs = get_libs(m, m->dir, &num,
+ &m->info_file->compiled[COMPILE_NORMAL]);
for (i = 0; i < num; i++)
ret = talloc_asprintf_append(ret, "-l%s ", libs[i]);
return ret;
struct ccan_file *file,
bool keep, char **output)
{
- file->compiled = maybe_temp_file(ctx, "", keep, file->fullname);
+ file->compiled[COMPILE_NORMAL]
+ = maybe_temp_file(ctx, "", keep, file->fullname);
if (!compile_and_link(ctx, file->fullname, ccan_dir,
example_obj_list(m, file),
compiler, cflags,
- lib_list(m), file->compiled, output)) {
+ lib_list(m), file->compiled[COMPILE_NORMAL],
+ output)) {
/* Don't keep failures. */
if (keep)
- unlink(file->compiled);
- talloc_free(file->compiled);
- file->compiled = NULL;
+ unlink(file->compiled[COMPILE_NORMAL]);
+ talloc_free(file->compiled[COMPILE_NORMAL]);
+ file->compiled[COMPILE_NORMAL] = NULL;
return false;
}
return true;
unsigned int default_time = default_timeout_ms;
cmd = talloc_asprintf(i, "echo '%s' | %s %s",
- input, i->compiled, input);
+ input, i->compiled[COMPILE_NORMAL], input);
output = run_with_timeout(i, cmd, &ok, &default_time);
if (!ok)
linenum++,
expect = find_expect(i, lines, &input,
&exact, &linenum)) {
- if (i->compiled == NULL)
+ if (i->compiled[COMPILE_NORMAL] == NULL)
continue;
score->total++;
}
}
+/* FIXME: Test with reduced features, valgrind! */
struct ccanlint examples_run = {
.key = "examples_run",
.name = "Module examples with expected output give that output",
struct score *score)
{
const char *flags = talloc_asprintf(score, "-I. %s", cflags);
- build_objects(m, keep, score, flags);
+ build_objects(m, keep, score, flags, COMPILE_NOFEAT);
}
struct ccanlint objects_build_without_features = {
return NULL;
}
-/* FIXME: Merge this into one place. */
-static char *obj_list(const struct manifest *m, bool link_with_module)
+static char *obj_list(const struct manifest *m, bool link_with_module,
+ enum compile_type ctype)
{
char *list = talloc_strdup(m, "");
struct ccan_file *i;
/* Objects from any other C files. */
list_for_each(&m->other_test_c_files, i, list)
- list = talloc_asprintf_append(list, " %s", i->compiled);
+ list = talloc_asprintf_append(list, " %s",
+ i->compiled[ctype]);
/* Our own object files. */
if (link_with_module)
list_for_each(&m->c_files, i, list)
- list = talloc_asprintf_append(list, " %s", i->compiled);
+ list = talloc_asprintf_append(list, " %s",
+ i->compiled[ctype]);
/* Other ccan modules. */
list_for_each(&m->deps, subm, list) {
- if (subm->compiled)
+ if (subm->compiled[ctype])
list = talloc_asprintf_append(list, " %s",
- subm->compiled);
+ subm->compiled[ctype]);
}
return list;
}
-static char *lib_list(const struct manifest *m)
+static char *lib_list(const struct manifest *m, enum compile_type ctype)
{
unsigned int i, num;
- char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
+ char **libs = get_libs(m, m->dir, &num,
+ &m->info_file->compiled[ctype]);
char *ret = talloc_strdup(m, "");
for (i = 0; i < num; i++)
static bool compile(const void *ctx,
struct manifest *m,
struct ccan_file *file,
- const char *flags,
bool fail,
bool link_with_module,
- bool keep, char **output)
+ bool keep,
+ enum compile_type ctype,
+ char **output)
{
- char *f = talloc_asprintf(ctx, "%s%s%s",
- flags, fail ? "-DFAIL " : "", cflags);
+ char *fname, *flags;
+
+ flags = talloc_asprintf(ctx, "%s%s%s",
+ fail ? "-DFAIL " : "",
+ cflags,
+ ctype == COMPILE_NOFEAT ? " -I." : "");
- file->compiled = maybe_temp_file(ctx, "", keep, file->fullname);
+ fname = maybe_temp_file(ctx, "", keep, file->fullname);
if (!compile_and_link(ctx, file->fullname, ccan_dir,
- obj_list(m, link_with_module), compiler, f,
- lib_list(m), file->compiled, output)) {
- talloc_free(file->compiled);
+ obj_list(m, link_with_module, ctype), compiler,
+ flags, lib_list(m, ctype), fname, output)) {
+ talloc_free(fname);
return false;
}
+
+ file->compiled[ctype] = fname;
return true;
}
static void compile_tests(struct manifest *m, bool keep,
- struct score *score, const char *incl)
+ struct score *score,
+ enum compile_type ctype)
{
char *cmdout;
struct ccan_file *i;
foreach_ptr(list, &m->compile_ok_tests, &m->run_tests, &m->api_tests) {
list_for_each(list, i, list) {
- if (!compile(score, m, i, incl, false,
- list == &m->api_tests, keep, &cmdout)) {
+ if (!compile(score, m, i, false,
+ list == &m->api_tests, keep,
+ ctype, &cmdout)) {
score_file_error(score, i, 0,
"Compile failed:\n%s",
cmdout);
/* For historical reasons, "fail" often means "gives warnings" */
list_for_each(&m->compile_fail_tests, i, list) {
- if (!compile(score, m, i, incl, false, false, false, &cmdout)) {
+ if (!compile(score, m, i, false, false, false,
+ ctype, &cmdout)) {
score_file_error(score, i, 0,
"Compile without -DFAIL failed:\n%s",
cmdout);
cmdout);
return;
}
- if (compile(score, m, i, incl, true, false, false, &cmdout)
+ if (compile(score, m, i, true, false, false,
+ ctype, &cmdout)
&& streq(cmdout, "")) {
score_file_error(score, i, 0,
"Compiled successfully with -DFAIL?");
bool keep,
unsigned int *timeleft, struct score *score)
{
- return compile_tests(m, keep, score, "");
+ compile_tests(m, keep, score, COMPILE_NORMAL);
}
struct ccanlint tests_compile = {
unsigned int *timeleft,
struct score *score)
{
- compile_tests(m, keep, score, "-I. ");
+ compile_tests(m, keep, score, COMPILE_NOFEAT);
}
struct ccanlint tests_compile_without_features = {
.name = "Module tests compile (without features)",
.check = do_compile_tests_without_features,
.can_run = features_reduced,
- .needs = "tests_compile reduce_features"
+ .needs = "tests_helpers_compile_without_features reduce_features"
};
REGISTER_TEST(tests_compile_without_features);
/* Objects from any other C files. */
list_for_each(&m->other_test_c_files, i, list)
- list = talloc_asprintf_append(list, " %s", i->compiled);
+ list = talloc_asprintf_append(list, " %s",
+ i->compiled[COMPILE_NORMAL]);
if (modobjs)
list = talloc_append_string(list, modobjs);
/* Other ccan modules (don't need coverage versions of those). */
list_for_each(&m->deps, subm, list) {
- if (subm->compiled)
+ if (subm->compiled[COMPILE_NORMAL])
list = talloc_asprintf_append(list, " %s",
- subm->compiled);
+ subm->compiled
+ [COMPILE_NORMAL]);
}
return list;
static char *lib_list(const struct manifest *m)
{
unsigned int i, num;
- char **libs = get_libs(m, m->dir, &num, &m->info_file->compiled);
+ char **libs = get_libs(m, m->dir, &num,
+ &m->info_file->compiled[COMPILE_NORMAL]);
char *ret = talloc_strdup(m, "");
for (i = 0; i < num; i++)
bool ran_some = false;
/* This tells gcov where we put those .gcno files. */
- outdir = talloc_dirname(score, m->info_file->compiled);
+ outdir = talloc_dirname(score,
+ m->info_file->compiled[COMPILE_NORMAL]);
covcmd = talloc_asprintf(m, "gcov %s -o %s",
full_gcov ? "" : "-n",
outdir);
#include <err.h>
#include <string.h>
#include <ctype.h>
+#include "reduce_features.h"
static const char *can_run(struct manifest *m)
{
static bool compile(struct manifest *m,
bool keep,
struct ccan_file *cfile,
+ const char *flags,
+ enum compile_type ctype,
char **output)
{
- cfile->compiled = maybe_temp_file(m, ".o", keep, cfile->fullname);
- return compile_object(m, cfile->fullname, ccan_dir, compiler, cflags,
- cfile->compiled, output);
+ cfile->compiled[ctype] = maybe_temp_file(m, ".o", keep, cfile->fullname);
+ return compile_object(m, cfile->fullname, ccan_dir, compiler, flags,
+ cfile->compiled[ctype], output);
}
-static void do_compile_test_helpers(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
+static void compile_test_helpers(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score,
+ const char *flags,
+ enum compile_type ctype)
{
struct ccan_file *i;
bool errors = false, warnings = false;
list_for_each(&m->other_test_c_files, i, list) {
char *cmdout;
- if (!compile(m, keep, i, &cmdout)) {
+ if (!compile(m, keep, i, flags, ctype, &cmdout)) {
errors = true;
score_file_error(score, i, 0, "Compile failed:\n%s",
cmdout);
}
}
+static void do_compile_test_helpers(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ compile_test_helpers(m, keep, timeleft, score, cflags,
+ COMPILE_NORMAL);
+}
+
struct ccanlint tests_helpers_compile = {
.key = "tests_helpers_compile",
.name = "Module test helper objects compile",
};
REGISTER_TEST(tests_helpers_compile);
+
+static const char *features_reduced(struct manifest *m)
+{
+ if (features_were_reduced)
+ return NULL;
+ return "No features to turn off";
+}
+
+static void do_compile_test_helpers_without_features(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ char *flags;
+
+ flags = talloc_asprintf(score, "%s -I.", cflags);
+
+ compile_test_helpers(m, keep, timeleft, score, flags,
+ COMPILE_NOFEAT);
+}
+
+struct ccanlint tests_helpers_compile_without_features = {
+ .key = "tests_helpers_compile_without_features",
+ .name = "Module tests helpers compile (without features)",
+ .check = do_compile_test_helpers_without_features,
+ .can_run = features_reduced,
+ .needs = "depends_build_without_features tests_exist"
+};
+REGISTER_TEST(tests_helpers_compile_without_features);
static bool run_test(void *ctx,
struct manifest *m,
unsigned int *timeleft, char **cmdout,
- struct ccan_file *i,
- bool use_valgrind)
+ struct ccan_file *i)
{
- if (use_valgrind) {
+ if (do_valgrind) {
const char *options;
options = concat(ctx,
per_file_options(&tests_pass_valgrind, i));
* unreadable by humans *and* doesn't support
* children reporting. */
i->valgrind_log = talloc_asprintf(m,
- "%s.valgrind-log",
- i->compiled);
+ "%s.valgrind-log",
+ i->compiled[COMPILE_NORMAL]);
talloc_set_destructor(i->valgrind_log,
unlink_file_destructor);
" --log-fd=3 %s %s"
" 3> %s",
options,
- i->compiled, i->valgrind_log);
+ i->compiled[COMPILE_NORMAL],
+ i->valgrind_log);
}
}
- return run_command(m, timeleft, cmdout, "%s", i->compiled);
+ return run_command(m, timeleft, cmdout, "%s",
+ i->compiled[COMPILE_NORMAL]);
}
-static void run_tests(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score,
- bool use_valgrind)
+static void do_run_tests(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
{
struct list_head *list;
struct ccan_file *i;
foreach_ptr(list, &m->run_tests, &m->api_tests) {
list_for_each(list, i, list) {
score->total++;
- if (run_test(score, m, timeleft, &cmdout, i,
- use_valgrind))
+ if (run_test(score, m, timeleft, &cmdout, i))
score->score++;
else
score_file_error(score, i, 0, "%s", cmdout);
score->pass = true;
}
-static void do_run_tests(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- run_tests(m, keep, timeleft, score, do_valgrind);
-}
-
-static void do_run_tests_without_features(struct manifest *m,
- bool keep,
- unsigned int *timeleft,
- struct score *score)
-{
- run_tests(m, keep, timeleft, score, false);
-}
/* Gcc's warn_unused_result is fascist bullshit. */
#define doesnt_matter()
return;
command = talloc_asprintf(m, "gdb -ex 'break tap.c:139' -ex 'run' %s",
- first->file->compiled);
+ first->file->compiled[COMPILE_NORMAL]);
if (system(command))
doesnt_matter();
}
};
REGISTER_TEST(tests_pass);
-
-struct ccanlint tests_pass_without_features = {
- .key = "tests_pass_without_features",
- .name = "Module's run and api tests pass (without features)",
- .check = do_run_tests_without_features,
- .handle = run_under_debugger,
- .needs = "tests_compile_without_features"
-};
-
-REGISTER_TEST(tests_pass_without_features);
concat(score,
per_file_options(&tests_pass_valgrind,
first->file)),
- first->file->compiled);
+ first->file->compiled[COMPILE_NORMAL]);
if (system(command))
doesnt_matter();
}
--- /dev/null
+#include <tools/ccanlint/ccanlint.h>
+#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>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <string.h>
+#include <ctype.h>
+
+/* We don't do these under valgrind: too slow! */
+static void do_run_tests_no_features(struct manifest *m,
+ bool keep,
+ unsigned int *timeleft,
+ struct score *score)
+{
+ struct list_head *list;
+ struct ccan_file *i;
+ char *cmdout;
+
+ score->total = 0;
+ foreach_ptr(list, &m->run_tests, &m->api_tests) {
+ list_for_each(list, i, list) {
+ score->total++;
+ if (run_command(m, timeleft, &cmdout, "%s",
+ i->compiled[COMPILE_NOFEAT]))
+ score->score++;
+ else
+ score_file_error(score, i, 0, "%s", cmdout);
+ }
+ }
+
+ 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, struct score *score)
+{
+ char *command;
+ struct file_error *first;
+
+ first = list_top(&score->per_file_errors, struct file_error, list);
+
+ if (!ask("Should I run the first failing test under the debugger?"))
+ return;
+
+ command = talloc_asprintf(m, "gdb -ex 'break tap.c:139' -ex 'run' %s",
+ first->file->compiled
+ [COMPILE_NOFEAT]);
+ if (system(command))
+ doesnt_matter();
+}
+
+struct ccanlint tests_pass_without_features = {
+ .key = "tests_pass_without_features",
+ .name = "Module's run and api tests pass (without features)",
+ .check = do_run_tests_no_features,
+ .handle = run_under_debugger,
+ .needs = "tests_compile_without_features"
+};
+
+REGISTER_TEST(tests_pass_without_features);