diff --git a/Makefile b/Makefile index f733361..c6bf7d4 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CC = gcc CFLAGS = -Wall -Wextra -std=gnu99 -Isrc/ -Iinclude/ -g -SRCS = runner.c report.c event.c +SRCS = runner.c report.c event.c stats.c OBJS = $(addprefix src/,$(subst .c,.o,$(SRCS))) sample: samples/simple.o libcriterion.a diff --git a/include/criterion/assert.h b/include/criterion/assert.h index a4bdd6a..015e330 100644 --- a/include/criterion/assert.h +++ b/include/criterion/assert.h @@ -37,7 +37,7 @@ enum criterion_assert_kind { # define assertImpl(Kind, Condition, ...) \ do { \ int passed = !!(Condition); \ - struct criterion_assert_stat stat = { \ + struct criterion_assert_stats stat = { \ .kind = Kind, \ .condition = #Condition, \ .message = "" __VA_ARGS__, \ diff --git a/include/criterion/stats.h b/include/criterion/stats.h index 4f0cd58..03da1da 100644 --- a/include/criterion/stats.h +++ b/include/criterion/stats.h @@ -26,18 +26,34 @@ # include "criterion.h" -struct criterion_assert_stat { +struct criterion_assert_stats { int kind; const char *condition; const char *message; bool passed; unsigned line; + + struct criterion_assert_stats *next; }; struct criterion_test_stats { struct criterion_test *test; + struct criterion_assert_stats *asserts; int passed; int failed; + + struct criterion_test_stats *next; +}; + +struct criterion_global_stats { + struct criterion_test_stats *tests; + size_t nb_tests; + size_t nb_asserts; + size_t tests_failed; + size_t tests_crashed; + size_t tests_passed; + size_t asserts_failed; + size_t asserts_passed; }; #endif /* !CRITERION_STATS_H_ */ diff --git a/src/event.c b/src/event.c index 8736d8d..7484f17 100644 --- a/src/event.c +++ b/src/event.c @@ -48,7 +48,7 @@ struct event *read_event(int fd) { if (kind != ASSERT) return unique_ptr(struct event, ({ .kind = kind, .data = NULL })); - const size_t assert_size = sizeof (struct criterion_assert_stat); + const size_t assert_size = sizeof (struct criterion_assert_stats); unsigned char *buf = malloc(assert_size); if (read(fd, buf, assert_size) < (ssize_t) assert_size) return NULL; @@ -60,5 +60,5 @@ void send_event(int kind, void *data, size_t size) { unsigned char buf[sizeof (int) + size]; memcpy(buf, &kind, sizeof (int)); memcpy(buf + sizeof (int), data, size); - write(EVENT_PIPE, buf, sizeof (buf)); + write(EVENT_PIPE, buf, sizeof (int) + size); } diff --git a/src/runner.c b/src/runner.c index e4f2895..211bf06 100644 --- a/src/runner.c +++ b/src/runner.c @@ -27,7 +27,7 @@ #include #include #include "criterion/assert.h" -#include "criterion/stats.h" +#include "stats.h" #include "runner.h" #include "report.h" #include "event.h" @@ -85,10 +85,10 @@ static struct test_set *read_all_tests(void) { }), destroy_test_set); } -static void map_tests(struct test_set *set, void (*fun)(struct criterion_test *)) { +static void map_tests(struct 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(*t); + fun(stats, *t); } __attribute__ ((always_inline)) @@ -115,7 +115,9 @@ static void setup_child(struct pipefds *fds) { close(fds->out); } -static void run_test(struct criterion_test *test) { +static void run_test(struct criterion_global_stats *stats, struct criterion_test *test) { + smart struct criterion_test_stats *test_stats = test_stats_init(test); + struct pipefds fds; if (pipe((int*) &fds) == -1) abort(); @@ -127,15 +129,15 @@ static void run_test(struct criterion_test *test) { run_test_child(test); _exit(0); } else { - struct criterion_test_stats stats = { .test = test }; close(fds.out); struct event *ev; while ((ev = read_event(fds.in)) != NULL) { + stat_push_event(stats, test_stats, ev); switch (ev->kind) { case PRE_INIT: report(PRE_INIT, test); break; case PRE_TEST: report(PRE_TEST, test); break; case ASSERT: report(PRE_TEST, ev->data); break; - case POST_TEST: report(POST_TEST, &stats); break; + case POST_TEST: report(POST_TEST, test_stats); break; case POST_FINI: report(POST_FINI, test); break; } sfree(ev); @@ -148,9 +150,10 @@ static void run_test(struct criterion_test *test) { void run_all(void) { report(PRE_EVERYTHING, NULL); smart struct test_set *set = read_all_tests(); + smart struct criterion_global_stats *stats = stats_init(); if (!set) abort(); - map_tests(set, run_test); + map_tests(set, stats, run_test); report(POST_EVERYTHING, NULL); } diff --git a/src/stats.c b/src/stats.c new file mode 100644 index 0000000..d83adcf --- /dev/null +++ b/src/stats.c @@ -0,0 +1,122 @@ +/* + * 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 "criterion/common.h" +#include "stats.h" + +#include + +static void nothing() {}; +static void push_pre_test(); +static void push_assert(); +static void push_post_test(); +static void push_test_crash(); + +typedef struct criterion_global_stats s_glob_stats; +typedef struct criterion_test_stats s_test_stats; +typedef struct criterion_assert_stats s_assert_stats; + +static void destroy_stats(void *ptr, UNUSED void *meta) { + s_glob_stats *stats = ptr; + sfree(stats->tests); +} + +s_glob_stats *stats_init(void) { + return unique_ptr(s_glob_stats, ({0}), destroy_stats); +} + +static void destroy_test_stats(void *ptr, UNUSED void *meta) { + s_test_stats *stats = ptr; + sfree(stats->asserts); + sfree(stats->next); +} + +s_test_stats *test_stats_init(struct criterion_test *t) { + return shared_ptr(s_test_stats, ({ .test = t }), destroy_test_stats); +} + +void stat_push_event(s_glob_stats *stats, + s_test_stats *test, + struct event *data) { + static void (*const handles[])(s_glob_stats *, s_test_stats *, void *) = { + nothing, // PRE_EVERYTHING + nothing, // PRE_INIT + push_pre_test, // PRE_TEST + push_assert, // ASSERT + push_test_crash, // TEST_CRASH + push_post_test, // POST_TEST + nothing, // POST_FINI + nothing, // POST_EVERYTHING + }; + + assert(data->kind > 0); + assert(data->kind <= (ssize_t) (sizeof (handles) / sizeof (void (*)(void)))); + + handles[data->kind](stats, test, data->data); +} + +static void push_pre_test(s_glob_stats *stats, + s_test_stats *test, + UNUSED void *ptr) { + test->next = stats->tests; + stats->tests = sref(test); + ++stats->nb_tests; +} + +static void destroy_assert(void *ptr, UNUSED void *meta) { + s_assert_stats *data = ptr; + sfree(data->next); +} + +static void push_assert(s_glob_stats *stats, + s_test_stats *test, + s_assert_stats *data) { + s_assert_stats *dup = unique_ptr(s_assert_stats, (*data), destroy_assert); + dup->next = test->asserts; + test->asserts = dup; + + if (data->passed) { + ++stats->asserts_passed; + ++test->passed; + } else { + ++stats->asserts_failed; + ++test->failed; + } +} + +static void push_post_test(s_glob_stats *stats, + s_test_stats *test, + UNUSED void *ptr) { + if (test->failed > 0) + ++stats->tests_failed; + else + ++stats->tests_passed; +} + +static void push_test_crash(s_glob_stats *stats, + s_test_stats *test, + UNUSED void *ptr) { + ++stats->tests_failed; + ++stats->tests_crashed; +} diff --git a/src/stats.h b/src/stats.h new file mode 100644 index 0000000..54a3118 --- /dev/null +++ b/src/stats.h @@ -0,0 +1,36 @@ +/* + * 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 STATS_H_ +# define STATS_H_ + +# include "criterion/stats.h" +# include "event.h" + +struct criterion_global_stats *stats_init(void); +struct criterion_test_stats *test_stats_init(struct criterion_test *t); +void stat_push_event(struct criterion_global_stats *stats, + struct criterion_test_stats *test, + struct event *data); + +#endif /* !STATS_H_ */