File Coverage

blib/lib/Test/Tech.pm
Criterion Covered Total %
statement 100 289 34.6
branch 22 174 12.6
condition 2 33 6.0
subroutine 19 29 65.5
pod 9 12 75.0
total 152 537 28.3


line stmt bran cond sub pod time code
1             #!perl
2             #
3             # Documentation, copyright and license is at the end of this file.
4             #
5             package Test::Tech;
6            
7             # use 5.001;
8 1     1   19413 use strict;
  1         7  
  1         71  
9 1     1   6 use warnings;
  1         3  
  1         62  
10 1     1   6 use warnings::register;
  1         18  
  1         470  
11            
12 1     1   1636 use Test (); # do not import the "Test" subroutines
  1         6315  
  1         34  
13 1     1   878 use Data::Secs2 1.22 qw(stringify);
  1         44514  
  1         151  
14 1     1   11 use Data::Str2Num 0.05;
  1         18  
  1         35  
15 1     1   5 use Data::Startup 0.03;
  1         13  
  1         22  
16            
17 1     1   5 use vars qw($VERSION $DATE $FILE);
  1         2  
  1         80  
18             $VERSION = '1.26';
19             $DATE = '2004/05/20';
20             $FILE = __FILE__;
21            
22 1     1   4 use vars qw(@ISA @EXPORT_OK);
  1         2  
  1         346  
23             require Exporter;
24             @ISA=('Exporter');
25             @EXPORT_OK = qw(demo finish is_skip ok ok_sub plan skip skip_sub
26             skip_tests stringify tech_config);
27            
28             #######
29             # For subroutine interface keep all data hidden in a local hash of private object
30             #
31             my $tech_p = new Test::Tech;
32            
33             sub new
34             {
35            
36             ####################
37             # $class is either a package name (scalar) or
38             # an object with a data pointer and a reference
39             # to a package name. A package name is also the
40             # name of a class
41             #
42 1     1 0 3 my ($class, @args) = @_;
43 1 50       6 $class = ref($class) if( ref($class) );
44 1         3 my $self = bless {}, $class;
45            
46             ######
47             # Make Test variables visible to tech_config
48             #
49 1         11 $self->{Test}->{ntest} = \$Test::ntest;
50 1         4 $self->{Test}->{TESTOUT} = \$Test::TESTOUT;
51 1         3 $self->{Test}->{TestLevel} = \$Test::TestLevel;
52 1         3 $self->{Test}->{ONFAIL} = \$Test::ONFAIL;
53 1 50       5 $self->{Test}->{TESTERR} = \$Test::TESTERR if defined $Test::TESTERR;
54            
55 1         3 $self->{TestDefault}->{TESTOUT} = $Test::TESTOUT;
56 1         3 $self->{TestDefault}->{TestLevel} = $Test::TestLevel;
57 1         2 $self->{TestDefault}->{ONFAIL} = $Test::ONFAIL;
58 1 50       5 $self->{TestDefault}->{TESTERR} = $Test::TESTERR if defined $Test::TESTERR;
59            
60             ######
61             # Test::Tech object data
62             #
63 1         3 $self->{Skip_Tests} = 0;
64 1         3 $self->{test_name} = '';
65 1         2 $self->{passed} = [];
66 1         4 $self->{failed} = [];
67 1         1 $self->{skipped} = [];
68 1         3 $self->{missed} = [];
69 1         2 $self->{unplanned} = [];
70 1         2 $self->{last_test} = 0;
71 1         3 $self->{num_tests} = 0;
72 1         1 $self->{highest_test} = 0;
73            
74             ######
75             # Redirect Test:: output thru Test::Tech::Output handle
76             # unless been redirected and never restored!!
77             #
78 1 50       14 unless( \*TESTOUT eq $Test::TESTOUT ) {
79 1         2 $self->{test_out} = $Test::TESTOUT;
80 1         8 tie *TESTOUT, 'Test::Tech::Output', $Test::TESTOUT, $self;
81 1         3 $Test::TESTOUT = \*TESTOUT;
82             }
83            
84 1         4 $self;
85            
86             }
87            
88             ######
89             # Demo
90             #
91             sub demo
92             {
93 1     1   5 use Data::Dumper;
  1         76  
  1         831  
94            
95             ######
96             # This subroutine uses no object data; therefore,
97             # drop any class or object.
98             #
99 0 0   0 1 0 shift if UNIVERSAL::isa($_[0],__PACKAGE__);
100            
101 0         0 my ($quoted_expression, @expression) = @_;
102            
103             #######
104             # A demo trys to simulate someone typing expresssions
105             # at a console.
106             #
107            
108             #########
109             # Print quoted expression so that see the non-executed
110             # expression. The extra space is so when pasted into
111             # a POD, the POD will process the line as code.
112             #
113 0         0 $quoted_expression =~ s/(\n+)/$1 /g;
114 0         0 print $Test::TESTOUT ' ' . $quoted_expression . "\n";
115            
116             ########
117             # @data is the result of the script executing the
118             # quoted expression.
119             #
120             # The demo output most likely will end up in a pod.
121             # The the process of running the generated script
122             # will execute the setup. Thus the input is the
123             # actual results. Putting a space in front of it
124             # tells the POD that it is code.
125             #
126 0 0       0 return unless @expression;
127            
128 0         0 $Data::Dumper::Terse = 1;
129 0         0 my $data = Dumper(@expression);
130 0         0 $data =~ s/(\n+)/$1 #/g;
131 0         0 $data =~ s/\\\\/\\/g;
132 0         0 $data =~ s/\\'/'/g;
133            
134 0         0 print $Test::TESTOUT "\n # " . $data . "\n" ;
135            
136             }
137            
138             #####
139             # Restore the Test:: moduel variable back to where they were when found
140             #
141             sub finish
142             {
143 0 0   0 1 0 $tech_p = Test::Tech->new() unless $tech_p;
144 0 0       0 my $self = UNIVERSAL::isa($_[0],__PACKAGE__) ? shift @_ : $tech_p;
145 0 0       0 $self = ref($self) ? $self : $tech_p;
146            
147 0 0       0 return undef unless $Test::TESTOUT; # if IO::Handle object may be destroyed and undef
148 0 0       0 return undef unless $Test::planned;
149            
150 0         0 my $missing = $self->{last_test} + 1;
151 0         0 $self->{test_name} = '';
152 0         0 while($missing <= $self->{num_tests}) {
153 0 0       0 $self->{Skip_Diag} = '' unless $self->{Skip_Diag};
154 0         0 print $Test::TESTOUT "not ok $missing Not Performed # missing $self->{Skip_Diag}\n";
155 0 0       0 if( 1.20 < $Test::VERSION ) {
156 0         0 print $Test::TESTERR "# Test $missing got: (Missing)\n";
157 0         0 print $Test::TESTERR "# Expected: (Missing)\n";
158             }
159             else {
160 0         0 print $Test::TESTOUT "# Test $missing got: (Missing)\n";
161 0         0 print $Test::TESTOUT "# Expected: (Missing)\n";
162             }
163 0         0 push @{$self->{missed}}, $missing++;
  0         0  
164             }
165            
166 0         0 $Test::TESTOUT = $self->{TestDefault}->{TESTOUT};
167 0         0 $Test::TestLevel = $self->{TestDefault}->{TestLevel};
168 0         0 $Test::ONFAIL = $self->{TestDefault}->{ONFAIL};
169 0 0       0 $Test::TESTERR = $self->{TestDefault}->{TESTERR} if defined $Test::TESTERR;
170            
171 0 0       0 if(@{$self->{unplanned}}) {
  0         0  
172 0         0 print $Test::TESTOUT '# Extra : ' . (join ' ', @{$self->{unplanned}}) . "\n";
  0         0  
173             }
174 0 0       0 if(@{$self->{missed}}) {
  0         0  
175 0         0 print $Test::TESTOUT '# Missing: ' . (join ' ', @{$self->{missed}}) . "\n";
  0         0  
176             }
177 0 0       0 if(@{$self->{skipped}}) {
  0         0  
178 0         0 print $Test::TESTOUT '# Skipped: ' . (join ' ', @{$self->{skipped}}) . "\n";
  0         0  
179             }
180 0 0       0 if(@{$self->{failed}}) {
  0         0  
181 0         0 print $Test::TESTOUT '# Failed : ' . (join ' ', @{$self->{failed}}) . "\n";
  0         0  
182             }
183 1     1   8 use integer;
  1         1  
  1         6  
184            
185 0 0       0 my $total = $self->{num_tests} if $self->{num_tests};
186 0 0 0     0 $total = $self->{last_test} if $self->{last_test} && $self->{num_tests} < $self->{last_test};
187 0         0 $total -= @{$self->{skipped}};
  0         0  
188            
189 0         0 my $passed = @{$self->{passed}};
  0         0  
190 0 0       0 print $Test::TESTOUT '# Passed : ' . "$passed/$total " . ((100*$passed)/$total) . "%\n" if $total;
191            
192             ######
193             # Only once per test run.
194             #
195 0         0 $Test::planned = 0;
196            
197 0 0       0 return ($total,$self->{unplanned},$self->{missed},$self->{skipped},$self->{passed},$self->{failed})
198             if wantarray;
199            
200 0 0       0 $passed ? 1 : 0;
201             }
202            
203             # *finish = &*Test::Tech::DESTORY; # DESTORY is alias for finish
204             sub DESTORY
205             {
206 0     0 0 0 finish( @_ );
207            
208             }
209            
210            
211             ######
212             #
213             #
214             sub is_skip
215             {
216 0 0   0 1 0 $tech_p = Test::Tech->new() unless $tech_p;
217 0 0 0     0 my $self = (UNIVERSAL::isa($_[0],__PACKAGE__) && ref($_[0])) ? shift @_ : $tech_p;
218 0 0       0 $self = ref($self) ? $self : $tech_p;
219 0 0       0 return ($self->{Skip_Tests}, $self->{Skip_Diag}) if wantarray;
220 0         0 $self->{Skip_Tests};
221            
222             }
223            
224             ######
225             # Cover function for &Test::ok that adds capability to test
226             # complex data structures.
227             #
228             sub ok
229             {
230 0     0 1 0 $Test::TestLevel++;
231 0         0 my $results = ok_sub('',@_);
232 0         0 $Test::TestLevel--;
233 0         0 $results;
234             }
235            
236             ######
237             # Cover function for &Test::ok that adds capability to test
238             # complex data structures.
239             #
240             sub ok_sub
241             {
242            
243             ######
244             # If no object, use the default $tech_p object.
245             #
246 0 0   0 1 0 $tech_p = Test::Tech->new() unless $tech_p;
247 0 0 0     0 my $self = (UNIVERSAL::isa($_[0],__PACKAGE__) && ref($_[0])) ? shift @_ : $tech_p;
248 0 0       0 $self = ref($self) ? $self : $tech_p;
249            
250 0         0 my ($diagnostic,$name) = ('','');
251 0 0 0     0 my $options = Data::Startup->new(pop @_) if (3 < @_) && ref($_[-1]);
252            
253 0 0       0 $diagnostic = $options->{diagnostic} if defined $options->{diagnostic};
254 0 0       0 $name = $options->{name} if defined $options->{name};
255            
256 0         0 my ($subroutine, $actual_result, $expected_result, $diagnostic_in, $name_in) = @_;
257            
258             #########
259             # Fill in undefined inputs
260             #
261 0 0       0 $diagnostic = $diagnostic_in if defined $diagnostic_in;
262 0 0       0 $name = $name_in if defined $name_in;
263 0 0       0 $diagnostic = $name unless defined $diagnostic;
264 0         0 $self->{test_name} = $name; # used by tied handle Test::Tech::Output
265            
266 0 0       0 if($self->{Skip_Tests}) { # skip rest of tests switch
267 0         0 &Test::skip( 1, '', '', $self->{Skip_Diag});
268 0         0 return 1;
269             }
270            
271 0         0 my $str_actual_result = stringify($actual_result);
272 0         0 my $str_expected_result = stringify($expected_result);
273 0         0 foreach ($str_actual_result,$str_expected_result) {
274 0 0       0 if(ref($_)) {
275 0         0 $$_ =~ s/\n\n/\n# /g;
276 0         0 $$_ =~ s/\n([^#])/\n# $1/g;
277 0         0 $diagnostic = 'Test::Tech::stringify() broken.';
278 0         0 $self->{test_name} .= ' # ' . $diagnostic;
279 0         0 &Test::ok($$_,'',$diagnostic,$diagnostic);
280 0         0 return 0;
281             }
282             }
283 0 0       0 if($subroutine) {
284 0 0       0 $diagnostic .= "\n" unless substr($diagnostic,-1,1) eq "\n";
285 0         0 $str_actual_result =~ s/\n/\n /g;
286 0         0 $str_expected_result =~ s/\n/\n /g;
287 0         0 $diagnostic .=
288             " got: $str_actual_result\n" .
289             " expected: $str_expected_result\n";
290 0         0 $str_actual_result = &$subroutine($actual_result,$expected_result);
291 0         0 $str_expected_result = 1;
292             }
293            
294 0         0 &Test::ok($str_actual_result, $str_expected_result, $diagnostic);
295            
296             }
297            
298            
299             ######
300             # Cover function for &Test::plan that sets the proper 'Test::TestLevel'
301             # and outputs some info on the current site
302             #
303             sub plan
304             {
305             ######
306             # This subroutine uses no object data; therefore,
307             # drop any class or object.
308             #
309 1 50   1 1 97 $tech_p = Test::Tech->new() unless $tech_p;
310 1 50 33     12 my $self = (UNIVERSAL::isa($_[0],__PACKAGE__) && ref($_[0])) ? shift @_ : $tech_p;
311 1 50       5 $self = ref($self) ? $self : $tech_p;
312            
313 1         5 &Test::plan( @_ );
314            
315             ###############
316             #
317             # Establish default for Test
318             #
319             # Test 1.24 resets global variables in plan which
320             # never happens in 1.15
321             #
322 1         8 $Test::TestLevel = 1;
323            
324 1         35 my $loctime = localtime();
325 1         25 my $gmtime = gmtime();
326            
327 1         3 my $perl = "$]";
328 1 50 33     8 if(defined(&Win32::BuildNumber) and defined &Win32::BuildNumber()) {
    50          
329 0         0 $perl .= " Win32 Build " . &Win32::BuildNumber();
330             }
331             elsif(defined $MacPerl::Version) {
332 0         0 $perl .= " MacPerl version " . $MacPerl::Version;
333             }
334            
335 1 50       186 print $Test::TESTOUT <<"EOF" unless 1.20 < $Test::VERSION ;
336             # OS : $^O
337             # Perl : $perl
338             # Local Time : $loctime
339             # GMT Time : $gmtime
340             # Test : $Test::VERSION
341             EOF
342            
343 1         11 print $Test::TESTOUT <<"EOF";
344             # Test::Tech : $VERSION
345             # Data::Secs2 : $Data::Secs2::VERSION
346             # Data::Startup : $Data::Startup::VERSION
347             # Data::Str2Num : $Data::Str2Num::VERSION
348             # Number of tests: $self->{num_tests}
349             # =cut
350             EOF
351            
352 1         150 1
353             }
354            
355            
356             ######
357             #
358             #
359             sub skip {
360 0     0 1 0 $Test::TestLevel++;
361 0         0 my $results = skip_sub( '', @_ );
362 0         0 $Test::TestLevel--;
363 0         0 $results;
364            
365             };
366            
367            
368             ######
369             #
370             #
371             sub skip_sub
372             {
373            
374             ######
375             # If no object, use the default $tech_p object.
376             #
377 0 0   0 0 0 $tech_p = Test::Tech->new() unless $tech_p;
378 0 0 0     0 my $self = (UNIVERSAL::isa($_[0],__PACKAGE__) && ref($_[0])) ? shift @_ : $tech_p;
379 0 0       0 $self = ref($self) ? $self : $tech_p;
380            
381 0         0 my ($diagnostic,$name) = ('','');
382 0 0 0     0 my $options = Data::Startup->new(pop @_) if (4 < @_) && ref($_[-1]);
383            
384 0 0       0 $diagnostic = $options->{diagnostic} if $options->{diagnostic};
385 0 0       0 $name = $options->{name} if $options->{name};
386            
387 0         0 my ($subroutine, $mod, $actual_result, $expected_result, $diagnostic_in, $name_in) = @_;
388            
389 0 0       0 $diagnostic = $diagnostic_in if defined $diagnostic_in;
390 0 0       0 $name = $name_in if defined $name_in;
391 0 0       0 $diagnostic = $name unless defined $diagnostic;
392 0         0 $self->{test_name} = $name; # used by tied handle Test::Tech::Output
393            
394 0 0       0 if($self->{Skip_Tests}) { # skip rest of tests switch
395 0         0 &Test::skip( 1, '', '', $self->{Skip_Diag});
396 0         0 return 1;
397             }
398            
399 0         0 my $str_actual_result = stringify($actual_result);
400 0         0 my $str_expected_result = stringify($expected_result);
401 0         0 foreach ($str_actual_result,$str_expected_result) {
402 0 0       0 if(ref($_)) {
403 0         0 $$_ =~ s/\n\n/\n# /g;
404 0         0 $$_ =~ s/\n([^#])/\n# $1/g;
405 0         0 $diagnostic = 'Test::Tech::stringify() broken.';
406 0         0 $self->{test_name} .= ' # ' . $diagnostic;
407 0         0 &Test::ok($$_,'',$diagnostic,$diagnostic);
408 0         0 return 0;
409             }
410             }
411            
412 0 0       0 if($subroutine) {
413 0 0       0 $diagnostic .= "\n" unless substr($diagnostic,-1,1) eq "\n";
414 0         0 $str_actual_result =~ s/\n/\n /g;
415 0         0 $str_expected_result =~ s/\n/\n /g;
416 0         0 $diagnostic .=
417             " got: $str_actual_result\n" .
418             " expected: $str_expected_result\n";
419 0         0 $str_actual_result = &$subroutine($actual_result,$expected_result);
420 0         0 $str_expected_result = 1;
421             }
422            
423 0         0 &Test::skip($mod, $str_actual_result, $str_expected_result, $diagnostic);
424             }
425            
426            
427             ######
428             #
429             #
430             sub skip_tests
431             {
432            
433             ######
434             # If no object, use the default $tech_p object.
435             #
436 0 0   0 1 0 $tech_p = Test::Tech->new() unless $tech_p;
437 0 0 0     0 my $self = (UNIVERSAL::isa($_[0],__PACKAGE__) && ref($_[0])) ? shift @_ : $tech_p;
438 0 0       0 $self = ref($self) ? $self : $tech_p;
439            
440 0         0 my ($value,$diagnostic) = @_;
441 0         0 my $result = $self->{Skip_Tests};
442 0 0       0 $value = 1 unless (defined $value);
443 0         0 $self->{Skip_Tests} = $value;
444 0 0       0 $diagnostic = 'Test not performed because of previous failure.' unless defined $diagnostic;
445 0 0       0 $self->{Skip_Diag} = $value ? $diagnostic : '';
446 0         0 $result;
447            
448             }
449            
450            
451             #######
452             # This accesses the values in the %tech hash
453             #
454             # Use a dot notation for following down layers
455             # of hashes of hashes
456             #
457             sub tech_config
458             {
459            
460             ######
461             # If no object, use the default $tech_p object.
462             #
463 0 0   0 1 0 $tech_p = Test::Tech->new() unless $tech_p;
464 0 0 0     0 my $self = (UNIVERSAL::isa($_[0],__PACKAGE__) && ref($_[0])) ? shift @_ : $tech_p;
465 0 0       0 $self = ref($self) ? $self : $tech_p;
466            
467 0         0 my ($key, $value) = @_;
468 0         0 my @keys = split /\./, $key;
469            
470             #########
471             # Follow the hash with the current
472             # dot index until there are no more
473             # hashes. For success, the dot hash
474             # notation must match the structure.
475             #
476 0         0 my $key_p = $self;
477 0         0 while (@keys) {
478            
479 0         0 $key = shift @keys;
480            
481             ######
482             # Do not allow creation of new configs
483             #
484 0 0       0 if( defined( $key_p->{$key}) ) {
485            
486             ########
487             # Follow the hash
488             #
489 0 0       0 if( ref($key_p->{$key}) eq 'HASH' ) {
490 0         0 $key_p = $key_p->{$key};
491             }
492             else {
493 0 0       0 if(@keys) {
494 0         0 warn( "More key levels than hashes.\n");
495 0         0 return undef;
496             }
497 0         0 last;
498             }
499             }
500             }
501            
502            
503             #########
504             # References to arrays and scalars in the config may
505             # be transparent.
506             #
507 0         0 my $current_value = $key_p->{$key};
508 0 0       0 if( ref($current_value) eq 'SCALAR') {
509 0         0 $current_value = $$current_value;
510             }
511 0 0 0     0 if (defined $value && $key ne 'ntest') {
512 0 0       0 if( ref($value) eq 'SCALAR' ) {
513 0         0 ${$key_p->{$key}} = $$value;
  0         0  
514             }
515             else {
516 0         0 ${$key_p->{$key}} = $value;
  0         0  
517             }
518             }
519            
520 0         0 $current_value;
521             }
522            
523            
524             ########
525             # Handle Tie to catch the Test module output
526             # so that it may be modified.
527             #
528             package Test::Tech::Output;
529 1     1   4696 use Tie::Handle;
  1         4153  
  1         34  
530 1     1   12 use vars qw(@ISA);
  1         2  
  1         1026  
531             @ISA=('Tie::Handle');
532            
533             #####
534             # Tie
535             #
536             sub TIEHANDLE
537             {
538 1     1   3 my($class, $test_handle, $tech) = @_;
539 1 50       4 $class = ref($class) if ref($class);
540 1         6 bless {test_out => $test_handle, tech => $tech}, $class;
541             }
542            
543            
544             #####
545             # Print out the test output
546             #
547             sub PRINT
548             {
549 5     5   4636 my $self = shift;
550 5 50       23 my $buf = join(defined $, ? $, : '',@_);
551 5 50       17 $buf .= $\ if defined $\;
552 5         21 my $test_name = $self->{tech}->{test_name};
553 5         8 my $skip_diag = $self->{tech}->{Skip_Diag};
554            
555             #####
556             # Insert test name after ok or not ok
557             #
558 5 50       12 $buf =~ s/(ok \d+)/$1 - $test_name /g if($test_name);
559            
560             ######
561             # Insert skip diag after a skip comment
562             #
563 5 50       10 $buf =~ s/(# skip.*?)(\s*|\n)/$1 - $skip_diag$2/ig if $skip_diag;
564            
565             #####
566             # Keep stats on what tests that pass, failed, skip, todo
567             #
568 5         220 $self->stats($buf);
569            
570             #####
571             # Output the modified buffer
572             #
573 5         7 my $handle = $self->{test_out};
574 5         99 print $handle $buf;
575             }
576            
577             #####
578             #
579             #
580             sub PRINTF
581             {
582 1     1   246 my $self = shift;
583 1         9 $self->PRINT (sprintf(shift,@_));
584             }
585            
586             sub stats
587             {
588 5     5   9 my ($self,$buf) = @_;
589             #####
590             # Stats
591 5         7 my $tech = $self->{tech};
592 5         7 my $test_num;
593 5 50       23 if($buf =~ /^\s*(not ok|ok)\s*(\d+)/) {
594 0         0 $test_num = $2;
595             }
596 5 50       13 if($test_num) {
597 0 0       0 if( $tech->{num_tests} < $test_num) {
598 0         0 push @{$tech->{unplanned}},$test_num;
  0         0  
599             }
600 0 0       0 if($tech->{last_test} + 1 != $test_num) {
601 0         0 push @{$tech->{missing}},$test_num;
  0         0  
602             }
603 0         0 $tech->{last_test} = $test_num;
604             }
605 5 100       43 if($buf =~ /^\d+\.\.(\d+)/) {
    50          
    50          
    50          
606 1         7 $tech->{num_tests} = $1;
607             }
608             elsif ($buf =~ /^\s*ok\s*(\d+).*?\#\s*skip/i) {
609 0           push @{$tech->{skipped}},$1;
  0            
610             }
611             elsif ($buf =~ /^\s*not ok\s*(\d+)/i) {
612 0           push @{$tech->{failed}},$1;
  0            
613             }
614             elsif ($buf =~ /^\s*ok\s*(\d+)/i) {
615 0           push @{$tech->{passed}},$1;
  0            
616             }
617             }
618            
619             1
620            
621             __END__