source: rtems/cpukit/libtest/t-test.c

Last change on this file was d586b3c, checked in by Sebastian Huber <sebastian.huber@…>, on 11/22/23 at 13:57:32

libtest: Add T_add_remark()

This can be used to report that nested test cases did run in a test
case.

Update #4971.

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