File Coverage

blib/lib/String/FlexMatch/Test.pm
Criterion Covered Total %
statement 65 120 54.1
branch 19 62 30.6
condition 10 23 43.4
subroutine 13 15 86.6
pod 5 5 100.0
total 112 225 49.7


line stmt bran cond sub pod time code
1 2     2   85044 use 5.008;
  2         8  
  2         82  
2 2     2   13 use strict;
  2         4  
  2         88  
3 2     2   11 use warnings;
  2         4  
  2         121  
4              
5             package String::FlexMatch::Test;
6             our $VERSION = '1.100820';
7             # ABSTRACT: test methods that can handle flexible strings
8 2     2   12 use Test::Builder;
  2         9  
  2         56  
9              
10             # Code that uses this testing package will likely need String::FlexMatch as
11             # well, therefore we load it here so the other code won't have to.
12 2     2   1620 use String::FlexMatch;
  2         5  
  2         22  
13 2     2   80 use Exporter qw(import);
  2         4  
  2         155  
14             our @EXPORT = qw(is_deeply_flex isnt_deeply_flex eq_array_flex eq_hash_flex);
15             my $Test = Test::Builder->new;
16              
17             # Basically copied code from Test::More 0.45, which didn't yet break
18             # String::FlexMatch. Back in that version the sane view was taken that if an
19             # object overrides stringification, it probably does so for a reason, and that
20             # stringification defines how the object wants to be compared. Newer versions
21             # of Test::More simply say that if you have a string and a reference, they
22             # can't possibly be the same.
23 2     2   11 use vars qw(@Data_Stack);
  2         4  
  2         2271  
24             my $DNE = bless [], 'Does::Not::Exist';
25              
26             sub is_deeply_flex {
27 12     12 1 19576 my ($got, $expect, $name) = @_;
28 12         14 my $ok;
29 12 50 66     58 if (!ref $got || !ref $expect) {
30 12         25 $ok = is_eq($got, $expect, $name);
31             } else {
32 0         0 local @Data_Stack = ();
33 0 0       0 if (_deep_check($got, $expect)) {
34 0         0 $ok = $Test->ok(1, $name);
35             } else {
36 0         0 $ok = $Test->ok(0, $name);
37 0         0 $ok = $Test->diag(_format_stack(@Data_Stack));
38             }
39             }
40 12         137 return $ok;
41             }
42              
43             sub is_eq {
44 12     12 1 16 my ($got, $expect, $name) = @_;
45 12 50 33     45 if (!defined $got || !defined $expect) {
46              
47             # undef only matches undef and nothing else
48 0   0     0 my $test = !defined $got && !defined $expect;
49 0         0 $Test->ok($test, $name);
50 0 0       0 $Test->_is_diag($got, 'eq', $expect) unless $test;
51 0         0 return $test;
52             }
53 12         25 return cmp_ok($got, 'eq', $expect, $name);
54             }
55              
56             sub cmp_ok {
57 12     12 1 18 my ($got, $type, $expect, $name) = @_;
58 12         13 my $test;
59             {
60 12         11 local $^W = 0;
  12         48  
61 12         26 local ($@, $!); # don't interfere with $@
62             # eval() sometimes resets $!
63 12         609 $test = eval "\$got $type \$expect";
64             }
65 12         55 my $ok = $Test->ok($test, $name);
66 12 50       2886 unless ($ok) {
67 0 0       0 if ($type =~ /^(eq|==)$/) {
68 0         0 $Test->_is_diag($got, $type, $expect);
69             } else {
70 0         0 $Test->_cmp_diag($got, $type, $expect);
71             }
72             }
73 12         28 return $ok;
74             }
75              
76             sub _format_stack {
77 0     0   0 my (@Stack) = @_;
78 0         0 my $var = '$FOO';
79 0         0 my $did_arrow = 0;
80 0         0 foreach my $entry (@Stack) {
81 0   0     0 my $type = $entry->{type} || '';
82 0         0 my $idx = $entry->{'idx'};
83 0 0       0 if ($type eq 'HASH') {
    0          
    0          
84 0 0       0 $var .= "->" unless $did_arrow++;
85 0         0 $var .= "{$idx}";
86             } elsif ($type eq 'ARRAY') {
87 0 0       0 $var .= "->" unless $did_arrow++;
88 0         0 $var .= "[$idx]";
89             } elsif ($type eq 'REF') {
90 0         0 $var = "\${$var}";
91             }
92             }
93 0         0 my @vals = @{ $Stack[-1]{vals} }[ 0, 1 ];
  0         0  
94 0         0 my @vars = ();
95 0         0 ($vars[0] = $var) =~ s/\$FOO/ \$got/;
96 0         0 ($vars[1] = $var) =~ s/\$FOO/\$expected/;
97 0         0 my $out = "Structures begin differing at:\n";
98 0         0 foreach my $idx (0 .. $#vals) {
99 0         0 my $val = $vals[$idx];
100 0 0       0 $vals[$idx] =
    0          
101             !defined $val ? 'undef'
102             : $val eq $DNE ? "Does not exist"
103             : "'$val'";
104             }
105 0         0 $out .= "$vars[0] = $vals[0]\n";
106 0         0 $out .= "$vars[1] = $vals[1]\n";
107 0         0 $out =~ s/^/ /msg;
108 0         0 return $out;
109             }
110              
111             sub eq_array_flex {
112 0     0 1 0 my ($a1, $a2) = @_;
113 0 0       0 return 1 if $a1 eq $a2;
114 0         0 my $ok = 1;
115 0 0       0 my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2;
116 0         0 for (0 .. $max) {
117 0 0       0 my $e1 = $_ > $#$a1 ? $DNE : $a1->[$_];
118 0 0       0 my $e2 = $_ > $#$a2 ? $DNE : $a2->[$_];
119 0         0 push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [ $e1, $e2 ] };
120 0         0 $ok = _deep_check($e1, $e2);
121 0 0       0 pop @Data_Stack if $ok;
122 0 0       0 last unless $ok;
123             }
124 0         0 return $ok;
125             }
126              
127             sub _deep_check {
128 13     13   19 my ($e1, $e2) = @_;
129 13         36 my $ok = 0;
130 13         14 my $eq;
131             {
132              
133             # Quiet uninitialized value warnings when comparing undefs.
134 13         13 local $^W = 0;
  13         32  
135              
136             # even after $^W we still got uninitialized warnings, so...
137 2     2   14 no warnings 'uninitialized';
  2         4  
  2         963  
138 13 100       203 if ($e1 eq $e2) {
139 8         19 $ok = 1;
140             } else {
141 5 50 66     77 if ( UNIVERSAL::isa($e1, 'ARRAY')
    100 100        
    50 33        
    50 33        
142             and UNIVERSAL::isa($e2, 'ARRAY')) {
143 0         0 $ok = eq_array_flex($e1, $e2);
144             } elsif (UNIVERSAL::isa($e1, 'HASH')
145             and UNIVERSAL::isa($e2, 'HASH')) {
146 3         9 $ok = eq_hash_flex($e1, $e2);
147             } elsif (UNIVERSAL::isa($e1, 'REF')
148             and UNIVERSAL::isa($e2, 'REF')) {
149 0         0 push @Data_Stack, { type => 'REF', vals => [ $e1, $e2 ] };
150 0         0 $ok = _deep_check($$e1, $$e2);
151 0 0       0 pop @Data_Stack if $ok;
152             } elsif (UNIVERSAL::isa($e1, 'SCALAR')
153             and UNIVERSAL::isa($e2, 'SCALAR')) {
154 0         0 push @Data_Stack, { type => 'REF', vals => [ $e1, $e2 ] };
155 0         0 $ok = _deep_check($$e1, $$e2);
156             } else {
157 2         7 push @Data_Stack, { vals => [ $e1, $e2 ] };
158 2         5 $ok = 0;
159             }
160             }
161             }
162 13         23 return $ok;
163             }
164              
165             sub eq_hash_flex {
166 6     6 1 20799 my ($a1, $a2) = @_;
167 6 50       24 return 1 if $a1 eq $a2;
168 6         8 my $ok = 1;
169 6 50       20 my $bigger = keys %$a1 > keys %$a2 ? $a1 : $a2;
170 6         16 foreach my $k (keys %$bigger) {
171 13 100       31 my $e1 = exists $a1->{$k} ? $a1->{$k} : $DNE;
172 13 50       27 my $e2 = exists $a2->{$k} ? $a2->{$k} : $DNE;
173 13         54 push @Data_Stack, { type => 'HASH', idx => $k, vals => [ $e1, $e2 ] };
174 13         36 $ok = _deep_check($e1, $e2);
175 13 100       33 pop @Data_Stack if $ok;
176 13 100       45 last unless $ok;
177             }
178 6         26 return $ok;
179             }
180             1;
181              
182              
183             __END__