File Coverage

blib/lib/Test/Debugger.pm
Criterion Covered Total %
statement 236 257 91.8
branch 107 126 84.9
condition 73 104 70.1
subroutine 29 29 100.0
pod 7 15 46.6
total 452 531 85.1


line stmt bran cond sub pod time code
1             package Test::Debugger;
2              
3             require 5.005_03;
4 6     6   3858 use strict;
  6         10  
  6         297  
5              
6             require Exporter;
7 6         2332 use vars qw(@ISA @EXPORT_OK @EXPORT $VERSION
8 6     6   32 $TESTOUT $ntest $object_handle %param_order %todo $ONFAIL $separate_todo);
  6         11  
9             # in case you don't have Devel::Messenger installed, I 'require' instead of 'use' it.
10             BEGIN {
11 6     6   12 eval { require Devel::Messenger; };
  6         3849  
12 6 50       56 if ($@) {
13             sub note;
14 47     47 0 62 sub note {};
15             } else {
16 0         0 undef ¬e;
17 0         0 import Devel::Messenger qw(note);
18             }
19 6         13 eval { require Test::Harness };
  6         7012  
20 6 50 33     1083265 if (!$@ and Test::Harness->VERSION >= 1.21) {
21 6         571 $separate_todo = 1;
22             }
23             }
24              
25             @ISA = qw(Exporter);
26             @EXPORT_OK = qw($TESTOUT ¶m_order);
27             @EXPORT = qw(&plan &ok &skip &todo);
28             $VERSION = 0.14;
29             $TESTOUT = *STDOUT{IO};
30             %todo = ();
31             $separate_todo ||= 0;
32              
33             #use constant PARAM_ORDER => ['self', 'expected', 'actual', 'message', 'error', 'operator'];
34 6     6   67 use constant PARAM_ORDER => ['actual', 'expected', 'message', 'error', 'operator'];
  6         12  
  6         3039  
35             # 'actual', 'expected', 'message' # Test.pm
36             # self, expected, actual, message, error, operator # Test::Debugger (pre-CPAN)
37              
38             %param_order = (
39             # ok, skip, todo
40             'skip' => ['skip'],
41             );
42              
43             # current supported relational operators
44             use constant OPERATOR => {
45             'eq' => {
46             'desc' => '',
47 3         13 'code' => sub { shift() eq shift() },
48             },
49             'ne' => {
50             'desc' => 'Not Equal to (alpha) ',
51 1         3 'code' => sub { shift() ne shift(); },
52             },
53             'gt' => {
54             'desc' => 'Greater Than (alpha) ',
55 2         3 'code' => sub { my $expected = shift; shift() gt $expected; },
  2         7  
56             },
57             'ge' => {
58             'desc' => 'Greater Than or Equal to (alpha) ',
59 3         3 'code' => sub { my $expected = shift; shift() ge $expected; },
  3         10  
60             },
61             'lt' => {
62             'desc' => 'Less Than (alpha) ',
63 3         4 'code' => sub { my $expected = shift; shift() lt $expected; },
  3         10  
64             },
65             'le' => {
66             'desc' => 'Less Than or Equal to (alpha) ',
67 3         4 'code' => sub { my $expected = shift; shift() le $expected; },
  3         10  
68             },
69             're' => {
70             'desc' => 'to Match Pattern ',
71 4         6 'code' => sub { my $expected = shift; shift =~ /$expected/; },
  4         69  
72             },
73             '=~' => {
74             'desc' => 'to Match Pattern ',
75 1         2 'code' => sub { my $expected = shift; shift =~ /$expected/; },
  1         9  
76             },
77             '==' => {
78             'desc' => '',
79 8         10 'code' => sub { my $expected = shift; shift() == $expected; },
  8         50  
80             },
81             '!=' => {
82             'desc' => 'Not Equal to ',
83 2         3 'code' => sub { my $expected = shift; shift() != $expected; },
  2         9  
84             },
85             '>' => {
86             'desc' => 'Greater Than ',
87 2         3 'code' => sub { my $expected = shift; shift() > $expected; },
  2         7  
88             },
89             '>=' => {
90             'desc' => 'Greater Than or Equal to ',
91 1         2 'code' => sub { my $expected = shift; shift() >= $expected; },
  1         4  
92             },
93             '<' => {
94             'desc' => 'Less Than ',
95 1         2 'code' => sub { my $expected = shift; shift() < $expected; },
  1         4  
96             },
97             '<=' => {
98             'desc' => 'Less Than or Equal to ',
99 1         2 'code' => sub { my $expected = shift; shift() <= $expected; },
  1         64  
100             },
101 6     6   43 };
  6         15  
  6         19508  
102              
103             #sub Test::Debugger::tied::TIESCALAR {
104             # my $var = $_[1];
105             # bless \$var, 'Test::Debugger::tied';
106             #}
107             #sub Test::Debugger::tied::FETCH { return ${$_[0]} }
108             #sub Test::Debugger::tied::STORE { ${$_[0]} = $_[1] }
109              
110             sub new {
111             # returns a test object handle
112 50     50 1 952 my $class = shift;
113 50         72 my $is_new = (!defined($object_handle));
114 50   100     174 $object_handle ||= bless {}, $class;
115 50         55 my $self = $object_handle;
116             #tie($self->{current}, 'Test::Debugger::tied', $ntest) if ($is_new);
117 50 100       140 $self->plan(@_) if (@_);
118 49 100       153 if ($is_new) {
119 5         55 my @caller = caller();
120 5 100       35 $self->{test_file} = ($caller[0] eq 'Test::Debugger') ? (caller(1))[1] : $caller[1];
121 5         15 $self->{last} = 0; # will be 0 in Test.pm
122 5   50     34 $self->{current} ||= 0; # Test.pm
123             #$self->{current} = 1 unless (defined($self->{current}));
124 5   50     15 $self->{final} ||= 0;
125 5         29 note "[$self->{test_file} #", $self->next_test_number, "]\n";
126             }
127 49         109 return $self;
128             }
129              
130             sub plan {
131 10 100 50 10 1 1489 my $self = (ref($_[0])) ? shift : (Test::Debugger->new(@_) and return);
132             #note \7, "planning\n";
133 6         32 my %opts = @_;
134 6 100 66     37 if (exists($opts{todo}) and ref($opts{todo}) eq 'ARRAY') {
135 1         2 %todo = map { $_ => 1 } @{$opts{todo}};
  2         8  
  1         2  
136             }
137 6 50 33     35 if (exists($opts{onfail}) and ref($opts{onfail}) eq 'CODE') {
138 0         0 $ONFAIL = $opts{onfail};
139             }
140 6 100       25 if (exists($opts{log_file})) {
141 4         12 $self->{log_file} = $opts{log_file};
142             }
143 6 50       35 if (exists($opts{next_test_number})) {
    50          
144 0         0 $self->next_test_number($opts{next_test_number});
145             } elsif (exists($opts{start})) {
146 0         0 $self->next_test_number($opts{start});
147             }
148 6 50 33     38 if (exists($opts{tests}) or exists($opts{skip})) {
149 6   50     25 my $tests = $opts{tests} || 0;
150 6   100     47 my $skip = $opts{skip} || 0;
151 6   100     46 my $message = $opts{skip_message} || '';
152 6         32 $self->_write_header($tests, $skip, $message);
153 5         14 $self->{final} = $tests;
154             #note \7, "final test shall be number $tests\n";
155             }
156 5 50       26 if (exists($opts{param_order})) {
157 0   0     0 my $p_order = $opts{param_order} || {};
158 0         0 foreach my $key (keys %$p_order) {
159 0   0     0 my $order = $p_order->{$key} || next;
160 0         0 $self->param_order($key => $order);
161             }
162             }
163             }
164              
165             sub next_test_number {
166             # set or return the next test number
167 42 50   42 1 99 my $self = (ref($_[0])) ? shift : Test::Debugger->new();
168 42         45 my $current = shift;
169 42 50       83 if (defined($current)) {
170             #note \7, "setting next_test_number to $current\n";
171 0         0 $self->{current} = $current - 1
172             }
173 42         138 return $self->{current} + 1;
174             }
175              
176             sub param_order {
177 46 100   46 1 123 my $self = (ref($_[0])) ? shift : Test::Debugger->new();
178 46   50     298 my $method = shift || 'ok';
179 46 100       196 if (@_) {
    100          
    100          
    50          
180 4         18 $self->{param_order}{$method} = shift;
181             } elsif (exists($self->{param_order}{$method})) {
182 24         23 return @{$self->{param_order}{$method}};
  24         96  
183             } elsif (exists($param_order{$method})) {
184 1 50       4 if (exists($self->{param_order}{ok})) {
185 0         0 return _unshift_self(@{$param_order{$method}}, @{$self->{param_order}{ok}});
  0         0  
  0         0  
186             } else {
187 1         2 return _unshift_self(@{$param_order{$method}}, @{PARAM_ORDER()});
  1         4  
  1         4  
188             }
189             } elsif (exists($self->{param_order}{ok})) {
190 17         20 return @{$self->{param_order}{ok}};
  17         59  
191             } else {
192 0         0 return @{PARAM_ORDER()};
  0         0  
193             }
194             }
195              
196             sub _unshift_self {
197 1     1   6 my @array = @_;
198 1         9 my $c = 0;
199 1         4 while ($c < @array) {
200 6 50       12 if ($array[$c] eq 'self') {
201 0         0 unshift(@array, splice(@array, $c, 1));
202 0         0 last;
203             }
204 6         11 $c++;
205             }
206 1         5 return @array;
207             }
208              
209             sub ok {
210 24     24 1 132 my $num_opts = scalar(@_);
211 24         29 my $first = $_[0];
212 24         31 my $second = $_[1];
213 24         54 my $opts = &_read_opts('ok', @_);
214 24 100 100     140 if ($num_opts == 1 or (ref($first) eq 'Test::Debugger' and $num_opts == 2)) {
      33        
215 2         3 $opts->{single} = 1;
216 2 50       6 $opts->{actual} = ($num_opts == 1) ? $first : $second;
217             }
218 24         83 $opts->{self}->_ok_($opts, '==', 'eq');
219             }
220              
221             sub todo {
222 2     2 1 11 my $opts = &_read_opts('todo', @_);
223             #$opts->{skip} = 1;
224 2         4 $opts->{todo} = 1;
225 2         5 $opts->{self}->_ok_($opts, '==', 'eq');
226             }
227              
228             sub skip {
229 1     1 1 8 my $opts = &_read_opts('skip', @_);
230 1         5 $opts->{self}->_ok_($opts, '==', 'eq');
231             }
232              
233             # XXX deprecated - please use 'todo' method instead
234             sub ok_skip {
235 5     5 0 63 my $num_opts = scalar(@_);
236 5         7 my $first = $_[0];
237 5         5 my $second = $_[1];
238 5         11 my $opts = &_read_opts('ok_skip', @_);
239 5 100 66     29 if ($num_opts == 1 or (ref($first) eq 'Test::Debugger' and $num_opts == 2)) {
      33        
240 3         3 $opts->{single} = 1;
241 3 50       6 $opts->{actual} = ($num_opts == 1) ? $first : $second;
242             }
243 5         11 $opts->{skip} = 1;
244 5         13 $opts->{self}->_ok_($opts, '==', 'eq');
245             }
246              
247             sub ok_ne {
248 1     1 0 8 my $opts = &_read_opts('ok_ne', @_);
249 1         5 $opts->{self}->_ok_($opts, '!=', 'ne');
250             }
251              
252             sub ok_gt {
253 2     2 0 12 my $opts = &_read_opts('ok_gt', @_);
254 2         83 $opts->{self}->_ok_($opts, '>', 'gt');
255             }
256              
257             sub ok_ge {
258 2     2 0 12 my $opts = &_read_opts('ok_ge', @_);
259 2         6 $opts->{self}->_ok_($opts, '>=', 'ge');
260             }
261              
262             sub ok_lt {
263 2     2 0 14 my $opts = &_read_opts('ok_lt', @_);
264 2         6 $opts->{self}->_ok_($opts, '<', 'lt');
265             }
266              
267             sub ok_le {
268 2     2 0 13 my $opts = &_read_opts('ok_le', @_);
269 2         7 $opts->{self}->_ok_($opts, '<=', 'le');
270             }
271              
272             sub ok_re {
273 1     1 0 7 my $opts = &_read_opts('ok_re', @_);
274 1         4 $opts->{self}->_ok_($opts, '=~', 're');
275             }
276              
277             sub _ok_ {
278 42     42   47 my $self = shift;
279 42         43 my $opts = shift;
280 42         45 my $numeric = shift;
281 42         49 my $alpha = shift;
282 42 100 100     181 unless ($opts->{single} or $opts->{operator}) {
283 23 100       55 my $expect = (defined($opts->{expected}) ? $opts->{expected} : undef);
284 23 100       47 my $actual = (defined($opts->{actual}) ? $opts->{actual} : undef);
285 23         24 my $op;
286 23 100 66     98 if (defined($expect) and defined($actual)) {
287 21 100 33     258 $op = ($expect =~ /\D/ or $actual =~ /\D/ or $expect eq '' or $actual eq '') ? $alpha : $numeric;
288             } else {
289 2         5 $op = $alpha;
290             }
291 23         44 $opts->{operator} = $op;
292             }
293 42         53 $self->_test(@{$opts}{'expected', 'actual', 'single', 'operator', 'skip', 'todo', 'message', 'error'});
  42         172  
294             }
295              
296             sub _read_opts {
297 42     42   62 my $method = shift;
298 42         304 my $self = Test::Debugger->new();
299 42         65 my %opts = ();
300 42         83 $opts{single} = 1;
301 42         88 foreach my $key ($self->param_order($method)) {
302 234         362 $opts{$key} = shift;
303             #note \7, "method $method reading param $key: " . (defined($opts{$key}) ? $opts{$key} : 'undef') . "\n";
304 234 100       482 if ($key eq 'expected') {
305 42         63 $opts{single} = 0;
306             }
307             }
308 42   66     136 $opts{self} ||= $self;
309 42   100     127 $opts{operator} ||= '';
310 42         82 return \%opts;
311             }
312              
313             sub _test {
314 42     42   64 my $self = shift;
315 42         50 my $expect = shift;
316 42         44 my $actual = shift;
317 42   100     144 my $single = shift || 0;
318 42   100     94 my $operator = shift || 'eq';
319 42   100     111 my $skip = shift || 0;
320 42   100     129 my $todo = shift || 0;
321 42   100     85 my $message = shift || '';
322 42   100     114 my $error = shift || '';
323 42         43 my $true = 0;
324             #note "self $self\n";
325             #note "expect $expect\n";
326             #note "actual $actual\n";
327             #note \7, "single $single\n";
328             #note "operator $operator\n";
329             #note "skip $skip\n";
330             #note "message $message\n";
331             #note "error $error\n";
332 42         59 $self->{current}++;
333 42 100       88 if ($single) {
334             #note "determining truth\n";
335 5 100       22 $true = $self->_truth(defined($actual) ? $actual : undef());
336 5         8 $expect = 'true';
337             } else {
338             #note "comparing values\n";
339 37 100       130 $true = $self->_compare_values(
    100          
340             $operator,
341             defined($expect) ? $expect : undef(),
342             defined($actual) ? $actual : undef(),
343             );
344             }
345             #note \7, "true $true\n";
346 42 100       622 if (exists($todo{$self->{current}})) {
347 2         3 $todo = 1;
348             }
349 42 50 33     105 if (!$separate_todo and $todo) {
350 0         0 $skip = 1;
351             }
352 42         99 $self->_write_result($true, $skip, $todo, $message);
353 42 100 100     202 if ($self->{log_file} and !$true) {
354 7 100       36 $self->_write_log(
    100          
355             $operator,
356             defined($expect) ? $expect : undef(),
357             defined($actual) ? $actual : undef(),
358             $skip,
359             $todo,
360             $message,
361             $error,
362             );
363             }
364 42         82 $self->{last} = $self->{current};
365 42 100 66     197 if ($self->{final} and $self->{current} >= $self->{final}) {
366 5         22 note "[$self->{test_file} #complete#]\n";
367             } else {
368 37         95 note "[$self->{test_file} #", $self->next_test_number, "]\n";
369             }
370 42 100       659 return ($skip ? 1 : $true);
371             }
372              
373             sub _truth {
374 5     5   6 my $self = shift;
375 5   100     14 my $actual = shift || 0;
376 5 100       11 return $actual ? 1 : 0;
377             }
378              
379             sub _compare_values {
380 37     37   43 my $self = shift;
381 37         44 my $operator = shift;
382 37         36 my $expect = shift;
383 37         38 my $actual = shift;
384 37         40 my $true = 0;
385 37 100 66     173 if (defined($expect) and defined($actual)) {
    100          
386 35 100       138 if (ref($expect) eq 'Regexp') {
387 2         3 $operator = 're';
388             }
389 35 50       76 if (exists(OPERATOR->{$operator})) {
390 35 100       553 $true = (OPERATOR->{$operator}{code}->($expect, $actual) ? 1 : 0);
391             } else {
392 0         0 $true = 0;
393 0         0 warn "Test::Debugger encountered an unknown operator ($operator)\n";
394             }
395             } elsif (defined($expect) eq defined($actual)) {
396 1         2 $true = 1;
397             }
398 37         86 return $true;
399             }
400              
401             sub _write_header {
402 6     6   31 my $self = shift;
403 6   50     22 my $tests = shift || 0;
404 6   100     64 my $skip = shift || 0;
405 6   100     51 my $message = shift || '';
406 6         19 my $todo = %todo;
407 6 100       45 my $TODO = $todo ? ' todo ' . join(' ', keys %todo) : '';
408 6 100       23 substr($message, 0, 0, ': ') if ($message);
409 6 100       186 print $TESTOUT "1.." . ($skip ? '0 # Skipped' . $message : $tests) . "$TODO\n";
410 6 100       195 exit if ($skip);
411             }
412              
413             sub _write_result {
414 42     42   46 my $self = shift;
415 42         44 my $true = shift;
416 42   100     155 my $skip = shift || 0;
417 42   100     127 my $todo = shift || 0;
418 42   100     158 my $message = shift || '';
419 42 100       65 if ($skip) {
420 6         29 print $TESTOUT "ok $self->{current} # Skip $message\n";
421             } else {
422 36 100       65 my $TODO = $todo ? ' # TODO' : '';
423 36 100       194 print $TESTOUT ($true ? "ok $self->{current}$TODO\n" : "not ok $self->{current}$TODO $message\n");
424             }
425             }
426              
427             sub _write_log {
428 7     7   8 my $self = shift;
429 7         9 my $operator = shift;
430 7         7 my $expect = shift;
431 7         6 my $actual = shift;
432 7   100     20 my $skip = shift || 0;
433 7   100     17 my $todo = shift || 0;
434 7   100     19 my $message = shift || '';
435 7   50     18 my $error = shift || '';
436 7         9 my $status;
437 7 100       13 if ($todo) {
    50          
438 2         3 $status = 'TODO';
439             } elsif ($skip) {
440 5         6 $status = 'SKIPPED';
441             } else {
442 0         0 $status = 'FAILED';
443             }
444 7 100       19 $expect = 'undef' unless defined($expect);
445 7 100       13 $actual = 'undef' unless defined($actual);
446 7 50       231 if (open(FILE,">>".$self->{log_file})) {
447 7         75 print FILE "$self->{test_file} $self->{current} $status.\n";
448 7 100       18 print FILE $message, "\n" if $message;
449 7 50       15 print FILE $error, "\n" if $error;
450 7         28 print FILE "### Expected ".OPERATOR->{$operator}{desc}."###\n$expect\n### Actual Results ###\n$actual\n\n";
451 7         217 close FILE;
452             }
453             }
454              
455             1;
456             __END__