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