1## no critic (RCS,VERSION,encapsulation,Module)
2
3use strict;
4use warnings;
5use Test::More;
6
7
8use Bytes::Random::Secure;
9
10# We'll use a weaker source because we're testing for function, quality
11# isn't being contested here.  Weak=>1 should assure we use /dev/urandom where
12# it's available.  Low entropy chosen to preserve our source.
13my $random = Bytes::Random::Secure->new(
14  NonBlocking => 1,
15  Weak        => 1,
16  Bits        => 64
17);
18
19
20# Tests for _closest_divisor().
21
22my @divisors = (  1,  1,  2,  4,  4,  8,  8,  8,  8,
23                 16, 16, 16, 16, 16, 16, 16, 16,
24                 32, 32, 32, 32, 32, 32, 32, 32, 32
25);
26
27# Nearest factor of 2**32 >= $ix;
28
29for my $ix ( 0 .. $#divisors ) {
30  is( $random->_closest_divisor($ix), $divisors[$ix],
31      "_closest_divisor($ix) == $divisors[$ix]" );
32}
33
34is( $random->_closest_divisor(), 1,
35    '_closest_divisor() == 1; No param list defaults to zero.' );
36
37ok( ! eval { $random->_closest_divisor(-1); 1; },
38    '_closest_divisor(-1) throws on negative input.' );
39
40ok( ! eval { $random->_closest_divisor(2**33); 1 },
41    '_closest_divisor(2**33) throws (out of range input).' );
42
43is( $random->_closest_divisor(2**32), 2**32,
44    "_closest_divisor(2**32) == 2**32." );
45
46is( $random->_closest_divisor( 2**32 - 1 ), 2**32,
47    '_closest_divisor(2**32-1) == 2**32' );
48
49# Tests for _ranged_randoms().
50
51for my $count ( 0 .. 11 ) {
52  is( scalar @{[ $random->_ranged_randoms(16,$count) ]}, $count,
53      "Requested $count ranged randoms, and got $count." );
54}
55
56is( scalar @{[ $random->_ranged_randoms(16) ]}, 0,
57    'Requested undefined quantity of ranged randoms, and got zero (default).' );
58
59my( $min, $max );
60$min = $max = $random->_ranged_randoms(200, 1);
61
62my $MAX_TRIES = 1_000_000;
63my $tries     = 0;
64while( ( $min > 0 || $max < 199 ) && $tries++ < $MAX_TRIES ) {
65  my $random = ($random->_ranged_randoms(200,1))[0];
66  $min = $random < $min ? $random : $min;
67  $max = $random > $max ? $random : $max;
68}
69is( $min, 0, '_ranged_randoms generates range minimum.' );
70is( $max, 199, '_ranged_randoms generates range maximum.' );
71note "It took $tries tries to hit both min and max.";
72
73# Testing random_string_from().
74
75is( $random->string_from( 'abc', 0 ), '',
76    'string_from() with a quantity of zero returns empty string.' );
77
78is( $random->string_from( 'abc' ), '',
79    'string_from() with an undefined quantity defaults to zero.' );
80
81is( length( $random->string_from( 'abc', 5 ) ), 5,
82    'string_from(): Requested 5, got 5.' );
83
84my %bag;
85$tries = 0;
86while( scalar( keys %bag ) < 26 && $tries++ < $MAX_TRIES ) {
87  $bag{ $random->string_from( 'abcdefghijklmnopqrstuvwxyz', 1 ) }++;
88}
89
90is( scalar( keys %bag ), 26,
91   'string_from() returned all bytes from bag, and only bytes from bag.'
92);
93
94ok( ! scalar( grep{ $_ =~ m/[^abcdefghijklmnopqrstuvwxyz]/ } keys %bag ),
95    'string_from(): No out of range characters in output.' );
96
97ok( $tries >= 26,
98    'string_from():Test validation: took at least 26 tries to hit all 26.' );
99
100note "It took $tries tries to hit them all at least once.";
101
102ok( ! eval { $random->string_from(); 1; },
103    'No bag string passed (or bag of zero length) throws an exception.' );
104
105done_testing();
106