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

5
Last change on this file since b406d071 was b406d071, checked in by Sebastian Huber <sebastian.huber@…>, on 10/01/19 at 12:16:34

libtest: Do all output in test runner

This ensures that lines are output atomically if they are produced by
different other contexts, e.g. interrupts, other processors, other
threads.

Update #3199.

  • Property mode set to 100644
File size: 19.1 KB
Line 
1/*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (C) 2018, 2019 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 <t.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
52#define T_SCOPE_SIZE 5
53
54typedef struct {
55        pthread_spinlock_t lock;
56        char *buf;
57        unsigned int buf_mask;
58        atomic_uint buf_head;
59        atomic_uint buf_tail;
60        void (*putchar)(int, void *);
61        void *putchar_arg;
62        T_verbosity verbosity;
63        const T_case_context *registered_cases;
64        const T_case_context *current_case;
65        void *fixture_context;
66        LIST_HEAD(, T_destructor) destructors;
67        T_time case_begin_time;
68        atomic_uint planned_steps;
69        atomic_uint steps;
70        atomic_uint failures;
71        jmp_buf case_begin_context;
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
88typedef struct {
89        char *s;
90        size_t n;
91} T_putchar_string_context;
92
93static void
94T_putchar_string(int c, void *arg)
95{
96        T_putchar_string_context *sctx;
97        char *s;
98        size_t n;
99
100        sctx = arg;
101        s = sctx->s;
102        n = sctx->n;
103
104        if (n == 1) {
105                c = '\0';
106        }
107
108        if (n > 1) {
109                *s = (char)c;
110                ++s;
111                --n;
112        }
113
114        sctx->s = s;
115        sctx->n = n;
116}
117
118int
119T_snprintf(char *s, size_t n, char const *fmt, ...)
120{
121        va_list ap;
122        int len;
123        T_putchar_string_context sctx = {
124                .s = s,
125                .n = n
126        };
127
128        va_start(ap, fmt);
129        len = _IO_Vprintf(T_putchar_string, &sctx, fmt, ap);
130        va_end(ap);
131
132        if (sctx.n > 0) {
133                *sctx.s = '\0';
134        }
135
136        return len;
137}
138
139static int
140T_vprintf_direct(char const *fmt, va_list ap)
141{
142        T_context *ctx;
143        unsigned int head;
144        unsigned int tail;
145
146        ctx = &T_instance;
147
148        head = atomic_load_explicit(&ctx->buf_head, memory_order_acquire);
149        tail = atomic_load_explicit(&ctx->buf_tail, memory_order_relaxed);
150
151        while (head != tail) {
152                (*ctx->putchar)(ctx->buf[tail], ctx->putchar_arg);
153                tail = (tail + 1) & ctx->buf_mask;
154        }
155
156        atomic_store_explicit(&ctx->buf_tail, tail, memory_order_relaxed);
157
158        return _IO_Vprintf(ctx->putchar, ctx->putchar_arg, fmt, ap);
159}
160
161static int
162T_vprintf_buffered(char const *fmt, va_list ap)
163{
164        unsigned int len;
165        T_context *ctx;
166        char buf[T_LINE_SIZE];
167        T_putchar_string_context sctx = {
168                .s = buf,
169                .n = sizeof(buf)
170        };
171        unsigned int head;
172        unsigned int tail;
173        unsigned int mask;
174        unsigned int capacity;
175
176        len = (unsigned int)_IO_Vprintf(T_putchar_string, &sctx, fmt, ap);
177
178        if (len >= sizeof(buf)) {
179                len = sizeof(buf) - 1;
180        }
181
182        ctx = &T_instance;
183        pthread_spin_lock(&ctx->lock);
184        head = atomic_load_explicit(&ctx->buf_head, memory_order_relaxed);
185        tail = atomic_load_explicit(&ctx->buf_tail, memory_order_relaxed);
186        mask = ctx->buf_mask;
187        capacity = (tail - head - 1) & mask;
188
189        if (len <= capacity) {
190                unsigned int todo;
191                char *c;
192
193                todo = len;
194                c = buf;
195
196                while (todo > 0) {
197                        ctx->buf[head] = *c;
198                        head = (head + 1) & mask;
199                        --todo;
200                        ++c;
201                }
202
203                atomic_store_explicit(&ctx->buf_head, head,
204                    memory_order_release);
205        } else {
206                /* If it does not fit into the buffer, discard everything */
207                len = 0;
208        }
209
210        pthread_spin_unlock(&ctx->lock);
211        return (int)len;
212}
213
214int
215T_vprintf(char const *fmt, va_list ap)
216{
217        int len;
218
219        if (T_is_runner()) {
220                len = T_vprintf_direct(fmt, ap);
221        } else {
222                len = T_vprintf_buffered(fmt, ap);
223        }
224
225        return len;
226}
227
228static int
229T_cpu(void)
230{
231#if defined(__rtems__)
232        return (int)_SMP_Get_current_processor();
233#elif defined(__linux__)
234        return sched_getcpu();
235#else
236        return 0;
237#endif
238}
239
240#if defined(__rtems__)
241static const char *
242T_object_name_to_string(Objects_Name name, char *buf)
243{
244        uint32_t on;
245        size_t i;
246        int s;
247
248        on = name.name_u32;
249        i = 0;
250
251        for (s = 24; s >= 0; s -= 8) {
252                unsigned char c;
253
254                c = (unsigned char)(on >> s);
255
256                if (c >= '!' && c <= '~') {
257                        buf[i] = (char)c;
258                        ++i;
259                }
260        }
261
262        buf[i] = '\0';
263        return buf;
264}
265
266static const char *
267T_thread_name(const Thread_Control *th, char *buf)
268{
269        if (th != NULL) {
270                const char *name;
271
272                name = th->Join_queue.Queue.name;
273
274                if (name != NULL && name[0] != '\0') {
275                        return name;
276                } else {
277                        return T_object_name_to_string(th->Object.name, buf);
278                }
279        } else {
280                buf[0] = '?';
281                buf[1] = '\0';
282                return buf;
283        }
284}
285#endif
286
287static const char *
288T_scope(char *buf)
289{
290        const char *r;
291
292#if defined(__rtems__)
293        ISR_Level level;
294        const Per_CPU_Control *cpu_self;
295
296        _ISR_Local_disable(level);
297        cpu_self = _Per_CPU_Get();
298
299        if (cpu_self->isr_nest_level == 0) {
300                Thread_Control *executing;
301
302                executing = _Per_CPU_Get_executing(cpu_self);
303                _ISR_Local_enable(level);
304                r = T_thread_name(executing, buf);
305        } else {
306                _ISR_Local_enable(level);
307                buf[0] = 'I';
308                buf[1] = 'S';
309                buf[2] = 'R';
310                buf[3] = '\0';
311                r = buf;
312        }
313#elif defined(__linux__)
314        static __thread char name[128];
315
316        (void)buf;
317
318        if (name[0] == '\0') {
319                pthread_getname_np(pthread_self(), name, sizeof(name));
320        }
321
322        r = &name[0];
323#else
324        buf[0] = '?';
325        buf[1] = '\0';
326        r = buf;
327#endif
328
329        return r;
330}
331
332static void
333T_set_runner(T_context *ctx)
334{
335#ifdef __rtems__
336        ISR_Level level;
337        const Per_CPU_Control *cpu_self;
338
339        _ISR_Local_disable(level);
340        cpu_self = _Per_CPU_Get();
341        ctx->runner_cpu = cpu_self;
342
343        if (cpu_self->isr_nest_level == 0) {
344                ctx->runner_thread = _Per_CPU_Get_executing(cpu_self);
345        } else {
346                ctx->runner_thread = NULL;
347        }
348
349        _ISR_Local_enable(level);
350#else
351        ctx->runner_valid = true;
352        ctx->runner_thread = pthread_self();
353#endif
354}
355
356int
357T_printf(char const *fmt, ...)
358{
359        va_list ap;
360        int len;
361
362        va_start(ap, fmt);
363        len = T_vprintf(fmt, ap);
364        va_end(ap);
365
366        return len;
367}
368
369void
370T_log(T_verbosity verbosity, char const *fmt, ...)
371{
372        T_context *ctx;
373
374        ctx = &T_instance;
375
376        if (ctx->verbosity >= verbosity) {
377                va_list ap;
378
379                T_printf("L:");
380                va_start(ap, fmt);
381                T_vprintf(fmt, ap);
382                va_end(ap);
383                T_printf("\n");
384        }
385}
386
387static unsigned int
388T_fetch_add_step(T_context *ctx)
389{
390        return atomic_fetch_add_explicit(&ctx->steps, 1, memory_order_relaxed);
391}
392
393static unsigned int
394T_add_failure(T_context *ctx)
395{
396        return atomic_fetch_add_explicit(&ctx->failures, 1,
397            memory_order_relaxed);
398}
399
400static void
401T_stop(T_context *ctx)
402{
403        const T_case_context *tc;
404
405        tc = ctx->current_case;
406
407        if (tc != NULL) {
408                const T_fixture *fixture;
409
410                fixture = tc->fixture;
411
412                if (fixture != NULL && fixture->stop != NULL) {
413                        (*fixture->stop)(ctx->fixture_context);
414                }
415        }
416
417        longjmp(ctx->case_begin_context, 1);
418}
419
420void T_plan(unsigned int planned_steps)
421{
422        T_context *ctx;
423        unsigned int expected;
424        bool success;
425
426        ctx = &T_instance;
427        expected = UINT_MAX;
428        success = atomic_compare_exchange_strong_explicit(&ctx->planned_steps,
429            &expected, planned_steps, memory_order_relaxed,
430            memory_order_relaxed);
431        T_check_true(success, NULL, "planned steps (%u) already set", expected);
432}
433
434void
435T_case_register(T_case_context *tc)
436{
437        T_context *ctx;
438
439        ctx = &T_instance;
440        tc->next = ctx->registered_cases;
441        ctx->registered_cases = tc;
442}
443
444T_verbosity
445T_set_verbosity(T_verbosity verbosity)
446{
447        T_context *ctx;
448        T_verbosity previous;
449
450        ctx = &T_instance;
451        previous = ctx->verbosity;
452        ctx->verbosity = verbosity;
453
454        return previous;
455}
456
457void *
458T_fixture_context(void)
459{
460        return T_instance.fixture_context;
461}
462
463void
464T_set_fixture_context(void *context)
465{
466        T_instance.fixture_context = context;
467}
468
469const char *
470T_case_name(void)
471{
472        const T_case_context *tc;
473
474        tc = T_instance.current_case;
475
476        if (tc != NULL) {
477                return tc->name;
478        } else {
479                return "?";
480        }
481}
482
483void
484T_check_true(bool ok, const T_check_context *t, const char *fmt, ...)
485{
486        T_context *ctx;
487        va_list ap;
488        char scope[T_SCOPE_SIZE];
489
490        ctx = &T_instance;
491
492        if (t != NULL) {
493                unsigned int step;
494
495                if ((t->flags & T_CHECK_QUIET) == 0) {
496                        step = T_fetch_add_step(ctx);
497                } else {
498                        step = UINT_MAX;
499                }
500
501                if ((t->flags & T_CHECK_STEP_FLAG) != 0 &&
502                     step != T_CHECK_STEP_FROM_FLAGS(t->flags)) {
503                        T_add_failure(ctx);
504                        T_printf("F:%u:%i:%s:%s:%i:planned step (%u)\n", step,
505                            T_cpu(), T_scope(scope), t->file, t->line,
506                            T_CHECK_STEP_FROM_FLAGS(t->flags));
507                } else if (!ok) {
508                        T_add_failure(ctx);
509
510                        if (ctx->verbosity >= T_NORMAL) {
511                                if ((t->flags & T_CHECK_QUIET) == 0) {
512                                        T_printf("F:%u:%i:%s:%s:%i:",
513                                            step, T_cpu(), T_scope(scope),
514                                            t->file, t->line);
515                                } else {
516                                        T_printf("F:*:%i:%s:%s:%i:", T_cpu(),
517                                            T_scope(scope), t->file, t->line);
518                                }
519
520                                va_start(ap, fmt);
521                                T_vprintf(fmt, ap);
522                                va_end(ap);
523
524                                T_printf("\n");
525                        }
526
527                        if ((t->flags & T_CHECK_STOP) != 0) {
528                                T_stop(ctx);
529                        }
530                } else if ((t->flags & T_CHECK_QUIET) == 0 &&
531                    ctx->verbosity >= T_VERBOSE) {
532                        T_printf("P:%u:%i:%s:%s:%i\n", step, T_cpu(),
533                            T_scope(scope), t->file, t->line);
534                }
535        } else if (!ok) {
536                T_add_failure(ctx);
537
538                T_printf("F:*:%i:%s:*:*:", T_cpu(), T_scope(scope));
539
540                va_start(ap, fmt);
541                T_vprintf(fmt, ap);
542                va_end(ap);
543
544                T_printf("\n");
545        }
546}
547
548static void
549T_do_log(T_context *ctx, T_verbosity verbosity, char const *fmt, ...)
550{
551        if (ctx->verbosity >= verbosity) {
552                va_list ap;
553
554                va_start(ap, fmt);
555                T_vprintf(fmt, ap);
556                va_end(ap);
557        }
558}
559
560static void
561T_system(T_context *ctx)
562{
563#if defined(__rtems__)
564        T_do_log(ctx, T_QUIET, "S:Platform:RTEMS\n");
565        T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
566        T_do_log(ctx, T_QUIET, "S:Version:%s\n", rtems_version());
567        T_do_log(ctx, T_QUIET, "S:BSP:%s\n", rtems_board_support_package());
568#if RTEMS_DEBUG
569        T_do_log(ctx, T_QUIET, "S:RTEMS_DEBUG:1\n");
570#else
571        T_do_log(ctx, T_QUIET, "S:RTEMS_DEBUG:0\n");
572#endif
573#if RTEMS_MULTIPROCESSING
574        T_do_log(ctx, T_QUIET, "S:RTEMS_MULTIPROCESSING:1\n");
575#else
576        T_do_log(ctx, T_QUIET, "S:RTEMS_MULTIPROCESSING:0\n");
577#endif
578#if RTEMS_POSIX_API
579        T_do_log(ctx, T_QUIET, "S:RTEMS_POSIX_API:1\n");
580#else
581        T_do_log(ctx, T_QUIET, "S:RTEMS_POSIX_API:0\n");
582#endif
583#if RTEMS_PROFILING
584        T_do_log(ctx, T_QUIET, "S:RTEMS_PROFILING:1\n");
585#else
586        T_do_log(ctx, T_QUIET, "S:RTEMS_PROFILING:0\n");
587#endif
588#if RTEMS_SMP
589        T_do_log(ctx, T_QUIET, "S:RTEMS_SMP:1\n");
590#else
591        T_do_log(ctx, T_QUIET, "S:RTEMS_SMP:0\n");
592#endif
593#elif defined(__linux__)
594        T_do_log(ctx, T_QUIET, "S:Platform:Linux\n");
595        T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
596#else
597        T_do_log(ctx, T_QUIET, "S:Platform:POSIX\n");
598#ifdef __VERSION__
599        T_do_log(ctx, T_QUIET, "S:Compiler:" __VERSION__ "\n");
600#endif
601#endif
602}
603
604void
605T_add_destructor(T_destructor *dtor, void (*destroy)(T_destructor *))
606{
607        T_context *ctx;
608
609        dtor->destroy = destroy;
610        ctx = &T_instance;
611        pthread_spin_lock(&ctx->lock);
612        LIST_INSERT_HEAD(&ctx->destructors, dtor, node);
613        pthread_spin_unlock(&ctx->lock);
614}
615
616void
617T_remove_destructor(T_destructor *dtor)
618{
619        T_context *ctx;
620
621        ctx = &T_instance;
622        pthread_spin_lock(&ctx->lock);
623        LIST_REMOVE(dtor, node);
624        pthread_spin_unlock(&ctx->lock);
625}
626
627static void
628T_call_destructors(const T_context *ctx)
629{
630        T_destructor *dtor;
631
632#ifdef __linux__
633        while (!LIST_EMPTY(&ctx->destructors)) {
634                dtor = LIST_FIRST(&ctx->destructors);
635                LIST_REMOVE(dtor, node);
636                (*dtor->destroy)(dtor);
637        }
638#else
639        T_destructor *tmp;
640
641        LIST_FOREACH_SAFE(dtor, &ctx->destructors, node, tmp) {
642                (*dtor->destroy)(dtor);
643        }
644#endif
645}
646
647static void
648T_call_actions_forward(const T_config *config, T_event event, const char *name)
649{
650        const T_action *actions;
651        size_t n;
652        size_t i;
653
654        actions = config->actions;
655        n = config->action_count;
656
657        for (i = 0; i < n; ++i) {
658                (*actions[i])(event, name);
659        }
660}
661
662static void
663T_call_actions_backward(const T_config *config, T_event event,
664    const char *name)
665{
666        const T_action *actions;
667        size_t n;
668        size_t i;
669
670        actions = config->actions;
671        n = config->action_count;
672
673        for (i = 0; i < n; ++i) {
674                (*actions[n - i - 1])(event, name);
675        }
676}
677
678static T_context *
679T_do_run_initialize(const T_config *config)
680{
681        T_context *ctx;
682
683        ctx = &T_instance;
684
685        pthread_spin_init(&ctx->lock, PTHREAD_PROCESS_PRIVATE);
686
687        ctx->config = config;
688        ctx->buf = config->buf;
689
690        if (config->buf_size > 0 &&
691            (config->buf_size & (config->buf_size - 1)) == 0) {
692                ctx->buf_mask = config->buf_size - 1;
693        } else {
694                ctx->buf_mask = 0;
695        }
696
697        atomic_store_explicit(&ctx->buf_head, 0, memory_order_relaxed);
698        ctx->buf_tail = 0;
699        ctx->putchar = config->putchar;
700        ctx->putchar_arg = config->putchar_arg;
701        ctx->verbosity = config->verbosity;
702        atomic_store_explicit(&ctx->steps, 0, memory_order_relaxed);
703        atomic_store_explicit(&ctx->failures, 0, memory_order_relaxed);
704        ctx->overall_cases = 0;
705        ctx->overall_steps = 0;
706        ctx->overall_failures = 0;
707
708        T_set_runner(ctx);
709        T_call_actions_forward(config, T_EVENT_RUN_INITIALIZE, config->name);
710        T_do_log(ctx, T_QUIET, "A:%s\n", config->name);
711        T_system(ctx);
712        ctx->run_begin_time = (*config->now)();
713
714        return ctx;
715}
716
717static void
718T_do_case_begin(T_context *ctx, const T_case_context *tc)
719{
720        const T_config *config;
721        const T_fixture *fixture;
722
723        config = ctx->config;
724        fixture = tc->fixture;
725        ctx->verbosity = config->verbosity;
726        ctx->current_case = tc;
727        LIST_INIT(&ctx->destructors);
728        atomic_store_explicit(&ctx->planned_steps, UINT_MAX,
729            memory_order_relaxed);
730        atomic_store_explicit(&ctx->steps, 0, memory_order_relaxed);
731        atomic_store_explicit(&ctx->failures, 0, memory_order_relaxed);
732
733        T_call_actions_forward(config, T_EVENT_CASE_EARLY, tc->name);
734        T_do_log(ctx, T_NORMAL, "B:%s\n", tc->name);
735        ctx->case_begin_time = (*config->now)();
736        T_call_actions_forward(config, T_EVENT_CASE_BEGIN, tc->name);
737
738        if (fixture != NULL) {
739                ctx->fixture_context = fixture->initial_context;
740
741                if (fixture->setup != NULL) {
742                        (*fixture->setup)(ctx->fixture_context);
743                }
744        }
745}
746
747static void
748T_do_case_end(T_context *ctx, const T_case_context *tc)
749{
750        const T_config *config;
751        const T_fixture *fixture;
752        unsigned int planned_steps;
753        unsigned int steps;
754        unsigned int failures;
755        T_time delta;
756        T_time_string ts;
757
758        config = ctx->config;
759        fixture = tc->fixture;
760
761        if (fixture != NULL && fixture->teardown != NULL) {
762                (*fixture->teardown)(ctx->fixture_context);
763        }
764
765        T_call_destructors(ctx);
766        T_call_actions_backward(config, T_EVENT_CASE_END, tc->name);
767
768        planned_steps = atomic_fetch_add_explicit(&ctx->planned_steps,
769            0, memory_order_relaxed);
770        steps = atomic_fetch_add_explicit(&ctx->steps, 0,
771            memory_order_relaxed);
772        failures = atomic_fetch_add_explicit(&ctx->failures, 0,
773            memory_order_relaxed);
774
775        if (planned_steps != UINT_MAX && planned_steps != steps &&
776            failures == 0) {
777                ++failures;
778
779                if (ctx->verbosity >= T_NORMAL) {
780                        char scope[T_SCOPE_SIZE];
781
782                        T_printf("F:*:%i:%s:*:*:actual steps (%u), "
783                            "planned steps (%u)\n", T_cpu(),
784                            T_scope(scope), steps, planned_steps);
785                }
786        }
787
788        delta = (*config->now)() - ctx->case_begin_time;
789        T_do_log(ctx, T_QUIET, "E:%s:N:%u:F:%u:D:%s\n",
790            tc->name, steps, failures, T_time_to_string_us(delta, ts));
791
792        ++ctx->overall_cases;
793        ctx->overall_steps += steps;
794        ctx->overall_failures += failures;
795
796        T_call_actions_backward(config, T_EVENT_CASE_LATE, tc->name);
797}
798
799static void
800T_run_case(T_context *ctx, const T_case_context *tc)
801{
802        T_do_case_begin(ctx, tc);
803
804        if (setjmp(ctx->case_begin_context) == 0) {
805                (*tc->body)();
806        }
807
808        T_do_case_end(ctx, tc);
809}
810
811static void
812T_do_run_all(T_context *ctx)
813{
814        const T_case_context *tc;
815
816        tc = ctx->registered_cases;
817
818        while (tc != NULL) {
819                T_run_case(ctx, tc);
820                tc = tc->next;
821        }
822}
823
824static bool
825T_do_run_finalize(T_context *ctx)
826{
827        const T_config *config;
828        T_time delta;
829        T_time_string ts;
830
831        config = ctx->config;
832        delta = (*config->now)() - ctx->run_begin_time;
833        T_do_log(ctx, T_QUIET, "Z:%s:C:%u:N:%u:F:%u:D:%s\n", config->name,
834            ctx->overall_cases, ctx->overall_steps, ctx->overall_failures,
835            T_time_to_string_us(delta, ts));
836        T_call_actions_backward(config, T_EVENT_RUN_FINALIZE, config->name);
837#ifdef __rtems__
838        ctx->runner_thread = NULL;
839        ctx->runner_cpu = NULL;
840#else
841        ctx->runner_valid = false;
842#endif
843        pthread_spin_destroy(&ctx->lock);
844
845        return ctx->overall_failures == 0;
846}
847
848int
849T_main(const T_config *config)
850{
851        T_context *ctx;
852
853        ctx = T_do_run_initialize(config);
854        T_do_run_all(ctx);
855
856        return T_do_run_finalize(ctx) ? 0 : 1;
857}
858
859bool T_is_runner(void)
860{
861        T_context *ctx;
862        bool is_runner;
863#ifdef __rtems__
864        ISR_Level level;
865        const Per_CPU_Control *cpu_self;
866#endif
867
868        ctx = &T_instance;
869#ifdef __rtems__
870        _ISR_Local_disable(level);
871        cpu_self = _Per_CPU_Get();
872
873        if (ctx->runner_thread != NULL) {
874                is_runner = cpu_self->isr_nest_level == 0 &&
875                    _Per_CPU_Get_executing(cpu_self) == ctx->runner_thread;
876        } else {
877                is_runner = cpu_self == ctx->runner_cpu;
878        }
879
880        _ISR_Local_enable(level);
881#else
882        is_runner = ctx->runner_valid &&
883            pthread_equal(pthread_self(), ctx->runner_thread) != 0;
884#endif
885
886        return is_runner;
887}
888
889#ifdef __rtems__
890RTEMS_LINKER_ROSET(_T, T_case_context *);
891#endif /* __rtems__ */
892
893void T_register(void)
894{
895#ifdef __rtems__
896        T_case_context * const *tc;
897
898        RTEMS_LINKER_SET_FOREACH(_T, tc) {
899                T_case_register(*tc);
900        }
901#endif /* __rtems__ */
902}
903
904void
905T_run_initialize(const T_config *config)
906{
907        (void)T_do_run_initialize(config);
908}
909
910void
911T_run_all(void)
912{
913        T_do_run_all(&T_instance);
914}
915
916void
917T_run_by_name(const char *name)
918{
919        T_context *ctx;
920        const T_case_context *tc;
921
922        ctx = &T_instance;
923        tc = ctx->registered_cases;
924
925        while (tc != NULL) {
926                if (strcmp(tc->name, name) == 0) {
927                        T_run_case(ctx, tc);
928                        break;
929                }
930
931                tc = tc->next;
932        }
933}
934
935static T_case_context default_case;
936
937void
938T_case_begin(const char *name, const T_fixture *fixture)
939{
940        T_case_context *tc;
941
942        tc = &default_case;
943        tc->name = name;
944        tc->fixture = fixture;
945        T_do_case_begin(&T_instance, tc);
946}
947
948void
949T_case_end(void)
950{
951        T_case_context *tc;
952
953        tc = &default_case;
954        T_do_case_end(&T_instance, tc);
955}
956
957bool
958T_run_finalize(void)
959{
960        return T_do_run_finalize(&T_instance);
961}
962
963T_time
964T_case_begin_time(void)
965{
966        return T_instance.case_begin_time;
967}
968
969void
970T_set_putchar(T_putchar new_putchar, void *new_arg, T_putchar *old_putchar,
971    void **old_arg)
972{
973        T_context *ctx;
974
975        ctx = &T_instance;
976        *old_putchar = ctx->putchar;
977        *old_arg = ctx->putchar_arg;
978        ctx->putchar = new_putchar;
979        ctx->putchar_arg = new_arg;
980}
Note: See TracBrowser for help on using the repository browser.