diff --git a/Makefile.am b/Makefile.am
index 371c649..5646b3b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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
diff --git a/include/criterion/criterion.h b/include/criterion/criterion.h
index 2c0d963..6f159ab 100644
--- a/include/criterion/criterion.h
+++ b/include/criterion/criterion.h
@@ -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_ */
diff --git a/include/criterion/ordered-set.h b/include/criterion/ordered-set.h
new file mode 100644
index 0000000..e585422
--- /dev/null
+++ b/include/criterion/ordered-set.h
@@ -0,0 +1,60 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright © 2015 Franklin "Snaipe" Mathieu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef CRITERION_ORDERED_SET_H_
+# define CRITERION_ORDERED_SET_H_
+
+# include
+# 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_ */
diff --git a/src/ordered-set.c b/src/ordered-set.c
new file mode 100644
index 0000000..6e41a3f
--- /dev/null
+++ b/src/ordered-set.c
@@ -0,0 +1,73 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright © 2015 Franklin "Snaipe" Mathieu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include
+#include
+#include
+
+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;
+}
diff --git a/src/process.c b/src/process.c
index 63528af..1a3c788 100644
--- a/src/process.c
+++ b/src/process.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include
+#include
#include
#include
#include
@@ -54,7 +55,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,8 +70,10 @@ 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]);
+
+ fflush(NULL); // flush all opened streams
if (criterion_options.no_early_exit)
return NULL;
else
diff --git a/src/process.h b/src/process.h
index 712648c..02f480e 100644
--- a/src/process.h
+++ b/src/process.h
@@ -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_ */
diff --git a/src/report.c b/src/report.c
index e135732..cd9b68d 100644
--- a/src/report.c
+++ b/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);
}
}
diff --git a/src/runner.c b/src/runner.c
index 6f31ffb..45a7bf1 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -26,6 +26,7 @@
#include
#include
#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;
diff --git a/src/runner.h b/src/runner.h
index 10ccfbb..ca243f0 100644
--- a/src/runner.h
+++ b/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_ */
diff --git a/src/sorted-list.c b/src/sorted-list.c
deleted file mode 100644
index 2283fa1..0000000
--- a/src/sorted-list.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include
-#include
-#include
-
-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;
- }
-}