1*5486feefSafresh1package Test2::Compare::Bag; 2*5486feefSafresh1use strict; 3*5486feefSafresh1use warnings; 4*5486feefSafresh1 5*5486feefSafresh1use base 'Test2::Compare::Base'; 6*5486feefSafresh1 7*5486feefSafresh1our $VERSION = '0.000162'; 8*5486feefSafresh1 9*5486feefSafresh1use Test2::Util::HashBase qw/ending meta items for_each/; 10*5486feefSafresh1 11*5486feefSafresh1use Carp qw/croak confess/; 12*5486feefSafresh1use Scalar::Util qw/reftype looks_like_number/; 13*5486feefSafresh1 14*5486feefSafresh1sub init { 15*5486feefSafresh1 my $self = shift; 16*5486feefSafresh1 17*5486feefSafresh1 $self->{+ITEMS} ||= []; 18*5486feefSafresh1 $self->{+FOR_EACH} ||= []; 19*5486feefSafresh1 20*5486feefSafresh1 $self->SUPER::init(); 21*5486feefSafresh1} 22*5486feefSafresh1 23*5486feefSafresh1sub name { '<BAG>' } 24*5486feefSafresh1 25*5486feefSafresh1sub meta_class { 'Test2::Compare::Meta' } 26*5486feefSafresh1 27*5486feefSafresh1sub verify { 28*5486feefSafresh1 my $self = shift; 29*5486feefSafresh1 my %params = @_; 30*5486feefSafresh1 31*5486feefSafresh1 return 0 unless $params{exists}; 32*5486feefSafresh1 my $got = $params{got} || return 0; 33*5486feefSafresh1 return 0 unless ref($got); 34*5486feefSafresh1 return 0 unless reftype($got) eq 'ARRAY'; 35*5486feefSafresh1 return 1; 36*5486feefSafresh1} 37*5486feefSafresh1 38*5486feefSafresh1sub add_prop { 39*5486feefSafresh1 my $self = shift; 40*5486feefSafresh1 $self->{+META} = $self->meta_class->new unless defined $self->{+META}; 41*5486feefSafresh1 $self->{+META}->add_prop(@_); 42*5486feefSafresh1} 43*5486feefSafresh1 44*5486feefSafresh1sub add_item { 45*5486feefSafresh1 my $self = shift; 46*5486feefSafresh1 my $check = pop; 47*5486feefSafresh1 my ($idx) = @_; 48*5486feefSafresh1 49*5486feefSafresh1 push @{$self->{+ITEMS}}, $check; 50*5486feefSafresh1} 51*5486feefSafresh1 52*5486feefSafresh1sub add_for_each { 53*5486feefSafresh1 my $self = shift; 54*5486feefSafresh1 push @{$self->{+FOR_EACH}} => @_; 55*5486feefSafresh1} 56*5486feefSafresh1 57*5486feefSafresh1sub deltas { 58*5486feefSafresh1 my $self = shift; 59*5486feefSafresh1 my %params = @_; 60*5486feefSafresh1 my ($got, $convert, $seen) = @params{qw/got convert seen/}; 61*5486feefSafresh1 62*5486feefSafresh1 my @deltas; 63*5486feefSafresh1 my $state = 0; 64*5486feefSafresh1 my @items = @{$self->{+ITEMS}}; 65*5486feefSafresh1 my @for_each = @{$self->{+FOR_EACH}}; 66*5486feefSafresh1 67*5486feefSafresh1 # Make a copy that we can munge as needed. 68*5486feefSafresh1 my @list = @$got; 69*5486feefSafresh1 my %unmatched = map { $_ => $list[$_] } 0..$#list; 70*5486feefSafresh1 71*5486feefSafresh1 my $meta = $self->{+META}; 72*5486feefSafresh1 push @deltas => $meta->deltas(%params) if defined $meta; 73*5486feefSafresh1 74*5486feefSafresh1 while (@items) { 75*5486feefSafresh1 my $item = shift @items; 76*5486feefSafresh1 77*5486feefSafresh1 my $check = $convert->($item); 78*5486feefSafresh1 79*5486feefSafresh1 my $match = 0; 80*5486feefSafresh1 for my $idx (0..$#list) { 81*5486feefSafresh1 next unless exists $unmatched{$idx}; 82*5486feefSafresh1 my $val = $list[$idx]; 83*5486feefSafresh1 my $deltas = $check->run( 84*5486feefSafresh1 id => [ARRAY => $idx], 85*5486feefSafresh1 convert => $convert, 86*5486feefSafresh1 seen => $seen, 87*5486feefSafresh1 exists => 1, 88*5486feefSafresh1 got => $val, 89*5486feefSafresh1 ); 90*5486feefSafresh1 91*5486feefSafresh1 unless ($deltas) { 92*5486feefSafresh1 $match++; 93*5486feefSafresh1 delete $unmatched{$idx}; 94*5486feefSafresh1 last; 95*5486feefSafresh1 } 96*5486feefSafresh1 } 97*5486feefSafresh1 unless ($match) { 98*5486feefSafresh1 push @deltas => $self->delta_class->new( 99*5486feefSafresh1 dne => 'got', 100*5486feefSafresh1 verified => undef, 101*5486feefSafresh1 id => [ARRAY => '*'], 102*5486feefSafresh1 got => undef, 103*5486feefSafresh1 check => $check, 104*5486feefSafresh1 ); 105*5486feefSafresh1 } 106*5486feefSafresh1 } 107*5486feefSafresh1 108*5486feefSafresh1 if (@for_each) { 109*5486feefSafresh1 my @checks = map { $convert->($_) } @for_each; 110*5486feefSafresh1 111*5486feefSafresh1 for my $idx (0..$#list) { 112*5486feefSafresh1 # All items are matched if we have conditions for all items 113*5486feefSafresh1 delete $unmatched{$idx}; 114*5486feefSafresh1 115*5486feefSafresh1 my $val = $list[$idx]; 116*5486feefSafresh1 117*5486feefSafresh1 for my $check (@checks) { 118*5486feefSafresh1 push @deltas => $check->run( 119*5486feefSafresh1 id => [ARRAY => $idx], 120*5486feefSafresh1 convert => $convert, 121*5486feefSafresh1 seen => $seen, 122*5486feefSafresh1 exists => 1, 123*5486feefSafresh1 got => $val, 124*5486feefSafresh1 ); 125*5486feefSafresh1 } 126*5486feefSafresh1 } 127*5486feefSafresh1 } 128*5486feefSafresh1 129*5486feefSafresh1 # if elements are left over, and ending is true, we have a problem! 130*5486feefSafresh1 if($self->{+ENDING} && keys %unmatched) { 131*5486feefSafresh1 for my $idx (sort keys %unmatched) { 132*5486feefSafresh1 my $elem = $list[$idx]; 133*5486feefSafresh1 push @deltas => $self->delta_class->new( 134*5486feefSafresh1 dne => 'check', 135*5486feefSafresh1 verified => undef, 136*5486feefSafresh1 id => [ARRAY => $idx], 137*5486feefSafresh1 got => $elem, 138*5486feefSafresh1 check => undef, 139*5486feefSafresh1 140*5486feefSafresh1 $self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (), 141*5486feefSafresh1 ); 142*5486feefSafresh1 } 143*5486feefSafresh1 } 144*5486feefSafresh1 145*5486feefSafresh1 return @deltas; 146*5486feefSafresh1} 147*5486feefSafresh1 148*5486feefSafresh11; 149*5486feefSafresh1 150*5486feefSafresh1__END__ 151*5486feefSafresh1 152*5486feefSafresh1=pod 153*5486feefSafresh1 154*5486feefSafresh1=encoding UTF-8 155*5486feefSafresh1 156*5486feefSafresh1=head1 NAME 157*5486feefSafresh1 158*5486feefSafresh1Test2::Compare::Bag - Internal representation of a bag comparison. 159*5486feefSafresh1 160*5486feefSafresh1=head1 DESCRIPTION 161*5486feefSafresh1 162*5486feefSafresh1This module is an internal representation of a bag for comparison purposes. 163*5486feefSafresh1 164*5486feefSafresh1=head1 METHODS 165*5486feefSafresh1 166*5486feefSafresh1=over 4 167*5486feefSafresh1 168*5486feefSafresh1=item $bool = $arr->ending 169*5486feefSafresh1 170*5486feefSafresh1=item $arr->set_ending($bool) 171*5486feefSafresh1 172*5486feefSafresh1Set this to true if you would like to fail when the array being validated has 173*5486feefSafresh1more items than the check. That is, if you check for 4 items but the array has 174*5486feefSafresh15 values, it will fail and list that unmatched item in the array as 175*5486feefSafresh1unexpected. If set to false then it is assumed you do not care about extra 176*5486feefSafresh1items. 177*5486feefSafresh1 178*5486feefSafresh1=item $arrayref = $arr->items() 179*5486feefSafresh1 180*5486feefSafresh1Returns the arrayref of values to be checked in the array. 181*5486feefSafresh1 182*5486feefSafresh1=item $arr->set_items($arrayref) 183*5486feefSafresh1 184*5486feefSafresh1Accepts an arrayref. 185*5486feefSafresh1 186*5486feefSafresh1B<Note:> that there is no validation when using C<set_items>, it is better to 187*5486feefSafresh1use the C<add_item> interface. 188*5486feefSafresh1 189*5486feefSafresh1=item $name = $arr->name() 190*5486feefSafresh1 191*5486feefSafresh1Always returns the string C<< "<BAG>" >>. 192*5486feefSafresh1 193*5486feefSafresh1=item $bool = $arr->verify(got => $got, exists => $bool) 194*5486feefSafresh1 195*5486feefSafresh1Check if C<$got> is an array reference or not. 196*5486feefSafresh1 197*5486feefSafresh1=item $arr->add_item($item) 198*5486feefSafresh1 199*5486feefSafresh1Push an item onto the list of values to be checked. 200*5486feefSafresh1 201*5486feefSafresh1=item @deltas = $arr->deltas(got => $got, convert => \&convert, seen => \%seen) 202*5486feefSafresh1 203*5486feefSafresh1Find the differences between the expected bag values and those in the C<$got> 204*5486feefSafresh1arrayref. 205*5486feefSafresh1 206*5486feefSafresh1=back 207*5486feefSafresh1 208*5486feefSafresh1=head1 SOURCE 209*5486feefSafresh1 210*5486feefSafresh1The source code repository for Test2-Suite can be found at 211*5486feefSafresh1F<https://github.com/Test-More/Test2-Suite/>. 212*5486feefSafresh1 213*5486feefSafresh1=head1 MAINTAINERS 214*5486feefSafresh1 215*5486feefSafresh1=over 4 216*5486feefSafresh1 217*5486feefSafresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt> 218*5486feefSafresh1 219*5486feefSafresh1=item Gianni Ceccarelli E<lt>dakkar@thenautilus.netE<gt> 220*5486feefSafresh1 221*5486feefSafresh1=back 222*5486feefSafresh1 223*5486feefSafresh1=head1 AUTHORS 224*5486feefSafresh1 225*5486feefSafresh1=over 4 226*5486feefSafresh1 227*5486feefSafresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt> 228*5486feefSafresh1 229*5486feefSafresh1=item Gianni Ceccarelli E<lt>dakkar@thenautilus.netE<gt> 230*5486feefSafresh1 231*5486feefSafresh1=back 232*5486feefSafresh1 233*5486feefSafresh1=head1 COPYRIGHT 234*5486feefSafresh1 235*5486feefSafresh1Copyright 2018 Chad Granum E<lt>exodist@cpan.orgE<gt>. 236*5486feefSafresh1 237*5486feefSafresh1Copyright 2018 Gianni Ceccarelli E<lt>dakkar@thenautilus.netE<gt> 238*5486feefSafresh1 239*5486feefSafresh1This program is free software; you can redistribute it and/or 240*5486feefSafresh1modify it under the same terms as Perl itself. 241*5486feefSafresh1 242*5486feefSafresh1See F<http://dev.perl.org/licenses/> 243*5486feefSafresh1 244*5486feefSafresh1=cut 245