LCOV - code coverage report
Current view: top level - src/tests - random.c (source / functions) Hit Total Coverage
Test: passgen-test.info Lines: 233 233 100.0 %
Date: 2024-05-03 06:05:14 Functions: 17 17 100.0 %

          Line data    Source code
       1             : #include "passgen/util/random.h"
       2             : 
       3             : #include <stdbool.h>
       4             : #include <stdio.h>
       5             : #include <stdlib.h>
       6             : 
       7             : #include "tests.h"
       8             : 
       9             : #define XORSHIFT_SEED 234720984723
      10             : 
      11             : /// Tests that a given function covers all possible outputs (0..max, inclusive).
      12             : #define TEST_COVERAGE(max, collate, function)                    \
      13             :     do {                                                         \
      14             :         size_t coverage_len = ((size_t) max) / (collate) + 1ULL; \
      15             :         uint8_t *coverage = calloc((coverage_len + 7) / 8, 1);   \
      16             :         bool full_coverage = false;                              \
      17             :         while(!full_coverage) {                                  \
      18             :             for(size_t i = 0; i < 256; i++) {                    \
      19             :                 size_t pos = function / (collate);               \
      20             :                 coverage[pos / 8] |= 1 << (pos % 8);             \
      21             :             }                                                    \
      22             :             full_coverage = true;                                \
      23             :             for(size_t i = 0; i <= (max / (collate)); i++) {     \
      24             :                 if(!(coverage[i / 8] & (1 << (i % 8)))) {        \
      25             :                     full_coverage = false;                       \
      26             :                     break;                                       \
      27             :                 }                                                \
      28             :             }                                                    \
      29             :         }                                                        \
      30             :         free(coverage);                                          \
      31             :     } while(false)
      32             : 
      33           2 : double standard_deviation(size_t count, uint32_t *elements) {
      34             :     (void) count;
      35             :     (void) elements;
      36             :     // TODO: determine deviation
      37           2 :     return 0;
      38             : }
      39             : 
      40             : /// Tests that a given function has an even distribution
      41             : #define TEST_DISTRIBUTION(max, bucket_num, target, function)   \
      42             :     uint32_t *buckets = calloc(bucket_num, sizeof(uint32_t));  \
      43             :     while(true) {                                              \
      44             :         size_t bucket = function / ((max) / (bucket_num) + 1); \
      45             :         buckets[bucket] += 1;                                  \
      46             :         if(buckets[bucket] == target) {                        \
      47             :             break;                                             \
      48             :         }                                                      \
      49             :     }                                                          \
      50             :     assert(standard_deviation(bucket_num, buckets) < 10);      \
      51             :     free(buckets)
      52             : 
      53           1 : test_result test_random_u8(void) {
      54             :     passgen_random random;
      55           1 :     assert(passgen_random_open(&random, NULL));
      56             : 
      57        2313 :     TEST_COVERAGE(UINT8_MAX, 1, passgen_random_u8(&random));
      58             : 
      59           1 :     passgen_random_close(&random);
      60             : 
      61           1 :     return test_ok;
      62             : }
      63             : 
      64           1 : test_result test_random_u8_max(void) {
      65             :     passgen_random random;
      66           1 :     assert(passgen_random_open(&random, NULL));
      67             : 
      68         255 :     for(size_t max = 1; max < UINT8_MAX; max++) {
      69      277344 :         TEST_COVERAGE(max - 1, 1, passgen_random_u8_max(&random, max));
      70             :     }
      71             : 
      72           1 :     passgen_random_close(&random);
      73             : 
      74           1 :     return test_ok;
      75             : }
      76             : 
      77           1 : test_result test_random_u16(void) {
      78             :     passgen_random random;
      79           1 :     assert(passgen_random_open(&random, NULL));
      80             : 
      81    48976355 :     TEST_COVERAGE(UINT16_MAX, 1, passgen_random_u16(&random));
      82             : 
      83           1 :     passgen_random_close(&random);
      84             : 
      85           1 :     return test_ok;
      86             : }
      87             : 
      88           1 : test_result test_random_u16_max(void) {
      89             :     passgen_random random;
      90           1 :     assert(passgen_random_open(&random, NULL));
      91             : 
      92         255 :     for(size_t max = 1; max < UINT8_MAX; max++) {
      93      270977 :         TEST_COVERAGE(max - 1, 1, passgen_random_u16_max(&random, max));
      94             :     }
      95             : 
      96          12 :     for(size_t max = 1; max < UINT16_MAX; max *= 3) {
      97     6040552 :         TEST_COVERAGE(max - 1, 1, passgen_random_u16_max(&random, max));
      98             :     }
      99             : 
     100           1 :     passgen_random_close(&random);
     101             : 
     102           1 :     return test_ok;
     103             : }
     104             : 
     105           1 : test_result test_random_u32(void) {
     106             :     passgen_random random;
     107           1 :     assert(passgen_random_open(&random, NULL));
     108             : 
     109    22037444 :     TEST_COVERAGE(UINT32_MAX, 1 << 16, passgen_random_u32(&random));
     110      956453 :     TEST_DISTRIBUTION(
     111             :         UINT32_MAX,
     112             :         1 << 10,
     113             :         1 << 10,
     114             :         passgen_random_u32(&random));
     115             : 
     116           1 :     passgen_random_close(&random);
     117             : 
     118           1 :     return test_ok;
     119             : }
     120             : 
     121           1 : test_result test_random_u32_max(void) {
     122             :     passgen_random random;
     123           1 :     assert(passgen_random_open(&random, NULL));
     124             : 
     125         255 :     for(size_t max = 1; max < UINT8_MAX; max++) {
     126      270119 :         TEST_COVERAGE(max - 1, 1, passgen_random_u32_max(&random, max));
     127             :     }
     128             : 
     129          12 :     for(size_t max = 1; max < UINT16_MAX; max *= 3) {
     130    16104007 :         TEST_COVERAGE(max - 1, 1, passgen_random_u32_max(&random, max));
     131             :     }
     132             : 
     133           1 :     passgen_random_close(&random);
     134             : 
     135           1 :     return test_ok;
     136             : }
     137             : 
     138           1 : test_result test_random_u64(void) {
     139             :     passgen_random random;
     140           1 :     assert(passgen_random_open(&random, NULL));
     141             : 
     142             :     // FIXME
     143             :     // TEST_COVERAGE(UINT64_MAX, 1ULL << 48, 1024, passgen_random_u32(&random));
     144      927705 :     TEST_DISTRIBUTION(
     145             :         UINT64_MAX,
     146             :         1 << 10,
     147             :         1 << 10,
     148             :         passgen_random_u64(&random));
     149             : 
     150           1 :     passgen_random_close(&random);
     151             : 
     152           1 :     return test_ok;
     153             : }
     154             : 
     155           1 : test_result test_random_u64_max(void) {
     156             :     passgen_random random;
     157           1 :     assert(passgen_random_open(&random, NULL));
     158             : 
     159         255 :     for(size_t max = 1; max < UINT8_MAX; max++) {
     160      276467 :         TEST_COVERAGE(max - 1, 1, passgen_random_u64_max(&random, max));
     161             :     }
     162             : 
     163          12 :     for(size_t max = 1; max < UINT16_MAX; max *= 3) {
     164    11302227 :         TEST_COVERAGE(max - 1, 1, passgen_random_u64_max(&random, max));
     165             :     }
     166             : 
     167     1000000 :     for(size_t i = 1; i < 1000000; i++) {
     168      999999 :         uint32_t max = passgen_random_u64(&random);
     169             : 
     170      999999 :         assert(passgen_random_u64_max(&random, max) < max);
     171             :     }
     172             : 
     173           1 :     passgen_random_close(&random);
     174             : 
     175           1 :     return test_ok;
     176             : }
     177             : 
     178           1 : test_result test_random_new(void) {
     179           1 :     passgen_random *random_default = passgen_random_new(NULL);
     180           1 :     assert(random_default);
     181           1 :     assert(random_default->read);
     182           1 :     assert(random_default->close);
     183             : 
     184           1 :     passgen_random *random = passgen_random_new("system");
     185           1 :     assert(random);
     186           1 :     assert_eq(random->read, random_default->read);
     187           1 :     assert_eq(random->close, random_default->close);
     188           1 :     passgen_random_free(random);
     189           1 :     passgen_random_free(random_default);
     190             : 
     191           1 :     random = passgen_random_new("zero");
     192           1 :     assert(random);
     193           1 :     assert(random->read);
     194           1 :     assert(random->close);
     195           1 :     assert_eq(passgen_random_u8(random), 0);
     196           1 :     assert_eq(passgen_random_u16(random), 0);
     197           1 :     assert_eq(passgen_random_u32(random), 0);
     198           1 :     assert_eq(passgen_random_u64(random), 0);
     199           1 :     passgen_random_free(random);
     200             : 
     201           1 :     random = passgen_random_new("xorshift:1234");
     202           1 :     assert(random);
     203           1 :     assert(random->read);
     204           1 :     assert(random->close);
     205           1 :     assert_eq(passgen_random_u8(random), 91);
     206           1 :     assert_eq(passgen_random_u16(random), 11632);
     207           1 :     assert_eq(passgen_random_u32(random), 79584);
     208           1 :     assert_eq(passgen_random_u64(random), 3801598356675656448ULL);
     209           1 :     assert_eq(passgen_random_u64_max(random, 999999999), 851051297);
     210           1 :     passgen_random_free(random);
     211             : 
     212           1 :     random = passgen_random_new("file:/dev/zero");
     213           1 :     assert(random);
     214           1 :     assert(random->read);
     215           1 :     assert(random->close);
     216           1 :     assert_eq(passgen_random_u8(random), 0);
     217           1 :     assert_eq(passgen_random_u16(random), 0);
     218           1 :     assert_eq(passgen_random_u32(random), 0);
     219           1 :     assert_eq(passgen_random_u64(random), 0);
     220           1 :     passgen_random_free(random);
     221             : 
     222           1 :     assert(!passgen_random_new("other"));
     223           1 :     assert(!passgen_random_new("xorshift:abc"));
     224           1 :     assert(!passgen_random_new("xorshift:"));
     225           1 :     assert(!passgen_random_new("file:"));
     226           1 :     assert(!passgen_random_new("file:/dev/nonexistant"));
     227             : 
     228           1 :     return test_ok;
     229             : }
     230             : 
     231           1 : test_result test_random_file(void) {
     232           1 :     FILE *file = fopen("/dev/zero", "r");
     233           1 :     assert(file);
     234           1 :     passgen_random *random = passgen_random_new_file(file);
     235           1 :     assert(random);
     236           1 :     assert(random->read);
     237           1 :     assert(random->close);
     238           1 :     assert_eq(passgen_random_u8(random), 0);
     239           1 :     assert_eq(passgen_random_u16(random), 0);
     240           1 :     assert_eq(passgen_random_u32(random), 0);
     241           1 :     assert_eq(passgen_random_u64(random), 0);
     242           1 :     passgen_random_free(random);
     243             : 
     244           1 :     return test_ok;
     245             : }
     246             : 
     247           1 : test_result test_random_zero(void) {
     248           1 :     passgen_random *random = passgen_random_new_zero();
     249           1 :     assert(random);
     250           1 :     assert(random->read);
     251           1 :     assert(random->close);
     252           1 :     assert_eq(passgen_random_u8(random), 0);
     253           1 :     assert_eq(passgen_random_u16(random), 0);
     254           1 :     assert_eq(passgen_random_u32(random), 0);
     255           1 :     assert_eq(passgen_random_u64(random), 0);
     256           1 :     passgen_random_free(random);
     257             : 
     258           1 :     return test_ok;
     259             : }
     260             : 
     261           1 : test_result test_random_new_path(void) {
     262             :     passgen_random *random;
     263           1 :     random = passgen_random_new_path("/dev/nonexistent");
     264           1 :     assert(!random);
     265             : 
     266             :     // reading from /dev/zero should always yield zero.
     267           1 :     random = passgen_random_new_path("/dev/zero");
     268           1 :     assert(random);
     269           1 :     assert(random->data);
     270           1 :     assert(passgen_random_u8(random) == 0);
     271           1 :     assert(passgen_random_u16(random) == 0);
     272           1 :     assert(passgen_random_u32(random) == 0);
     273           1 :     assert(passgen_random_u64(random) == 0);
     274           1 :     passgen_random_free(random);
     275             : 
     276           1 :     return test_ok;
     277             : }
     278             : 
     279           1 : test_result test_random_open(void) {
     280             :     passgen_random random;
     281           1 :     assert(passgen_random_open(&random, NULL));
     282           1 :     assert(random.read);
     283           1 :     assert(random.close);
     284           1 :     passgen_random_close(&random);
     285           1 :     assert(!random.read);
     286             : 
     287           1 :     assert(passgen_random_open(&random, "system"));
     288           1 :     assert(random.read);
     289           1 :     assert(random.close);
     290           1 :     passgen_random_close(&random);
     291             : 
     292           1 :     assert(passgen_random_open(&random, "xorshift:1234"));
     293           1 :     assert(random.read);
     294           1 :     assert(random.close);
     295           1 :     passgen_random_close(&random);
     296             : 
     297           1 :     assert(passgen_random_open(&random, "zero"));
     298           1 :     assert(random.read);
     299           1 :     assert(random.close);
     300           1 :     passgen_random_close(&random);
     301             : 
     302           1 :     assert(passgen_random_open(&random, "file:/dev/random"));
     303           1 :     assert(random.read);
     304           1 :     assert(random.close);
     305           1 :     passgen_random_close(&random);
     306             : 
     307           1 :     assert(!passgen_random_open(&random, "other"));
     308           1 :     assert(!passgen_random_open(&random, "file:/dev/notexists"));
     309           1 :     assert(!passgen_random_open(&random, "xorshift:"));
     310           1 :     assert(!passgen_random_open(&random, "xorshift:abc"));
     311             : 
     312           1 :     return test_ok;
     313             : }
     314             : 
     315           1 : test_result test_random_open_path(void) {
     316             :     passgen_random random;
     317           1 :     assert(!passgen_random_open_path(&random, "/dev/nonexistent"));
     318             : 
     319           1 :     assert(passgen_random_open_path(&random, "/dev/zero"));
     320           1 :     assert(random.data);
     321           1 :     assert(passgen_random_u8(&random) == 0);
     322           1 :     assert(passgen_random_u16(&random) == 0);
     323           1 :     assert(passgen_random_u32(&random) == 0);
     324           1 :     assert(passgen_random_u64(&random) == 0);
     325           1 :     passgen_random_close(&random);
     326           1 :     assert(!random.data);
     327             : 
     328           1 :     return test_ok;
     329             : }
     330             : 
     331           1 : test_result test_random_read(void) {
     332             :     passgen_random random;
     333           1 :     assert(passgen_random_open_xorshift(&random, XORSHIFT_SEED));
     334             : 
     335           1 :     uint8_t data[2000] = {0};
     336             : 
     337             :     // fill small.
     338           1 :     passgen_random_read(&random, &data[0], 1);
     339           1 :     assert(random.pos == 1);
     340           1 :     assert(data[0] == random.buffer[0]);
     341           1 :     assert(data[1] == 0);
     342             : 
     343           1 :     passgen_random_read(&random, &data[0], 2);
     344           1 :     assert(random.pos == 3);
     345           1 :     assert(data[0] == random.buffer[1]);
     346           1 :     assert(data[1] == random.buffer[2]);
     347           1 :     assert(data[2] == 0);
     348             : 
     349           1 :     passgen_random_read(&random, &data[0], 4);
     350           1 :     assert(random.pos == 7);
     351           1 :     assert(data[0] == random.buffer[3]);
     352           1 :     assert(data[1] == random.buffer[4]);
     353           1 :     assert(data[2] == random.buffer[5]);
     354           1 :     assert(data[3] == random.buffer[6]);
     355           1 :     assert(data[4] == 0);
     356             : 
     357           1 :     passgen_random_read(&random, &data[0], 8);
     358           1 :     assert(random.pos == 15);
     359           1 :     assert(data[0] == random.buffer[7]);
     360           1 :     assert(data[1] == random.buffer[8]);
     361           1 :     assert(data[2] == random.buffer[9]);
     362           1 :     assert(data[3] == random.buffer[10]);
     363           1 :     assert(data[4] == random.buffer[11]);
     364           1 :     assert(data[5] == random.buffer[12]);
     365           1 :     assert(data[6] == random.buffer[13]);
     366           1 :     assert(data[7] == random.buffer[14]);
     367           1 :     assert(data[8] == 0);
     368             : 
     369             :     // test wraparound.
     370        1009 :     while(random.pos != (sizeof(random.buffer) - 1)) {
     371        1008 :         passgen_random_read(&random, &data[0], 1);
     372             :     }
     373           1 :     passgen_random_read(&random, &data[0], 1);
     374           1 :     assert(random.pos == 0);
     375             : 
     376        1024 :     while(random.pos != (sizeof(random.buffer) - 1)) {
     377        1023 :         passgen_random_read(&random, &data[0], 1);
     378             :     }
     379           1 :     passgen_random_read(&random, &data[0], 3);
     380           1 :     assert(data[1] == random.buffer[0]);
     381           1 :     assert(data[2] == random.buffer[1]);
     382           1 :     assert(random.pos == 2);
     383             : 
     384             :     // test reading larger.
     385           1 :     passgen_random_read(&random, &data[0], sizeof(random.buffer) + 1);
     386           1 :     assert(random.pos == 2);
     387             : 
     388           1 :     passgen_random_close(&random);
     389             : 
     390           1 :     return test_ok;
     391             : }
     392             : 
     393           1 : test_result test_random_xorshift(void) {
     394             :     passgen_random random;
     395             : 
     396             :     // using a state of zero generates only zeroes.
     397           1 :     assert(passgen_random_open_xorshift(&random, 0) != NULL);
     398           1 :     assert(passgen_random_u8(&random) == 0);
     399           1 :     assert(passgen_random_u16(&random) == 0);
     400           1 :     assert(passgen_random_u32(&random) == 0);
     401           1 :     assert(passgen_random_u64(&random) == 0);
     402           1 :     passgen_random_close(&random);
     403             : 
     404             :     // same seed always yields the same output
     405           1 :     assert(passgen_random_open_xorshift(&random, 123) != NULL);
     406           1 :     assert(passgen_random_u8(&random) == 187);
     407           1 :     assert(passgen_random_u16(&random) == 31102);
     408           1 :     assert(passgen_random_u32(&random) == 7933);
     409           1 :     assert(passgen_random_u64(&random) == 2214108778545186304ULL);
     410           1 :     passgen_random_close(&random);
     411             : 
     412           1 :     return test_ok;
     413             : }
     414             : 
     415             : #undef XORSHIFT_SEED

Generated by: LCOV version 1.14