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   1202 use strict;
  169         387  
  169         5117  
3 169     169   891 use warnings;
  169         408  
  169         4439  
4              
5 169     169   966 use base 'Test2::Compare::Base';
  169         357  
  169         19231  
6              
7             our $VERSION = '0.000155';
8              
9 169     169   1194 use Test2::Util::HashBase qw/inref meta ending items order for_each_key for_each_val/;
  169         413  
  169         1271  
10              
11 169     169   63291 use Carp qw/croak confess/;
  169         391  
  169         9254  
12 169     169   1413 use Scalar::Util qw/reftype/;
  169         350  
  169         184370  
13              
14             sub init {
15 2117     2117 0 28988 my $self = shift;
16              
17 2117 100       5673 if( defined( my $ref = $self->{+INREF} ) ) {
18 1557 100       4703 croak "Cannot specify both 'inref' and 'items'" if $self->{+ITEMS};
19 1556 100       3388 croak "Cannot specify both 'inref' and 'order'" if $self->{+ORDER};
20 1555         6693 $self->{+ITEMS} = {%$ref};
21 1555         6586 $self->{+ORDER} = [sort keys %$ref];
22             }
23             else {
24             # Clone the ref to be safe
25 560 100       1332 $self->{+ITEMS} = $self->{+ITEMS} ? {%{$self->{+ITEMS}}} : {};
  3         14  
26 560 100       1139 if ($self->{+ORDER}) {
27 2         5 my @all = keys %{$self->{+ITEMS}};
  2         14  
28 2         5 my %have = map { $_ => 1 } @{$self->{+ORDER}};
  3         11  
  2         5  
29 2         4 my @missing = grep { !$have{$_} } @all;
  5         13  
30 2 100       100 croak "Keys are missing from the 'order' array: " . join(', ', sort @missing)
31             if @missing;
32             }
33             else {
34 558         892 $self->{+ORDER} = [sort keys %{$self->{+ITEMS}}];
  558         1698  
35             }
36             }
37              
38 2114   50     10535 $self->{+FOR_EACH_KEY} ||= [];
39 2114   50     9177 $self->{+FOR_EACH_VAL} ||= [];
40              
41 2114         6295 $self->SUPER::init();
42             }
43              
44 16     16 1 75 sub name { '' }
45              
46 3     3 0 21 sub meta_class { 'Test2::Compare::Meta' }
47              
48             sub verify {
49 2121     2121 1 3571 my $self = shift;
50 2121         6559 my %params = @_;
51 2121         4799 my ($got, $exists) = @params{qw/got exists/};
52              
53 2121 100       4512 return 0 unless $exists;
54 2117 100       4242 return 0 unless defined $got;
55 2115 100       4753 return 0 unless ref($got);
56 2112 100       6019 return 0 unless reftype($got) eq 'HASH';
57 2105         6277 return 1;
58             }
59              
60             sub add_prop {
61 4     4 0 61 my $self = shift;
62 4 100       30 $self->{+META} = $self->meta_class->new unless defined $self->{+META};
63 4         31 $self->{+META}->add_prop(@_);
64             }
65              
66             sub add_field {
67 973     973 0 3818 my $self = shift;
68 973         1757 my ($name, $check) = @_;
69              
70 973 100       1943 croak "field name is required"
71             unless defined $name;
72              
73             croak "field '$name' has already been specified"
74 972 100       2240 if exists $self->{+ITEMS}->{$name};
75              
76 971         1386 push @{$self->{+ORDER}} => $name;
  971         2046  
77 971         2924 $self->{+ITEMS}->{$name} = $check;
78             }
79              
80             sub add_for_each_key {
81 2     2 0 5 my $self = shift;
82 2         5 push @{$self->{+FOR_EACH_KEY}} => @_;
  2         7  
83             }
84              
85             sub add_for_each_val {
86 2     2 0 4 my $self = shift;
87 2         3 push @{$self->{+FOR_EACH_VAL}} => @_;
  2         8  
88             }
89              
90             sub deltas {
91 2110     2110 1 3718 my $self = shift;
92 2110         5396 my %params = @_;
93 2110         4615 my ($got, $convert, $seen) = @params{qw/got convert seen/};
94              
95 2110         3108 my @deltas;
96 2110         3588 my $items = $self->{+ITEMS};
97 2110         3401 my $each_key = $self->{+FOR_EACH_KEY};
98 2110         3760 my $each_val = $self->{+FOR_EACH_VAL};
99              
100             # Make a copy that we can munge as needed.
101 2110         12209 my %fields = %$got;
102              
103 2110         4722 my $meta = $self->{+META};
104 2110 100       5253 push @deltas => $meta->deltas(%params) if defined $meta;
105              
106 2110         3215 for my $key (@{$self->{+ORDER}}) {
  2110         4852  
107 3208         9000 my $check = $convert->($items->{$key});
108 3208         13098 my $exists = exists $fields{$key};
109 3208         6074 my $val = delete $fields{$key};
110              
111 3208 100       7039 if ($exists) {
112 3179         6428 for my $kcheck (@$each_key) {
113 4         164 $kcheck = $convert->($kcheck);
114              
115 4         32 push @deltas => $kcheck->run(
116             id => [HASHKEY => $key],
117             convert => $convert,
118             seen => $seen,
119             exists => $exists,
120             got => $key,
121             );
122             }
123              
124 3179         5403 for my $vcheck (@$each_val) {
125 4         15 $vcheck = $convert->($vcheck);
126              
127 4         25 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 3208 100       13665 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 2110 100       5639 if (keys %fields) {
147 1833         6837 for my $key (sort keys %fields) {
148 6221         9780 my $val = $fields{$key};
149              
150 6221         9302 for my $kcheck (@$each_key) {
151 2         20 $kcheck = $convert->($kcheck);
152              
153 2         37 push @deltas => $kcheck->run(
154             id => [HASHKEY => $key],
155             convert => $convert,
156             seen => $seen,
157             got => $key,
158             exists => 1,
159             );
160             }
161              
162 6221         8911 for my $vcheck (@$each_val) {
163 2         10 $vcheck = $convert->($vcheck);
164              
165 2         16 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 6221 100       12613 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       62 $self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (),
184             );
185             }
186             }
187             }
188              
189 2110         7817 return @deltas;
190             }
191              
192             1;
193              
194             __END__