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 169     169   1198 use strict;
  169         407  
  169         5294  
3 169     169   930 use warnings;
  169         370  
  169         4618  
4              
5 169     169   925 use base 'Test2::Compare::Base';
  169         372  
  169         74086  
6              
7             our $VERSION = '0.000156';
8              
9 169     169   1208 use Test2::Util::HashBase qw/inref meta ending items order for_each/;
  169         333  
  169         740  
10              
11 169     169   51157 use Carp qw/croak confess/;
  169         338  
  169         8775  
12 169     169   1078 use Scalar::Util qw/reftype looks_like_number/;
  169         319  
  169         173106  
13              
14             sub init {
15 1095     1095 0 18648 my $self = shift;
16              
17 1095 100       3563 if( defined( my $ref = $self->{+INREF}) ) {
18 435 100       1242 croak "Cannot specify both 'inref' and 'items'" if $self->{+ITEMS};
19 434 100       1137 croak "Cannot specify both 'inref' and 'order'" if $self->{+ORDER};
20 433 100       1426 croak "'inref' must be an array reference, got '$ref'" unless reftype($ref) eq 'ARRAY';
21 432         990 my $order = $self->{+ORDER} = [];
22 432         953 my $items = $self->{+ITEMS} = {};
23 432         1308 for (my $i = 0; $i < @$ref; $i++) {
24 1128         1977 push @$order => $i;
25 1128         3277 $items->{$i} = $ref->[$i];
26             }
27             }
28             else {
29 660   100     4402 $self->{+ITEMS} ||= {};
30             croak "All indexes listed in the 'items' hashref must be numeric"
31 660 100       1290 if grep { !looks_like_number($_) } keys %{$self->{+ITEMS}};
  7         266  
  660         3003  
32              
33 659   100     3200 $self->{+ORDER} ||= [sort { $a <=> $b } keys %{$self->{+ITEMS}}];
  1         7  
  657         3079  
34             croak "All indexes listed in the 'order' arrayref must be numeric"
35 659 100 100     1274 if grep { !(looks_like_number($_) || (ref($_) && reftype($_) eq 'CODE')) } @{$self->{+ORDER}};
  7         151  
  659         1932  
36             }
37              
38 1090   50     6263 $self->{+FOR_EACH} ||= [];
39              
40 1090         4559 $self->SUPER::init();
41             }
42              
43 21     21 1 73 sub name { '' }
44              
45 3     3 0 20 sub meta_class { 'Test2::Compare::Meta' }
46              
47             sub verify {
48 1091     1091 1 2090 my $self = shift;
49 1091         3510 my %params = @_;
50              
51 1091 100       2779 return 0 unless $params{exists};
52 1088         1816 my $got = $params{got};
53 1088 100       2164 return 0 unless defined $got;
54 1086 100       2500 return 0 unless ref($got);
55 1081 100       3493 return 0 unless reftype($got) eq 'ARRAY';
56 1073         3211 return 1;
57             }
58              
59             sub add_prop {
60 4     4 0 46 my $self = shift;
61 4 100       24 $self->{+META} = $self->meta_class->new unless defined $self->{+META};
62 4         37 $self->{+META}->add_prop(@_);
63             }
64              
65             sub top_index {
66 2335     2335 1 3320 my $self = shift;
67 2335         2992 my @order = @{$self->{+ORDER}};
  2335         6263  
68              
69 2335         5439 while(@order) {
70 1711         2763 my $idx = pop @order;
71 1711 100       3366 next if ref $idx;
72 1694         3776 return $idx;
73             }
74              
75 641         1280 return undef; # No indexes
76             }
77              
78             sub add_item {
79 2330     2330 1 3863 my $self = shift;
80 2330         3400 my $check = pop;
81 2330         4022 my ($idx) = @_;
82              
83 2330         5010 my $top = $self->top_index;
84              
85 2330 100 100     7492 croak "elements must be added in order!"
      100        
86             if $top && $idx && $idx <= $top;
87              
88 2329 100       6652 $idx = defined($top) ? $top + 1 : 0
    100          
89             unless defined($idx);
90              
91 2329         3303 push @{$self->{+ORDER}} => $idx;
  2329         4650  
92 2329         9825 $self->{+ITEMS}->{$idx} = $check;
93             }
94              
95             sub add_filter {
96 22     22 1 322 my $self = shift;
97 22         62 my ($code) = @_;
98 22 100 100     744 croak "A single coderef is required"
      100        
      100        
99             unless @_ == 1 && $code && ref $code && reftype($code) eq 'CODE';
100              
101 17         42 push @{$self->{+ORDER}} => $code;
  17         244  
102             }
103              
104             sub add_for_each {
105 3     3 0 7 my $self = shift;
106 3         7 push @{$self->{+FOR_EACH}} => @_;
  3         15  
107             }
108              
109             sub deltas {
110 1079     1079 1 2190 my $self = shift;
111 1079         3346 my %params = @_;
112 1079         3038 my ($got, $convert, $seen) = @params{qw/got convert seen/};
113              
114 1079         1647 my @deltas;
115 1079         1872 my $state = 0;
116 1079         1592 my @order = @{$self->{+ORDER}};
  1079         3577  
117 1079         2180 my $items = $self->{+ITEMS};
118 1079         1925 my $for_each = $self->{+FOR_EACH};
119              
120 1079         1896 my $meta = $self->{+META};
121 1079 100       2500 push @deltas => $meta->deltas(%params) if defined $meta;
122              
123             # Make a copy that we can munge as needed.
124 1079         3191 my @list = @$got;
125              
126 1079         3187 while (@order) {
127 3493         6596 my $idx = shift @order;
128 3493         5996 my $overflow = 0;
129 3493         4878 my $val;
130              
131             # We have a filter, not an index
132 3493 100       6830 if (ref($idx)) {
133 18         91 @list = $idx->(@list);
134 18         436 next;
135             }
136              
137 3475 50       7192 confess "Internal Error: Stacks are out of sync (state > idx)"
138             if $state > $idx + 1;
139              
140 3475         7154 while ($state <= $idx) {
141 3479         5903 $overflow = !@list;
142 3479         5116 $val = shift @list;
143              
144             # check-all goes here so we hit each item, even unspecified ones.
145 3479         7423 for my $check (@$for_each) {
146 4         14 $check = $convert->($check);
147 4 50       30 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 3479         6894 $state++;
157             }
158              
159 3475 50       7251 confess "Internal Error: Stacks are out of sync (state != idx + 1)"
160             unless $state == $idx + 1;
161              
162 3475         10236 my $check = $convert->($items->{$idx});
163              
164 3475 100       21749 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 1079   100     3851 while (@list && (@$for_each || $self->{+ENDING})) {
      100        
174 8         21 my $item = shift @list;
175              
176 8         20 for my $check (@$for_each) {
177 2         6 $check = $convert->($check);
178 2         13 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       47 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       26 $self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (),
197             );
198             }
199              
200 8         51 $state++;
201             }
202              
203 1079         3840 return @deltas;
204             }
205              
206             1;
207              
208             __END__