Added suite separation and ordered set shenanigans
This commit is contained in:
parent
7391527f1a
commit
6adaa8706f
10 changed files with 209 additions and 89 deletions
|
@ -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
|
||||
|
|
|
@ -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_ */
|
||||
|
|
37
include/criterion/ordered-set.h
Normal file
37
include/criterion/ordered-set.h
Normal 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
50
src/ordered-set.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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_ */
|
||||
|
|
15
src/report.c
15
src/report.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
110
src/runner.c
110
src/runner.c
|
@ -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;
|
||||
|
|
11
src/runner.h
11
src/runner.h
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue