File Coverage

blib/lib/Schedule/Activity/Node.pm
Criterion Covered Total %
statement 182 193 94.3
branch 99 116 85.3
condition 45 75 60.0
subroutine 19 20 95.0
pod 0 10 0.0
total 345 414 83.3


line stmt bran cond sub pod time code
1             package Schedule::Activity::Node;
2              
3 2     2   7004 use strict;
  2         5  
  2         78  
4 2     2   21 use warnings;
  2         5  
  2         124  
5 2     2   11 use List::Util qw/any/;
  2         4  
  2         241  
6 2     2   703 use Ref::Util qw/is_arrayref is_hashref is_ref/;
  2         2952  
  2         192  
7 2     2   17 use Scalar::Util qw/blessed looks_like_number/;
  2         4  
  2         5743  
8              
9             our $VERSION='0.3.0';
10              
11             my %property=map {$_=>undef} qw/tmmin tmavg tmmax next finish message attribute note attributes require/;
12              
13             my %defaults=(
14             'tmmax/tmavg'=>5/4,
15             'tmavg/tmmin'=>4/3,
16             'tmmax/tmmin'=>5/3,
17             );
18              
19             sub new {
20 173933     173933 0 904221 my ($ref,%opt)=@_;
21 173933   33     591060 my $class=is_ref($ref)||$ref;
22 173933         547461 return bless(\%opt,$class);
23             }
24              
25             sub defaulting {
26 411     411 0 13394 my ($node)=@_;
27 411 0   0   1745 my $mult=sub { my ($x,$y)=@_; if(defined($y)) { return $x*$y } return };
  0         0  
  0         0  
  0         0  
  0         0  
28 411         696 my @lln;
29 411         865 foreach my $k (qw/tmmin tmavg tmmax/) {
30 1233 100       3395 if(looks_like_number($$node{$k})) { push @lln,1 }
  1066         2104  
31 167         289 else { delete($$node{$k}); push @lln,0 }
  167         331  
32             }
33 411 100   1071   1811 if(any {!$_} @lln) {
  1071         1944  
34 82 100       186 if($lln[1]) {
    100          
    100          
35 75   33     453 $$node{tmmax}//=$$node{tmavg}*$defaults{'tmmax/tmavg'};
36 75   66     296 $$node{tmmin}//=$$node{tmavg}/$defaults{'tmavg/tmmin'};
37             }
38             elsif($lln[0]) {
39 2 100       6 if($lln[2]) { $$node{tmavg}=0.5*($$node{tmmin}+$$node{tmmax}) }
  1         6  
40             else {
41 1   33     9 $$node{tmmax}//=$$node{tmmin}*$defaults{'tmmax/tmmin'};
42 1   33     16 $$node{tmavg}//=$$node{tmmin}*$defaults{'tmavg/tmmin'};
43             }
44             }
45             elsif($lln[2]) {
46 1   33     10 $$node{tmavg}//=$$node{tmmax}/$defaults{'tmmax/tmavg'};
47 1   33     8 $$node{tmmin}//=$$node{tmmax}/$defaults{'tmmax/tmmin'};
48             }
49             }
50 411         2164 return;
51             }
52              
53             sub nextnames {
54 718     718 0 6525 my ($self,$filtered,$node)=@_;
55 718 100       1438 if(!defined($node)) { $node=$$self{next}; $filtered=1 }
  1         3  
  1         2  
56 718 100       1316 if(is_arrayref($node)) {
    100          
    50          
57 705         982 my @res;
58 705         1310 foreach my $next (@$node) {
59 1661 100 100     3006 if(!$filtered) { push @res,$next }
  1651 100 66     3243  
    100          
60 3         6 elsif(defined($next)&&!is_ref($next)) { push @res,$next }
61 5         11 elsif(is_hashref($next)&&$$next{keyname}) { push @res,$$next{keyname} }
62             }
63 705         2192 return @res;
64             }
65 12         59 elsif(is_hashref($node)) { return keys %$node }
66 0         0 elsif(!defined($node)) { return }
67 1         9 die 'Expected array/hash'; # only used during validation, not runtime
68             }
69              
70             sub nextremap {
71 404     404 0 5903 my ($self,$mapping)=@_;
72 404 100       1131 if(is_arrayref($$self{next})) {
    100          
73 299         435 my @nexts=grep {defined($_)} map {$$mapping{$_}} @{$$self{next}};
  824         1651  
  824         1733  
  299         597  
74 299 100       566 if(@nexts) { $$self{next}=\@nexts }
  297         572  
75 2         7 else { delete($$self{next}) }
76             }
77             elsif(is_hashref($$self{next})) {
78 7         15 while(my ($name,$next)=each %{$$self{next}}) {
  35         166  
79 28         46 my $x=$$mapping{$name};
80 28 100       61 if($x) { $$next{node}=$x }
  26         58  
81 2         6 else { delete($$self{next}{$name}) }
82             }
83 7 100       15 if(!%{$$self{next}}) { delete($$self{next}) }
  7         25  
  1         3  
84             }
85 404         859 return $self;
86             }
87              
88             sub validate {
89 414     414 0 348933 my (%node)=@_;
90 414 50       987 if($node{_valid}) { return }
  0         0  
91 414         783 my (@errors,@invalids,@tmseq);
92 414         1154 foreach my $k (grep {!exists($property{$_})} keys(%node)) { push @errors,"Invalid key: $k" }
  1889         3627  
  1         5  
93 414         899 foreach my $k (map {"tm$_"} qw/min avg max/) {
  1242         2692  
94 1242 100       2341 if(defined($node{$k})) {
95 1219 100       3584 if (!looks_like_number($node{$k})) { push @errors,"Invalid value: $k" }
  1 100       4  
96 1         21 elsif($node{$k}<0) { push @errors,"Negative value: $k" }
97 1217         2407 else { push @tmseq,$node{$k} }
98             }
99 23         68 else { push @invalids,$k }
100             }
101 414         1049 @invalids=sort(@invalids);
102 414 100 100     1055 if(@invalids&&($#invalids!=2)) { push @errors,'Incomplete time specification missing: '.join(' ',@invalids) }
  1         5  
103 414 100       918 if($#tmseq==2) {
104 405 100       901 if($tmseq[0]>$tmseq[1]) { push @errors,'Invalid: tmmin>tmavg' }
  1         2  
105 405 100       936 if($tmseq[1]>$tmseq[2]) { push @errors,'Invalid: tmavg>tmmax' }
  1         2  
106             }
107 414 100       929 if(exists($node{next})) {
108 307         467 my @nexts;
109 307         516 eval { @nexts=nextnames(undef,0,$node{next}) };
  307         843  
110 307 100       807 if($@) { push @errors,'Expected array/hash: next' }
  1         2  
111 307 50       580 @invalids=grep {!defined($_)||is_ref($_)} @nexts;
  851         2775  
112 307 100       676 if(@invalids) { push @errors,'Invalid entry in: next' }
  1         3  
113 307 100       814 if(is_hashref($node{next})) {
114 6         11 my $weight=0;
115 6   50     12 foreach my $x (map {$$_{weight}//1} values %{$node{next}}) { $weight+=$x }
  26         73  
  6         18  
  26         42  
116 6 100       23 if($weight<=0) { push @errors,'Sum of weights must be positive' }
  1         2  
117             }
118             }
119 414 100       972 if(exists($node{finish})) {
120 103 100 66     396 if(!defined($node{finish})||is_ref($node{finish})) { push @errors,'Expected name: finish' }
  1         4  
121             }
122 414 100       812 if(!@errors) { $node{_valid}=1 }
  406         904  
123 414         1593 return @errors;
124             }
125              
126 191493   50 191493 0 337646 sub slack { my ($self)=@_; return ($$self{tmavg}//0)-($$self{tmmin}//$$self{tmavg}//0) }
  191493   66     674286  
      50        
127 187641   66 187641 0 336517 sub buffer { my ($self)=@_; return ($$self{tmmax}//$$self{tmavg}//0)-($$self{tmavg}//0) }
  187641   50     607904  
      50        
128              
129             sub increment {
130 185980     185980 0 431411 my ($self,$tm,$slack,$buffer)=@_;
131 185980 50 50     399510 if(is_ref($tm)) { $$tm +=$$self{tmavg}//0 }
  185980         508049  
132 185980 50       394299 if(is_ref($slack)) { $$slack +=$self->slack() }
  185980         416602  
133 185980 50       430784 if(is_ref($buffer)) { $$buffer+=$self->buffer() }
  185980         393219  
134 185980         383522 return $self;
135             }
136              
137             sub _randweighted {
138 8280     8280   15396 my ($weight,$L)=@_;
139 8280         16947 my $y=rand($weight);
140 8280         12915 my $i=0;
141 8280   50     43126 while(($i<$#$L)&&($y>($$L[$i][1]{weight}//1))) { $y-=$$L[$i][1]{weight}//1; $i++ }
  8317   50     21801  
  8317   100     29481  
142 8280   66     51326 return $$L[$i][1]{node}//$$L[$i][0];
143             }
144              
145             sub nextrandom {
146 153382     153382 0 525641 my ($self,%opt)=@_;
147 153382 50       409271 if(!$$self{next}) { return }
  0         0  
148 153382         258731 my (@candidates,$weight);
149 153382 100       367389 if(is_arrayref($$self{next})) {
    50          
150 145102         209994 foreach my $next (@{$$self{next}}) {
  145102         350066  
151 447371 100 100     1883405 if($opt{not}&&($opt{not} eq $next)) { next }
  115919         268871  
152 331452 100       672341 if(!is_ref($next)) { push @candidates,$next; next }
  150         197  
  150         185  
153 331302 50       703337 if(!is_hashref($next)) { next }
  0         0  
154 331302 50 66     1191173 if(blessed($$next{require})&&$opt{attr}) {
155 25826 100       50555 if(!$$next{require}->matches($opt{tm},%{$opt{attr}})) { next } }
  25826         89951  
  25516         73548  
156 305786         767472 push @candidates,$next;
157             } }
158             elsif(is_hashref($$self{next})) {
159 8280         14346 while(my ($next,$href)=each %{$$self{next}}) {
  40051         135691  
160 31771 50       64871 if(!is_hashref($href)) { next }
  0         0  
161 31771 100       73477 if($$href{node}) {
162 17371 100 66     79468 if($opt{not}&&($opt{not} eq $$href{node})) { next }
  3451         9467  
163 13920 50 66     65213 if(blessed($$href{node}{require})&&$opt{attr}) {
164 6960 100       14791 if(!$$href{node}{require}->matches($opt{tm},%{$opt{attr}})) { next } } }
  6960         26721  
  3480         11462  
165 24840   50     66411 my $w=$$href{weight}//1;
166 24840 50       50879 if($w>0) { $weight+=$w; push @candidates,[$next,$href] }
  24840         42220  
  24840         70898  
167             } }
168 153382 100       366476 if(!@candidates) { return }
  110         449  
169 153272 100       325909 if($weight) { return _randweighted($weight,\@candidates) }
  8280         19657  
170 144992         857888 else { return $candidates[ int(rand(1+$#candidates)) ] }
171             }
172              
173             sub hasnext {
174 19635     19635 0 61130 my ($self,$node)=@_;
175 19635 50       60462 if(!$$self{next}) { return }
  0         0  
176 19635 100   63734   52400 if(is_arrayref($$self{next})) { return (any {$_ eq $node} @{$$self{next}}) }
  19606         109103  
  63734         251652  
  19606         97294  
177 29 50   97   109 if(is_hashref($$self{next})) { return (any {$$_{node} eq $node} values %{$$self{next}}) }
  29         187  
  97         1038  
  29         266  
178 0           return;
179             }
180              
181             1;