Commit fdfcdafb authored by Rusty Russell's avatar Rusty Russell

First cut, some hacks, three simple modules.

parents
_info
.depends
CFLAGS=-O3 -Wall -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I.
ALL=$(patsubst %/test, %, $(wildcard */test))
ALL_DEPENDS=$(patsubst %, %/.depends, $(ALL))
default: test-all
test-all: $(ALL_DEPENDS)
@$(MAKE) `for f in $(ALL); do echo test-$$f test-$$f; while read d; do echo test-$$d test-$$f; done < $$f/.depends; done | tsort`
$(ALL_DEPENDS): %/.depends: %/_info
@$< depends > $@ || ( rm -f $@; exit 1 )
test-%: FORCE run_tests
@echo Testing $*...
@if ./run_tests $* | grep ^'not ok'; then exit 1; else exit 0; fi
FORCE:
run_tests: run_tests.o tap/tap.o talloc/talloc.o
clean:
rm -f run_tests run_tests.o
#include <stdio.h>
#include <string.h>
#include "config.h"
/**
* build_assert - routines for build-time assertions
*
* This code provides routines which will cause compilation to fail should some
* assertion be untrue: such failures are preferable to run-time assertions,
* but much more limited since they can only depends on compile-time constants.
*
* These assertions are most useful when two parts of the code must be kept in
* sync: it is better to avoid such cases if possible, but seconds best is to
* detect invalid changes at build time.
*
* For example, a tricky piece of code might rely on a certain element being at
* the start of the structure. To ensure that future changes don't break it,
* you would catch such changes in your code like so:
*
* Example:
* char *foo_string(struct foo *foo)
* {
* // This trick requires that the string be first in the structure
* BUILD_ASSERT(offsetof(struct foo, string) == 0);
* return (char *)foo;
* }
*/
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0)
/* Nothing. */
return 0;
return 1;
}
#ifndef CCAN_BUILD_ASSERT_H
#define CCAN_BUILD_ASSERT_H
/**
* BUILD_ASSERT - assert a build-time dependency.
* @cond: the compile-time condition which must be true.
*
* Your compile will fail if the condition isn't true, or can't be evaluated
* by the compiler. This can only be used within a function.
*
* Example:
* char *foo_to_char(struct foo *foo)
* {
* // This code needs string to be at start of foo.
* BUILD_ASSERT(offsetof(struct foo, string) == 0);
* return (char *)foo;
* }
*/
#define BUILD_ASSERT(cond) \
do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)
/**
* EXPR_BUILD_ASSERT - assert a build-time dependency, as an expression.
* @cond: the compile-time condition which must be true.
*
* Your compile will fail if the condition isn't true, or can't be evaluated
* by the compiler. This can be used in an expression: its value is "0".
*
* Example:
* #define foo_to_char(foo) \
* ((char *)(foo) \
* + EXPR_BUILD_ASSERT(offsetof(struct foo, string) == 0))
*/
#define EXPR_BUILD_ASSERT(cond) \
(sizeof(char [1 - 2*!(cond)]) - 1)
#endif /* CCAN_BUILD_ASSERT_H */
#include "build_assert/build_assert.h"
int main(int argc, char *argv[])
{
#ifdef FAIL
return EXPR_BUILD_ASSERT(1 == 0);
#else
return 0;
#endif
}
#include "build_assert/build_assert.h"
int main(int argc, char *argv[])
{
#ifdef FAIL
BUILD_ASSERT(1 == 0);
#endif
return 0;
}
#include "build_assert/build_assert.h"
int main(int argc, char *argv[])
{
BUILD_ASSERT(1 == 1);
return 0;
}
#include "build_assert/build_assert.h"
#include "tap/tap.h"
int main(int argc, char *argv[])
{
plan_tests(1);
ok1(EXPR_BUILD_ASSERT(1 == 1) == 0);
return exit_status();
}
#include <stdio.h>
#include <string.h>
#include "config.h"
/**
* check_type - routines for compile time type checking
*
* C has fairly weak typing: ints get automatically converted to longs, signed
* to unsigned, etc. There are some cases where this is best avoided, and
* these macros provide methods for evoking warnings (or build errors) when
* a precise type isn't used.
*
* On compilers which don't support typeof() these routines are less effective,
* since they have to use sizeof() which can only distiguish between types of
* different size.
*/
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
#if !HAVE_TYPEOF
printf("build_assert\n");
#endif
return 0;
}
return 1;
}
#ifndef CCAN_CHECK_TYPE_H
#define CCAN_CHECK_TYPE_H
#include "config.h"
/**
* check_type - issue a warning or build failure if type is not correct.
* @expr: the expression whose type we should check (not evaluated).
* @type: the exact type we expect the expression to be.
*
* This macro is usually used within other macros to try to ensure that a macro
* argument is of the expected type. No type promotion of the expression is
* done: an unsigned int is not the same as an int!
*
* check_type() always evaluates to 1.
*
* If your compiler does not support typeof, then the best we can do is fail
* to compile if the sizes of the types are unequal (a less complete check).
*
* Example:
* // They should always pass a 64-bit value to _set_some_value!
* #define set_some_value(expr) \
* _set_some_value((check_type((expr), uint64_t), (expr)))
*/
/**
* check_types_match - issue a warning or build failure if types are not same.
* @expr1: the first expression (not evaluated).
* @expr2: the second expression (not evaluated).
*
* This macro is usually used within other macros to try to ensure that
* arguments are of identical types. No type promotion of the expressions is
* done: an unsigned int is not the same as an int!
*
* check_types_match() always evaluates to 1.
*
* If your compiler does not support typeof, then the best we can do is fail
* to compile if the sizes of the types are unequal (a less complete check).
*
* Example:
* // Do subtraction to get to enclosing type, but make sure that
* // pointer is of correct type for that member.
* #define container_of(mbr_ptr, encl_type, mbr) \
* (check_types_match((mbr_ptr), &((encl_type *)0)->mbr), \
* ((encl_type *) \
* ((char *)(mbr_ptr) - offsetof(enclosing_type, mbr))))
*/
#if HAVE_TYPEOF
#define check_type(expr, type) \
((typeof(expr) *)0 != (type *)0)
#define check_types_match(expr1, expr2) \
((typeof(expr1) *)0 != (typeof(expr2) *)0)
#else
#include "build_assert/build_assert.h"
/* Without typeof, we can only test the sizes. */
#define check_type(expr, type) \
EXPR_BUILD_ASSERT(sizeof(expr) == sizeof(type))
#define check_types_match(expr1, expr2) \
EXPR_BUILD_ASSERT(sizeof(expr1) == sizeof(expr2))
#endif /* HAVE_TYPEOF */
#endif /* CCAN_CHECK_TYPE_H */
#include "check_type/check_type.h"
int main(int argc, char *argv[])
{
#ifdef FAIL
check_type(argc, char);
#endif
return 0;
}
#include "check_type/check_type.h"
int main(int argc, char *argv[])
{
#ifdef FAIL
#if HAVE_TYPEOF
check_type(argc, unsigned int);
#else
/* This doesn't work without typeof, so just fail */
#error "Fail without typeof"
#endif
#endif
return 0;
}
#include "check_type/check_type.h"
int main(int argc, char *argv[])
{
unsigned char x = argc;
#ifdef FAIL
check_types_match(argc, x);
#endif
return x;
}
#include "check_type/check_type.h"
#include "tap/tap.h"
int main(int argc, char *argv[])
{
int x = 0, y = 0;
plan_tests(9);
ok1(check_type(argc, int) == 0);
ok1(check_type(&argc, int *) == 0);
ok1(check_types_match(argc, argc) == 0);
ok1(check_types_match(argc, x) == 0);
ok1(check_types_match(&argc, &x) == 0);
ok1(check_type(x++, int) == 0);
ok(x == 0, "check_type does not evaluate expression");
ok1(check_types_match(x++, y++) == 0);
ok(x == 0 && y == 0, "check_types_match does not evaluate expressions");
return 0;
}
/* Simple config.h for gcc. */
#define HAVE_TYPEOF 1
#define HAVE_STATEMENT_EXPR 1
#include <stdio.h>
#include <string.h>
#include "config.h"
/**
* container_of - routine for upcasting
*
* It is often convenient to create code where the caller registers a pointer
* to a generic structure and a callback. The callback might know that the
* pointer points to within a larger structure, and container_of gives a
* convenient and fairly type-safe way of returning to the enclosing structure.
*
* This idiom is an alternative to providing a void * pointer for every
* callback.
*
* Example:
* struct info
* {
* int my_stuff;
* struct timer timer;
* };
*
* static void my_timer_callback(struct timer *timer)
* {
* struct info *info = container_of(timer, struct info, timer);
* printf("my_stuff is %u\n", info->my_stuff);
* }
*
* int main()
* {
* struct info info = { .my_stuff = 1 };
*
* register_timer(&info.timer);
* ...
*/
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("check_type\n");
return 0;
}
return 1;
}
#ifndef CCAN_CONTAINER_OF_H
#define CCAN_CONTAINER_OF_H
#include <stddef.h>
#include "config.h"
#include "check_type/check_type.h"
#define container_of(member_ptr, containing_type, member) \
((containing_type *) \
((char *)(member_ptr) - offsetof(containing_type, member)) \
- check_types_match(*(member_ptr), ((containing_type *)0)->member))
#endif /* CCAN_CONTAINER_OF_H */
#include "container_of/container_of.h"
#include <stdlib.h>
struct foo {
int a;
char b;
};
int main(int argc, char *argv[])
{
struct foo foo = { .a = 1, .b = 2 };
int *intp = &foo.a;
char *p;
#ifdef FAIL
p = container_of(intp, struct foo, a);
#else
p = (char *)intp;
#endif
return p == NULL;
}
#include "container_of/container_of.h"
#include <stdlib.h>
struct foo {
int a;
char b;
};
int main(int argc, char *argv[])
{
struct foo foo = { .a = 1, .b = 2 }, *foop;
int *intp = &foo.a;
#ifdef FAIL
foop = container_of(intp, struct foo, b);
#else
foop = NULL;
#endif
return intp == NULL;
}
#include "container_of/container_of.h"
#include "tap/tap.h"
struct foo {
int a;
char b;
};
int main(int argc, char *argv[])
{
struct foo foo = { .a = 1, .b = 2 };
int *intp = &foo.a;
char *charp = &foo.b;
plan_tests(2);
ok1(container_of(intp, struct foo, a) == &foo);
ok1(container_of(charp, struct foo, b) == &foo);
return exit_status();
}
#include <err.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <assert.h>
#include <unistd.h>
#include "tap/tap.h"
#include "talloc/talloc.h"
#include "string/string.h"
#define CFLAGS "-O3 -Wall -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Werror -I."
/* FIXME: Use build bug later. */
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
static struct test *tests = NULL;
static struct obj *objs = NULL;
static int verbose;
struct test_type
{
const char *name;
void (*testfn)(struct test_type *t, const char *name);
};
struct test
{
struct test *next;
struct test_type *type;
char *name;
};
struct obj
{
struct obj *next;
char *name;
};
static char *output_name(const char *name)
{
char *ret;
assert(strends(name, ".c"));
ret = talloc_strdup(name, name);
ret[strlen(ret) - 2] = '\0';
return ret;
}
static char *obj_list(void)
{
char *list = talloc_strdup(objs, "");
struct obj *i;
for (i = objs; i; i = i->next)
list = talloc_asprintf_append(list, "%s ", i->name);
/* FIXME */
list = talloc_asprintf_append(list, "tap/tap.o");
return list;
}
static void compile_objs(void)
{
struct obj *i;
for (i = objs; i; i = i->next) {
char *cmd = talloc_asprintf(i, "gcc " CFLAGS " -o %s.o -c %s%s",
output_name(i->name), i->name,
verbose ? "" : "> /dev/null 2>&1");
ok(system(cmd) == 0, "%s", cmd);
}
}
static void cleanup_objs(void)
{
struct obj *i;
for (i = objs; i; i = i->next)
unlink(talloc_asprintf(i, "%s.o", output_name(i->name)));
}
static void add_test(const char *testdir, const char *name, struct test_type *t)
{
struct test *test = talloc(testdir, struct test);
test->next = tests;
test->type = t;
test->name = talloc_asprintf(test, "%s/%s", testdir, name);
tests = test;
}
static void add_obj(const char *testdir, const char *name)
{
struct obj *obj = talloc(testdir, struct obj);
obj->next = objs;
obj->name = talloc_asprintf(obj, "%s/%s", testdir, name);
objs = obj;
}
static int build(const char *name, int fail)
{
const char *cmd;
int ret;
cmd = talloc_asprintf(name, "gcc " CFLAGS " %s -o %s %s %s%s",
fail ? "-DFAIL" : "",
output_name(name), name, obj_list(),
verbose ? "" : "> /dev/null 2>&1");
if (verbose)
fprintf(stderr, "Running %s\n", cmd);
ret = system(cmd);
if (ret == -1)
diag("cmd '%s' failed to execute", cmd);
return ret;
}
static void compile_ok(struct test_type *t, const char *name)
{
ok(build(name, 0) == 0, "%s %s", t->name, name);
}
static void compile_fail(struct test_type *t, const char *name)
{
if (build(name, 0) != 0)
fail("non-FAIL build %s", name);
else
ok(build(name, 1) > 0, "%s %s", t->name, name);
}
static void run(const char *name)
{
if (system(output_name(name)) == -1)
fail("running %s had error %m", name);
}
static void cleanup(const char *name)
{
unlink(output_name(name));
}
static struct test_type test_types[] = {
{ "compile_ok", compile_ok },
{ "compile_fail", compile_fail },
{ "run", compile_ok },
};
int main(int argc, char *argv[])
{
DIR *dir;
struct dirent *d;
char *testdir;
struct test *test;
unsigned int num_tests = 0, num_objs = 0;
if (argc > 1 && streq(argv[1], "--verbose")) {
verbose = 1;
argc--;
argv++;
}
if (argc != 2)
errx(1, "Usage: run_tests [--verbose] <dir>");
testdir = talloc_asprintf(NULL, "%s/test", argv[1]);
dir = opendir(testdir);
if (!dir)
err(1, "Opening '%s'", testdir);
while ((d = readdir(dir)) != NULL) {
unsigned int i;
if (d->d_name[0] == '.' || !strends(d->d_name, ".c"))
continue;
for (i = 0; i < ARRAY_SIZE(test_types); i++) {
if (strstarts(d->d_name, test_types[i].name)) {
add_test(testdir, d->d_name, &test_types[i]);
num_tests++;
break;
}
}
if (i == ARRAY_SIZE(test_types)) {
add_obj(testdir, d->d_name);
num_objs++;
}
}
plan_tests(num_tests + num_objs);
/* First all the extra object compilations. */
compile_objs();
/* Do all the test compilations. */
for (test = tests; test; test = test->next)
test->type->testfn(test->type, test->name);
cleanup_objs();
/* Now run all the ones which wanted to run. */
for (test = tests; test; test = test->next) {
if (streq(test->type->name, "run"))
run(test->name);
cleanup(test->name);
}
exit(exit_status());
}
#! /bin/sh
# First, test normal config.
if ! make -s; then
echo Normal config failed.
exit 1
fi
# Now, remove one HAVE_ at a time.
cp config.h original-config.h
trap "mv original-config.h config.h && rm -f .newconfig" EXIT
while grep -q '1$' config.h; do
tr '\012' @ < config.h | sed 's/1@/0@/' | tr @ '\012' > .newconfig
diff -u config.h .newconfig
mv .newconfig config.h
if ! make -s; then
echo Failed config:
cat config.h
exit 1
fi
done
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment