source: rtems/cpukit/libtest/t-test.c @ 1cbe577

Last change on this file since 1cbe577 was 78baeb7, checked in by Sebastian Huber <sebastian.huber@…>, on 11/11/20 at 16:09:58

libtest: Allow assert checks during test begin

Allow assert checks in test begin actions and setup fixture methods.

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