LCOV - code coverage report
Current view: top level - src/tests - tests.c (source / functions) Hit Total Coverage
Test: passgen-test.info Lines: 51 136 37.5 %
Date: 2024-05-03 06:05:14 Functions: 2 7 28.6 %

          Line data    Source code
       1             : #include "tests.h"
       2             : #include <getopt.h>
       3             : #include <stdbool.h>
       4             : #include <stdlib.h>
       5             : #include <string.h>
       6             : #include <time.h>
       7             : #include <unistd.h>
       8             : 
       9             : test_result test_ok = {.ok = true};
      10             : 
      11             : // check if a string has the given prefix
      12           0 : static inline bool strprefix(const char *prefix, const char *string) {
      13           0 :     for(size_t offset = 0; prefix[offset]; offset++) {
      14           0 :         if(prefix[offset] != string[offset]) {
      15           0 :             return false;
      16             :         }
      17             :     }
      18             : 
      19           0 :     return true;
      20             : }
      21             : 
      22             : // swap memory of a given size
      23           0 : static inline void memswap(void *a, void *b, size_t size) {
      24           0 :     char data[size];
      25             : 
      26           0 :     memcpy(data, a, size);
      27           0 :     memcpy(a, b, size);
      28           0 :     memcpy(b, data, size);
      29           0 : }
      30             : 
      31             : // shuffle the list of tests
      32           0 : static inline void shuffle(test_entry *tests) {
      33           0 :     for(size_t i = 0; i < tests_len - 1; i++) {
      34           0 :         size_t j = i + rand() / (RAND_MAX / (tests_len - i) + 1);
      35           0 :         memswap(&tests[i], &tests[j], sizeof(test_entry));
      36             :     }
      37           0 : }
      38             : 
      39           0 : static inline void filter(bool *enabled, const char *name) {
      40           0 :     for(size_t i = 0; i < tests_len; i++) {
      41           0 :         if(strprefix(name, tests[i].name)) {
      42           0 :             enabled[i] = true;
      43             :         }
      44             :     }
      45           0 : }
      46             : 
      47           0 : static void show_help(const char *name) {
      48           0 :     fprintf(
      49             :         stderr,
      50             :         "%s%s",
      51             :         name,
      52             :         " [OPTIONS] [FILTERS...]\n"
      53             :         "Runs unit tests for passgen and reports results.\n\n"
      54             :         "Options\n"
      55             :         "  -h, --help\n"
      56             :         "    Shows this help text\n"
      57             :         "  -v, --verbose\n"
      58             :         "    Shows more verbose information\n"
      59             :         "  -s, --shuffle\n"
      60             :         "    Randomises the order in which tests are run\n\n"
      61             :         "Filters\n"
      62             :         "  Match based on prefix. A filter such as 'random' would match any "
      63             :         "test "
      64             :         "case\n"
      65             :         "  beginning with random, such as random_init, random_u8, etc.\n");
      66           0 : }
      67             : 
      68           1 : int main(int argc, char *argv[]) {
      69             :     // initialise shitty pseudorandom number generator
      70           1 :     srand(time(NULL));
      71           1 :     int verbosity = isatty(fileno(stdout));
      72             : 
      73             :     static struct option long_options[] = {
      74             :         {"verbose", no_argument, 0, 'v'},
      75             :         {"shuffle", no_argument, 0, 's'},
      76             :         {"help", no_argument, 0, 'h'},
      77             :         {0, 0, 0, 0}};
      78             : 
      79           0 :     while(1) {
      80           1 :         int option_index = 0;
      81             : 
      82           1 :         int c = getopt_long(argc, argv, "vsh", long_options, &option_index);
      83             : 
      84             :         /* Detect the end of the options. */
      85           1 :         if(c == -1) break;
      86             : 
      87           0 :         switch(c) {
      88           0 :             case 0:
      89             :                 /* If this option set a flag, do nothing else now. */
      90           0 :                 if(long_options[option_index].flag != 0) break;
      91           0 :                 printf("option %s", long_options[option_index].name);
      92           0 :                 if(optarg) printf(" with arg %s", optarg);
      93           0 :                 printf("\n");
      94           0 :                 break;
      95             : 
      96           0 :             case 's':
      97           0 :                 shuffle(tests);
      98           0 :                 break;
      99             : 
     100           0 :             case 'v':
     101           0 :                 verbosity = 2;
     102           0 :                 break;
     103             : 
     104           0 :             case 'h':
     105           0 :                 show_help(argv[0]);
     106           0 :                 return EXIT_SUCCESS;
     107             : 
     108           0 :             case '?':
     109           0 :                 show_help(argv[0]);
     110           0 :                 return EXIT_FAILURE;
     111             : 
     112           0 :             default:
     113           0 :                 abort();
     114             :         }
     115             :     }
     116             : 
     117           1 :     bool enabled_tests[tests_len];
     118           1 :     if(optind < argc) {
     119           0 :         memset(enabled_tests, 0, sizeof(enabled_tests));
     120             : 
     121           0 :         for(int i = optind; i < argc; i++) {
     122           0 :             filter(enabled_tests, argv[i]);
     123             :         }
     124             :     } else {
     125           1 :         memset(enabled_tests, 1, sizeof(enabled_tests));
     126             :     }
     127             : 
     128             :     // determine max test name width and count of enabled tests.
     129           1 :     int width = 20;
     130           1 :     size_t enabled_count = 0;
     131         139 :     for(size_t i = 0; i < tests_len; i++) {
     132         138 :         if(enabled_tests[i]) {
     133         138 :             enabled_count++;
     134         138 :             int len = strlen(tests[i].name);
     135         138 :             if(len > width) width = len;
     136             :         }
     137             :     }
     138             : 
     139           1 :     size_t failures = 0;
     140           1 :     size_t success = 0;
     141         139 :     for(size_t i = 0; tests[i].name; ++i) {
     142         138 :         if(enabled_tests[i]) {
     143         138 :             test_run r = {
     144             :                 .entry = tests[i],
     145             :                 .verbosity = verbosity,
     146         138 :                 .number = success + failures,
     147             :                 .amount = enabled_count};
     148             : 
     149         138 :             if(run(r, width)) {
     150         138 :                 success += 1;
     151             :             } else {
     152           0 :                 failures += 1;
     153             :             }
     154             :         }
     155             :     }
     156             : 
     157           1 :     if(success == enabled_count) {
     158           1 :         switch(verbosity) {
     159           1 :             case 0:
     160           1 :                 printf("=> %zi/%zi tests passed.\n", success, enabled_count);
     161           1 :                 break;
     162           0 :             case 1:
     163             :             case 2:
     164           0 :                 printf(
     165             :                     "\033[1;34m=>\033[0m %zi/%zi tests "
     166             :                     "\033[32mpassed\033[0m.\n",
     167             :                     success,
     168             :                     enabled_count);
     169           0 :                 break;
     170             :         }
     171           1 :     } else {
     172           0 :         switch(verbosity) {
     173           0 :             case 0:
     174           0 :                 printf(
     175             :                     "=> %zi/%zi tests passed, %zi failures.\n",
     176             :                     success,
     177             :                     enabled_count,
     178             :                     failures);
     179           0 :                 break;
     180           0 :             case 1:
     181             :             case 2:
     182           0 :                 printf(
     183             :                     "\033[1;34m=>\033[0m %zi/%zi tests "
     184             :                     "\033[32mpassed\033[0m, "
     185             :                     "%zi \033[31mfailure\033[0m.\n",
     186             :                     success,
     187             :                     enabled_count,
     188             :                     failures);
     189           0 :                 break;
     190             :         }
     191           1 :     }
     192             : 
     193           1 :     if(failures) {
     194           0 :         return EXIT_FAILURE;
     195             :     } else {
     196           1 :         return EXIT_SUCCESS;
     197             :     }
     198             : }
     199             : 
     200         138 : bool run(test_run test, int padding) {
     201         138 :     switch(test.verbosity) {
     202         138 :         case 0:
     203             :             // not a tty, regular
     204         138 :             printf(
     205             :                 "[%i/%i] running %s\n",
     206         138 :                 test.number + 1,
     207             :                 test.amount,
     208             :                 test.entry.name);
     209         138 :             fflush(stdout);
     210         138 :             break;
     211           0 :         case 1:
     212             :             // tty, regular
     213           0 :             printf(
     214             :                 "\033[1K\r[%i/%i] running %s",
     215           0 :                 test.number + 1,
     216             :                 test.amount,
     217             :                 test.entry.name);
     218           0 :             fflush(stdout);
     219           0 :             break;
     220           0 :         default:
     221           0 :             break;
     222             :     }
     223             : 
     224         138 :     clock_t before = clock();
     225         138 :     test_result ret = test.entry.func();
     226         138 :     clock_t total = clock() - before;
     227             : 
     228         138 :     double time = total / (CLOCKS_PER_SEC * 1.0);
     229             : 
     230         138 :     switch(test.verbosity) {
     231         138 :         case 0:
     232         138 :             if(!ret.ok) {
     233           0 :                 printf(
     234             :                     "  %s failed at %s:%zi\n",
     235             :                     ret.assertion,
     236             :                     ret.func,
     237             :                     ret.line);
     238         138 :             } else if(time > 0.1) {
     239           3 :                 printf("=> test run took too long (%4.3lfs).\n", time);
     240             :             }
     241         138 :             break;
     242           0 :         case 1:
     243           0 :             if(!ret.ok) {
     244           0 :                 printf(
     245             :                     "\n  \033[0;33m%s\033[0m \033[0;31mfailed\033[0m at "
     246             :                     "%s:%zi\n",
     247             :                     ret.assertion,
     248             :                     ret.func,
     249             :                     ret.line);
     250           0 :             } else if(time > 0.1) {
     251           0 :                 printf("\n  \033[0;33mtest run took %4.3lfs.\033[0m\n", time);
     252           0 :             } else if((test.number + 1) == test.amount) {
     253           0 :                 printf("\n");
     254             :             }
     255           0 :             break;
     256           0 :         case 2:
     257           0 :             if(ret.ok) {
     258           0 :                 printf(
     259             :                     "%-*s \033[0;32mpassed\033[0m in %s%4.3lfs\033[0m.\n",
     260             :                     padding,
     261             :                     test.entry.name,
     262             :                     (time > 0.1) ? "\033[0;33m" : "",
     263             :                     time);
     264             :             } else {
     265           0 :                 printf(
     266             :                     "%-*s \033[0;31mfailed\033[0m in %4.3lfs.\n",
     267             :                     padding,
     268             :                     test.entry.name,
     269             :                     time);
     270           0 :                 printf(
     271             :                     "    \033[0;31m%s\033[0m failed at %s:%zi\n",
     272             :                     ret.assertion,
     273             :                     ret.func,
     274             :                     ret.line);
     275             :             }
     276           0 :             break;
     277           0 :         default:
     278           0 :             break;
     279             :     }
     280             : 
     281         138 :     return ret.ok;
     282             : }

Generated by: LCOV version 1.14