File Coverage

blib/lib/ExtUtils/TBone.pm
Criterion Covered Total %
statement 81 90 90.0
branch 22 34 64.7
condition 4 9 44.4
subroutine 19 22 86.3
pod 14 16 87.5
total 140 171 81.8


line stmt bran cond sub pod time code
1             package ExtUtils::TBone;
2              
3              
4             =head1 NAME
5              
6             ExtUtils::TBone - a "skeleton" for writing "t/*.t" test files.
7              
8              
9             =head1 SYNOPSIS
10              
11             Include a copy of this module in your t directory (as t/ExtUtils/TBone.pm),
12             and then write your t/*.t files like this:
13              
14             use lib "./t"; # to pick up a ExtUtils::TBone
15             use ExtUtils::TBone;
16              
17             # Make a tester... here are 3 different alternatives:
18             my $T = typical ExtUtils::TBone; # standard log
19             my $T = new ExtUtils::TBone; # no log
20             my $T = new ExtUtils::TBone "testout/Foo.tlog"; # explicit log
21            
22             # Begin testing, and expect 3 tests in all:
23             $T->begin(3); # expect 3 tests
24             $T->msg("Something for the log file"); # message for the log
25            
26             # Run some tests:
27             $T->ok($this); # test 1: no real info logged
28             $T->ok($that, # test 2: logs a comment
29             "Is that ok, or isn't it?");
30             $T->ok(($this eq $that), # test 3: logs comment + vars
31             "Do they match?",
32             This => $this,
33             That => $that);
34            
35             # That last one could have also been written...
36             $T->ok_eq($this, $that); # does 'eq' and logs operands
37             $T->ok_eqnum($this, $that); # does '==' and logs operands
38            
39             # End testing:
40             $T->end;
41              
42              
43             =head1 DESCRIPTION
44              
45             This module is intended for folks who release CPAN modules with
46             "t/*.t" tests. It makes it easy for you to output syntactically
47             correct test-output while at the same time logging all test
48             activity to a log file. Hopefully, bug reports which include
49             the contents of this file will be easier for you to investigate.
50              
51             =head1 OUTPUT
52              
53             =head2 Standard output
54              
55             Pretty much as described by C, with a special
56             "# END" comment placed at the very end:
57              
58             1..3
59             ok 1
60             not ok 2
61             ok 3
62             # END
63              
64              
65             =head1 Log file
66              
67             A typical log file output by this module looks like this:
68              
69             1..3
70            
71             ** A message logged with msg().
72             ** Another one.
73             1: My first test, using test(): how'd I do?
74             1: ok 1
75            
76             ** Yet another message.
77             2: My second test, using test_eq()...
78             2: A: The first string
79             2: B: The second string
80             2: not ok 2
81            
82             3: My third test.
83             3: ok 3
84            
85             # END
86              
87             Each test() is logged with the test name and results, and
88             the test-number prefixes each line.
89             This allows you to scan a large file easily with "grep" (or, ahem, "perl").
90             A blank line follows each test's record, for clarity.
91              
92              
93             =head1 PUBLIC INTERFACE
94              
95             =cut
96              
97             # Globals:
98 2     2   1072 use strict;
  2         3  
  2         68  
99 2     2   20 use vars qw($VERSION);
  2         4  
  2         103  
100 2     2   1626 use FileHandle;
  2         27603  
  2         63  
101 2     2   861 use File::Basename;
  2         4  
  2         3486  
102              
103             # The package version, both in 1.23 style *and* usable by MakeMaker:
104             $VERSION = substr q$Revision: 1.124 $, 10;
105              
106              
107              
108             #------------------------------
109              
110             =head2 Construction
111              
112             =over 4
113              
114             =cut
115              
116             #------------------------------
117              
118             =item new [ARGS...]
119              
120             I
121             Create a new tester. Any arguments are sent to log_open().
122              
123             =cut
124              
125             sub new {
126 3     3 1 98 my $self = bless {
127             OUT =>\*STDOUT,
128             Begin=>0,
129             End =>0,
130             Count=>0,
131             }, shift;
132 3 50       24 $self->log_open(@_) if @_;
133 3         7 $self;
134             }
135              
136             #------------------------------
137              
138             =item typical
139              
140             I
141             Create a typical tester.
142             Use this instead of new() for most applicaitons.
143             The directory "testout" is created for you automatically, to hold
144             the output log file, and log_warnings() is invoked.
145              
146             =cut
147              
148             sub typical {
149 2     2 1 49 my $class = shift;
150 2         152 my ($tfile) = basename $0;
151 2 50       52 unless (-d "testout") {
152 0 0       0 mkdir "testout", 0755
153             or die "Couldn't create a 'testout' subdirectory: $!\n";
154             ### warn "$class: created 'testout' directory\n";
155             }
156 2         14 my $self = $class->new($class->catfile('.', 'testout', "${tfile}log"));
157 2         12 $self->log_warnings;
158 2         7 $self;
159             }
160              
161             #------------------------------
162             # DESTROY
163             #------------------------------
164             # Class method, destructor.
165             # Automatically closes the log.
166             #
167             sub DESTROY {
168 1     1   5 $_[0]->log_close;
169             }
170              
171              
172             #------------------------------
173              
174             =back
175              
176             =head2 Doing tests
177              
178             =over 4
179              
180             =cut
181              
182             #------------------------------
183              
184             =item begin NUMTESTS
185              
186             I
187             Start testing.
188             This outputs the 1..NUMTESTS line to the standard output.
189              
190             =cut
191              
192             sub begin {
193 3     3 1 19 my ($self, $n) = @_;
194 3 50       15 return if $self->{Begin}++;
195              
196 3         22 $self->l_print("1..$n\n\n");
197 3         4 print {$self->{OUT}} "1..$n\n";
  3         44  
198             }
199              
200             #------------------------------
201              
202             =item end
203              
204             I
205             Indicate the end of testing.
206             This outputs a "# END" line to the standard output.
207              
208             =cut
209              
210             sub end {
211 3     3 1 16 my ($self) = @_;
212 3 50       44 return if $self->{End}++;
213 3         11 $self->l_print("# END\n");
214 3         5 print {$self->{OUT}} "# END\n";
  3         12  
215             }
216              
217             #------------------------------
218              
219             =item ok BOOL, [TESTNAME], [PARAMHASH...]
220              
221             I
222             Do a test, and log some information connected with it.
223             This outputs the test result lines to the standard output:
224              
225             ok 12
226             not ok 13
227              
228             Use it like this:
229              
230             $T->ok(-e $dotforward);
231              
232             Or better yet, like this:
233              
234             $T->ok((-e $dotforward),
235             "Does the user have a .forward file?");
236              
237             Or even better, like this:
238              
239             $T->ok((-e $dotforward),
240             "Does the user have a .forward file?",
241             User => $ENV{USER},
242             Path => $dotforward,
243             Fwd => $ENV{FWD});
244              
245             That last one, if it were test #3, would be logged as:
246              
247             3: Does the user have a .forward file?
248             3: User: "alice"
249             3: Path: "/home/alice/.forward"
250             3: Fwd: undef
251             3: ok
252              
253             You get the idea. Note that defined quantities are logged with delimiters
254             and with all nongraphical characters suitably escaped, so you can see
255             evidence of unexpected whitespace and other badnasties.
256             Had "Fwd" been the string "this\nand\nthat", you'd have seen:
257              
258             3: Fwd: "this\nand\nthat"
259              
260             And unblessed array refs like ["this", "and", "that"] are
261             treated as multiple values:
262              
263             3: Fwd: "this"
264             3: Fwd: "and"
265             3: Fwd: "that"
266              
267             =cut
268              
269             sub ok {
270 17     17 1 209 my ($self, $ok, $test, @ps) = @_;
271 17         29 ++($self->{Count}); # next test
272              
273             # Report to harness:
274 17 100       45 my $status = ($ok ? "ok " : "not ok ") . $self->{Count};
275 17         21 print {$self->{OUT}} $status, "\n";
  17         39  
276              
277             # Log:
278 17 50       58 $self->ln_print($test, "\n") if $test;
279 17         46 while (@ps) {
280 58         104 my ($k, $v) = (shift @ps, shift @ps);
281 58 100 66     194 my @vs = ((ref($v) and (ref($v) eq 'ARRAY'))? @$v : ($v));
282 58         120 foreach (@vs) {
283 59 50       116 if (!defined($_)) { # value not defined: output keyword
284 0         0 $self->ln_print(qq{ $k: undef\n});
285             }
286             else { # value defined: output quoted, encoded form
287 59         131 s{([\n\t\x00-\x1F\x7F-\xFF\\\"])}
288 13         48 {'\\'.sprintf("%02X",ord($1)) }exg;
289 59         100 s{\\0A}{\\n}g;
290 59         169 $self->ln_print(qq{ $k: "$_"\n});
291             }
292             }
293             }
294 17         37 $self->ln_print($status, "\n");
295 17         36 $self->l_print("\n");
296 17         65 1;
297             }
298              
299              
300             #------------------------------
301              
302             =item ok_eq ASTRING, BSTRING, [TESTNAME], [PARAMHASH...]
303              
304             I
305             Convenience front end to ok(): test whether C, and
306             logs the operands as 'A' and 'B'.
307              
308             =cut
309              
310             sub ok_eq {
311 10     10 1 77 my ($self, $this, $that, $test, @ps) = @_;
312 10   50     47 $self->ok(($this eq $that),
313             ($test || "(Is 'A' string-equal to 'B'?)"),
314             A => $this,
315             B => $that,
316             @ps);
317             }
318              
319              
320             #------------------------------
321              
322             =item ok_eqnum ANUM, BNUM, [TESTNAME], [PARAMHASH...]
323              
324             I
325             Convenience front end to ok(): test whether C, and
326             logs the operands as 'A' and 'B'.
327              
328             =cut
329              
330             sub ok_eqnum {
331 0     0 1 0 my ($self, $this, $that, $test, @ps) = @_;
332 0   0     0 $self->ok(($this == $that),
333             ($test || "(Is 'A' numerically-equal to 'B'?)"),
334             A => $this,
335             B => $that,
336             @ps);
337             }
338              
339             #------------------------------
340              
341             =back
342              
343             =head2 Logging messages
344              
345             =over 4
346              
347             =cut
348              
349             #------------------------------
350              
351             =item log_open PATH
352              
353             I
354             Open a log file for messages to be output to. This is invoked
355             for you automatically by C and C.
356              
357             =cut
358              
359             sub log_open {
360 3     3 1 8 my ($self, $path) = @_;
361 3         15 $self->{LogPath} = $path;
362 3   50     28 $self->{LOG} = FileHandle->new(">$path") || die "open $path: $!";
363 3         455 $self;
364             }
365              
366             #------------------------------
367              
368             =item log_close
369              
370             I
371             Close the log file and stop logging.
372             You shouldn't need to invoke this directly; the destructor does it.
373              
374             =cut
375              
376             sub log_close {
377 1     1 1 2 my $self = shift;
378 1 50       0 close(delete $self->{LOG}) if $self->{LOG};
379             }
380              
381             #------------------------------
382              
383             =item log_warnings
384              
385             I
386             Invoking this redefines $SIG{__WARN__} to log to STDERR and
387             to the tester's log. This is automatically invoked when
388             using the C constructor.
389              
390             =cut
391              
392             sub log_warnings {
393 2     2 1 4 my ($self) = @_;
394             $SIG{__WARN__} = sub {
395 0     0   0 print STDERR $_[0];
396 0         0 $self->log("warning: ", $_[0]);
397 2         27 };
398             }
399              
400             #------------------------------
401              
402             =item log MESSAGE...
403              
404             I
405             Log a message to the log file. No alterations are made on the
406             text of the message. See msg() for an alternative.
407              
408             =cut
409              
410             sub log {
411 0     0 1 0 my $self = shift;
412 0 0       0 print {$self->{LOG}} @_ if $self->{LOG};
  0         0  
413             }
414              
415             #------------------------------
416              
417             =item msg MESSAGE...
418              
419             I
420             Log a message to the log file. Lines are prefixed with "** " for clarity,
421             and a terminating newline is forced.
422              
423             =cut
424              
425             sub msg {
426 1     1 1 7 my $self = shift;
427 1         3 my $text = join '', @_;
428 1         3 chomp $text;
429 1         5 $text =~ s{^}{** }gm;
430 1         4 $self->l_print($text, "\n");
431             }
432              
433             #------------------------------
434             #
435             # l_print MESSAGE...
436             #
437             # Instance method, private.
438             # Print to the log file if there is one.
439             #
440             sub l_print {
441 117     117 0 148 my $self = shift;
442 117 50       298 print { $self->{LOG} } @_ if $self->{LOG};
  117         592  
443             }
444              
445             #------------------------------
446             #
447             # ln_print MESSAGE...
448             #
449             # Instance method, private.
450             # Print to the log file, prefixed by message number.
451             #
452             sub ln_print {
453 93     93 0 113 my $self = shift;
454 93         245 foreach (split /\n/, join('', @_)) {
455 93         279 $self->l_print("$self->{Count}: $_\n");
456             }
457             }
458              
459             #------------------------------
460              
461             =back
462              
463             =head2 Utilities
464              
465             =over 4
466              
467             =cut
468              
469             #------------------------------
470              
471             =item catdir DIR, ..., DIR
472              
473             I
474             Concatenate several directories into a path ending in a directory.
475             Lightweight version of the one in C; this method
476             dates back to a more-innocent time when File::Spec was younger
477             and less ubiquitous.
478              
479             Paths are assumed to be absolute.
480             To signify a relative path, the first DIR must be ".",
481             which is processed specially.
482              
483             On Mac, the path I end in a ':'.
484             On Unix, the path I end in a '/'.
485              
486             =cut
487              
488             sub catdir {
489 12     12 1 105 my $self = shift;
490 12 100       34 my $relative = shift @_ if ($_[0] eq '.');
491 12 100       30 if ($^O eq 'Mac') {
492 5 100       25 return ($relative ? ':' : '') . (join ':', @_) . ':';
493             }
494             else {
495 7 100       46 return ($relative ? './' : '/') . join '/', @_;
496             }
497             }
498              
499             #------------------------------
500              
501             =item catfile DIR, ..., DIR, FILE
502              
503             I
504             Like catdir(), but last element is assumed to be a file.
505             Note that, at a minimum, you must supply at least a single DIR.
506              
507             =cut
508              
509             sub catfile {
510 6     6 1 61 my $self = shift;
511 6         12 my $file = pop;
512 6 100       19 if ($^O eq 'Mac') {
513 2         7 return $self->catdir(@_) . $file;
514             }
515             else {
516 4         16 return $self->catdir(@_) . "/$file";
517             }
518             }
519              
520             #------------------------------
521              
522             =back
523              
524              
525             =head1 VERSION
526              
527             $Id: TBone.pm,v 1.124 2001/08/20 20:30:07 eryq Exp $
528              
529              
530             =head1 CHANGE LOG
531              
532             =over 4
533              
534             =item Version 1.124 (2001/08/20)
535              
536             The terms-of-use have been placed in the distribution file "COPYING".
537             Also, small documentation tweaks were made.
538              
539              
540             =item Version 1.122 (2001/08/20)
541              
542             Changed output of C<"END"> to C<"# END">; apparently, "END" is
543             not a directive. Maybe it never was.
544             I
545              
546             The storyteller
547             need not say "the end" aloud;
548             Silence is enough.
549              
550             Automatically invoke C when constructing
551             via C.
552              
553              
554             =item Version 1.120 (2001/08/17)
555              
556             Added log_warnings() to support the logging of SIG{__WARN__}
557             messages to the log file (if any).
558              
559              
560             =item Version 1.116 (2000/03/23)
561              
562             Cosmetic improvements only.
563              
564              
565             =item Version 1.112 (1999/05/12)
566              
567             Added lightweight catdir() and catfile() (a la File::Spec)
568             to enhance portability to Mac environment.
569              
570              
571             =item Version 1.111 (1999/04/18)
572              
573             Now uses File::Basename to create "typical" logfile name,
574             for portability.
575              
576              
577             =item Version 1.110 (1999/04/17)
578              
579             Fixed bug in constructor that surfaced if no log was being used.
580              
581             =back
582              
583             Created: Friday-the-13th of February, 1998.
584              
585              
586             =head1 AUTHOR
587              
588             Eryq (F).
589             President, ZeeGee Software Inc. (F).
590              
591             Go to F for the latest downloads
592             and on-line documentation for this module.
593              
594             Enjoy. Yell if it breaks.
595              
596             =cut
597              
598             #------------------------------
599              
600             1;
601             __END__