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   1220 use strict;
  169         337  
  169         6016  
3 169     169   938 use warnings;
  169         321  
  169         5915  
4              
5 169     169   895 use base 'Test2::Compare::Base';
  169         317  
  169         23917  
6              
7             our $VERSION = '0.000156';
8              
9 169     169   1133 use Test2::Util::HashBase qw/ending meta items for_each/;
  169         344  
  169         1075  
10              
11 169     169   38783 use Carp qw/croak confess/;
  169         345  
  169         8533  
12 169     169   986 use Scalar::Util qw/reftype looks_like_number/;
  169         308  
  169         124574  
13              
14             sub init {
15 431     431 0 6216 my $self = shift;
16              
17 431   100     1826 $self->{+ITEMS} ||= [];
18 431   50     1543 $self->{+FOR_EACH} ||= [];
19              
20 431         1010 $self->SUPER::init();
21             }
22              
23 11     11 1 74 sub name { '' }
24              
25 3     3 0 20 sub meta_class { 'Test2::Compare::Meta' }
26              
27             sub verify {
28 443     443 1 749 my $self = shift;
29 443         1475 my %params = @_;
30              
31 443 100       928 return 0 unless $params{exists};
32 442   100     945 my $got = $params{got} || return 0;
33 439 100       859 return 0 unless ref($got);
34 435 100       1281 return 0 unless reftype($got) eq 'ARRAY';
35 433         1201 return 1;
36             }
37              
38             sub add_prop {
39 4     4 0 64 my $self = shift;
40 4 100       23 $self->{+META} = $self->meta_class->new unless defined $self->{+META};
41 4         36 $self->{+META}->add_prop(@_);
42             }
43              
44             sub add_item {
45 1334     1334 1 1986 my $self = shift;
46 1334         1655 my $check = pop;
47 1334         2061 my ($idx) = @_;
48              
49 1334         1711 push @{$self->{+ITEMS}}, $check;
  1334         3532  
50             }
51              
52             sub add_for_each {
53 2     2 0 8 my $self = shift;
54 2         5 push @{$self->{+FOR_EACH}} => @_;
  2         9  
55             }
56              
57             sub deltas {
58 441     441 1 690 my $self = shift;
59 441         1024 my %params = @_;
60 441         1012 my ($got, $convert, $seen) = @params{qw/got convert seen/};
61              
62 441         609 my @deltas;
63 441         592 my $state = 0;
64 441         536 my @items = @{$self->{+ITEMS}};
  441         1186  
65 441         616 my @for_each = @{$self->{+FOR_EACH}};
  441         698  
66              
67             # Make a copy that we can munge as needed.
68 441         1439 my @list = @$got;
69 441         1268 my %unmatched = map { $_ => $list[$_] } 0..$#list;
  1927         5047  
70              
71 441         1164 my $meta = $self->{+META};
72 441 100       966 push @deltas => $meta->deltas(%params) if defined $meta;
73              
74 441         894 while (@items) {
75 1364         2249 my $item = shift @items;
76              
77 1364         3104 my $check = $convert->($item);
78              
79 1364         3376 my $match = 0;
80 1364         3480 for my $idx (0..$#list) {
81 4874 100       10537 next unless exists $unmatched{$idx};
82 3354         5572 my $val = $list[$idx];
83 3354         10538 my $deltas = $check->run(
84             id => [ARRAY => $idx],
85             convert => $convert,
86             seen => $seen,
87             exists => 1,
88             got => $val,
89             );
90              
91 3354 100       16209 unless ($deltas) {
92 1354         1813 $match++;
93 1354         2475 delete $unmatched{$idx};
94 1354         2632 last;
95             }
96             }
97 1364 100       4817 unless ($match) {
98 10         54 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 441 100       997 if (@for_each) {
109 2         10 my @checks = map { $convert->($_) } @for_each;
  2         7  
110              
111 2         16 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         11 my $val = $list[$idx];
116              
117 6         12 for my $check (@checks) {
118 6         24 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 441 100 100     1157 if($self->{+ENDING} && keys %unmatched) {
131 2         11 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 441         1712 return @deltas;
146             }
147              
148             1;
149              
150             __END__