File Coverage

blib/lib/Test2/Compare/Array.pm
Criterion Covered Total %
statement 117 117 100.0
branch 45 48 93.7
condition 29 30 96.6
subroutine 16 16 100.0
pod 6 10 60.0
total 213 221 96.3


line stmt bran cond sub pod time code
1             package Test2::Compare::Array;
2 168     168   1157 use strict;
  168         333  
  168         5071  
3 168     168   1232 use warnings;
  168         487  
  168         4223  
4              
5 168     168   824 use base 'Test2::Compare::Base';
  168         336  
  168         67222  
6              
7             our $VERSION = '0.000153';
8              
9 168     168   1144 use Test2::Util::HashBase qw/inref meta ending items order for_each/;
  168         323  
  168         692  
10              
11 168     168   48598 use Carp qw/croak confess/;
  168         344  
  168         7983  
12 168     168   929 use Scalar::Util qw/reftype looks_like_number/;
  168         322  
  168         162907  
13              
14             sub init {
15 1078     1078 0 16096 my $self = shift;
16              
17 1078 100       3344 if( defined( my $ref = $self->{+INREF}) ) {
18 430 100       1110 croak "Cannot specify both 'inref' and 'items'" if $self->{+ITEMS};
19 429 100       1066 croak "Cannot specify both 'inref' and 'order'" if $self->{+ORDER};
20 428 100       1365 croak "'inref' must be an array reference, got '$ref'" unless reftype($ref) eq 'ARRAY';
21 427         937 my $order = $self->{+ORDER} = [];
22 427         872 my $items = $self->{+ITEMS} = {};
23 427         1286 for (my $i = 0; $i < @$ref; $i++) {
24 1120         1907 push @$order => $i;
25 1120         3021 $items->{$i} = $ref->[$i];
26             }
27             }
28             else {
29 648   100     4070 $self->{+ITEMS} ||= {};
30             croak "All indexes listed in the 'items' hashref must be numeric"
31 648 100       1048 if grep { !looks_like_number($_) } keys %{$self->{+ITEMS}};
  7         271  
  648         2535  
32              
33 647   100     2594 $self->{+ORDER} ||= [sort { $a <=> $b } keys %{$self->{+ITEMS}}];
  1         8  
  645         2528  
34             croak "All indexes listed in the 'order' arrayref must be numeric"
35 647 100 100     1078 if grep { !(looks_like_number($_) || (ref($_) && reftype($_) eq 'CODE')) } @{$self->{+ORDER}};
  7         144  
  647         1755  
36             }
37              
38 1073   50     5016 $self->{+FOR_EACH} ||= [];
39              
40 1073         4210 $self->SUPER::init();
41             }
42              
43 21     21 1 67 sub name { '' }
44              
45 3     3 0 20 sub meta_class { 'Test2::Compare::Meta' }
46              
47             sub verify {
48 1074     1074 1 1975 my $self = shift;
49 1074         3259 my %params = @_;
50              
51 1074 100       2499 return 0 unless $params{exists};
52 1071         1727 my $got = $params{got};
53 1071 100       2217 return 0 unless defined $got;
54 1069 100       2261 return 0 unless ref($got);
55 1064 100       3152 return 0 unless reftype($got) eq 'ARRAY';
56 1056         2889 return 1;
57             }
58              
59             sub add_prop {
60 4     4 0 38 my $self = shift;
61 4 100       23 $self->{+META} = $self->meta_class->new unless defined $self->{+META};
62 4         26 $self->{+META}->add_prop(@_);
63             }
64              
65             sub top_index {
66 2315     2315 1 3114 my $self = shift;
67 2315         3061 my @order = @{$self->{+ORDER}};
  2315         5802  
68              
69 2315         5156 while(@order) {
70 1703         3000 my $idx = pop @order;
71 1703 100       3778 next if ref $idx;
72 1686         3723 return $idx;
73             }
74              
75 629         1220 return undef; # No indexes
76             }
77              
78             sub add_item {
79 2310     2310 1 3608 my $self = shift;
80 2310         3117 my $check = pop;
81 2310         3930 my ($idx) = @_;
82              
83 2310         4525 my $top = $self->top_index;
84              
85 2310 100 100     7737 croak "elements must be added in order!"
      100        
86             if $top && $idx && $idx <= $top;
87              
88 2309 100       6115 $idx = defined($top) ? $top + 1 : 0
    100          
89             unless defined($idx);
90              
91 2309         3161 push @{$self->{+ORDER}} => $idx;
  2309         4549  
92 2309         8853 $self->{+ITEMS}->{$idx} = $check;
93             }
94              
95             sub add_filter {
96 22     22 1 82 my $self = shift;
97 22         53 my ($code) = @_;
98 22 100 100     913 croak "A single coderef is required"
      100        
      100        
99             unless @_ == 1 && $code && ref $code && reftype($code) eq 'CODE';
100              
101 17         36 push @{$self->{+ORDER}} => $code;
  17         58  
102             }
103              
104             sub add_for_each {
105 3     3 0 5 my $self = shift;
106 3         5 push @{$self->{+FOR_EACH}} => @_;
  3         12  
107             }
108              
109             sub deltas {
110 1062     1062 1 1811 my $self = shift;
111 1062         2731 my %params = @_;
112 1062         2831 my ($got, $convert, $seen) = @params{qw/got convert seen/};
113              
114 1062         1637 my @deltas;
115 1062         1599 my $state = 0;
116 1062         1520 my @order = @{$self->{+ORDER}};
  1062         3335  
117 1062         1845 my $items = $self->{+ITEMS};
118 1062         1769 my $for_each = $self->{+FOR_EACH};
119              
120 1062         1852 my $meta = $self->{+META};
121 1062 100       2189 push @deltas => $meta->deltas(%params) if defined $meta;
122              
123             # Make a copy that we can munge as needed.
124 1062         3244 my @list = @$got;
125              
126 1062         2596 while (@order) {
127 3465         6158 my $idx = shift @order;
128 3465         5358 my $overflow = 0;
129 3465         4710 my $val;
130              
131             # We have a filter, not an index
132 3465 100       7126 if (ref($idx)) {
133 18         87 @list = $idx->(@list);
134 18         328 next;
135             }
136              
137 3447 50       7166 confess "Internal Error: Stacks are out of sync (state > idx)"
138             if $state > $idx + 1;
139              
140 3447         6875 while ($state <= $idx) {
141 3451         5522 $overflow = !@list;
142 3451         5344 $val = shift @list;
143              
144             # check-all goes here so we hit each item, even unspecified ones.
145 3451         6894 for my $check (@$for_each) {
146 4         10 $check = $convert->($check);
147 4 50       23 push @deltas => $check->run(
148             id => [ARRAY => $state],
149             convert => $convert,
150             seen => $seen,
151             exists => !$overflow,
152             $overflow ? () : (got => $val),
153             );
154             }
155              
156 3451         6750 $state++;
157             }
158              
159 3447 50       7072 confess "Internal Error: Stacks are out of sync (state != idx + 1)"
160             unless $state == $idx + 1;
161              
162 3447         10129 my $check = $convert->($items->{$idx});
163              
164 3447 100       20859 push @deltas => $check->run(
165             id => [ARRAY => $idx],
166             convert => $convert,
167             seen => $seen,
168             exists => !$overflow,
169             $overflow ? () : (got => $val),
170             );
171             }
172              
173 1062   100     2911 while (@list && (@$for_each || $self->{+ENDING})) {
      100        
174 8         17 my $item = shift @list;
175              
176 8         23 for my $check (@$for_each) {
177 2         7 $check = $convert->($check);
178 2         12 push @deltas => $check->run(
179             id => [ARRAY => $state],
180             convert => $convert,
181             seen => $seen,
182             got => $item,
183             exists => 1,
184             );
185             }
186              
187             # if items are left over, and ending is true, we have a problem!
188 8 100       31 if ($self->{+ENDING}) {
189             push @deltas => $self->delta_class->new(
190             dne => 'check',
191             verified => undef,
192             id => [ARRAY => $state],
193             got => $item,
194             check => undef,
195              
196 7 100       31 $self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (),
197             );
198             }
199              
200 8         39 $state++;
201             }
202              
203 1062         3450 return @deltas;
204             }
205              
206             1;
207              
208             __END__