File Coverage

blib/lib/Mock/Data/Plugin/Number.pm
Criterion Covered Total %
statement 62 62 100.0
branch 49 56 87.5
condition 31 42 73.8
subroutine 9 9 100.0
pod 6 7 85.7
total 157 176 89.2


line stmt bran cond sub pod time code
1             package Mock::Data::Plugin::Number;
2 2     2   1108 use Mock::Data::Plugin -exporter_setup => 1;
  2         5  
  2         16  
3 2     2   383 use Mock::Data::Util 'mock_data_subclass';
  2         4  
  2         11  
4             my @generators= qw( integer decimal float byte sequence uuid );
5             export(@generators);
6              
7             # ABSTRACT: Mock::Data plugin that provides basic numeric generators
8             our $VERSION = '0.03'; # VERSION
9              
10              
11             sub apply_mockdata_plugin {
12 1     1 0 3 my ($class, $mock)= @_;
13 1         23 $mock->add_generators(
14             map +("Number::$_" => $class->can($_)), @generators
15             );
16             }
17              
18             our $int_bits= 15;
19             ++$int_bits while (1 << $int_bits) > 1 # old perls used to perform MOD 32 on the shift argument
20             and $int_bits < 256; # prevent infinite loop in case of broken assumptions
21              
22             our $float_bits= 22;
23             ++$float_bits while 2**$float_bits != 2**$float_bits + 1
24             and $float_bits < 256; # prevent infinite loop in case of broken assumptions
25              
26              
27             sub integer {
28 135     135 1 218 my $mock= shift;
29 135 100       288 my $params= ref $_[0] eq 'HASH'? shift : undef;
30 135         182 my $size= shift;
31 135         191 my $signed;
32 135 100       241 if ($params) {
33 60   66     389 $size //= $params->{digits} // $params->{size};
      66        
34 60         109 $signed= !$params->{unsigned};
35             }
36 75         113 else { $signed= 1; }
37              
38 135 100       241 if (defined $size) {
39 85         225 my $digits= 1 + int rand($size-1);
40 85         191 my $val= 10**($digits-1) + int rand(9 * 10**($digits-1));
41 85 100 100     413 return $signed && int rand 2? -$val : $val;
42             } else {
43 50 100 100     140 my $bits= ($params? $params->{bits} : undef) // 32;
44 50 50       111 $bits= $int_bits if $bits > $int_bits; # can't generate more than $int_bits anyway
45 50 100       139 $bits= int rand($signed? $bits : $bits+1);
46             # calls to rand() only return 53 bits, because it is a double. To get 64, need to
47             # combine multiple rands. Also, can't get 32 bits from rand on 32bit arch.
48 50 100 66     201 my $val= $bits < $int_bits && $bits < $float_bits? int(rand(1<<$int_bits))
49             : unpack('J', byte($mock, 8)) >> ($int_bits-$bits);
50 50 100 100     311 return $signed && int rand 2? -$val : $val;
51             }
52             }
53              
54              
55             sub decimal {
56 55     55 1 94 my $mock= shift;
57 55 50       106 my $params= ref $_ eq 'HASH'? shift : undef;
58 55 50 66     152 my $size= shift // ($params? $params->{size} : undef) // 11;
      50        
59 55         78 my $scale= 0;
60 55 100       142 ($size, $scale)= @$size if ref $size eq 'ARRAY';
61 55 100       135 my $val= integer($mock, $size > $scale? $size : $scale-1);
62 55 100       119 if ($scale) {
63 35 100       74 if ($val < 0) {
64 23 100       97 substr($val,1,0,'0'x($scale+2 - length $val))
65             if length $val < $scale+2;
66             } else {
67 12 100       55 substr($val,0,0,'0'x($scale+1 - length $val))
68             if length $val < $scale+1;
69             }
70 35         71 substr($val, -$scale, 0)= '.';
71             }
72 55         211 return $val;
73             }
74              
75              
76             sub float {
77 60     60 1 91 my $mock= shift;
78 60 100       142 my $params= ref $_[0] eq 'HASH'? shift : undef;
79 60 100 100     264 my $size= shift // ($params? ($params->{digits} // $params->{size}) : undef);
      100        
80 60 100       130 if (defined $size) {
81 15         61 return decimal($mock, [$size, 1+int rand($size-1)]);
82             }
83             else {
84 45 100 66     190 my $bits= shift // ($params? $params->{bits} : undef) // 23;
      100        
85 45 50       93 $bits= $float_bits if $bits > $float_bits;
86             # This algorithm chooses floating point numbers that don't lose precision when cast into
87             # a float of the specified number of significant bits.
88 45 100       126 my $sign= int rand 2? -1 : 1;
89 45         132 my $exponent= 2 ** -int rand $bits;
90 45         81 my $significand= int rand 2**$bits;
91 45         210 return $sign * $exponent * $significand;
92             }
93             }
94              
95              
96             sub byte {
97 23     23 1 41 my ($mock, $count)= @_;
98 23 100       81 return pack('C',rand(256))
99             unless $count;
100 18         33 my $buf= '';
101 18 50       56 if (defined $count) {
102 18 50 33     72 if ($int_bits > 32 && $float_bits > 32) {
103 18         448 $buf .= pack('L', rand(1<<32))
104             while length $buf < $count;
105             }
106 18         84 $buf .= pack('S', rand(1<<16))
107             while length $buf < $count;
108 18         30 substr($buf,$count)= '';
109             }
110 18         74 return $buf;
111             }
112              
113              
114             sub sequence {
115 35     35 1 56 my $mock= shift;
116 35 100       74 my $params= ref $_[0] eq 'HASH'? shift : undef;
117             my $name= shift // ($params? $params->{sequence_name} : undef)
118 35 50 66     120 // Carp::croak("sequence_name is required for sequence generator");
      33        
119 35         102 return ++$mock->generator_state->{"Number::sequence"}{$name};
120             }
121              
122              
123             sub uuid {
124 10     10 1 97 sprintf "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
125             rand(1<<16), rand(1<<16), rand(1<<16),
126             (4<<12)|rand(1<<12), (1<<15)|rand(1<<14),
127             rand(1<<16), rand(1<<16), rand(1<<16)
128             }
129              
130             1;
131              
132             __END__