File Coverage

blib/lib/Test/Expr.pm
Criterion Covered Total %
statement 86 131 65.6
branch 16 32 50.0
condition 15 32 46.8
subroutine 14 14 100.0
pod n/a
total 131 209 62.6


line stmt bran cond sub pod time code
1             package Test::Expr;
2             our $VERSION = '0.000011';
3              
4 3     3   79550 use 5.012; use warnings;
  3     3   18  
  3         13  
  3         11  
  3         69  
5 3     3   2187 use Keyword::Declare;
  3         430377  
  3         24  
6 3     3   2181 use Data::Dump;
  3         13161  
  3         189  
7 3     3   20 use List::Util 'max';
  3         5  
  3         148  
8 3     3   1139 use Test::More;
  3         109373  
  3         23  
9 3     3   1925 use parent 'Exporter';
  3         818  
  3         16  
10             our @EXPORT = @Test::More::EXPORT;
11              
12             sub _trim {
13 59     59   89 my $str = shift;
14 59         286 $str =~ s{\A\s*|\s*\Z}{}g;
15 59         151 return $str;
16             }
17              
18             my $PERL_MATCHABLE = qr{ ^ (?&PerlOWS)
19             (?>
20             (?&PerlMatch)
21             | (?&PerlSubstitution)
22             | (?&PerlTransliteration)
23             )
24             $PPR::GRAMMAR
25             }xms;
26              
27 3     3   387 use re 'eval';
  3         6  
  3         673  
28             my $PERL_FIND_VARS = qr{
29             ^ (?&PerlExpression)
30              
31             (?(DEFINE)
32             (?
33             (
34             (?= [\$\@%] )
35             (?>
36             (?&PerlScalarAccess)
37             | (?&PerlHashAccess)
38             | (?&PerlArrayAccess)
39             )
40             )
41             (?{ $Test::Expr::vars{$^N} = 1 })
42             )
43              
44             (?
45             (
46             (?>(?&PerlVariableScalarNoSpace))
47             (?:
48             (?:
49             (?:
50             ->
51             (?&PerlParenthesesList)
52             |
53             (?: -> )?+
54             (?> \$\* | (?&PerlArrayIndexer) | (?&PerlHashIndexer) )
55             )
56             (?:
57             (?: -> )?+
58             (?> \$\* | (?&PerlArrayIndexer) | (?&PerlHashIndexer) | (?&PerlParenthesesList) )
59             )*+
60             )?+
61             (?:
62             ->
63             [\@%]
64             (?> \* | (?&PerlArrayIndexer) | (?&PerlHashIndexer) )
65             )?+
66             )?+
67             )
68             (?{ $Test::Expr::vars{$^N} = 1 })
69             ) # End of rule
70              
71              
72             (?
73             (
74             (?>(?&PerlVariableArrayNoSpace))
75             (?:
76             (?: -> )?+
77             (?> \$\* | (?&PerlArrayIndexer) | (?&PerlHashIndexer) | (?&PerlParenthesesList) )
78             )*+
79             (?:
80             ->
81             [\@%]
82             (?> \* | (?&PerlArrayIndexer) | (?&PerlHashIndexer) )
83             )?+
84             )
85             (?{ $Test::Expr::vars{$^N} = 1 })
86             ) # End of rule
87             )
88              
89             $PPR::GRAMMAR
90             }xms;
91              
92             my $PERL_LITERAL = qr{^ (?> (?&PerlString)
93             | (?&PerlQuotelikeQR)
94             | (?&PerlQuotelikeQW)
95             | (?&PerlNumber) )
96             $
97             $PPR::GRAMMAR
98             }xms;
99              
100             my $PERL_EXPR = qr{
101             ^ (?>(?&PerlOWS)) (? (?>(?&PerlHighBinaryExpression)) )
102             (?>(?&PerlOWS)) (? (?> [<>]=?|[=!]=|<=>|[~=!]~|\b(?:[lg]t|[lgn]e|eq|cmp)\b))
103             (?>(?&PerlOWS)) (? .*)
104              
105             (?(DEFINE)
106              
107             (?
108             (?>(?&PerlPrefixPostfixTerm))
109             (?: (?>(?&PerlOWS)) (?>(?&PerlInfixHighBinaryOperator))
110             (?>(?&PerlOWS)) (?&PerlPrefixPostfixTerm) )*+
111             )
112              
113             (?
114             (?> [+] (?! [+=] )
115             | - (?! [-=] )
116             | [.]{2,3}+
117             | [.%x] (?! [=] )
118             | [&|^][.] (?! [=] )
119             | [*&|/]{1,2}+ (?! [=] )
120             | [<>]{2} (?! [=] )
121             | \^ (?! [=] )
122             )
123             ) # End of rule
124             )
125              
126             $PPR::GRAMMAR
127              
128             }mxs;
129              
130 0           sub import {
131 3     3   51 my ($package) = @_;
132 3         619 $package->export_to_level(1, @_);
133 3         9  
134 0 50 50 21   0 keyword ok (Expr $test) {{{
  0         0  
  0         0  
  3         49  
  21         2233182  
  21         56  
  21         36  
135 0         0 ok do{«$test»}, q{«$test»};
  0         0  
  0         0  
  3         21  
  21         46  
  21         42  
  21         112  
136 3         21 }}}
  3         30  
137 3         135  
  3         66  
138 3 50 50 3   115872 keyword ok (ListElem $test, Comma, ListElem $desc) {
  0     22   0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         32  
  22         2710428  
  22         65  
  22         43  
  22         32  
  22         47  
139 3         12 # Work out what values to report if there's a problem...
140 0         0 (my $test_code = $test) =~ s/^\s*do\s*\{(.*)\}\s*$/$1/;
  22         154  
141              
142             # Handle low-precedence prefix not...
143 0         0 (my $pos_test_code = $test_code) =~ s/^\s*(not)\b//;
  22         77  
144 0 0       0 my $negative = $pos_test_code eq $test_code ? q{} : 'not';
  22 100       85  
145              
146             # Extract components of test, if possible...
147 0         0 $pos_test_code =~ $PERL_EXPR;
  22         3886  
148 0         0 my ($lhs, $op, $rhs) = @+{qw};
  22         366  
149              
150             # These hold rearranged code to capture and test component values (if possible)...
151 0         0 my $test_setup;
  22         83  
152             my $test_expr;
153              
154             # Don't try to capture explicit regex matches on the rhs...
155 0 0 0     0 if ($op && $op =~ /[=!~]~/ && $rhs =~ $PERL_MATCHABLE) {
  22 0 0     670  
    100 100        
    100 100        
156 0         0 $test_setup = qq{ my \$_____l_h_s_____ = $lhs; };
  6         95  
157 0         0 $test_expr = qq{ $negative \$_____l_h_s_____ $op $rhs };
  6         23  
158 0         0 $rhs = "";
  6         12  
159             }
160              
161             # Catch lhs and rhs values for other comparison operators...
162             elsif ($op) {
163 0         0 $test_setup = qq{ my \$_____l_h_s_____ = $lhs; my \$_____r_h_s_____ = $rhs; };
  13         55  
164 0         0 $test_expr = qq{ $negative \$_____l_h_s_____ $op \$_____r_h_s_____ };
  13         40  
165             }
166              
167             # Otherwise just execute the text verbatim...
168             else {
169 0         0 $lhs = "";
  3         12  
170 0         0 $rhs = "";
  3         8  
171 0         0 $test_setup = qq{my \$_____l_h_s_____ = $test_code;};
  3         9  
172 0         0 $test_expr = q{$_____l_h_s_____};
  3         5  
173             }
174              
175             # Extract and tidy all variables in the test expression...
176 0         0 %Test::Expr::vars = ();
  22         58  
177 0         0 $test_code =~ $PERL_FIND_VARS;
  22         3638  
178 0         0 my @vars = grep {defined} keys %Test::Expr::vars;
  0         0  
  22         233  
  15         43  
179 0         0 ($lhs, $rhs, @vars) = map {_trim $_} $lhs, $rhs, @vars;
  0         0  
  22         46  
  59         145  
180              
181             # Find maximum width of any reported value, so we can align them...
182 0         0 my $var_len = max map {length} $lhs, $rhs, @vars;
  0         0  
  22         57  
  59         156  
183              
184             # Generate diagnostics (reporting each distinct value only once)...
185 0         0 my %seen = ( $rhs => 1, $lhs => 1 );
  22         103  
186             my @diagnostics = (
187             ( $lhs && $lhs !~ $PERL_LITERAL
188             ? qq{diag sprintf(q{ %${var_len}s --> }, q{$lhs}),
189             Data::Dump::dump(\$_____l_h_s_____);}
190             : ()
191             ),
192             ( $rhs && $rhs !~ $PERL_LITERAL
193             ? qq{diag sprintf(q{ %${var_len}s --> }, q{$rhs}),
194             Data::Dump::dump(\$_____r_h_s_____);}
195             : ()
196             ),
197 0         0 map {qq{diag sprintf(q{ %${var_len}s --> }, q{$_}), Data::Dump::dump($_);}}
  8         32  
198 0 0 0     0 grep { !$seen{$_}++ }
  0 0 0     0  
  22 100 100     685  
  15 100 100     48  
199             @vars
200             );
201              
202             # Build the diagnostic code...
203 0 0       0 unshift @diagnostics, qq{diag q{ because:};}
  22 100       81  
204             if @diagnostics;
205 0 0 0     0 unshift @diagnostics, qq{diag q{}; diag q{ ($test_code) was false};}
  22 100 50     1626  
206             if (eval($desc)//'') ne qq{$test_code};
207              
208             # Build the test code...
209 0         0 my $new_test_code = qq{
  22         205  
210             {
211             $test_setup
212             if ($test_expr) {
213             Test::More::ok(1, $desc);
214             }
215             else {
216             fail($desc); @diagnostics diag q{};
217             }
218             }
219             };
220 0         0 $new_test_code =~ tr/\n/ /;
  22         66  
221 0         0 return $new_test_code;
  22         96  
222 3         27 }
  3         27  
223 3         142 }
224 3     3   165505  
225             1; # Magic true value required at end of module
226             __END__