## Coverage Report

#### Created: 2026-05-04 15:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/liu/actions-runner/_work/ccv/ccv/test/case_main.h

    Line
    Count
    Source
    1
    #if !defined(_GUARD_case_main_h_) && !defined(CASE_DISABLE_MAIN)
    2
    #define _GUARD_case_main_h_
    3
    4
    #include <string.h>
    5
    #include <assert.h>
    6
    7
    static int case_print_hi(char* str, const char* const hi)
    8
    0
    {
    9
    0
      if (!hi)
    10
    0
        return printf("%s", str);
    11
    0
      const size_t hilen = strlen(hi);
    12
    0
      char* savestr = strstr(str, hi);
    13
    0
      int nchr = 0;
    14
    0
      while (savestr)
    15
    0
      {
    16
    0
        for (;str < savestr; ++str, ++nchr)
    17
    0
          putchar(str[0]);
    18
    0
        nchr += printf("\033[7m%s\033[0m", hi); // decorate with underline.
    19
    0
        str += hilen;
    20
    0
        savestr = strstr(str, hi);
    21
    0
      }
    22
    0
      nchr += printf("%s", str);
    23
    0
      return nchr;
    24
    0
    }
    25
    26
    static void case_run(case_t* test_case, const char* const match_test, int i, int total, int* pass, int* skip, int* fail)
    27
    1.45k
    {
    28
      // Change the current directory.
    29
    1.45k
      if (test_case->dir && test_case->dir[0] != 0 && strcmp(test_case->dir, ".") != 0)
    30
    1.45k
        chdir(test_case->dir);
    31
    1.45k
      int clr = 0;
    32
    1.45k
      if (isatty(fileno(stdout)))
    33
    0
      {
    34
    0
        clr += printf("\033[0;34m[%d/%d]\033[0;0m \033[1;33m[RUN]\033[0;0m ", i + 1, total);
    35
    0
        clr += case_print_hi(test_case->name, match_test);
    36
    0
        clr += printf(" ...");
    37
    0
      } else
    38
    1.45k
        clr += printf("[%d/%d] [RUN] %s ...", i + 1, total, test_case->name);
    39
    1.45k
      fflush(stdout);
    40
    1.45k
      int result = 0;
    41
    1.45k
      test_case->func(test_case->name, &result);
    42
    1.45k
      if (result == 0)
    43
    1.09k
      {
    44
    1.09k
        (*pass)++;
    45
    73.4k
        for (; clr > 0; --clr72.3k)
    46
    72.3k
          printf("\b");
    47
    1.09k
        if (isatty(fileno(stdout)))
    48
    0
        {
    49
    0
          printf("\r\033[0;34m[%d/%d]\033[0;0m \033[1;32m[PASS]\033[0;0m ", i + 1, total);
    50
    0
          case_print_hi(test_case->name, match_test);
    51
    0
          printf("    \n");
    52
    0
        } else
    53
    1.09k
          printf("\r[%d/%d] [PASS] %s    \n", i + 1, total, test_case->name);
    54
    1.09k
      } else if (359result == -2359) {
    55
    359
        (*skip)++;
    56
    359
        if (isatty(fileno(stdout)))
    57
    0
        {
    58
    0
          printf("\n\033[0;34m[%d/%d]\033[0;0m \033[1;32m[SKIP]\033[0;0m ", i + 1, total);
    59
    0
          case_print_hi(test_case->name, match_test);
    60
    0
          printf("\n");
    61
    0
        } else
    62
    359
          printf("\n[%d/%d] [SKIP] %s\n", i + 1, total, test_case->name);
    63
    359
      } else {
    64
    0
        (*fail)++;
    65
    0
        if (isatty(fileno(stdout)))
    66
    0
        {
    67
    0
          printf("\n\033[0;34m[%d/%d]\033[0;0m \033[1;31m[FAIL]\033[0;0m ", i + 1, total);
    68
    0
          case_print_hi(test_case->name, match_test);
    69
    0
          printf("\n");
    70
    0
        } else
    71
    0
          printf("\n[%d/%d] [FAIL] %s\n", i + 1, total, test_case->name);
    72
    0
      }
    73
    1.45k
    }
    74
    75
    static void case_conclude(int pass, int skip, int fail)
    76
    1
    {
    77
    1
      if (isatty(fileno(stdout)))
    78
    0
      {
    79
    0
        if (skip > 0)
    80
    0
        {
    81
    0
          if (fail == 0)
    82
    0
            printf("\033[0;32mall test case(s) passed, %d test case(s) skipped, congratulations!\033[0;0m\n", skip);
    83
    0
          else
    84
    0
            printf("\033[0;31m%d of %d test case(s) passed, %d test case(s) skipped\033[0;0m\n", pass, fail + pass, skip);
    85
    0
        } else {
    86
    0
          if (fail == 0)
    87
    0
            printf("\033[0;32mall test case(s) passed, congratulations!\033[0;0m\n");
    88
    0
          else
    89
    0
            printf("\033[0;31m%d of %d test case(s) passed\033[0;0m\n", pass, fail + pass);
    90
    0
        }
    91
    1
      } else {
    92
    1
        if (skip > 0)
    93
    1
        {
    94
    1
          if (fail == 0)
    95
    1
            printf("all test case(s) passed, %d test case(s) skipped, congratulations!\n", skip);
    96
    0
          else
    97
    0
            printf("%d of %d test case(s) passed, %d test case(s) skipped\n", pass, fail + pass, skip);
    98
    1
        } else {
    99
    0
          if (fail == 0)
    100
    0
            printf("all test case(s) passed, congratulations!\n");
    101
    0
          else
    102
    0
            printf("%d of %d test case(s) passed\n", pass, fail + pass);
    103
    0
        }
    104
    1
      }
    105
    1
    }
    106
    107
    #if defined(__ELF__) || defined(__APPLE__)
    108
    // in ELF object format, we can simply query custom section rather than scan through the whole binary memory
    109
    // to find function pointer. We do this whenever possible because in this way, we don't have access error
    110
    // when hooking up with memory checkers such as address sanitizer or valgrind
    111
    112
    #ifdef __ELF__
    113
    static case_t __test_case_ctx_probe __attribute__((used, section("case_data_probe"), aligned(8))) = {0};
    114
    115
    extern case_t __start_case_data[];
    116
    extern case_t __stop_case_data[];
    117
    118
    extern case_t __start_case_data_probe[];
    119
    extern case_t __stop_case_data_probe[];
    120
    #else
    121
    #include <mach-o/dyld.h>
    122
    #include <mach-o/getsect.h>
    123
    #ifdef __LP64__
    124
      typedef struct section_64 case_section;
    125
      typedef struct mach_header_64 case_header;
    126
    #else
    127
      typedef struct section case_section;
    128
      typedef struct mach_header case_header;
    129
    #endif
    130
    static case_t __test_case_ctx_probe __attribute__((used, section("__DATA,case_data_probe"), aligned(8))) = {0};
    131
    #endif
    132
    133
    int main(int argc, char** argv)
    134
    1
    {
    135
    1
    #ifdef __ELF__
    136
    1
      int case_size = (intptr_t)__stop_case_data_probe - (intptr_t)__start_case_data_probe;
    137
    1
      int test_size = (intptr_t)__stop_case_data - (intptr_t)__start_case_data;
    138
    1
      unsigned char* start_case_data = (unsigned char*)__start_case_data;
    139
    1
      unsigned char* stop_case_data = (unsigned char*)__stop_case_data;
    140
    #else
    141
      uint32_t image_count = _dyld_image_count();
    142
      int case_size = 0;
    143
      for (uint32_t image_index = 0; !case_size && image_index < image_count; image_index++)
    144
      {
    145
        const case_header* mach_header = (const case_header*)_dyld_get_image_header(image_index);
    146
        unsigned long size;
    147
        uint8_t *data = getsectiondata(mach_header, "__DATA", "case_data_probe", &size);
    148
        if (data)
    149
          case_size = (int)size;
    150
      }
    151
      int test_size = 0;
    152
      unsigned char* start_case_data = 0;
    153
      unsigned char* stop_case_data = 0;
    154
      for (uint32_t image_index = 0; image_index < image_count; image_index++)
    155
      {
    156
        const case_header* mach_header = (const case_header*)_dyld_get_image_header(image_index);
    157
        unsigned long size;
    158
        uint8_t *data = getsectiondata(mach_header, "__DATA", "case_data", &size);
    159
        if (!data)
    160
          continue;
    161
        start_case_data = data;
    162
        stop_case_data = data + size;
    163
        test_size = (int)size;
    164
        break;
    165
      }
    166
    #endif
    167
    1
      char buf[1024];
    168
    1
      char* cur_dir = getcwd(buf, 1024);
    169
    1
      static uint64_t the_sig = 0x883253372849284B;
    170
    1
      int scan_mode = (test_size % case_size != 0);
    171
    1
      const char* match_test = (argc == 2) ? argv[1]0 : 0;
    172
    1
      int i, total = 0;
    173
    1
      if (!scan_mode)
    174
    1
        total = test_size / case_size;
    175
    1.45k
      for (i = 0; i < total; i++1.45k)
    176
    1.45k
      {
    177
    1.45k
        case_t* test_case = (case_t*)(start_case_data + i * case_size);
    178
        // If it doesn't match well, fallback to scan mode.
    179
    1.45k
        if (test_case->sig_head != the_sig || test_case->sig_tail != the_sig + 2)
    180
    0
        {
    181
    0
          scan_mode = 1;
    182
    0
          break;
    183
    0
        }
    184
    1.45k
      }
    185
    1
      int len, pass = 0, skip = 0, fail = 0;
    186
      // In scan mode, we will scan the whole section for a matching test case.
    187
    1
      if (scan_mode)
    188
    0
      {
    189
    0
        total = 0;
    190
    0
        len = (intptr_t)stop_case_data - (intptr_t)start_case_data - sizeof(case_t) + 1;
    191
    0
        for (i = 0; i < len; i++)
    192
    0
        {
    193
    0
          case_t* test_case = (case_t*)(start_case_data + i);
    194
    0
          if (test_case->sig_head == the_sig && test_case->sig_tail == the_sig + 2 &&
    195
    0
            (!match_test || strstr(test_case->name, match_test)))
    196
    0
            total++;
    197
    0
        }
    198
    0
      }
    199
    1
      if (__test_case_setup)
    200
    1
        __test_case_setup();
    201
    1
      if (scan_mode)
    202
    0
      {
    203
    0
        int j = 0;
    204
    0
        for (i = 0; i < len; i++)
    205
    0
        {
    206
    0
          case_t* test_case = (case_t*)(start_case_data + i);
    207
    0
          if (test_case->sig_head == the_sig && test_case->sig_tail == the_sig + 2 &&
    208
    0
            (!match_test || strstr(test_case->name, match_test)))
    209
    0
          {
    210
    0
            case_run(test_case, match_test, j++, total, &pass, &skip, &fail);
    211
    0
            chdir(cur_dir);
    212
    0
          }
    213
    0
        }
    214
    1
      } else {
    215
    1
        int matched_total = match_test ? 00 : total;
    216
    1
        if (match_test)
    217
    0
          for (i = 0; i < total; i++)
    218
    0
          {
    219
    0
            case_t* test_case = (case_t*)(start_case_data + i * case_size);
    220
    0
            if (strstr(test_case->name, match_test))
    221
    0
              matched_total++;
    222
    0
          }
    223
    1
        int j = 0;
    224
        // Simple case, I don't need to scan the data section.
    225
    1.45k
        for (i = 0; i < total; i++1.45k)
    226
    1.45k
        {
    227
    1.45k
          case_t* test_case = (case_t*)(start_case_data + i * case_size);
    228
    1.45k
          if (!match_test || strstr(test_case->name, match_test)0)
    229
    1.45k
            case_run(test_case, match_test, j++, matched_total, &pass, &skip, &fail);
    230
    1.45k
          chdir(cur_dir);
    231
    1.45k
        }
    232
    1
      }
    233
    1
      if (__test_case_teardown)
    234
    0
        __test_case_teardown();
    235
    1
      case_conclude(pass, skip, fail);
    236
    1
      return fail;
    237
    1
    }
    238
    239
    #else
    240
    241
    #include <stdio.h>
    242
    #include <unistd.h>
    243
    #include <stdlib.h>
    244
    #include <fcntl.h>
    245
    #include <ctype.h>
    246
    #include <signal.h>
    247
    #include <setjmp.h>
    248
    249
    /* the following functions come from Hans Boehm's conservative gc with slightly
    250
     * modifications, here is the licence:
    251
     * Copyright (c) 1988, 1989 Hans-J. Boehm, Alan J. Demers
    252
     * Copyright (c) 1991-1996 by Xerox Corporation.  All rights reserved.
    253
     * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
    254
     * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
    255
    256
     * The file linux_threads.c is also
    257
     * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
    258
    259
     * The files Makefile.am, and configure.in are
    260
     * Copyright (c) 2001 by Red Hat Inc. All rights reserved.
    261
    262
     * Several files supporting GNU-style builds are copyrighted by the Free
    263
     * Software Foundation, and carry a different license from that given
    264
     * below.
    265
    266
     * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
    267
     * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
    268
    269
     * Permission is hereby granted to use or copy this program
    270
     * for any purpose,  provided the above notices are retained on all copies.
    271
     * Permission to modify the code and to distribute modified code is granted,
    272
     * provided the above notices are retained, and a notice that the code was
    273
     * modified is included with the above copyright notice.
    274
    275
     * A few of the files needed to use the GNU-style build procedure come with
    276
     * slightly different licenses, though they are all similar in spirit.  A few
    277
     * are GPL'ed, but with an exception that should cover all uses in the
    278
     * collector. */
    279
    280
    /* Repeatedly perform a read call until the buffer is filled or */
    281
    /* we encounter EOF.            */
    282
    static ssize_t case_repeat_read(int fd, char *buf, size_t count)
    283
    {
    284
        ssize_t num_read = 0;
    285
        ssize_t result;
    286
    287
        while (num_read < count)
    288
      {
    289
        result = read(fd, buf + num_read, count - num_read);
    290
        if (result < 0)
    291
          return result;
    292
        if (result == 0)
    293
          break;
    294
        num_read += result;
    295
        }
    296
        return num_read;
    297
    }
    298
    299
    /* Determine the length of a file by incrementally reading it into a buffer */
    300
    /* This would be silly to use on a file supporting lseek, but Linux */
    301
    /* /proc files usually do not.  */
    302
    static size_t case_get_file_len(int f)
    303
    {
    304
        size_t total = 0;
    305
        ssize_t result;
    306
        char buf[500];
    307
    308
        do {
    309
        result = read(f, buf, 500);
    310
        if (result == -1)
    311
          return 0;
    312
        total += result;
    313
        } while (result > 0);
    314
        return total;
    315
    }
    316
    317
    static size_t case_get_maps_len(void)
    318
    {
    319
        int f = open("/proc/self/maps", O_RDONLY);
    320
        size_t result = case_get_file_len(f);
    321
        close(f);
    322
        return result;
    323
    }
    324
    325
    /*
    326
     * Copy the contents of /proc/self/maps to a buffer in our address space.
    327
     * Return the address of the buffer, or zero on failure.
    328
     * This code could be simplified if we could determine its size
    329
     * ahead of time.
    330
     */
    331
    static char* case_get_maps()
    332
    {
    333
        int f;
    334
        int result;
    335
        static char init_buf[1];
    336
        static char *maps_buf = init_buf;
    337
        static size_t maps_buf_sz = 1;
    338
        size_t maps_size, old_maps_size = 0;
    339
    340
        /* Note that in the presence of threads, the maps file can  */
    341
        /* essentially shrink asynchronously and unexpectedly as  */
    342
        /* threads that we already think of as dead release their */
    343
        /* stacks.  And there is no easy way to read the entire */
    344
        /* file atomically.  This is arguably a misfeature of the */
    345
        /* /proc/.../maps interface.        */
    346
    347
        /* Since we dont believe the file can grow      */
    348
        /* asynchronously, it should suffice to first determine */
    349
        /* the size (using lseek or read), and then to reread the */
    350
        /* file.  If the size is inconsistent we have to retry. */
    351
        /* This only matters with threads enabled, and if we use  */
    352
        /* this to locate roots (not the default).      */
    353
    354
        /* Determine the initial size of /proc/self/maps.   */
    355
        /* Note that lseek doesn't work, at least as of 2.6.15. */
    356
      maps_size = case_get_maps_len();
    357
      if (0 == maps_size)
    358
        return 0;
    359
    360
        /* Read /proc/self/maps, growing maps_buf as necessary. */
    361
        /* Note that we may not allocate conventionally, and  */
    362
        /* thus can't use stdio.          */
    363
      do {
    364
          while (maps_size >= maps_buf_sz)
    365
        {
    366
          /* Grow only by powers of 2, since we leak "too small" buffers. */
    367
          while (maps_size >= maps_buf_sz)
    368
            maps_buf_sz *= 2;
    369
          maps_buf = malloc(maps_buf_sz);
    370
          /* Recompute initial length, since we allocated.  */
    371
          /* This can only happen a few times per program   */
    372
          /* execution.           */
    373
          maps_size = case_get_maps_len();
    374
          if (0 == maps_size)
    375
            return 0;
    376
          }
    377
          f = open("/proc/self/maps", O_RDONLY);
    378
          if (-1 == f)
    379
          return 0;
    380
        old_maps_size = maps_size;
    381
          maps_size = 0;
    382
          do {
    383
          result = case_repeat_read(f, maps_buf, maps_buf_sz - 1);
    384
          if (result <= 0)
    385
            return 0;
    386
          maps_size += result;
    387
          } while (result == maps_buf_sz - 1);
    388
          close(f);
    389
      } while (maps_size >= maps_buf_sz || maps_size < old_maps_size);
    390
      /* In the single-threaded case, the second clause is false. */
    391
        maps_buf[maps_size] = '\0';
    392
    393
        /* Apply fn to result. */
    394
      return maps_buf;
    395
    }
    396
    397
    //
    398
    //  GC_parse_map_entry parses an entry from /proc/self/maps so we can
    399
    //  locate all writable data segments that belong to shared libraries.
    400
    //  The format of one of these entries and the fields we care about
    401
    //  is as follows:
    402
    //  XXXXXXXX-XXXXXXXX r-xp 00000000 30:05 260537     name of mapping...\n
    403
    //  ^^^^^^^^ ^^^^^^^^ ^^^^          ^^
    404
    //  start    end      prot          maj_dev
    405
    //
    406
    //  Note that since about august 2003 kernels, the columns no longer have
    407
    //  fixed offsets on 64-bit kernels.  Hence we no longer rely on fixed offsets
    408
    //  anywhere, which is safer anyway.
    409
    //
    410
    411
    /*
    412
     * Assign various fields of the first line in buf_ptr to *start, *end,
    413
     * *prot, *maj_dev and *mapping_name.  Mapping_name may be NULL.
    414
     * *prot and *mapping_name are assigned pointers into the original
    415
     * buffer.
    416
     */
    417
    static char* case_parse_map_entry(char* buf_ptr, void** start, void** end, char** prot, unsigned int* maj_dev, char** mapping_name)
    418
    {
    419
        char *start_start, *end_start, *maj_dev_start;
    420
        char *p;
    421
        char *endp;
    422
    423
        if (buf_ptr == NULL || *buf_ptr == '\0')
    424
            return NULL;
    425
    426
        p = buf_ptr;
    427
        while (isspace(*p))
    428
        ++p;
    429
        start_start = p;
    430
        *start = (void*)strtoul(start_start, &endp, 16);
    431
      p = endp;
    432
    433
        ++p;
    434
        end_start = p;
    435
        *end = (void*)strtoul(end_start, &endp, 16);
    436
      p = endp;
    437
    438
        while (isspace(*p))
    439
        ++p;
    440
        *prot = p;
    441
        /* Skip past protection field to offset field */
    442
      while (!isspace(*p))
    443
        ++p;
    444
      while (isspace(*p))
    445
        ++p;
    446
        /* Skip past offset field, which we ignore */
    447
      while (!isspace(*p))
    448
        ++p;
    449
      while (isspace(*p))
    450
        ++p;
    451
        maj_dev_start = p;
    452
        *maj_dev = strtoul(maj_dev_start, NULL, 16);
    453
    454
        if (mapping_name == 0)
    455
      {
    456
          while (*p && *p++ != '\n');
    457
        } else {
    458
          while (*p && *p != '\n' && *p != '/' && *p != '[')
    459
          p++;
    460
          *mapping_name = p;
    461
          while (*p && *p++ != '\n');
    462
        }
    463
    464
        return p;
    465
    }
    466
    467
    #define MIN_PAGE_SIZE (256)
    468
    469
    static jmp_buf case_jmp_buf;
    470
    471
    static void case_fault_handler(int sig)
    472
    {
    473
      longjmp(case_jmp_buf, 1);
    474
    }
    475
    476
    static struct sigaction old_segv_act;
    477
    static struct sigaction old_bus_act;
    478
    479
    static void case_setup_temporary_fault_handler(void)
    480
    {
    481
      struct sigaction act;
    482
      act.sa_handler = case_fault_handler;
    483
      act.sa_flags = SA_RESTART;
    484
      (void)sigemptyset(&act.sa_mask);
    485
      (void)sigaction(SIGSEGV, &act, &old_segv_act);
    486
      (void)sigaction(SIGBUS, &act, &old_bus_act);
    487
    }
    488
    489
    static void case_reset_fault_handler(void)
    490
    {
    491
      (void)sigaction(SIGSEGV, &old_segv_act, 0);
    492
      (void)sigaction(SIGBUS, &old_bus_act, 0);
    493
    }
    494
    495
    static void case_find_limit(char* p, void** start, void** end)
    496
    {
    497
      static volatile char* result;
    498
      static volatile char sink;
    499
      case_setup_temporary_fault_handler();
    500
      if (setjmp(case_jmp_buf) == 0)
    501
      {
    502
        result = (char*)((uint64_t)p & (uint64_t)~(MIN_PAGE_SIZE - 1));
    503
        for (;;)
    504
        {
    505
          result -= MIN_PAGE_SIZE;
    506
          sink = *result;
    507
        }
    508
      }
    509
      *start = (void*)(result + MIN_PAGE_SIZE);
    510
      if (setjmp(case_jmp_buf) == 0)
    511
      {
    512
        result = (char*)((uint64_t)p & (uint64_t)~(MIN_PAGE_SIZE - 1));
    513
        for (;;)
    514
        {
    515
          result += MIN_PAGE_SIZE;
    516
          sink = *result;
    517
        }
    518
      }
    519
      *end = (void*)result;
    520
      case_reset_fault_handler();
    521
    }
    522
    523
    static char _test_end[8];
    524
    525
    int main(int argc, char** argv)
    526
    {
    527
      const char* match_test = (argc == 2) ? argv[1] : 0;
    528
      char* buf = case_get_maps();
    529
      void* start;
    530
      void* end;
    531
      char* prot[4];
    532
      unsigned int maj_dev;
    533
      static uint64_t the_sig = 0x883253372849284B;
    534
      if (buf == 0)
    535
      {
    536
        case_find_limit(_test_end, &start, &end);
    537
      } else {
    538
        do {
    539
          buf = case_parse_map_entry(buf, &start, &end, (char**)&prot, &maj_dev, 0);
    540
          if (buf == NULL)
    541
            break;
    542
        } while ((intptr_t)start >= (intptr_t)&_test_end || (intptr_t)&_test_end >= (intptr_t)end);
    543
      }
    544
      char* start_pointer = (char*)start;
    545
      int total = 0;
    546
      int len = (intptr_t)end - (intptr_t)start - sizeof(case_t) + 1;
    547
      int i;
    548
      for (i = 0; i < len; i++)
    549
      {
    550
        case_t* test_case = (case_t*)(start_pointer + i);
    551
        if (test_case->sig_head == the_sig && test_case->sig_tail == the_sig + 2 &&
    552
          (!match_test || strstr(test_case->name, match_test)))
    553
          total++;
    554
      }
    555
      char dir_buf[1024];
    556
      char* cur_dir = getcwd(dir_buf, 1024);
    557
      if (__test_case_setup)
    558
        __test_case_setup();
    559
      int j = 0, pass = 0, skip = 0, fail = 0;
    560
      for (i = 0; i < len; i++)
    561
      {
    562
        case_t* test_case = (case_t*)(start_pointer + i);
    563
        if (test_case->sig_head == the_sig && test_case->sig_tail == the_sig + 2 &&
    564
          (!match_test || strstr(test_case->name, match_test)))
    565
        {
    566
          case_run(test_case, match_test, j++, total, &pass, &skip, &fail);
    567
          chdir(cur_dir);
    568
        }
    569
      }
    570
      if (__test_case_teardown)
    571
        __test_case_teardown();
    572
      case_conclude(pass, skip, fail);
    573
      return fail;
    574
    }
    575
    576
    #endif
    577
    #endif