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 169     169   1239 use strict;
  169         389  
  169         5719  
3 169     169   939 use warnings;
  169         338  
  169         5343  
4              
5 169     169   874 use base 'Test2::Compare::Base';
  169         321  
  169         24166  
6              
7             our $VERSION = '0.000155';
8              
9 169     169   1246 use Test2::Util::HashBase qw/ending meta items for_each/;
  169         312  
  169         1109  
10              
11 169     169   40771 use Carp qw/croak confess/;
  169         372  
  169         8643  
12 169     169   971 use Scalar::Util qw/reftype looks_like_number/;
  169         343  
  169         128796  
13              
14             sub init {
15 407     407 0 5900 my $self = shift;
16              
17 407   100     1727 $self->{+ITEMS} ||= [];
18 407   50     1516 $self->{+FOR_EACH} ||= [];
19              
20 407         947 $self->SUPER::init();
21             }
22              
23 11     11 1 60 sub name { '' }
24              
25 3     3 0 22 sub meta_class { 'Test2::Compare::Meta' }
26              
27             sub verify {
28 419     419 1 690 my $self = shift;
29 419         1200 my %params = @_;
30              
31 419 100       932 return 0 unless $params{exists};
32 418   100     946 my $got = $params{got} || return 0;
33 415 100       848 return 0 unless ref($got);
34 411 100       1042 return 0 unless reftype($got) eq 'ARRAY';
35 409         1067 return 1;
36             }
37              
38             sub add_prop {
39 4     4 0 50 my $self = shift;
40 4 100       26 $self->{+META} = $self->meta_class->new unless defined $self->{+META};
41 4         41 $self->{+META}->add_prop(@_);
42             }
43              
44             sub add_item {
45 1262     1262 1 1729 my $self = shift;
46 1262         1580 my $check = pop;
47 1262         1935 my ($idx) = @_;
48              
49 1262         1597 push @{$self->{+ITEMS}}, $check;
  1262         3439  
50             }
51              
52             sub add_for_each {
53 2     2 0 4 my $self = shift;
54 2         4 push @{$self->{+FOR_EACH}} => @_;
  2         7  
55             }
56              
57             sub deltas {
58 417     417 1 685 my $self = shift;
59 417         963 my %params = @_;
60 417         902 my ($got, $convert, $seen) = @params{qw/got convert seen/};
61              
62 417         542 my @deltas;
63 417         579 my $state = 0;
64 417         552 my @items = @{$self->{+ITEMS}};
  417         1010  
65 417         661 my @for_each = @{$self->{+FOR_EACH}};
  417         788  
66              
67             # Make a copy that we can munge as needed.
68 417         1132 my @list = @$got;
69 417         1179 my %unmatched = map { $_ => $list[$_] } 0..$#list;
  1823         4838  
70              
71 417         1100 my $meta = $self->{+META};
72 417 100       888 push @deltas => $meta->deltas(%params) if defined $meta;
73              
74 417         903 while (@items) {
75 1292         2059 my $item = shift @items;
76              
77 1292         3177 my $check = $convert->($item);
78              
79 1292         3031 my $match = 0;
80 1292         3125 for my $idx (0..$#list) {
81 4610 100       10681 next unless exists $unmatched{$idx};
82 3162         4891 my $val = $list[$idx];
83 3162         10035 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       14697 unless ($deltas) {
92 1282         1768 $match++;
93 1282         2340 delete $unmatched{$idx};
94 1282         2161 last;
95             }
96             }
97 1292 100       4232 unless ($match) {
98 10         39 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       1059 if (@for_each) {
109 2         6 my @checks = map { $convert->($_) } @for_each;
  2         8  
110              
111 2         7 for my $idx (0..$#list) {
112             # All items are matched if we have conditions for all items
113 6         21 delete $unmatched{$idx};
114              
115 6         9 my $val = $list[$idx];
116              
117 6         13 for my $check (@checks) {
118 6         21 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     1056 if($self->{+ENDING} && keys %unmatched) {
131 2         10 for my $idx (sort keys %unmatched) {
132 3         11 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       9 $self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (),
141             );
142             }
143             }
144              
145 417         1628 return @deltas;
146             }
147              
148             1;
149              
150             __END__