LCOV - code coverage report
Current view: top level - core/test - test.c Hit Total Coverage
Test: ROOT-Sim develop Documentation Coverage Lines: 14 21 66.7 %
Date: 2021-03-02 11:24:52

          Line data    Source code
       1           1 : /**
       2             :  * @file test/test.c
       3             :  *
       4             :  * @brief Test framework source
       5             :  *
       6             :  * The source of the minimal test framework used in the code base tests
       7             :  *
       8             :  * SPDX-FileCopyrightText: 2008-2021 HPDCS Group <rootsim@googlegroups.com>
       9             :  * SPDX-License-Identifier: GPL-3.0-only
      10             :  */
      11             : #include <test.h>
      12             : 
      13             : #include <arch/thread.h>
      14             : 
      15             : #include <limits.h>
      16             : #include <memory.h>
      17             : #include <stdarg.h>
      18             : #include <stdatomic.h>
      19             : #include <stdbool.h>
      20             : #include <stdlib.h>
      21             : 
      22             : #ifndef ROOTSIM_TEST_NAME
      23           0 : #define ROOTSIM_TEST_NAME "rs_test"
      24             : #endif
      25             : 
      26           0 : static char *t_out_buf;
      27           0 : static size_t t_out_buf_size;
      28           0 : static size_t t_out_wrote;
      29             : 
      30           0 : static char **test_argv;
      31             : 
      32           1 : __attribute__((weak)) lp_id_t n_lps;
      33             : #ifdef ROOTSIM_MPI
      34           1 : __attribute__((weak)) nid_t nid;
      35           0 : __attribute__((weak)) nid_t n_nodes = 1;
      36             : #endif
      37           1 : __attribute__((weak)) rid_t n_threads;
      38           1 : __attribute__((weak)) __thread rid_t rid;
      39             : 
      40           1 : __attribute__((weak)) int log_level;
      41             : 
      42             : __attribute__((weak))
      43           1 : void _log_log(int level, const char *file, unsigned line, const char *fmt, ...)
      44             : {
      45             :         (void) level;
      46             :         (void) file;
      47             :         (void) line;
      48             :         (void) fmt;
      49             : }
      50             : 
      51           1 : int main(int argc, char **argv);
      52             : 
      53             : /**
      54             :  * @brief Initializes ISO C compliant argc and argv from the test configuration
      55             :  * @param argc_p a pointer to a variable which will hold the computed argc value
      56             :  * @param argv_p a pointer to a variable which will hold the computer argv value
      57             :  * @return 0 in case of success, -1 in case of failure
      58             :  */
      59           1 : static int init_arguments(int *argc_p, char ***argv_p)
      60             : {
      61             :         int argc = 0;
      62             :         if (test_config.test_arguments) {
      63             :                 while (test_config.test_arguments[argc]) {
      64             :                         ++argc;
      65             :                 }
      66             :         }
      67             :         ++argc;
      68             : 
      69             :         char **argv = malloc(sizeof(*argv) * (argc + 1));
      70             :         if(argv == NULL)
      71             :                 return -1;
      72             : 
      73             :         argv[0] = ROOTSIM_TEST_NAME;
      74             : 
      75             :         if (test_config.test_arguments) {
      76             :                 memcpy(&argv[1], test_config.test_arguments,
      77             :                        sizeof(*argv) * argc);
      78             :         } else {
      79             :                 argv[1] = NULL;
      80             :         }
      81             : 
      82             :         *argc_p = argc;
      83             :         *argv_p = argv;
      84             :         return 0;
      85             : }
      86             : 
      87             : /**
      88             :  * @brief The exit handler, to exit cleanly even in case of errors
      89             :  */
      90           1 : static void test_atexit(void)
      91             : {
      92             :         free(test_argv);
      93             :         free(t_out_buf);
      94             : }
      95             : 
      96             : /**
      97             :  * @brief The test wrapper which allows to intervene before the actual main()
      98             :  */
      99             : __attribute__((constructor))
     100           1 : void main_wrapper(void)
     101             : {
     102             :         int test_argc = 0;
     103             : 
     104             :         puts("Starting " ROOTSIM_TEST_NAME " test");
     105             : 
     106             :         n_threads = test_config.threads_count;
     107             : 
     108             :         atexit(test_atexit);
     109             : 
     110             :         if (init_arguments(&test_argc, &test_argv) == -1)
     111             :                 exit(TEST_BAD_FAIL_EXIT_CODE);
     112             : 
     113             :         t_out_buf_size = 1;
     114             :         t_out_buf = malloc(t_out_buf_size);
     115             :         if (t_out_buf == NULL)
     116             :                 exit(TEST_BAD_FAIL_EXIT_CODE);
     117             : 
     118             :         int test_ret = main(test_argc, test_argv);
     119             :         if (test_ret)
     120             :                 exit(test_ret);
     121             : 
     122             :         if (t_out_wrote < test_config.expected_output_size) {
     123             :                 puts("Test failed: output is shorter than the expected one");
     124             :                 exit(-1);
     125             :         }
     126             : 
     127             :         puts("Successfully run " ROOTSIM_TEST_NAME " test");
     128             :         exit(0);
     129             : }
     130             : 
     131           0 : static int test_printf_internal(const char *restrict fmt, va_list args)
     132             : {
     133             :         va_list args_cpy;
     134             :         va_copy(args_cpy, args);
     135             :         size_t p_size = vsnprintf(t_out_buf, t_out_buf_size, fmt, args_cpy);
     136             :         va_end(args_cpy);
     137             : 
     138             :         if (t_out_wrote + p_size > test_config.expected_output_size) {
     139             :                 puts("Test failed: output is longer than the expected one");
     140             :                 exit(-1);
     141             :         }
     142             : 
     143             :         if (p_size >= t_out_buf_size) {
     144             :                 do {
     145             :                         t_out_buf_size *= 2;
     146             :                 } while(p_size >= t_out_buf_size);
     147             : 
     148             :                 free(t_out_buf);
     149             :                 t_out_buf = malloc(t_out_buf_size);
     150             :                 if (t_out_buf == NULL)
     151             :                         exit(TEST_BAD_FAIL_EXIT_CODE);
     152             : 
     153             :                 vsnprintf(t_out_buf, t_out_buf_size, fmt, args);
     154             :         }
     155             : 
     156             :         if (memcmp(t_out_buf, test_config.expected_output + t_out_wrote, p_size)) {
     157             :                 printf("Test failed: output is different from the expected one %zu\n", t_out_wrote);
     158             :                 exit(-1);
     159             :         }
     160             :         t_out_wrote += p_size;
     161             : 
     162             :         return p_size;
     163             : }
     164             : 
     165             : /**
     166             :  * @brief Registers a formatted string to compare against the expected output
     167             :  * @return the number of successfully registered characters
     168             :  */
     169           1 : int test_printf(const char *restrict fmt, ...)
     170             : {
     171             :         va_list args;
     172             :         va_start(args, fmt);
     173             :         int ret = test_printf_internal(fmt, args);
     174             :         va_end(args);
     175             :         return ret;
     176             : }
     177             : 
     178             : /**
     179             :  * @brief Registers a formatted string to compare against the expected output
     180             :  * @return the number of successfully registered characters
     181             :  *
     182             :  * Cloned definition needed because the LLVM plugin expects a suffixed symbol
     183             :  */
     184           1 : int test_printf_pr(const char *restrict fmt, ...)
     185             : {
     186             :         va_list args;
     187             :         va_start(args, fmt);
     188             :         int ret = test_printf_internal(fmt, args);
     189             :         va_end(args);
     190             :         return ret;
     191             : }
     192             : 
     193             : /**
     194             :  * @brief Synchronizes threads on a barrier
     195             :  * @return true if this thread has been elected as leader, false otherwise
     196             :  *
     197             :  * This is a more battle tested although less performing version of the thread
     198             :  * barrier. We can't rely on the pthread barrier because it's not portable.
     199             :  */
     200           1 : bool test_thread_barrier(void)
     201             : {
     202             :         static atomic_uint b_in, b_out, b_cr;
     203             : 
     204             :         unsigned i;
     205             :         unsigned count = test_config.threads_count;
     206             :         unsigned max_in_before_reset = (UINT_MAX / 2) - (UINT_MAX / 2) % count;
     207             :         do {
     208             :                 i = atomic_fetch_add_explicit(
     209             :                         &b_in, 1U, memory_order_acq_rel) + 1;
     210             :         } while (__builtin_expect(i > max_in_before_reset, 0));
     211             : 
     212             :         unsigned cr = atomic_load_explicit(&b_cr, memory_order_relaxed);
     213             : 
     214             :         bool leader = i == cr + count;
     215             :         if (leader) {
     216             :                 atomic_store_explicit(&b_cr, cr + count, memory_order_release);
     217             :         } else {
     218             :                 while (i > cr) {
     219             :                         cr = atomic_load_explicit(&b_cr, memory_order_relaxed);
     220             :                 }
     221             :         }
     222             :         atomic_thread_fence(memory_order_acquire);
     223             : 
     224             :         unsigned o = atomic_fetch_add_explicit(&b_out, 1, memory_order_release) + 1;
     225             :         if (__builtin_expect(o == max_in_before_reset, 0)) {
     226             :                 atomic_thread_fence(memory_order_acquire);
     227             :                 atomic_store_explicit(&b_cr, 0, memory_order_relaxed);
     228             :                 atomic_store_explicit(&b_out, 0, memory_order_relaxed);
     229             :                 atomic_store_explicit(&b_in, 0, memory_order_release);
     230             :         }
     231             :         return leader;
     232             : }

Generated by: LCOV version 1.14