source: rtems/cpukit/libtest/t-test.c @ 426b6cd

Last change on this file since 426b6cd was 426b6cd, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 27, 2021 at 3:41:00 PM

libtest: Use dependency injection

This helps static analyzers.

  • Property mode set to 100644
File size: 26.5 KB
Line 
1/*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (C) 2018, 2020 embedded brains GmbH
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#define _GNU_SOURCE
29
30#include <rtems/test.h>
31
32#include <sys/queue.h>
33#include <limits.h>
34#include <pthread.h>
35#include <sched.h>
36#include <setjmp.h>
37#include <stdatomic.h>
38
39#ifdef __rtems__
40#include <rtems/score/io.h>
41#include <rtems/score/percpu.h>
42#include <rtems/score/smp.h>
43#include <rtems/score/threadimpl.h>
44#include <rtems/linkersets.h>
45#include <rtems/version.h>
46#else
47#include "t-test-printf.h"
48#endif /* __rtems__ */
49
50#define T_LINE_SIZE 128
51
52typedef struct {
53        pthread_spinlock_t lock;
54        char *buf;
55        unsigned int buf_mask;
56        atomic_uint buf_head;
57        atomic_uint buf_tail;
58        void (*putchar)(int, void *);
59        void *putchar_arg;
60        T_verbosity verbosity;
61        const T_case_context *registered_cases;
62        const T_case_context *current_case;
63        T_fixture_node *fixtures;
64        T_fixture_node case_fixture;
65        LIST_HEAD(, T_destructor) destructors;
66        T_time case_begin_time;
67        atomic_uint planned_steps;
68        atomic_uint steps;
69        atomic_uint failures;
70        bool case_begin_context_valid;
71        jmp_buf case_begin_context;
72        unsigned int fixture_steps;
73        unsigned int overall_cases;
74        unsigned int overall_steps;
75        unsigned int overall_failures;
76        T_time run_begin_time;
77#ifdef __rtems__
78        Thread_Control *runner_thread;
79        const Per_CPU_Control *runner_cpu;
80#else
81        bool runner_valid;
82        pthread_t runner_thread;
83#endif
84        const T_config *config;
85} T_context;
86
87static T_context T_instance;
88
89const T_check_context T_special = {
90        .file = "*",
91        .line = -1,
92        .flags = T_CHECK_FMT | T_CHECK_QUIET
93};
94
95static bool
96T_do_is_runner(T_context *ctx)
97{
98        bool is_runner;
99#ifdef __rtems__
100        ISR_Level level;
101        const Per_CPU_Control *cpu_self;
102#endif
103
104#ifdef __rtems__
105        _ISR_Local_disable(level);
106        cpu_self = _Per_CPU_Get();
107
108        if (ctx->runner_thread != NULL) {
109                is_runner = cpu_self->isr_nest_level == 0 &&
110                    _Per_CPU_Get_executing(cpu_self) == ctx->runner_thread;
111        } else {
112                is_runner = cpu_self == ctx->runner_cpu;
113        }
114
115        _ISR_Local_enable(level);
116#else
117        is_runner = ctx->runner_valid &&
118            pthread_equal(pthread_self(), ctx->runner_thread) != 0;
119#endif
120
121        return is_runner;
122}
123
124bool T_is_runner(void)
125{
126        return T_do_is_runner(&T_instance);
127}
128
129typedef struct {
130        char *s;
131        size_t n;
132} T_putchar_string_context;
133
134static void
135T_putchar_string(int c, void *arg)
136{
137        T_putchar_string_context *sctx;
138        char *s;
139        size_t n;
140
141        sctx = arg;
142        s = sctx->s;
143        n = sctx->n;
144
145        if (n == 1) {
146                c = '\0';
147        }
148
149        if (n > 1) {
150                *s = (char)c;
151                ++s;
152                --n;
153        }
154
155        sctx->s = s;
156        sctx->n = n;
157}
158
159int
160T_snprintf(char *s, size_t n, char const *fmt, ...)
161{
162        va_list ap;
163        int len;
164        T_putchar_string_context sctx = {
165                .s = s,
166                .n = n
167        };
168
169        va_start(ap, fmt);
170        len = _IO_Vprintf(T_putchar_string, &sctx, fmt, ap);
171        va_end(ap);
172
173        if (sctx.n > 0) {
174                *sctx.s = '\0';
175        }
176
177        return len;
178}
179
180static void
181T_output_buffer_drain(T_context *ctx)
182{
183        unsigned int head;
184        unsigned int tail;
185
186        head = atomic_load_explicit(&ctx->buf_head, memory_order_acquire);
187        tail = atomic_load_explicit(&ctx->buf_tail, memory_order_relaxed);
188
189        while (head != tail) {
190                (*ctx->putchar)(ctx->buf[tail], ctx->putchar_arg);
191                tail = (tail + 1) & ctx->buf_mask;
192        }
193
194        atomic_store_explicit(&ctx->buf_tail, tail, memory_order_relaxed);
195}
196
197static unsigned int
198T_output_buffer_fill(T_context *ctx, const char *buf, unsigned int len)
199{
200        unsigned int head;
201        unsigned int tail;
202        unsigned int mask;
203        unsigned int capacity;
204
205        pthread_spin_lock(&ctx->lock);
206        head = atomic_load_explicit(&ctx->buf_head, memory_order_relaxed);
207        tail = atomic_load_explicit(&ctx->buf_tail, memory_order_relaxed);
208        mask = ctx->buf_mask;
209        capacity = (tail - head - 1) & mask;
210
211        if (len <= capacity) {
212                unsigned int todo;
213                const char *c;
214
215                todo = len;
216                c = buf;
217
218                while (todo > 0) {
219                        ctx->buf[head] = *c;
220                        head = (head + 1) & mask;
221                        --todo;
222                        ++c;
223                }
224
225                atomic_store_explicit(&ctx->buf_head, head,
226                    memory_order_release);
227        } else {
228                /* If it does not fit into the buffer, discard everything */
229                len = 0;
230        }
231
232        pthread_spin_unlock(&ctx->lock);
233        return len;
234}
235
236static int
237T_vprintf_direct(char const *fmt, va_list ap)
238{
239        T_context *ctx;
240
241        ctx = &T_instance;
242        T_output_buffer_drain(ctx);
243        return _IO_Vprintf(ctx->putchar, ctx->putchar_arg, fmt, ap);
244}
245
246static int
247T_vprintf_buffered(char const *fmt, va_list ap)
248{
249        char buf[T_LINE_SIZE];
250        T_putchar_string_context sctx = {
251                .s = buf,
252                .n = sizeof(buf)
253        };
254        unsigned int len;
255
256        len = (unsigned int)_IO_Vprintf(T_putchar_string, &sctx, fmt, ap);
257
258        if (len >= sizeof(buf)) {
259                len = sizeof(buf) - 1;
260        }
261
262        return (int)T_output_buffer_fill(&T_instance, buf, len);
263}
264
265int
266T_vprintf(char const *fmt, va_list ap)
267{
268        int len;
269
270        if (T_is_runner()) {
271                len = T_vprintf_direct(fmt, ap);
272        } else {
273                len = T_vprintf_buffered(fmt, ap);
274        }
275
276        return len;
277}
278
279static int
280T_do_puts(T_context *ctx, const char *buf, size_t len)
281{
282        if (T_do_is_runner(ctx)) {
283                size_t i;
284
285                T_output_buffer_drain(ctx);
286
287                for (i = 0; i < len; ++i) {
288                        (*ctx->putchar)(buf[i], ctx->putchar_arg);
289                }
290        } else {
291                len = T_output_buffer_fill(ctx, buf, len);
292        }
293
294        return (int)len;
295}
296
297int
298T_puts(const char *buf, size_t len)
299{
300        return T_do_puts(&T_instance, buf, len);
301}
302
303static int
304T_cpu(void)
305{
306#if defined(__rtems__)
307        return (int)_SMP_Get_current_processor();
308#elif defined(__linux__)
309        return sched_getcpu();
310#else
311        return 0;
312#endif
313}
314
315size_t
316T_str_copy(char *dst, const char *src, size_t n)
317{
318        size_t i;
319
320        i = 0;
321
322        while (*src != '\0' && i < n) {
323                *dst = *src;
324                ++dst;
325                ++src;
326                ++i;
327        }
328
329        return i;
330}
331
332#if defined(__rtems__)
333static size_t
334T_object_name_to_string(char *dst, Objects_Name name, size_t n)
335{
336        uint32_t on;
337        size_t i;
338        int s;
339
340        on = name.name_u32;
341        i = 0;
342
343        for (s = 24; s >= 0; s -= 8) {
344                unsigned char c;
345
346                c = (unsigned char)(on >> s);
347
348                if (c >= '!' && c <= '~' && i < n) {
349                        *dst = (char)c;
350                        ++dst;
351                        ++i;
352                }
353        }
354
355        return i;
356}
357
358static size_t
359T_thread_name(char *dst, const Thread_Control *th, size_t n)
360{
361        if (th != NULL) {
362                const char *name;
363
364                name = th->Join_queue.Queue.name;
365
366                if (name != NULL && name[0] != '\0') {
367                        return T_str_copy(dst, name, n);
368                }
369
370                return T_object_name_to_string(dst, th->Object.name, n);
371        }
372
373        return T_str_copy(dst, "?", n);
374}
375#endif
376
377static size_t
378T_scope(T_context *ctx, char *dst, size_t n)
379{
380        T_fixture_node *node;
381        size_t len;
382
383#if defined(__rtems__)
384        ISR_Level level;
385        const Per_CPU_Control *cpu_self;
386
387        _ISR_Local_disable(level);
388        cpu_self = _Per_CPU_Get();
389
390        if (cpu_self->isr_nest_level == 0) {
391                Thread_Control *executing;
392
393                executing = _Per_CPU_Get_executing(cpu_self);
394                _ISR_Local_enable(level);
395                len = T_thread_name(dst, executing, n);
396        } else {
397                _ISR_Local_enable(level);
398                len = T_str_copy(dst, "ISR", n);
399        }
400
401#elif defined(__linux__)
402        static __thread char name[128];
403
404        if (name[0] == '\0') {
405                pthread_getname_np(pthread_self(), name, sizeof(name));
406        }
407
408        len = T_str_copy(dst, name, n);
409#else
410        len = T_str_copy(dst, "?", n);
411#endif
412
413        dst += len;
414        n -= len;
415        node = &ctx->case_fixture;
416
417        do {
418                const T_fixture *fixture;
419
420                fixture = node->fixture;
421
422                if (fixture != NULL && fixture->scope != NULL) {
423                        size_t m;
424
425                        m = (*fixture->scope)(node->context, dst, n);
426                        dst += m;
427                        n -= m;
428                        len += m;
429                }
430
431                node = node->previous;
432        } while (node != NULL);
433
434        return len;
435}
436
437static void
438T_do_make_runner(T_context *ctx)
439{
440#ifdef __rtems__
441        ISR_Level level;
442        const Per_CPU_Control *cpu_self;
443
444        _ISR_Local_disable(level);
445        cpu_self = _Per_CPU_Get();
446        ctx->runner_cpu = cpu_self;
447
448        if (cpu_self->isr_nest_level == 0) {
449                ctx->runner_thread = _Per_CPU_Get_executing(cpu_self);
450        } else {
451                ctx->runner_thread = NULL;
452        }
453
454        _ISR_Local_enable(level);
455#else
456        ctx->runner_valid = true;
457        ctx->runner_thread = pthread_self();
458#endif
459}
460
461void
462T_make_runner(void)
463{
464        T_do_make_runner(&T_instance);
465}
466
467int
468T_printf(char const *fmt, ...)
469{
470        va_list ap;
471        int len;
472
473        va_start(ap, fmt);
474        len = T_vprintf(fmt, ap);
475        va_end(ap);
476
477        return len;
478}
479
480void
481T_log(T_verbosity verbosity, char const *fmt, ...)
482{
483        T_context *ctx;
484
485        ctx = &T_instance;
486
487        if (ctx->verbosity >= verbosity) {
488                va_list ap;
489
490                T_printf("L:");
491                va_start(ap, fmt);
492                T_vprintf(fmt, ap);
493                va_end(ap);
494                T_printf("\n");
495        }
496}
497
498static unsigned int
499T_fetch_add_step(T_context *ctx)
500{
501        return atomic_fetch_add_explicit(&ctx->steps, 1, memory_order_relaxed);
502}
503
504static unsigned int
505T_add_failure(T_context *ctx)
506{
507        return atomic_fetch_add_explicit(&ctx->failures, 1,
508            memory_order_relaxed);
509}
510
511static void
512T_actions_forward(const T_config *config, T_event event, const char *name)
513{
514        const T_action *actions;
515        size_t n;
516        size_t i;
517
518        actions = config->actions;
519        n = config->action_count;
520
521        for (i = 0; i < n; ++i) {
522                (*actions[i])(event, name);
523        }
524}
525
526static void
527T_actions_backward(const T_config *config, T_event event,
528    const char *name)
529{
530        const T_action *actions;
531        size_t n;
532        size_t i;
533
534        actions = config->actions;
535        n = config->action_count;
536
537        for (i = 0; i < n; ++i) {
538                (*actions[n - i - 1])(event, name);
539        }
540}
541
542T_NO_RETURN static void
543T_do_stop(T_context *ctx)
544{
545        T_fixture_node *node;
546
547        node = ctx->fixtures;
548
549        while (node != NULL) {
550                const T_fixture *fixture;
551                void *node_context;
552
553                fixture = node->fixture;
554                node_context = node->context;
555                node = node->next;
556
557                if (fixture != NULL && fixture->stop != NULL) {
558                        (*fixture->stop)(node_context);
559                }
560        }
561
562        if (T_do_is_runner(ctx) && ctx->case_begin_context_valid) {
563                longjmp(ctx->case_begin_context, 1);
564        } else {
565                T_actions_backward(ctx->config, T_EVENT_CASE_STOP,
566                    T_case_name());
567#ifdef __GNUC__
568                __builtin_unreachable();
569#endif
570        }
571}
572
573T_NO_RETURN void
574T_stop(void)
575{
576        T_do_stop(&T_instance);
577}
578
579void T_plan(unsigned int planned_steps)
580{
581        T_context *ctx;
582        unsigned int expected;
583        bool success;
584
585        ctx = &T_instance;
586        expected = UINT_MAX;
587        success = atomic_compare_exchange_strong_explicit(&ctx->planned_steps,
588            &expected, planned_steps, memory_order_relaxed,
589            memory_order_relaxed);
590        T_check(&T_special, success, "planned steps (%u) already set",
591            expected);
592}
593
594const T_fixture T_empty_fixture;
595
596void
597T_push_plan(T_fixture_node *node, unsigned int planned_steps)
598{
599        T_push_fixture(node, &T_empty_fixture);
600        T_plan(planned_steps);
601}
602
603void
604T_pop_plan(void)
605{
606        T_pop_fixture();
607}
608
609void
610T_check_step(const T_check_context *t, unsigned int expected)
611{
612        T_check_context tt;
613
614        tt = *t;
615        tt.flags |= T_CHECK_STEP(expected);
616        T_check(&tt, true, "actual step is not equal to expected step (%u)",
617            expected);
618}
619
620void
621T_case_register(T_case_context *tc)
622{
623        T_context *ctx;
624
625        ctx = &T_instance;
626        tc->next = ctx->registered_cases;
627        ctx->registered_cases = tc;
628}
629
630T_verbosity
631T_set_verbosity(T_verbosity verbosity)
632{
633        T_context *ctx;
634        T_verbosity previous;
635
636        ctx = &T_instance;
637        previous = ctx->verbosity;
638        ctx->verbosity = verbosity;
639
640        return previous;
641}
642
643void *
644T_fixture_context(void)
645{
646        return T_instance.fixtures->context;
647}
648
649void
650T_set_fixture_context(void *context)
651{
652        T_instance.fixtures->context = context;
653}
654
655const char *
656T_case_name(void)
657{
658        const T_case_context *tc;
659
660        tc = T_instance.current_case;
661
662        if (tc != NULL) {
663                return tc->name;
664        } else {
665                return "?";
666        }
667}
668
669static const char *
670T_file(const T_check_context *t)
671{
672        const char *file;
673
674        file = strrchr(t->file, '/');
675
676        if (file == NULL) {
677                return t->file;
678        }
679
680        return file + 1;
681}
682
683static const char T_planned_step_fmt[] = "planned step (%u)";
684
685static void
686T_check_putc(int c, void *arg)
687{
688        T_putchar_string_context *sctx;
689        size_t n;
690
691        sctx = arg;
692        n = sctx->n;
693
694        if (n > 0) {
695                char *s;
696
697                s = sctx->s;
698                *s = (char)c;
699                sctx->s = s + 1;
700                sctx->n = n - 1;
701        }
702}
703
704static void
705T_check_print_steps(T_context *ctx, T_putchar_string_context *sctx,
706    unsigned int step)
707{
708        T_fixture_node *node;
709
710        node = &ctx->case_fixture;
711
712        while (true) {
713                node = node->previous;
714
715                if (node != NULL) {
716                        _IO_Printf(T_check_putc, sctx, "%u.",
717                            node->next_steps);
718                } else {
719                        break;
720                }
721        }
722
723        if (step != UINT_MAX) {
724                _IO_Printf(T_check_putc, sctx, "%u", step);
725        } else {
726                T_check_putc('*', sctx);
727        }
728}
729
730void
731T_check(const T_check_context *t, bool ok, ...)
732{
733        T_context *ctx;
734        va_list ap;
735        char line[T_LINE_SIZE];
736        unsigned int step;
737        int line_number;
738        const char *fmt;
739
740        ctx = &T_instance;
741
742        if ((t->flags & T_CHECK_QUIET) == 0) {
743                step = T_fetch_add_step(ctx);
744        } else {
745                step = UINT_MAX;
746        }
747
748        va_start(ap, ok);
749        line[0] = '\0';
750        line_number = -1;
751        fmt = NULL;
752
753        if ((t->flags & T_CHECK_STEP_FLAG) != 0 &&
754             step != T_CHECK_STEP_FROM_FLAGS(t->flags)) {
755                T_add_failure(ctx);
756                line[0] = 'F';
757                line_number = t->line;
758                fmt = T_planned_step_fmt;
759        } else if (!ok) {
760                T_add_failure(ctx);
761
762                if (ctx->verbosity >= T_NORMAL) {
763                        line[0] = 'F';
764                        line_number = t->line;
765
766                        if ((t->flags & T_CHECK_FMT) != 0) {
767                                fmt = va_arg(ap, const char *);
768                        }
769                }
770        } else if ((t->flags & T_CHECK_QUIET) == 0 &&
771            ctx->verbosity >= T_VERBOSE) {
772                line[0] = 'P';
773                line_number = t->line;
774        }
775
776        if (line[0] != '\0') {
777                T_putchar_string_context sctx;
778                size_t chunk;
779
780                sctx.n = sizeof(line) - 1;
781                sctx.s = &line[1];
782                T_check_putc(':', &sctx);
783                T_check_print_steps(ctx, &sctx, step);
784                _IO_Printf(T_check_putc, &sctx, ":%i:", T_cpu());
785                chunk = T_scope(ctx, sctx.s, sctx.n);
786                sctx.s += chunk;
787                sctx.n -= chunk;
788                T_check_putc(':', &sctx);
789                chunk = T_str_copy(sctx.s, T_file(t), sctx.n);
790                sctx.s += chunk;
791                sctx.n -= chunk;
792                T_check_putc(':', &sctx);
793
794                if (line_number >= 0) {
795                        _IO_Printf(T_check_putc, &sctx, "%i", line_number);
796                } else {
797                        T_check_putc('*', &sctx);
798                }
799
800                if (fmt != NULL) {
801                        if (fmt == T_planned_step_fmt) {
802                                T_check_putc(':', &sctx);
803                                _IO_Printf(T_check_putc, &sctx, fmt,
804                                    T_CHECK_STEP_FROM_FLAGS(t->flags));
805                        } else {
806                                T_check_putc(':', &sctx);
807                                _IO_Vprintf(T_check_putc, &sctx, fmt, ap);
808                        }
809                }
810
811                T_check_putc('\n', &sctx);
812                line[sizeof(line) - 1] = '\n';
813                T_puts(&line[0], sizeof(line) - sctx.n);
814        }
815
816        if (!ok && (t->flags & T_CHECK_STOP) != 0) {
817                T_do_stop(ctx);
818        }
819
820        va_end(ap);
821}
822
823static void
824T_do_log(T_context *ctx, T_verbosity verbosity, char const *fmt, ...)
825{
826        if (ctx->verbosity >= verbosity) {
827                va_list ap;
828
829                va_start(ap, fmt);
830                T_vprintf(fmt, ap);
831                va_end(ap);
832        }
833}
834
835static void
836T_system(T_context *ctx)
837{
838#if defined(__rtems__)
839        T_do_log(ctx, T_QUIET, "S:Platform:RTEMS\n");
840        T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
841        T_do_log(ctx, T_QUIET, "S:Version:%s\n", rtems_version());
842        T_do_log(ctx, T_QUIET, "S:BSP:%s\n", rtems_board_support_package());
843#if RTEMS_DEBUG
844        T_do_log(ctx, T_QUIET, "S:RTEMS_DEBUG:1\n");
845#else
846        T_do_log(ctx, T_QUIET, "S:RTEMS_DEBUG:0\n");
847#endif
848#if RTEMS_MULTIPROCESSING
849        T_do_log(ctx, T_QUIET, "S:RTEMS_MULTIPROCESSING:1\n");
850#else
851        T_do_log(ctx, T_QUIET, "S:RTEMS_MULTIPROCESSING:0\n");
852#endif
853#if RTEMS_POSIX_API
854        T_do_log(ctx, T_QUIET, "S:RTEMS_POSIX_API:1\n");
855#else
856        T_do_log(ctx, T_QUIET, "S:RTEMS_POSIX_API:0\n");
857#endif
858#if RTEMS_PROFILING
859        T_do_log(ctx, T_QUIET, "S:RTEMS_PROFILING:1\n");
860#else
861        T_do_log(ctx, T_QUIET, "S:RTEMS_PROFILING:0\n");
862#endif
863#if RTEMS_SMP
864        T_do_log(ctx, T_QUIET, "S:RTEMS_SMP:1\n");
865#else
866        T_do_log(ctx, T_QUIET, "S:RTEMS_SMP:0\n");
867#endif
868#elif defined(__linux__)
869        T_do_log(ctx, T_QUIET, "S:Platform:Linux\n");
870        T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
871#else
872        T_do_log(ctx, T_QUIET, "S:Platform:POSIX\n");
873#ifdef __VERSION__
874        T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
875#endif
876#endif
877}
878
879void
880T_add_destructor(T_destructor *dtor, void (*destroy)(T_destructor *))
881{
882        T_context *ctx;
883
884        dtor->destroy = destroy;
885        ctx = &T_instance;
886        pthread_spin_lock(&ctx->lock);
887        LIST_INSERT_HEAD(&ctx->destructors, dtor, node);
888        pthread_spin_unlock(&ctx->lock);
889}
890
891void
892T_remove_destructor(T_destructor *dtor)
893{
894        T_context *ctx;
895
896        ctx = &T_instance;
897        pthread_spin_lock(&ctx->lock);
898        LIST_REMOVE(dtor, node);
899        pthread_spin_unlock(&ctx->lock);
900}
901
902static void
903T_call_destructors(const T_context *ctx)
904{
905        T_destructor *dtor;
906
907#ifdef __linux__
908        while (!LIST_EMPTY(&ctx->destructors)) {
909                dtor = LIST_FIRST(&ctx->destructors);
910                LIST_REMOVE(dtor, node);
911                (*dtor->destroy)(dtor);
912        }
913#else
914        T_destructor *tmp;
915
916        LIST_FOREACH_SAFE(dtor, &ctx->destructors, node, tmp) {
917                (*dtor->destroy)(dtor);
918        }
919#endif
920}
921
922static T_context *
923T_do_run_initialize(const T_config *config)
924{
925        T_context *ctx;
926
927        ctx = &T_instance;
928
929        pthread_spin_init(&ctx->lock, PTHREAD_PROCESS_PRIVATE);
930
931        ctx->config = config;
932        ctx->buf = config->buf;
933
934        if (config->buf_size > 0 &&
935            (config->buf_size & (config->buf_size - 1)) == 0) {
936                ctx->buf_mask = config->buf_size - 1;
937        } else {
938                ctx->buf_mask = 0;
939        }
940
941        ctx->fixtures = &ctx->case_fixture;
942        atomic_store_explicit(&ctx->buf_head, 0, memory_order_relaxed);
943        ctx->buf_tail = 0;
944        ctx->putchar = config->putchar;
945        ctx->putchar_arg = config->putchar_arg;
946        ctx->verbosity = config->verbosity;
947        ctx->overall_cases = 0;
948        ctx->overall_steps = 0;
949        ctx->overall_failures = 0;
950
951        T_do_make_runner(ctx);
952        T_actions_forward(config, T_EVENT_RUN_INITIALIZE_EARLY, config->name);
953        T_do_log(ctx, T_QUIET, "A:%s\n", config->name);
954        T_system(ctx);
955        ctx->run_begin_time = (*config->now)();
956        T_actions_forward(config, T_EVENT_RUN_INITIALIZE_LATE, config->name);
957
958        return ctx;
959}
960
961static void
962T_do_case_begin(T_context *ctx, const T_case_context *tc)
963{
964        const T_config *config;
965        const T_fixture *fixture;
966
967        config = ctx->config;
968        fixture = tc->fixture;
969        ctx->verbosity = config->verbosity;
970        ctx->current_case = tc;
971        ctx->fixtures = &ctx->case_fixture;
972        LIST_INIT(&ctx->destructors);
973        atomic_store_explicit(&ctx->planned_steps, UINT_MAX,
974            memory_order_relaxed);
975        atomic_store_explicit(&ctx->steps, 0, memory_order_relaxed);
976        atomic_store_explicit(&ctx->failures, 0, memory_order_relaxed);
977        ctx->fixture_steps = 0;
978
979        T_actions_forward(config, T_EVENT_CASE_EARLY, tc->name);
980        T_do_log(ctx, T_NORMAL, "B:%s\n", tc->name);
981        ctx->case_begin_time = (*config->now)();
982        T_actions_forward(config, T_EVENT_CASE_BEGIN, tc->name);
983
984        if (fixture != NULL) {
985                ctx->case_fixture.fixture = fixture;
986                ctx->case_fixture.context = fixture->initial_context;
987
988                if (fixture->setup != NULL) {
989                        (*fixture->setup)(ctx->case_fixture.context);
990                }
991        }
992}
993
994static void
995T_check_steps(unsigned int planned_steps, unsigned int steps,
996    unsigned int failures)
997{
998        if (planned_steps != UINT_MAX && planned_steps != steps &&
999            failures == 0) {
1000                T_check(&T_special, false, "actual steps (%u), "
1001                    "planned steps (%u)", steps, planned_steps);
1002        }
1003}
1004
1005static void T_do_pop_fixture(T_context *);
1006
1007static void
1008T_do_case_end(T_context *ctx, const T_case_context *tc)
1009{
1010        const T_config *config;
1011        unsigned int planned_steps;
1012        unsigned int steps;
1013        unsigned int failures;
1014        T_time delta;
1015        T_time_string ts;
1016
1017        while (ctx->fixtures != NULL) {
1018                T_do_pop_fixture(ctx);
1019        }
1020
1021        T_call_destructors(ctx);
1022        config = ctx->config;
1023        T_actions_backward(config, T_EVENT_CASE_END, tc->name);
1024
1025        planned_steps = atomic_fetch_add_explicit(&ctx->planned_steps,
1026            0, memory_order_relaxed);
1027        steps = atomic_fetch_add_explicit(&ctx->steps, 0,
1028            memory_order_relaxed);
1029        failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1030            memory_order_relaxed);
1031        T_check_steps(planned_steps, steps, failures);
1032
1033        failures = atomic_load_explicit(&ctx->failures, memory_order_relaxed);
1034        delta = (*config->now)() - ctx->case_begin_time;
1035        steps += ctx->fixture_steps;
1036        T_do_log(ctx, T_QUIET, "E:%s:N:%u:F:%u:D:%s\n",
1037            tc->name, steps, failures, T_time_to_string_us(delta, ts));
1038
1039        ++ctx->overall_cases;
1040        ctx->overall_steps += steps;
1041        ctx->overall_failures += failures;
1042
1043        T_actions_backward(config, T_EVENT_CASE_LATE, tc->name);
1044}
1045
1046static void
1047T_run_case(T_context *ctx, const T_case_context *tc)
1048{
1049        if (setjmp(ctx->case_begin_context) == 0) {
1050                ctx->case_begin_context_valid = true;
1051                T_do_case_begin(ctx, tc);
1052                (*tc->body)();
1053        }
1054
1055        ctx->case_begin_context_valid = false;
1056        T_do_case_end(ctx, tc);
1057}
1058
1059static void
1060T_do_run_all(T_context *ctx)
1061{
1062        const T_case_context *tc;
1063
1064        tc = ctx->registered_cases;
1065
1066        while (tc != NULL) {
1067                T_run_case(ctx, tc);
1068                tc = tc->next;
1069        }
1070}
1071
1072static bool
1073T_do_run_finalize(T_context *ctx)
1074{
1075        const T_config *config;
1076        T_time delta;
1077        T_time_string ts;
1078
1079        config = ctx->config;
1080        T_actions_backward(config, T_EVENT_RUN_FINALIZE_EARLY, config->name);
1081        delta = (*config->now)() - ctx->run_begin_time;
1082        T_do_log(ctx, T_QUIET, "Z:%s:C:%u:N:%u:F:%u:D:%s\n", config->name,
1083            ctx->overall_cases, ctx->overall_steps, ctx->overall_failures,
1084            T_time_to_string_us(delta, ts));
1085        T_actions_backward(config, T_EVENT_RUN_FINALIZE_LATE, config->name);
1086#ifdef __rtems__
1087        ctx->runner_thread = NULL;
1088        ctx->runner_cpu = NULL;
1089#else
1090        ctx->runner_valid = false;
1091#endif
1092        pthread_spin_destroy(&ctx->lock);
1093
1094        return ctx->overall_failures == 0;
1095}
1096
1097int
1098T_main(const T_config *config)
1099{
1100        T_context *ctx;
1101
1102        ctx = T_do_run_initialize(config);
1103        T_do_run_all(ctx);
1104
1105        return T_do_run_finalize(ctx) ? 0 : 1;
1106}
1107
1108#ifdef __rtems__
1109RTEMS_LINKER_ROSET(_T, T_case_context *);
1110#endif /* __rtems__ */
1111
1112void T_register(void)
1113{
1114#ifdef __rtems__
1115        T_case_context * const *tc;
1116
1117        RTEMS_LINKER_SET_FOREACH(_T, tc) {
1118                T_case_register(*tc);
1119        }
1120#endif /* __rtems__ */
1121}
1122
1123void
1124T_run_initialize(const T_config *config)
1125{
1126        (void)T_do_run_initialize(config);
1127}
1128
1129void
1130T_run_all(void)
1131{
1132        T_do_run_all(&T_instance);
1133}
1134
1135void
1136T_run_by_name(const char *name)
1137{
1138        T_context *ctx;
1139        const T_case_context *tc;
1140
1141        ctx = &T_instance;
1142        tc = ctx->registered_cases;
1143
1144        while (tc != NULL) {
1145                if (strcmp(tc->name, name) == 0) {
1146                        T_run_case(ctx, tc);
1147                        break;
1148                }
1149
1150                tc = tc->next;
1151        }
1152}
1153
1154static T_case_context default_case;
1155
1156void
1157T_case_begin(const char *name, const T_fixture *fixture)
1158{
1159        T_case_context *tc;
1160
1161        tc = &default_case;
1162        tc->name = name;
1163        tc->fixture = fixture;
1164        T_do_case_begin(&T_instance, tc);
1165}
1166
1167void
1168T_case_end(void)
1169{
1170        T_case_context *tc;
1171
1172        tc = &default_case;
1173        T_do_case_end(&T_instance, tc);
1174}
1175
1176bool
1177T_run_finalize(void)
1178{
1179        return T_do_run_finalize(&T_instance);
1180}
1181
1182T_time
1183T_case_begin_time(void)
1184{
1185        return T_instance.case_begin_time;
1186}
1187
1188void
1189T_set_putchar(T_putchar new_putchar, void *new_arg, T_putchar *old_putchar,
1190    void **old_arg)
1191{
1192        T_context *ctx;
1193
1194        ctx = &T_instance;
1195        *old_putchar = ctx->putchar;
1196        *old_arg = ctx->putchar_arg;
1197        ctx->putchar = new_putchar;
1198        ctx->putchar_arg = new_arg;
1199}
1200
1201T_time
1202T_now(void)
1203{
1204        T_context *ctx;
1205        const T_config *config;
1206
1207        ctx = &T_instance;
1208        config = ctx->config;
1209        return (*config->now)();
1210}
1211
1212void *
1213T_push_fixture(T_fixture_node *node, const T_fixture *fixture)
1214{
1215        T_context *ctx;
1216        T_fixture_node *old;
1217        void *context;
1218
1219        ctx = &T_instance;
1220        old = ctx->fixtures;
1221        old->previous = node;
1222        node->next = old;
1223        node->previous = NULL;
1224        node->fixture = fixture;
1225        context = fixture->initial_context;
1226        node->context = context;
1227        node->next_planned_steps = atomic_exchange_explicit(
1228            &ctx->planned_steps, UINT_MAX, memory_order_relaxed);
1229        node->next_steps = atomic_exchange_explicit(&ctx->steps, 0,
1230            memory_order_relaxed);
1231        node->failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1232            memory_order_relaxed);
1233        ctx->fixtures = node;
1234
1235        if (fixture != NULL && fixture->setup != NULL) {
1236                (*fixture->setup)(context);
1237        }
1238
1239        return context;
1240}
1241
1242static void
1243T_do_pop_fixture(T_context *ctx)
1244{
1245        T_fixture_node *node;
1246        const T_fixture *fixture;
1247        T_fixture_node *next;
1248
1249        node = ctx->fixtures;
1250        next = node->next;
1251        ctx->fixtures = next;
1252        fixture = node->fixture;
1253
1254        if (fixture != NULL && fixture->teardown != NULL) {
1255                (*fixture->teardown)(node->context);
1256        }
1257
1258        if (next != NULL) {
1259                unsigned int planned_steps;
1260                unsigned int steps;
1261                unsigned int failures;
1262
1263                next->previous = NULL;
1264                planned_steps = atomic_exchange_explicit(&ctx->planned_steps,
1265                    node->next_planned_steps, memory_order_relaxed);
1266                steps = atomic_exchange_explicit(&ctx->steps, node->next_steps,
1267                    memory_order_relaxed);
1268                failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1269                    memory_order_relaxed);
1270                ctx->fixture_steps += steps;
1271                T_check_steps(planned_steps, steps, node->failures - failures);
1272        }
1273
1274        memset(node, 0, sizeof(*node));
1275}
1276
1277void
1278T_pop_fixture(void)
1279{
1280        T_do_pop_fixture(&T_instance);
1281}
1282
1283size_t
1284T_get_scope(const char * const * const *desc, char *buf, size_t n,
1285    const size_t *second_indices)
1286{
1287        size_t c;
1288        size_t i;
1289
1290        c = n;
1291        i = 0;
1292
1293        while (true) {
1294                const char * const *desc2;
1295                size_t m;
1296
1297                desc2 = desc[i];
1298
1299                if (desc2 == NULL) {
1300                        break;
1301                }
1302
1303                if (c > 1) {
1304                        buf[0] = '/';
1305                        --c;
1306                        ++buf;
1307                } else {
1308                        break;
1309                }
1310
1311                m = T_str_copy(buf, desc2[second_indices[i]], c);
1312                buf += m;
1313                c -= m;
1314                ++i;
1315        }
1316
1317        return n - c;
1318}
1319
1320#ifdef __BIGGEST_ALIGNMENT__
1321#define T_BIGGEST_ALIGNMENT __BIGGEST_ALIGNMENT__
1322#else
1323#define T_BIGGEST_ALIGNMENT sizeof(long long)
1324#endif
1325
1326typedef struct __attribute__((__aligned__(T_BIGGEST_ALIGNMENT))) {
1327        T_destructor base;
1328        void (*destroy)(void *);
1329} T_malloc_destructor;
1330
1331static void
1332T_malloc_destroy(T_destructor *base)
1333{
1334        T_malloc_destructor *dtor;
1335
1336        dtor = (T_malloc_destructor *)(uintptr_t)base;
1337
1338        if (dtor->destroy != NULL) {
1339                (*dtor->destroy)(dtor + 1);
1340        }
1341
1342        (*T_instance.config->deallocate)(dtor);
1343}
1344
1345static void *
1346T_do_malloc(size_t size, void (*destroy)(void *))
1347{
1348        T_malloc_destructor *dtor;
1349        size_t new_size;
1350
1351        new_size = sizeof(*dtor) + size;
1352        if (new_size <= size) {
1353                return NULL;
1354        }
1355
1356        if (T_instance.config->allocate == NULL) {
1357                return NULL;
1358        }
1359
1360        dtor = (*T_instance.config->allocate)(new_size);
1361        if (dtor != NULL) {
1362                dtor->destroy = destroy;
1363                T_add_destructor(&dtor->base, T_malloc_destroy);
1364                ++dtor;
1365        }
1366
1367        return dtor;
1368}
1369
1370void *
1371T_malloc(size_t size)
1372{
1373        return T_do_malloc(size, NULL);
1374}
1375
1376void *
1377T_calloc(size_t nelem, size_t elsize)
1378{
1379        return T_zalloc(nelem * elsize, NULL);
1380}
1381
1382void *
1383T_zalloc(size_t size, void (*destroy)(void *))
1384{
1385        void *p;
1386
1387        p = T_do_malloc(size, destroy);
1388        if (p != NULL) {
1389                p = memset(p, 0, size);
1390        }
1391
1392        return p;
1393}
1394
1395void
1396T_free(void *ptr)
1397{
1398        T_malloc_destructor *dtor;
1399
1400        dtor = ptr;
1401        --dtor;
1402        T_remove_destructor(&dtor->base);
1403        (*T_instance.config->deallocate)(dtor);
1404}
Note: See TracBrowser for help on using the repository browser.