RTEMS
t-test.c
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 
52 typedef 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 
86 static T_context T_instance;
87 
88 const T_check_context T_special = {
89  .file = "*",
90  .line = -1,
91  .flags = T_CHECK_FMT | T_CHECK_QUIET
92 };
93 
94 static bool
95 T_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 
123 bool T_is_runner(void)
124 {
125  return T_do_is_runner(&T_instance);
126 }
127 
128 typedef struct {
129  char *s;
130  size_t n;
132 
133 static void
134 T_putchar_string(int c, void *arg)
135 {
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 
158 int
159 T_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 
179 static void
180 T_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 
196 static unsigned int
197 T_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 
235 static int
236 T_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 
245 static int
246 T_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 
264 int
265 T_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 
278 static int
279 T_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 
296 int
297 T_puts(const char *buf, size_t len)
298 {
299  return T_do_puts(&T_instance, buf, len);
300 }
301 
302 static int
303 T_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 
314 size_t
315 T_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__)
332 static size_t
333 T_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 
357 static size_t
358 T_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 
376 static size_t
377 T_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 
436 static void
437 T_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 
460 void
461 T_make_runner(void)
462 {
463  T_do_make_runner(&T_instance);
464 }
465 
466 int
467 T_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 
479 void
480 T_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 
497 static unsigned int
498 T_fetch_add_step(T_context *ctx)
499 {
500  return atomic_fetch_add_explicit(&ctx->steps, 1, memory_order_relaxed);
501 }
502 
503 static unsigned int
504 T_add_failure(T_context *ctx)
505 {
506  return atomic_fetch_add_explicit(&ctx->failures, 1,
507  memory_order_relaxed);
508 }
509 
510 T_NO_RETURN static void
511 T_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 
532 T_NO_RETURN void
533 T_stop(void)
534 {
535  T_do_stop(&T_instance);
536 }
537 
538 void 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 
553 const T_fixture T_empty_fixture;
554 
555 void
556 T_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 
562 void
563 T_pop_plan(void)
564 {
565  T_pop_fixture();
566 }
567 
568 void
569 T_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 
579 void
580 T_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 
589 T_verbosity
590 T_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 
602 void *
603 T_fixture_context(void)
604 {
605  return T_instance.fixtures->context;
606 }
607 
608 void
609 T_set_fixture_context(void *context)
610 {
611  T_instance.fixtures->context = context;
612 }
613 
614 const char *
615 T_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 
628 static const char *
629 T_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 
642 static const char T_planned_step_fmt[] = "planned step (%u)";
643 
644 static void
645 T_check_putc(int c, void *arg)
646 {
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 
663 static void
664 T_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 
689 void
690 T_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') {
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 
782 static void
783 T_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 
794 static void
795 T_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 
838 void
839 T_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 
850 void
851 T_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 
861 static void
862 T_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 
881 static void
882 T_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 
896 static void
897 T_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 
912 static T_context *
913 T_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 
951 static void
952 T_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  T_actions_forward(config, T_EVENT_CASE_BEGIN, tc->name);
973 
974  if (fixture != NULL) {
975  ctx->case_fixture.fixture = fixture;
976  ctx->case_fixture.context = fixture->initial_context;
977 
978  if (fixture->setup != NULL) {
979  (*fixture->setup)(ctx->case_fixture.context);
980  }
981  }
982 }
983 
984 static void
985 T_check_steps(unsigned int planned_steps, unsigned int steps,
986  unsigned int failures)
987 {
988  if (planned_steps != UINT_MAX && planned_steps != steps &&
989  failures == 0) {
990  T_check(&T_special, false, "actual steps (%u), "
991  "planned steps (%u)", steps, planned_steps);
992  }
993 }
994 
995 static void
996 T_do_case_end(T_context *ctx, const T_case_context *tc)
997 {
998  const T_config *config;
999  unsigned int planned_steps;
1000  unsigned int steps;
1001  unsigned int failures;
1002  T_time delta;
1003  T_time_string ts;
1004 
1005  while (ctx->fixtures != NULL) {
1006  T_pop_fixture();
1007  }
1008 
1009  T_call_destructors(ctx);
1010  config = ctx->config;
1011  T_actions_backward(config, T_EVENT_CASE_END, tc->name);
1012 
1013  planned_steps = atomic_fetch_add_explicit(&ctx->planned_steps,
1014  0, memory_order_relaxed);
1015  steps = atomic_fetch_add_explicit(&ctx->steps, 0,
1016  memory_order_relaxed);
1017  failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1018  memory_order_relaxed);
1019  T_check_steps(planned_steps, steps, failures);
1020 
1021  failures = atomic_load_explicit(&ctx->failures, memory_order_relaxed);
1022  delta = (*config->now)() - ctx->case_begin_time;
1023  steps += ctx->fixture_steps;
1024  T_do_log(ctx, T_QUIET, "E:%s:N:%u:F:%u:D:%s\n",
1025  tc->name, steps, failures, T_time_to_string_us(delta, ts));
1026 
1027  ++ctx->overall_cases;
1028  ctx->overall_steps += steps;
1029  ctx->overall_failures += failures;
1030 
1031  T_actions_backward(config, T_EVENT_CASE_LATE, tc->name);
1032 }
1033 
1034 static void
1035 T_run_case(T_context *ctx, const T_case_context *tc)
1036 {
1037  T_do_case_begin(ctx, tc);
1038 
1039  if (setjmp(ctx->case_begin_context) == 0) {
1040  (*tc->body)();
1041  }
1042 
1043  T_do_case_end(ctx, tc);
1044 }
1045 
1046 static void
1047 T_do_run_all(T_context *ctx)
1048 {
1049  const T_case_context *tc;
1050 
1051  tc = ctx->registered_cases;
1052 
1053  while (tc != NULL) {
1054  T_run_case(ctx, tc);
1055  tc = tc->next;
1056  }
1057 }
1058 
1059 static bool
1060 T_do_run_finalize(T_context *ctx)
1061 {
1062  const T_config *config;
1063  T_time delta;
1064  T_time_string ts;
1065 
1066  config = ctx->config;
1067  T_actions_backward(config, T_EVENT_RUN_FINALIZE_EARLY, config->name);
1068  delta = (*config->now)() - ctx->run_begin_time;
1069  T_do_log(ctx, T_QUIET, "Z:%s:C:%u:N:%u:F:%u:D:%s\n", config->name,
1070  ctx->overall_cases, ctx->overall_steps, ctx->overall_failures,
1071  T_time_to_string_us(delta, ts));
1072  T_actions_backward(config, T_EVENT_RUN_FINALIZE_LATE, config->name);
1073 #ifdef __rtems__
1074  ctx->runner_thread = NULL;
1075  ctx->runner_cpu = NULL;
1076 #else
1077  ctx->runner_valid = false;
1078 #endif
1079  pthread_spin_destroy(&ctx->lock);
1080 
1081  return ctx->overall_failures == 0;
1082 }
1083 
1084 int
1085 T_main(const T_config *config)
1086 {
1087  T_context *ctx;
1088 
1089  ctx = T_do_run_initialize(config);
1090  T_do_run_all(ctx);
1091 
1092  return T_do_run_finalize(ctx) ? 0 : 1;
1093 }
1094 
1095 #ifdef __rtems__
1096 RTEMS_LINKER_ROSET(_T, T_case_context *);
1097 #endif /* __rtems__ */
1098 
1099 void T_register(void)
1100 {
1101 #ifdef __rtems__
1102  T_case_context * const *tc;
1103 
1104  RTEMS_LINKER_SET_FOREACH(_T, tc) {
1105  T_case_register(*tc);
1106  }
1107 #endif /* __rtems__ */
1108 }
1109 
1110 void
1111 T_run_initialize(const T_config *config)
1112 {
1113  (void)T_do_run_initialize(config);
1114 }
1115 
1116 void
1117 T_run_all(void)
1118 {
1119  T_do_run_all(&T_instance);
1120 }
1121 
1122 void
1123 T_run_by_name(const char *name)
1124 {
1125  T_context *ctx;
1126  const T_case_context *tc;
1127 
1128  ctx = &T_instance;
1129  tc = ctx->registered_cases;
1130 
1131  while (tc != NULL) {
1132  if (strcmp(tc->name, name) == 0) {
1133  T_run_case(ctx, tc);
1134  break;
1135  }
1136 
1137  tc = tc->next;
1138  }
1139 }
1140 
1141 static T_case_context default_case;
1142 
1143 void
1144 T_case_begin(const char *name, const T_fixture *fixture)
1145 {
1146  T_case_context *tc;
1147 
1148  tc = &default_case;
1149  tc->name = name;
1150  tc->fixture = fixture;
1151  T_do_case_begin(&T_instance, tc);
1152 }
1153 
1154 void
1155 T_case_end(void)
1156 {
1157  T_case_context *tc;
1158 
1159  tc = &default_case;
1160  T_do_case_end(&T_instance, tc);
1161 }
1162 
1163 bool
1164 T_run_finalize(void)
1165 {
1166  return T_do_run_finalize(&T_instance);
1167 }
1168 
1169 T_time
1170 T_case_begin_time(void)
1171 {
1172  return T_instance.case_begin_time;
1173 }
1174 
1175 void
1176 T_set_putchar(T_putchar new_putchar, void *new_arg, T_putchar *old_putchar,
1177  void **old_arg)
1178 {
1179  T_context *ctx;
1180 
1181  ctx = &T_instance;
1182  *old_putchar = ctx->putchar;
1183  *old_arg = ctx->putchar_arg;
1184  ctx->putchar = new_putchar;
1185  ctx->putchar_arg = new_arg;
1186 }
1187 
1188 T_time
1189 T_now(void)
1190 {
1191  T_context *ctx;
1192  const T_config *config;
1193 
1194  ctx = &T_instance;
1195  config = ctx->config;
1196  return (*config->now)();
1197 }
1198 
1199 void *
1200 T_push_fixture(T_fixture_node *node, const T_fixture *fixture)
1201 {
1202  T_context *ctx;
1203  T_fixture_node *old;
1204  void *context;
1205 
1206  ctx = &T_instance;
1207  old = ctx->fixtures;
1208  old->previous = node;
1209  node->next = old;
1210  node->previous = NULL;
1211  node->fixture = fixture;
1212  context = fixture->initial_context;
1213  node->context = context;
1214  node->next_planned_steps = atomic_exchange_explicit(
1215  &ctx->planned_steps, UINT_MAX, memory_order_relaxed);
1216  node->next_steps = atomic_exchange_explicit(&ctx->steps, 0,
1217  memory_order_relaxed);
1218  node->failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1219  memory_order_relaxed);
1220  ctx->fixtures = node;
1221 
1222  if (fixture != NULL && fixture->setup != NULL) {
1223  (*fixture->setup)(context);
1224  }
1225 
1226  return context;
1227 }
1228 
1229 void
1230 T_pop_fixture(void)
1231 {
1232  T_context *ctx;
1233  T_fixture_node *node;
1234  const T_fixture *fixture;
1235  T_fixture_node *next;
1236 
1237  ctx = &T_instance;
1238  node = ctx->fixtures;
1239  next = node->next;
1240  ctx->fixtures = next;
1241  fixture = node->fixture;
1242 
1243  if (fixture != NULL && fixture->teardown != NULL) {
1244  (*fixture->teardown)(node->context);
1245  }
1246 
1247  if (next != NULL) {
1248  unsigned int planned_steps;
1249  unsigned int steps;
1250  unsigned int failures;
1251 
1252  next->previous = NULL;
1253  planned_steps = atomic_exchange_explicit(&ctx->planned_steps,
1254  node->next_planned_steps, memory_order_relaxed);
1255  steps = atomic_exchange_explicit(&ctx->steps, node->next_steps,
1256  memory_order_relaxed);
1257  failures = atomic_fetch_add_explicit(&ctx->failures, 0,
1258  memory_order_relaxed);
1259  ctx->fixture_steps += steps;
1260  T_check_steps(planned_steps, steps, node->failures - failures);
1261  }
1262 
1263  memset(node, 0, sizeof(*node));
1264 }
1265 
1266 size_t
1267 T_get_scope(const char * const * const *desc, char *buf, size_t n,
1268  const size_t *second_indices)
1269 {
1270  size_t c;
1271  size_t i;
1272 
1273  c = n;
1274  i = 0;
1275 
1276  while (true) {
1277  const char * const *desc2;
1278  size_t m;
1279 
1280  desc2 = desc[i];
1281 
1282  if (desc2 == NULL) {
1283  break;
1284  }
1285 
1286  if (c > 1) {
1287  buf[0] = '/';
1288  --c;
1289  ++buf;
1290  } else {
1291  break;
1292  }
1293 
1294  m = T_str_copy(buf, desc2[second_indices[i]], c);
1295  buf += m;
1296  c -= m;
1297  ++i;
1298  }
1299 
1300  return n - c;
1301 }
uint32_t name_u32
Definition: object.h:68
#define _ISR_Local_disable(_level)
Disables interrupts on this processor.
Definition: isrlevel.h:57
SuperCore SMP Support API.
Thread_queue_Queue Queue
The actual thread queue.
Definition: threadq.h:583
Per CPU Core Structure.
Definition: percpu.h:347
const char * name
The thread queue name.
Definition: threadq.h:436
uint32_t ISR_Level
Definition: isrlevel.h:41
Objects_Control Object
Definition: thread.h:727
const char * rtems_board_support_package(void)
Returns the board support package name.
Definition: rtems-version.c:33
#define _ISR_Local_enable(_level)
Enables interrupts on this processor.
Definition: isrlevel.h:74
uint32_t isr_nest_level
Definition: percpu.h:369
Version API.
Definition: test.h:61
Thread_queue_Control Join_queue
Thread queue for thread join operations and multi-purpose lock.
Definition: thread.h:746
Inlined Routines from the Thread Handler.
const char * rtems_version(void)
Returns the version string.
Definition: version.c:32
Objects_Name name
Definition: objectdata.h:45