File Coverage

blib/lib/Test2/Compare/Hash.pm
Criterion Covered Total %
statement 101 101 100.0
branch 37 38 97.3
condition 2 4 50.0
subroutine 15 15 100.0
pod 3 9 33.3
total 158 167 94.6


line stmt bran cond sub pod time code
1             package Test2::Compare::Hash;
2 169     169   1210 use strict;
  169         369  
  169         6356  
3 169     169   967 use warnings;
  169         330  
  169         4501  
4              
5 169     169   916 use base 'Test2::Compare::Base';
  169         341  
  169         24919  
6              
7             our $VERSION = '0.000156';
8              
9 169     169   1145 use Test2::Util::HashBase qw/inref meta ending items order for_each_key for_each_val/;
  169         384  
  169         1071  
10              
11 169     169   56381 use Carp qw/croak confess/;
  169         360  
  169         9456  
12 169     169   1100 use Scalar::Util qw/reftype/;
  169         358  
  169         196852  
13              
14             sub init {
15 2153     2153 0 29387 my $self = shift;
16              
17 2153 100       5390 if( defined( my $ref = $self->{+INREF} ) ) {
18 1561 100       3928 croak "Cannot specify both 'inref' and 'items'" if $self->{+ITEMS};
19 1560 100       3404 croak "Cannot specify both 'inref' and 'order'" if $self->{+ORDER};
20 1559         6699 $self->{+ITEMS} = {%$ref};
21 1559         6046 $self->{+ORDER} = [sort keys %$ref];
22             }
23             else {
24             # Clone the ref to be safe
25 592 100       1364 $self->{+ITEMS} = $self->{+ITEMS} ? {%{$self->{+ITEMS}}} : {};
  3         15  
26 592 100       1179 if ($self->{+ORDER}) {
27 2         12 my @all = keys %{$self->{+ITEMS}};
  2         9  
28 2         6 my %have = map { $_ => 1 } @{$self->{+ORDER}};
  3         9  
  2         7  
29 2         5 my @missing = grep { !$have{$_} } @all;
  5         15  
30 2 100       104 croak "Keys are missing from the 'order' array: " . join(', ', sort @missing)
31             if @missing;
32             }
33             else {
34 590         834 $self->{+ORDER} = [sort keys %{$self->{+ITEMS}}];
  590         1907  
35             }
36             }
37              
38 2150   50     10342 $self->{+FOR_EACH_KEY} ||= [];
39 2150   50     9392 $self->{+FOR_EACH_VAL} ||= [];
40              
41 2150         6327 $self->SUPER::init();
42             }
43              
44 16     16 1 84 sub name { '' }
45              
46 3     3 0 20 sub meta_class { 'Test2::Compare::Meta' }
47              
48             sub verify {
49 2157     2157 1 3512 my $self = shift;
50 2157         6277 my %params = @_;
51 2157         5052 my ($got, $exists) = @params{qw/got exists/};
52              
53 2157 100       4269 return 0 unless $exists;
54 2153 100       4387 return 0 unless defined $got;
55 2151 100       5158 return 0 unless ref($got);
56 2148 100       6530 return 0 unless reftype($got) eq 'HASH';
57 2141         6618 return 1;
58             }
59              
60             sub add_prop {
61 4     4 0 39 my $self = shift;
62 4 100       23 $self->{+META} = $self->meta_class->new unless defined $self->{+META};
63 4         27 $self->{+META}->add_prop(@_);
64             }
65              
66             sub add_field {
67 1029     1029 0 3739 my $self = shift;
68 1029         1794 my ($name, $check) = @_;
69              
70 1029 100       2189 croak "field name is required"
71             unless defined $name;
72              
73             croak "field '$name' has already been specified"
74 1028 100       2307 if exists $self->{+ITEMS}->{$name};
75              
76 1027         1354 push @{$self->{+ORDER}} => $name;
  1027         2101  
77 1027         2867 $self->{+ITEMS}->{$name} = $check;
78             }
79              
80             sub add_for_each_key {
81 2     2 0 4 my $self = shift;
82 2         3 push @{$self->{+FOR_EACH_KEY}} => @_;
  2         8  
83             }
84              
85             sub add_for_each_val {
86 2     2 0 6 my $self = shift;
87 2         3 push @{$self->{+FOR_EACH_VAL}} => @_;
  2         7  
88             }
89              
90             sub deltas {
91 2146     2146 1 3507 my $self = shift;
92 2146         5422 my %params = @_;
93 2146         4661 my ($got, $convert, $seen) = @params{qw/got convert seen/};
94              
95 2146         3269 my @deltas;
96 2146         4282 my $items = $self->{+ITEMS};
97 2146         3540 my $each_key = $self->{+FOR_EACH_KEY};
98 2146         3811 my $each_val = $self->{+FOR_EACH_VAL};
99              
100             # Make a copy that we can munge as needed.
101 2146         14398 my %fields = %$got;
102              
103 2146         5020 my $meta = $self->{+META};
104 2146 100       4639 push @deltas => $meta->deltas(%params) if defined $meta;
105              
106 2146         3199 for my $key (@{$self->{+ORDER}}) {
  2146         5095  
107 3268         9341 my $check = $convert->($items->{$key});
108 3268         13332 my $exists = exists $fields{$key};
109 3268         6402 my $val = delete $fields{$key};
110              
111 3268 100       7007 if ($exists) {
112 3239         6925 for my $kcheck (@$each_key) {
113 4         10 $kcheck = $convert->($kcheck);
114              
115 4         25 push @deltas => $kcheck->run(
116             id => [HASHKEY => $key],
117             convert => $convert,
118             seen => $seen,
119             exists => $exists,
120             got => $key,
121             );
122             }
123              
124 3239         5448 for my $vcheck (@$each_val) {
125 4         15 $vcheck = $convert->($vcheck);
126              
127 4         24 push @deltas => $vcheck->run(
128             id => [HASH => $key],
129             convert => $convert,
130             seen => $seen,
131             exists => $exists,
132             got => $val,
133             );
134             }
135             }
136              
137 3268 100       13661 push @deltas => $check->run(
138             id => [HASH => $key],
139             convert => $convert,
140             seen => $seen,
141             exists => $exists,
142             $exists ? (got => $val) : (),
143             );
144             }
145              
146 2146 100       5862 if (keys %fields) {
147 1869         7054 for my $key (sort keys %fields) {
148 6321         9492 my $val = $fields{$key};
149              
150 6321         9578 for my $kcheck (@$each_key) {
151 2         17 $kcheck = $convert->($kcheck);
152              
153 2         15 push @deltas => $kcheck->run(
154             id => [HASHKEY => $key],
155             convert => $convert,
156             seen => $seen,
157             got => $key,
158             exists => 1,
159             );
160             }
161              
162 6321         9109 for my $vcheck (@$each_val) {
163 2         6 $vcheck = $convert->($vcheck);
164              
165 2         23 push @deltas => $vcheck->run(
166             id => [HASH => $key],
167             convert => $convert,
168             seen => $seen,
169             got => $val,
170             exists => 1,
171             );
172             }
173              
174             # if items are left over, and ending is true, we have a problem!
175 6321 100       13223 if ($self->{+ENDING}) {
176             push @deltas => $self->delta_class->new(
177             dne => 'check',
178             verified => undef,
179             id => [HASH => $key],
180             got => $val,
181             check => undef,
182              
183 8 50       53 $self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (),
184             );
185             }
186             }
187             }
188              
189 2146         7896 return @deltas;
190             }
191              
192             1;
193              
194             __END__