File Coverage

blib/lib/Test/Deep/UnorderedPairs.pm
Criterion Covered Total %
statement 76 78 97.4
branch 14 16 87.5
condition 1 2 50.0
subroutine 21 23 91.3
pod 3 5 60.0
total 115 124 92.7


line stmt bran cond sub pod time code
1 2     2   68422 use strict;
  2         6  
  2         77  
2 2     2   13 use warnings;
  2         4  
  2         135  
3             package Test::Deep::UnorderedPairs; # git description: v0.004-2-gb6f217e
4             # ABSTRACT: A Test::Deep plugin for comparing an unordered list of tuples
5             # KEYWORDS: testing tests plugin hash list tuples pairs unordered
6             # vim: set ts=8 sts=4 sw=4 tw=78 et :
7              
8             our $VERSION = '0.005';
9              
10 2     2   12 use parent 'Test::Deep::Cmp';
  2         4  
  2         12  
11 2     2   6718 use Exporter 'import';
  2         5  
  2         139  
12 2     2   12 use Carp ();
  2         4  
  2         41  
13 2     2   1279 use Test::Deep::Hash;
  2         3776  
  2         30  
14 2     2   1414 use Test::Deep::ArrayLength;
  2         477  
  2         23  
15              
16             # I'm not sure what name is best; decide later
17             our @EXPORT = qw(tuples unordered_pairs samehash);
18              
19             sub tuples
20             {
21 13     13 1 1404 return __PACKAGE__->new('tuples', @_);
22             }
23             sub unordered_pairs
24             {
25 0     0 1 0 return __PACKAGE__->new('unordered_pairs', @_);
26             }
27             sub samehash
28             {
29 0     0 1 0 return __PACKAGE__->new('samehash', @_);
30             }
31              
32             sub init
33             {
34 13     13 0 87 my ($self, $name, @vals) = @_;
35              
36 13   50     27 $name ||= 'tuples';
37 13 100       325 Carp::confess $name . ' must have an even number of elements'
38             if @vals % 2;
39              
40 12         292 $self->{name} = $name; # use in later diagnostic messages?
41 12         33 $self->{val} = \@vals;
42             }
43              
44             sub descend
45             {
46 12     12 0 27132 my ($self, $got) = @_;
47              
48 12         32 my $exp = $self->{val};
49              
50 12 100       67 return 0 unless Test::Deep::ArrayLength->new(@$exp + 0)->descend($got);
51              
52             # check that all the keys are present -- can test as a bag
53              
54 10         5492 my @exp_keys = _keys_of_list($exp);
55 10         25 my @got_keys = _keys_of_list($got);
56              
57 10 100       70 return 0 unless Test::Deep::descend(\@got_keys, Test::Deep::UnorderedPairKeys->new(@exp_keys));
58              
59 7         3651 Test::Deep::descend($got, Test::Deep::UnorderedPairElements->new($exp));
60             }
61              
62             sub _keys_of_list
63             {
64 21     21   46 my $list = shift;
65              
66 21         29 my $i = 0;
67 21 100       38 map { $i++ % 2 ? () : $_ } @$list;
  84         271  
68             }
69              
70              
71             package Test::Deep::UnorderedPairKeys; # git description: v0.004-2-gb6f217e
72 2     2   950 use parent 'Test::Deep::Set';
  2         12  
  2         16  
73              
74             our $VERSION = '0.005';
75              
76             sub init
77             {
78             # quack like a bag
79 10     10   111 shift->SUPER::init(0, '', @_);
80             }
81              
82             sub diagnostics
83             {
84 3     3   3524 my ($self, $where, $last) = @_;
85              
86 3         8 my $error = $last->{diag};
87 3         16 my $diag = <
88             Comparing keys of $where
89             $error
90             EOM
91              
92 3         9 return $diag;
93             }
94              
95              
96             package Test::Deep::UnorderedPairElements; # git description: v0.004-2-gb6f217e
97 2     2   3954 use parent 'Test::Deep::Cmp';
  2         5  
  2         19  
98              
99             our $VERSION = '0.005';
100              
101             sub init
102             {
103 7     7   54 my ($self, $val) = @_;
104 7         170 $self->{val} = $val;
105             }
106              
107             # we assume the keys are already verified as identical.
108             sub descend
109             {
110 7     7   703 my ($self, $got) = @_;
111              
112             # make copy, as we are going to modify this one!
113 7         37 my @exp = @{$self->{val}};
  7         53  
114 7         36 my $data = $self->data;
115              
116 7         88 GOT_KEY: for (my $got_index = 0; $got_index < @$got; $got_index += 1)
117             {
118             # find the first occurrence of $key in @exp
119 13         44 EXP_KEY: for (my $exp_index = 0; $exp_index < @exp; $exp_index += 1)
120             {
121 19 100       58 if (not Test::Deep::eq_deeply_cache($got->[$got_index], $exp[$exp_index]))
122             {
123             # advance to the next key position
124 6         738 ++$exp_index;
125 6         21 next;
126             }
127              
128             # found a matching key in got and exp
129              
130 13         962 $data->{got_index} = ++$got_index;
131 13         29 $data->{exp_value} = $exp[++$exp_index];
132              
133 13 100       37 if (Test::Deep::eq_deeply_cache($got->[$got_index], $data->{exp_value}))
134             {
135             # splice this out of the exp list and continue with the next key
136 10         710 splice(@exp, $exp_index - 1, 2);
137 10         45 next GOT_KEY;
138             }
139              
140             # values do not match - keep looking for another match unless there are no more!
141             }
142              
143             # got to the end of exp_keys, but still no matches found
144 3         352 return 0;
145             }
146              
147             # exhausted all got_keys. if everything matched, @exp would be empty
148 4 50       18 return @exp ? 0 : 1;
149             }
150              
151             sub render_stack
152             {
153 3     3   1675 my ($self, $var, $data) = @_;
154 3 50       17 $var .= "->" unless $Test::Deep::Stack->incArrow;
155 3         64 $var .= '[' . $data->{got_index} . ']';
156              
157 3         9 return $var;
158             }
159              
160             sub reset_arrow
161             {
162 3     3   13 return 0;
163             }
164              
165             sub renderGot
166             {
167 3     3   107 my ($self, $got) = @_;
168 3         13 return $self->SUPER::renderGot($got->[$self->data->{got_index}]);
169             }
170              
171             sub renderExp
172             {
173 3     3   80 my $self = shift;
174 3         9 return $self->SUPER::renderGot($self->data->{exp_value});
175             }
176              
177             1;
178              
179             __END__