package Test2::Compare::Bag;
use strict;
use warnings;
use base 'Test2::Compare::Base';
our $VERSION = '0.000122';
use Test2::Util::HashBase qw/ending meta items for_each/;
use Carp qw/croak confess/;
use Scalar::Util qw/reftype looks_like_number/;
sub init {
my $self = shift;
$self->{+ITEMS} ||= [];
$self->{+FOR_EACH} ||= [];
$self->SUPER::init();
}
sub name { '<BAG>' }
sub meta_class { 'Test2::Compare::Meta' }
sub verify {
my $self = shift;
my %params = @_;
return 0 unless $params{exists};
my $got = $params{got} || return 0;
return 0 unless ref($got);
return 0 unless reftype($got) eq 'ARRAY';
return 1;
}
sub add_prop {
my $self = shift;
$self->{+META} = $self->meta_class->new unless defined $self->{+META};
$self->{+META}->add_prop(@_);
}
sub add_item {
my $self = shift;
my $check = pop;
my ($idx) = @_;
push @{$self->{+ITEMS}}, $check;
}
sub add_for_each {
my $self = shift;
push @{$self->{+FOR_EACH}} => @_;
}
sub deltas {
my $self = shift;
my %params = @_;
my ($got, $convert, $seen) = @params{qw/got convert seen/};
my @deltas;
my $state = 0;
my @items = @{$self->{+ITEMS}};
my @for_each = @{$self->{+FOR_EACH}};
# Make a copy that we can munge as needed.
my @list = @$got;
my %unmatched = map { $_ => $list[$_] } 0..$#list;
my $meta = $self->{+META};
push @deltas => $meta->deltas(%params) if defined $meta;
while (@items) {
my $item = shift @items;
my $check = $convert->($item);
my $match = 0;
for my $idx (0..$#list) {
next unless exists $unmatched{$idx};
my $val = $list[$idx];
my $deltas = $check->run(
id => [ARRAY => $idx],
convert => $convert,
seen => $seen,
exists => 1,
got => $val,
);
unless ($deltas) {
$match++;
delete $unmatched{$idx};
last;
}
}
unless ($match) {
push @deltas => $self->delta_class->new(
dne => 'got',
verified => undef,
id => [ARRAY => '*'],
got => undef,
check => $check,
);
}
}
if (@for_each) {
my @checks = map { $convert->($_) } @for_each;
for my $idx (0..$#list) {
# All items are matched if we have conditions for all items
delete $unmatched{$idx};
my $val = $list[$idx];
for my $check (@checks) {
push @deltas => $check->run(
id => [ARRAY => $idx],
convert => $convert,
seen => $seen,
exists => 1,
got => $val,
);
}
}
}
# if elements are left over, and ending is true, we have a problem!
if($self->{+ENDING} && keys %unmatched) {
for my $idx (sort keys %unmatched) {
my $elem = $list[$idx];
push @deltas => $self->delta_class->new(
dne => 'check',
verified => undef,
id => [ARRAY => $idx],
got => $elem,
check => undef,
$self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (),
);
}
}
return @deltas;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Test2::Compare::Bag - Internal representation of a bag comparison.
=head1 DESCRIPTION
This module is an internal representation of a bag for comparison purposes.
=head1 METHODS
=over 4
=item $bool = $arr->ending
=item $arr->set_ending($bool)
Set this to true if you would like to fail when the array being validated has
more items than the check. That is, if you check for 4 items but the array has
5 values, it will fail and list that unmatched item in the array as
unexpected. If set to false then it is assumed you do not care about extra
items.
=item $arrayref = $arr->items()
Returns the arrayref of values to be checked in the array.
=item $arr->set_items($arrayref)
Accepts an arrayref.
B<Note:> that there is no validation when using C<set_items>, it is better to
use the C<add_item> interface.
=item $name = $arr->name()
Always returns the string C<< "<BAG>" >>.
=item $bool = $arr->verify(got => $got, exists => $bool)
Check if C<$got> is an array reference or not.
=item $arr->add_item($item)
Push an item onto the list of values to be checked.
=item @deltas = $arr->deltas(got => $got, convert => \&convert, seen => \%seen)
Find the differences between the expected bag values and those in the C<$got>
arrayref.
=back
=head1 SOURCE
The source code repository for Test2-Suite can be found at
F<https://github.com/Test-More/Test2-Suite/>.
=head1 MAINTAINERS
=over 4
=item Chad Granum E<lt>exodist@cpan.orgE<gt>
=item Gianni Ceccarelli E<lt>dakkar@thenautilus.netE<gt>
=back
=head1 AUTHORS
=over 4
=item Chad Granum E<lt>exodist@cpan.orgE<gt>
=item Gianni Ceccarelli E<lt>dakkar@thenautilus.netE<gt>
=back
=head1 COPYRIGHT
Copyright 2018 Chad Granum E<lt>exodist@cpan.orgE<gt>.
Copyright 2018 Gianni Ceccarelli E<lt>dakkar@thenautilus.netE<gt>
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
See F<http://dev.perl.org/licenses/>
=cut
Copyright 2K16 - 2K18 Indonesian Hacker Rulez