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   43337 use strict;
  2         2  
  2         62  
2 2     2   6 use warnings;
  2         2  
  2         81  
3             package Test::Deep::UnorderedPairs; # git description: v0.003-22-gb9b9b18
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.004';
9              
10 2     2   7 use parent 'Test::Deep::Cmp';
  2         3  
  2         11  
11 2     2   4063 use Exporter 'import';
  2         4  
  2         44  
12 2     2   8 use Carp ();
  2         1  
  2         21  
13 2     2   1087 use Test::Deep::Hash;
  2         2606  
  2         19  
14 2     2   889 use Test::Deep::ArrayLength;
  2         348  
  2         19  
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 787 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 77 my ($self, $name, @vals) = @_;
35              
36 13   50     24 $name ||= 'tuples';
37 13 100       214 Carp::confess $name . ' must have an even number of elements'
38             if @vals % 2;
39              
40 12         311 $self->{name} = $name; # use in later diagnostic messages?
41 12         29 $self->{val} = \@vals;
42             }
43              
44             sub descend
45             {
46 12     12 0 15082 my ($self, $got) = @_;
47              
48 12         22 my $exp = $self->{val};
49              
50 12 100       52 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         5506 my @exp_keys = _keys_of_list($exp);
55 10         15 my @got_keys = _keys_of_list($got);
56              
57 10 100       43 return 0 unless Test::Deep::descend(\@got_keys, Test::Deep::UnorderedPairKeys->new(@exp_keys));
58              
59 7         2735 Test::Deep::descend($got, Test::Deep::UnorderedPairElements->new($exp));
60             }
61              
62             sub _keys_of_list
63             {
64 21     21   42 my $list = shift;
65              
66 21         17 my $i = 0;
67 21 100       28 map { $i++ % 2 ? () : $_ } @$list;
  84         155  
68             }
69              
70              
71             package Test::Deep::UnorderedPairKeys; # git description: v0.003-22-gb9b9b18
72 2     2   816 use parent 'Test::Deep::Set';
  2         12  
  2         11  
73              
74             sub init
75             {
76             # quack like a bag
77 10     10   85 shift->SUPER::init(0, '', @_);
78             }
79              
80             sub diagnostics
81             {
82 3     3   2707 my ($self, $where, $last) = @_;
83              
84 3         7 my $error = $last->{diag};
85 3         9 my $diag = <
86             Comparing keys of $where
87             $error
88             EOM
89              
90 3         8 return $diag;
91             }
92              
93              
94             package Test::Deep::UnorderedPairElements; # git description: v0.003-22-gb9b9b18
95 2     2   2651 use parent 'Test::Deep::Cmp';
  2         3  
  2         10  
96              
97             sub init
98             {
99 7     7   34 my ($self, $val) = @_;
100 7         107 $self->{val} = $val;
101             }
102              
103             # we assume the keys are already verified as identical.
104             sub descend
105             {
106 7     7   454 my ($self, $got) = @_;
107              
108             # make copy, as we are going to modify this one!
109 7         34 my @exp = @{$self->{val}};
  7         19  
110 7         33 my $data = $self->data;
111              
112 7         69 GOT_KEY: for (my $got_index = 0; $got_index < @$got; $got_index += 1)
113             {
114             # find the first occurrence of $key in @exp
115 13         27 EXP_KEY: for (my $exp_index = 0; $exp_index < @exp; $exp_index += 1)
116             {
117 19 100       44 if (not Test::Deep::eq_deeply_cache($got->[$got_index], $exp[$exp_index]))
118             {
119             # advance to the next key position
120 6         435 ++$exp_index;
121 6         13 next;
122             }
123              
124             # found a matching key in got and exp
125              
126 13         577 $data->{got_index} = ++$got_index;
127 13         17 $data->{exp_value} = $exp[++$exp_index];
128              
129 13 100       24 if (Test::Deep::eq_deeply_cache($got->[$got_index], $data->{exp_value}))
130             {
131             # splice this out of the exp list and continue with the next key
132 10         425 splice(@exp, $exp_index - 1, 2);
133 10         27 next GOT_KEY;
134             }
135              
136             # values do not match - keep looking for another match unless there are no more!
137             }
138              
139             # got to the end of exp_keys, but still no matches found
140 3         204 return 0;
141             }
142              
143             # exhausted all got_keys. if everything matched, @exp would be empty
144 4 50       12 return @exp ? 0 : 1;
145             }
146              
147             sub render_stack
148             {
149 3     3   744 my ($self, $var, $data) = @_;
150 3 50       11 $var .= "->" unless $Test::Deep::Stack->incArrow;
151 3         47 $var .= '[' . $data->{got_index} . ']';
152              
153 3         6 return $var;
154             }
155              
156             sub reset_arrow
157             {
158 3     3   38 return 0;
159             }
160              
161             sub renderGot
162             {
163 3     3   76 my ($self, $got) = @_;
164 3         9 return $self->SUPER::renderGot($got->[$self->data->{got_index}]);
165             }
166              
167             sub renderExp
168             {
169 3     3   49 my $self = shift;
170 3         7 return $self->SUPER::renderGot($self->data->{exp_value});
171             }
172              
173             1;
174              
175             __END__