From 6adaa8706f58f8ca4a65e00bc4d74f3747dd9e76 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sun, 22 Mar 2015 20:13:47 +0100 Subject: [PATCH 1/3] Added suite separation and ordered set shenanigans --- Makefile.am | 6 +- include/criterion/criterion.h | 29 +++++++-- include/criterion/ordered-set.h | 37 +++++++++++ src/ordered-set.c | 50 +++++++++++++++ src/process.c | 6 +- src/process.h | 4 +- src/report.c | 15 +++-- src/runner.c | 110 +++++++++++++++++++------------- src/runner.h | 11 ++++ src/sorted-list.c | 30 --------- 10 files changed, 209 insertions(+), 89 deletions(-) create mode 100644 include/criterion/ordered-set.h create mode 100644 src/ordered-set.c delete mode 100644 src/sorted-list.c 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..31af324 --- /dev/null +++ b/include/criterion/ordered-set.h @@ -0,0 +1,37 @@ +#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..98582e5 --- /dev/null +++ b/src/ordered-set.c @@ -0,0 +1,50 @@ +#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..ed7da20 100644 --- a/src/process.c +++ b/src/process.c @@ -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; 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; - } -} From a09b65cc5fed01258105e835ee8f9498a41c87e3 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sun, 22 Mar 2015 20:19:16 +0100 Subject: [PATCH 2/3] Added open streams flushing before the test worker exits --- src/process.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/process.c b/src/process.c index ed7da20..1a3c788 100644 --- a/src/process.c +++ b/src/process.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include #include #include #include @@ -71,6 +72,8 @@ struct process *spawn_test_worker(struct criterion_test *test, func(test, suite); close(fds[1]); + + fflush(NULL); // flush all opened streams if (criterion_options.no_early_exit) return NULL; else From 8e164e7440d1bdf811d268e6190db59df2d0ef18 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Sun, 22 Mar 2015 20:20:17 +0100 Subject: [PATCH 3/3] Added missing headers on ordered set --- include/criterion/ordered-set.h | 23 +++++++++++++++++++++++ src/ordered-set.c | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/include/criterion/ordered-set.h b/include/criterion/ordered-set.h index 31af324..e585422 100644 --- a/include/criterion/ordered-set.h +++ b/include/criterion/ordered-set.h @@ -1,3 +1,26 @@ +/* + * 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_ diff --git a/src/ordered-set.c b/src/ordered-set.c index 98582e5..6e41a3f 100644 --- a/src/ordered-set.c +++ b/src/ordered-set.c @@ -1,3 +1,26 @@ +/* + * 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