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 168     168   1162 use strict;
  168         377  
  168         4855  
3 168     168   816 use warnings;
  168         329  
  168         4353  
4              
5 168     168   842 use base 'Test2::Compare::Base';
  168         327  
  168         17563  
6              
7             our $VERSION = '0.000153';
8              
9 168     168   1132 use Test2::Util::HashBase qw/inref meta ending items order for_each_key for_each_val/;
  168         325  
  168         1040  
10              
11 168     168   58880 use Carp qw/croak confess/;
  168         373  
  168         8652  
12 168     168   1014 use Scalar::Util qw/reftype/;
  168         318  
  168         172940  
13              
14             sub init {
15 2113     2113 0 27000 my $self = shift;
16              
17 2113 100       5051 if( defined( my $ref = $self->{+INREF} ) ) {
18 1557 100       3589 croak "Cannot specify both 'inref' and 'items'" if $self->{+ITEMS};
19 1556 100       3517 croak "Cannot specify both 'inref' and 'order'" if $self->{+ORDER};
20 1555         6613 $self->{+ITEMS} = {%$ref};
21 1555         6005 $self->{+ORDER} = [sort keys %$ref];
22             }
23             else {
24             # Clone the ref to be safe
25 556 100       1064 $self->{+ITEMS} = $self->{+ITEMS} ? {%{$self->{+ITEMS}}} : {};
  3         10  
26 556 100       857 if ($self->{+ORDER}) {
27 2         3 my @all = keys %{$self->{+ITEMS}};
  2         7  
28 2         4 my %have = map { $_ => 1 } @{$self->{+ORDER}};
  3         10  
  2         6  
29 2         4 my @missing = grep { !$have{$_} } @all;
  5         13  
30 2 100       103 croak "Keys are missing from the 'order' array: " . join(', ', sort @missing)
31             if @missing;
32             }
33             else {
34 554         662 $self->{+ORDER} = [sort keys %{$self->{+ITEMS}}];
  554         1526  
35             }
36             }
37              
38 2110   50     10032 $self->{+FOR_EACH_KEY} ||= [];
39 2110   50     8276 $self->{+FOR_EACH_VAL} ||= [];
40              
41 2110         6382 $self->SUPER::init();
42             }
43              
44 14     14 1 55 sub name { '' }
45              
46 3     3 0 19 sub meta_class { 'Test2::Compare::Meta' }
47              
48             sub verify {
49 2117     2117 1 3404 my $self = shift;
50 2117         6049 my %params = @_;
51 2117         4681 my ($got, $exists) = @params{qw/got exists/};
52              
53 2117 100       4254 return 0 unless $exists;
54 2113 100       3646 return 0 unless defined $got;
55 2111 100       4272 return 0 unless ref($got);
56 2108 100       5743 return 0 unless reftype($got) eq 'HASH';
57 2101         6103 return 1;
58             }
59              
60             sub add_prop {
61 4     4 0 35 my $self = shift;
62 4 100       18 $self->{+META} = $self->meta_class->new unless defined $self->{+META};
63 4         20 $self->{+META}->add_prop(@_);
64             }
65              
66             sub add_field {
67 971     971 0 2749 my $self = shift;
68 971         1350 my ($name, $check) = @_;
69              
70 971 100       1600 croak "field name is required"
71             unless defined $name;
72              
73             croak "field '$name' has already been specified"
74 970 100       1850 if exists $self->{+ITEMS}->{$name};
75              
76 969         1017 push @{$self->{+ORDER}} => $name;
  969         1647  
77 969         2309 $self->{+ITEMS}->{$name} = $check;
78             }
79              
80             sub add_for_each_key {
81 2     2 0 3 my $self = shift;
82 2         4 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         4 push @{$self->{+FOR_EACH_VAL}} => @_;
  2         6  
88             }
89              
90             sub deltas {
91 2106     2106 1 3330 my $self = shift;
92 2106         4948 my %params = @_;
93 2106         4387 my ($got, $convert, $seen) = @params{qw/got convert seen/};
94              
95 2106         2838 my @deltas;
96 2106         3490 my $items = $self->{+ITEMS};
97 2106         3675 my $each_key = $self->{+FOR_EACH_KEY};
98 2106         3245 my $each_val = $self->{+FOR_EACH_VAL};
99              
100             # Make a copy that we can munge as needed.
101 2106         13425 my %fields = %$got;
102              
103 2106         4582 my $meta = $self->{+META};
104 2106 100       4591 push @deltas => $meta->deltas(%params) if defined $meta;
105              
106 2106         3018 for my $key (@{$self->{+ORDER}}) {
  2106         5095  
107 3206         8183 my $check = $convert->($items->{$key});
108 3206         12481 my $exists = exists $fields{$key};
109 3206         5547 my $val = delete $fields{$key};
110              
111 3206 100       6336 if ($exists) {
112 3177         6248 for my $kcheck (@$each_key) {
113 4         122 $kcheck = $convert->($kcheck);
114              
115 4         19 push @deltas => $kcheck->run(
116             id => [HASHKEY => $key],
117             convert => $convert,
118             seen => $seen,
119             exists => $exists,
120             got => $key,
121             );
122             }
123              
124 3177         4915 for my $vcheck (@$each_val) {
125 4         14 $vcheck = $convert->($vcheck);
126              
127 4         15 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 3206 100       12516 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 2106 100       5323 if (keys %fields) {
147 1829         6474 for my $key (sort keys %fields) {
148 6215         8878 my $val = $fields{$key};
149              
150 6215         8767 for my $kcheck (@$each_key) {
151 2         8 $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 6215         8044 for my $vcheck (@$each_val) {
163 2         5 $vcheck = $convert->($vcheck);
164              
165 2         18 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 6215 100       11999 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       30 $self->{+ENDING} eq 'implicit' ? (note => 'implicit end') : (),
184             );
185             }
186             }
187             }
188              
189 2106         7285 return @deltas;
190             }
191              
192             1;
193              
194             __END__