File Coverage

blib/lib/Test2/Compare/Bag.pm
Criterion Covered Total %
statement 83 83 100.0
branch 21 22 95.4
condition 8 9 88.8
subroutine 14 14 100.0
pod 4 8 50.0
total 130 136 95.5


line stmt bran cond sub pod time code
1             package Test2::Compare::Bag;
2 168     168   1137 use strict;
  168         389  
  168         5719  
3 168     168   932 use warnings;
  168         323  
  168         4943  
4              
5 168     168   841 use base 'Test2::Compare::Base';
  168         287  
  168         22417  
6              
7             our $VERSION = '0.000153';
8              
9 168     168   1086 use Test2::Util::HashBase qw/ending meta items for_each/;
  168         311  
  168         1012  
10              
11 168     168   38145 use Carp qw/croak confess/;
  168         333  
  168         8203  
12 168     168   954 use Scalar::Util qw/reftype looks_like_number/;
  168         342  
  168         118720  
13              
14             sub init {
15 407     407 0 5138 my $self = shift;
16              
17 407   100     1504 $self->{+ITEMS} ||= [];
18 407   50     1246 $self->{+FOR_EACH} ||= [];
19              
20 407         780 $self->SUPER::init();
21             }
22              
23 11     11 1 51 sub name { '' }
24              
25 3     3 0 18 sub meta_class { 'Test2::Compare::Meta' }
26              
27             sub verify {
28 419     419 1 616 my $self = shift;
29 419         1130 my %params = @_;
30              
31 419 100       817 return 0 unless $params{exists};
32 418   100     794 my $got = $params{got} || return 0;
33 415 100       679 return 0 unless ref($got);
34 411 100       896 return 0 unless reftype($got) eq 'ARRAY';
35 409         1000 return 1;
36             }
37              
38             sub add_prop {
39 4     4 0 36 my $self = shift;
40 4 100       21 $self->{+META} = $self->meta_class->new unless defined $self->{+META};
41 4         29 $self->{+META}->add_prop(@_);
42             }
43              
44             sub add_item {
45 1262     1262 1 1379 my $self = shift;
46 1262         1318 my $check = pop;
47 1262         1547 my ($idx) = @_;
48              
49 1262         1424 push @{$self->{+ITEMS}}, $check;
  1262         2684  
50             }
51              
52             sub add_for_each {
53 2     2 0 4 my $self = shift;
54 2         3 push @{$self->{+FOR_EACH}} => @_;
  2         6  
55             }
56              
57             sub deltas {
58 417     417 1 535 my $self = shift;
59 417         827 my %params = @_;
60 417         799 my ($got, $convert, $seen) = @params{qw/got convert seen/};
61              
62 417         477 my @deltas;
63 417         502 my $state = 0;
64 417         480 my @items = @{$self->{+ITEMS}};
  417         890  
65 417         551 my @for_each = @{$self->{+FOR_EACH}};
  417         571  
66              
67             # Make a copy that we can munge as needed.
68 417         1029 my @list = @$got;
69 417         951 my %unmatched = map { $_ => $list[$_] } 0..$#list;
  1823         3742  
70              
71 417         887 my $meta = $self->{+META};
72 417 100       787 push @deltas => $meta->deltas(%params) if defined $meta;
73              
74 417         837 while (@items) {
75 1292         1688 my $item = shift @items;
76              
77 1292         2277 my $check = $convert->($item);
78              
79 1292         2442 my $match = 0;
80 1292         2583 for my $idx (0..$#list) {
81 4610 100       7689 next unless exists $unmatched{$idx};
82 3162         3996 my $val = $list[$idx];
83 3162         8290 my $deltas = $check->run(
84             id => [ARRAY => $idx],
85             convert => $convert,
86             seen => $seen,
87             exists => 1,
88             got => $val,
89             );
90              
91 3162 100       11840 unless ($deltas) {
92 1282         1382 $match++;
93 1282         1837 delete $unmatched{$idx};
94 1282         1884 last;
95             }
96             }
97 1292 100       3549 unless ($match) {
98 10         44 push @deltas => $self->delta_class->new(
99             dne => 'got',
100             verified => undef,
101             id => [ARRAY => '*'],
102             got => undef,
103             check => $check,
104             );
105             }
106             }
107              
108 417 100       705 if (@for_each) {
109 2         6 my @checks = map { $convert->($_) } @for_each;
  2         7  
110              
111 2         7 for my $idx (0..$#list) {
112             # All items are matched if we have conditions for all items
113 6         19 delete $unmatched{$idx};
114              
115 6         9 my $val = $list[$idx];
116              
117 6         11 for my $check (@checks) {
118 6         18 push @deltas => $check->run(
119             id => [ARRAY => $idx],
120             convert => $convert,
121             seen => $seen,
122             exists => 1,
123             got => $val,
124             );
125             }
126             }
127             }
128              
129             # if elements are left over, and ending is true, we have a problem!
130 417 100 100     920 if($self->{+ENDING} && keys %unmatched) {
131 2         8 for my $idx (sort keys %unmatched) {
132 3         10 my $elem = $list[$idx];
133             push @deltas => $self->delta_class->new(
134             dne => 'check',
135             verified => undef,
136             id => [ARRAY => $idx],
137             got => $elem,
138             check => undef,
139              
140 3 50       8 $self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (),
141             );
142             }
143             }
144              
145 417         1262 return @deltas;
146             }
147              
148             1;
149              
150             __END__