Added suite separation and ordered set shenanigans

This commit is contained in:
Snaipe 2015-03-22 20:13:47 +01:00
parent 7391527f1a
commit 6adaa8706f
10 changed files with 209 additions and 89 deletions

View file

@ -4,11 +4,11 @@ SUBDIRS = dependencies/csptr samples
lib_LTLIBRARIES = libcriterion.la
WARNINGS = -Wall -Wextra \
-Wno-unused-result -Wno-missing-field-initializers
-Wno-unused-result
libcriterion_la_CFLAGS = \
$(WARNINGS) \
-std=gnu99 \
-std=gnu11 \
-fplan9-extensions \
-I$(top_srcdir)/include/ \
-I$(top_srcdir)/dependencies/csptr/include/ \
@ -30,6 +30,7 @@ subdirinclude_HEADERS = \
include/criterion/hooks.h \
include/criterion/logging.h \
include/criterion/options.h \
include/criterion/ordered-set.h \
include/criterion/stats.h
libcriterion_la_SOURCES = \
@ -47,4 +48,5 @@ libcriterion_la_SOURCES = \
src/options.c \
src/timer.c \
src/timer.h \
src/ordered-set.c \
src/main.c

View file

@ -47,9 +47,12 @@ struct criterion_test {
struct criterion_test_extra_data *const data;
};
struct criterion_test_set {
struct criterion_test **tests;
size_t nb_tests;
struct criterion_suite {
const char *name;
struct criterion_test_extra_data *const data;
#if 1
void *pad[2];
#endif
};
# define IDENTIFIER_(Category, Name, Suffix) \
@ -57,12 +60,15 @@ struct criterion_test_set {
# define TEST_PROTOTYPE_(Category, Name) \
void IDENTIFIER_(Category, Name, impl)(void)
# define SUITE_IDENTIFIER_(Name, Suffix) \
suite_ ## Name ## _ ## Suffix
# define Test(...) Test_(__VA_ARGS__, .sentinel_ = 0)
# define Test_(Category, Name, ...) \
TEST_PROTOTYPE_(Category, Name); \
struct criterion_test_extra_data IDENTIFIER_(Category, Name, extra) = { \
.file_ = __FILE__, \
.line_ = __LINE__, \
.file_ = __FILE__, \
.line_ = __LINE__, \
__VA_ARGS__ \
}; \
SECTION_("criterion_tests") \
@ -74,6 +80,19 @@ struct criterion_test_set {
}; \
TEST_PROTOTYPE_(Category, Name)
# define TestSuite(...) TestSuite_(__VA_ARGS__, .sentinel_ = 0)
# define TestSuite_(Name, ...) \
struct criterion_test_extra_data SUITE_IDENTIFIER_(Name, extra) = { \
.file_ = __FILE__, \
.line_ = 0, \
__VA_ARGS__ \
}; \
SECTION_("crit_suites") \
const struct criterion_suite SUITE_IDENTIFIER_(Name, meta) = { \
.name = #Name, \
.data = &SUITE_IDENTIFIER_(Name, extra), \
}
int criterion_run_all_tests(void);
#endif /* !CRITERION_H_ */

View file

@ -0,0 +1,37 @@
#ifndef CRITERION_ORDERED_SET_H_
# define CRITERION_ORDERED_SET_H_
# include <stddef.h>
# include "criterion.h"
struct criterion_ordered_set {
struct criterion_ordered_set_node *first;
size_t size;
int (*const cmp)(void *, void *);
void (*const dtor)(void *, void *);
};
struct criterion_ordered_set_node {
struct criterion_ordered_set_node *next;
char data[0];
};
struct criterion_suite_set {
struct criterion_suite suite;
struct criterion_ordered_set *tests;
};
struct criterion_test_set {
struct criterion_ordered_set *suites;
size_t tests;
};
struct criterion_ordered_set *new_ordered_set(int (*cmp)(void *, void *), void (*dtor)(void *, void *));
void *insert_ordered_set(struct criterion_ordered_set *l, void *ptr, size_t size);
# define FOREACH_SET(Elt, Set) \
for (struct criterion_ordered_set_node *n = Set->first; n; n = n->next) \
for (int cond = 1; cond;) \
for (Elt = (void*) n->data; cond; cond = 0)
#endif /* !CRITERION_ORDERED_SET_H_ */

50
src/ordered-set.c Normal file
View file

@ -0,0 +1,50 @@
#include <criterion/common.h>
#include <criterion/ordered-set.h>
#include <csptr/smart_ptr.h>
static void destroy_ordered_set(void *ptr, UNUSED void *meta) {
sfree(((struct criterion_ordered_set *) ptr)->first);
}
__attribute__ ((always_inline))
static inline void nothing() {}
static void destroy_ordered_set_node(void *ptr, void *meta) {
struct criterion_ordered_set *set = *(void **) meta;
struct criterion_ordered_set_node *n = ptr;
(set->dtor ?: nothing)(n->data, NULL);
sfree(((struct criterion_ordered_set_node *) ptr)->next);
}
struct criterion_ordered_set *new_ordered_set(int (*cmp)(void *, void *), f_destructor dtor) {
return unique_ptr(struct criterion_ordered_set,
{ .cmp = cmp, .dtor = dtor }, destroy_ordered_set);
}
void *insert_ordered_set(struct criterion_ordered_set *l, void *ptr, size_t size) {
int cmp;
struct criterion_ordered_set_node *n, *prev = NULL;
for (n = l->first; n && (cmp = l->cmp(ptr, n->data)) > 0; n = n->next)
prev = n;
if (n && !cmp) // element already exists
return n->data;
struct criterion_ordered_set_node *new = smalloc(
.size = sizeof(struct criterion_ordered_set_node) + size,
.dtor = destroy_ordered_set_node
);
if (!new)
return NULL;
memcpy(new->data, ptr, size);
new->next = n;
if (prev) {
prev->next = new;
} else {
l->first = new;
}
++l->size;
return new->data;
}

View file

@ -54,7 +54,9 @@ struct event *worker_read_event(struct process *proc) {
return read_event(proc->in);
}
struct process *spawn_test_worker(struct criterion_test *test, void (*func)(struct criterion_test *)) {
struct process *spawn_test_worker(struct criterion_test *test,
struct criterion_suite *suite,
void (*func)(struct criterion_test *, struct criterion_suite *)) {
int fds[2];
if (pipe(fds) == -1)
abort();
@ -67,7 +69,7 @@ struct process *spawn_test_worker(struct criterion_test *test, void (*func)(stru
close(fds[0]);
EVENT_PIPE = fds[1];
func(test);
func(test, suite);
close(fds[1]);
if (criterion_options.no_early_exit)
return NULL;

View file

@ -42,7 +42,9 @@ struct process_status {
void set_runner_pid(void);
bool is_runner(void);
struct process_status wait_proc(struct process *proc);
struct process *spawn_test_worker(struct criterion_test *test, void (*func)(struct criterion_test *));
struct process *spawn_test_worker(struct criterion_test *test,
struct criterion_suite *suite,
void (*func)(struct criterion_test *, struct criterion_suite *));
struct event *worker_read_event(struct process *proc);
#endif /* !PROCESS_H_ */

View file

@ -26,6 +26,7 @@
#include "criterion/stats.h"
#include "criterion/logging.h"
#include "criterion/options.h"
#include "criterion/ordered-set.h"
#include "report.h"
#include "timer.h"
@ -95,10 +96,16 @@ ReportHook(POST_FINI)() {}
ReportHook(PRE_ALL)(struct criterion_test_set *set) {
if (criterion_options.enable_tap_format) {
size_t enabled_count = 0, i = 0;
for (struct criterion_test **test = set->tests; i < set->nb_tests; ++i)
if (!(test[i])->data->disabled)
++enabled_count;
size_t enabled_count = 0;
FOREACH_SET(struct criterion_suite_set *s, set->suites) {
if ((s->suite.data && s->suite.data->disabled) || !s->tests)
continue;
FOREACH_SET(struct criterion_test *test, s->tests) {
if (!test->data->disabled)
++enabled_count;
}
}
criterion_important("1.." SIZE_T_FORMAT "\n", enabled_count);
}
}

View file

@ -26,6 +26,7 @@
#include <unistd.h>
#include <csptr/smart_ptr.h>
#include "criterion/options.h"
#include "criterion/ordered-set.h"
#include "stats.h"
#include "runner.h"
#include "report.h"
@ -34,58 +35,77 @@
#include "timer.h"
IMPL_SECTION_LIMITS(struct criterion_test, criterion_tests);
IMPL_SECTION_LIMITS(struct criterion_suite, crit_suites);
static int compare_test(const void *a, const void *b) {
struct criterion_test *first = *(struct criterion_test **) a;
struct criterion_test *second = *(struct criterion_test **) b;
TestSuite(default);
// likely to happen
if (first->category == second->category) {
return strcmp(first->name, second->name);
} else {
return strcmp(first->category, second->category)
?: strcmp(first->name, second->name);
int cmp_suite(void *a, void *b) {
struct criterion_suite *s1 = a, *s2 = b;
return strcmp(s1->name, s2->name);
}
int cmp_test(void *a, void *b) {
struct criterion_test *s1 = a, *s2 = b;
return strcmp(s1->name, s2->name);
}
static void dtor_suite_set(void *ptr, UNUSED void *meta) {
struct criterion_suite_set *s = ptr;
sfree(s->tests);
}
static struct criterion_test_set criterion_init(void) {
struct criterion_ordered_set *suites = new_ordered_set(cmp_suite, dtor_suite_set);
FOREACH_SUITE_SEC(s) {
struct criterion_suite_set css = {
.suite = *s,
};
insert_ordered_set(suites, &css, sizeof (css));
}
FOREACH_TEST_SEC(test) {
struct criterion_suite_set css = {
.suite = { .name = test->category },
};
struct criterion_suite_set *s = insert_ordered_set(suites, &css, sizeof (css));
if (!s->tests)
s->tests = new_ordered_set(cmp_test, NULL);
insert_ordered_set(s->tests, test, sizeof(*test));
}
const size_t nb_tests = SECTION_END(criterion_tests)
- SECTION_START(criterion_tests);
return (struct criterion_test_set) {
suites,
nb_tests,
};
}
static void destroy_test_set(void *ptr, UNUSED void *meta) {
struct criterion_test_set *set = ptr;
free(set->tests);
}
typedef void (*f_test_run)(struct criterion_global_stats *, struct criterion_test *, struct criterion_suite *);
static struct criterion_test_set *read_all_tests(void) {
size_t nb_tests = SECTION_END(criterion_tests) - SECTION_START(criterion_tests);
static void map_tests(struct criterion_test_set *set, struct criterion_global_stats *stats, f_test_run fun) {
FOREACH_SET(struct criterion_suite_set *s, set->suites) {
if ((s->suite.data && s->suite.data->disabled) || !s->tests)
continue;
struct criterion_test **tests = malloc(nb_tests * sizeof (void *));
if (tests == NULL)
return NULL;
size_t i = 0;
for (struct criterion_test *test = SECTION_START(criterion_tests); test < SECTION_END(criterion_tests); ++test)
tests[i++] = test;
qsort(tests, nb_tests, sizeof (void *), compare_test);
return unique_ptr(struct criterion_test_set, {
.tests = tests,
.nb_tests = nb_tests
}, destroy_test_set);
}
static void map_tests(struct criterion_test_set *set, struct criterion_global_stats *stats, void (*fun)(struct criterion_global_stats *, struct criterion_test *)) {
size_t i = 0;
for (struct criterion_test **t = set->tests; i < set->nb_tests; ++i, ++t) {
fun(stats, *t);
if (!is_runner())
return;
FOREACH_SET(struct criterion_test *t, s->tests) {
fun(stats, t, &s->suite);
if (!is_runner())
return;
}
}
}
__attribute__ ((always_inline))
static inline void nothing() {}
static void run_test_child(struct criterion_test *test) {
static void run_test_child(struct criterion_test *test, struct criterion_suite *suite) {
send_event(PRE_INIT, NULL, 0);
if (suite->data)
(suite->data->init ?: nothing)();
(test->data->init ?: nothing)();
send_event(PRE_TEST, NULL, 0);
@ -98,16 +118,18 @@ static void run_test_child(struct criterion_test *test) {
send_event(POST_TEST, &elapsed_time, sizeof (double));
(test->data->fini ?: nothing)();
if (suite->data)
(suite->data->fini ?: nothing)();
send_event(POST_FINI, NULL, 0);
}
static void run_test(struct criterion_global_stats *stats, struct criterion_test *test) {
static void run_test(struct criterion_global_stats *stats, struct criterion_test *test, struct criterion_suite *suite) {
if (test->data->disabled)
return;
smart struct criterion_test_stats *test_stats = test_stats_init(test);
smart struct process *proc = spawn_test_worker(test, run_test_child);
smart struct process *proc = spawn_test_worker(test, suite, run_test_child);
if (proc == NULL && !is_runner())
return;
@ -145,15 +167,13 @@ static void run_test(struct criterion_global_stats *stats, struct criterion_test
}
static int criterion_run_all_tests_impl(void) {
smart struct criterion_test_set *set = read_all_tests();
struct criterion_test_set set = criterion_init();
report(PRE_ALL, set);
report(PRE_ALL, &set);
set_runner_pid();
smart struct criterion_global_stats *stats = stats_init();
if (!set)
abort();
map_tests(set, stats, run_test);
map_tests(&set, stats, run_test);
if (!is_runner())
return -1;

View file

@ -27,5 +27,16 @@
# include "criterion/criterion.h"
DECL_SECTION_LIMITS(struct criterion_test, criterion_tests);
DECL_SECTION_LIMITS(struct criterion_suite, crit_suites);
# define FOREACH_TEST_SEC(Test) \
for (struct criterion_test *Test = SECTION_START(criterion_tests); \
Test < SECTION_END(criterion_tests); \
++Test)
# define FOREACH_SUITE_SEC(Suite) \
for (struct criterion_suite *Suite = SECTION_START(crit_suites); \
Suite < SECTION_END(crit_suites); \
++Suite)
#endif /* !CRITERION_RUNNER_H_ */

View file

@ -1,30 +0,0 @@
#include <criterion/common.h>
#include <criterion/sorted-list.h>
#include <csptr/smart_ptr.h>
static void destroy_sorted_list(void *ptr, UNUSED void *meta) {
sfree(((struct criterion_sorted_list *) ptr)->first);
}
struct criterion_sorted_list *new_sorted_list(int (*cmp)(void *, void *)) {
return unique_ptr(struct criterion_sorted_list,
{ .cmp = cmp }, destroy_sorted_list);
}
void insert_sorted_list(struct criterion_sorted_list *l, void *ptr, size_t size) {
struct criterion_sorted_list_node *n, *prev = NULL;
for (n = l->first; l->cmp(ptr, n->data) > 0; ++n)
prev = n;
struct criterion_sorted_list_node *new =
smalloc(sizeof(struct criterion_sorted_list_node) + size, 0, UNIQUE);
memcpy(new->data, ptr, size);
if (prev) {
new->next = prev->next;
prev->next = new;
} else {
new->next = NULL;
l->first = new;
}
}