Add the original source packages to maemo, source lenny
[dh-make-perl] / dev / i386 / libio-stringy-perl / io-stringy-2.110 / t / ExtUtils / TBone.pm
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<Test::Harness>, 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 use strict;
99 use vars qw($VERSION);
100 use FileHandle;
101 use File::Basename;
102
103 # The package version, both in 1.23 style *and* usable by MakeMaker:
104 $VERSION = substr q$Revision: 1.1 $, 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<Class method, constructor.>
121 Create a new tester.  Any arguments are sent to log_open().
122
123 =cut
124
125 sub new {
126     my $self = bless {
127         OUT  =>\*STDOUT,
128         Begin=>0,
129         End  =>0,
130         Count=>0,
131     }, shift;
132     $self->log_open(@_) if @_;
133     $self;
134 }
135
136 #------------------------------
137
138 =item typical
139
140 I<Class method, constructor.>
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     my $class = shift;
150     my ($tfile) = basename $0;
151     unless (-d "testout") {
152         mkdir "testout", 0755 
153             or die "Couldn't create a 'testout' subdirectory: $!\n";
154         ### warn "$class: created 'testout' directory\n";
155     }
156     my $self = $class->new($class->catfile('.', 'testout', "${tfile}log"));
157     $self->log_warnings;
158     $self;
159 }
160
161 #------------------------------
162 # DESTROY
163 #------------------------------
164 # Class method, destructor.
165 # Automatically closes the log.
166 #
167 sub DESTROY {
168     $_[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<Instance method.>
187 Start testing.  
188 This outputs the 1..NUMTESTS line to the standard output.
189
190 =cut
191
192 sub begin {
193     my ($self, $n) = @_;
194     return if $self->{Begin}++;
195
196     $self->l_print("1..$n\n\n");
197     print {$self->{OUT}} "1..$n\n";
198 }
199
200 #------------------------------
201
202 =item end
203
204 I<Instance method.>
205 Indicate the end of testing.
206 This outputs a "# END" line to the standard output.
207
208 =cut
209
210 sub end {
211     my ($self) = @_;
212     return if $self->{End}++;
213     $self->l_print("# END\n");
214     print {$self->{OUT}} "# END\n";
215 }
216
217 #------------------------------
218
219 =item ok BOOL, [TESTNAME], [PARAMHASH...]
220
221 I<Instance method.>
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     my ($self, $ok, $test, @ps) = @_;
271     ++($self->{Count});      # next test
272
273     # Report to harness:
274     my $status = ($ok ? "ok " : "not ok ") . $self->{Count};
275     print {$self->{OUT}} $status, "\n";
276
277     # Log:
278     $self->ln_print($test, "\n") if $test;
279     while (@ps) {
280         my ($k, $v) = (shift @ps, shift @ps);
281         my @vs = ((ref($v) and (ref($v) eq 'ARRAY'))? @$v : ($v));
282         foreach (@vs) { 
283             if (!defined($_)) {  # value not defined: output keyword
284                 $self->ln_print(qq{  $k: undef\n});
285             }
286             else {               # value defined: output quoted, encoded form
287                 s{([\n\t\x00-\x1F\x7F-\xFF\\\"])}
288                  {'\\'.sprintf("%02X",ord($1)) }exg;
289                 s{\\0A}{\\n}g;
290                 $self->ln_print(qq{  $k: "$_"\n});
291             }
292         }
293     }
294     $self->ln_print($status, "\n");
295     $self->l_print("\n");
296     1;
297 }
298
299
300 #------------------------------
301
302 =item ok_eq ASTRING, BSTRING, [TESTNAME], [PARAMHASH...]
303
304 I<Instance method.>  
305 Convenience front end to ok(): test whether C<ASTRING eq BSTRING>, and
306 logs the operands as 'A' and 'B'.
307
308 =cut
309
310 sub ok_eq {
311     my ($self, $this, $that, $test, @ps) = @_;
312     $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<Instance method.>  
325 Convenience front end to ok(): test whether C<ANUM == BNUM>, and
326 logs the operands as 'A' and 'B'.  
327
328 =cut
329
330 sub ok_eqnum {
331     my ($self, $this, $that, $test, @ps) = @_;
332     $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<Instance method.>
354 Open a log file for messages to be output to.  This is invoked
355 for you automatically by C<new(PATH)> and C<typical()>.
356
357 =cut
358
359 sub log_open {
360     my ($self, $path) = @_;
361     $self->{LogPath} = $path;
362     $self->{LOG} = FileHandle->new(">$path") || die "open $path: $!";
363     $self;
364 }
365
366 #------------------------------
367
368 =item log_close
369
370 I<Instance method.>
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     my $self = shift;
378     close(delete $self->{LOG}) if $self->{LOG};
379 }
380
381 #------------------------------
382
383 =item log_warnings
384
385 I<Instance method.>
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<typical> constructor.
389
390 =cut
391
392 sub log_warnings {
393     my ($self) = @_;
394     $SIG{__WARN__} = sub {
395         print STDERR $_[0];
396         $self->log("warning: ", $_[0]);
397     };
398 }
399
400 #------------------------------
401
402 =item log MESSAGE...
403
404 I<Instance method.>
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     my $self = shift;
412     print {$self->{LOG}} @_ if $self->{LOG};
413 }
414
415 #------------------------------
416
417 =item msg MESSAGE...
418
419 I<Instance method.>
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     my $self = shift;
427     my $text = join '', @_;
428     chomp $text; 
429     $text =~ s{^}{** }gm;
430     $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     my $self = shift;
442     print { $self->{LOG} } @_ if $self->{LOG};
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     my $self = shift;
454     foreach (split /\n/, join('', @_)) { 
455         $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<Class/instance method.>
474 Concatenate several directories into a path ending in a directory.
475 Lightweight version of the one in C<File::Spec>; 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<does> end in a ':'.
484 On Unix, the path I<does not> end in a '/'.
485
486 =cut
487
488 sub catdir {
489     my $self = shift;
490     my $relative = shift @_ if ($_[0] eq '.');
491     if ($^O eq 'Mac') {
492         return ($relative ? ':' : '') . (join ':', @_) . ':';
493     }
494     else {
495         return ($relative ? './' : '/') . join '/', @_;
496     }
497 }
498
499 #------------------------------
500
501 =item catfile DIR, ..., DIR, FILE
502
503 I<Class/instance method.>
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     my $self = shift;
511     my $file = pop;
512     if ($^O eq 'Mac') {
513         return $self->catdir(@_) . $file;
514     }
515     else {
516         return $self->catdir(@_) . "/$file";
517     }
518 }
519
520 #------------------------------
521
522 =back
523
524
525 =head1 VERSION
526
527 $Id: TBone.pm,v 1.1 2005/02/10 19:38:36 dfs 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<Thanks to Michael G. Schwern for the bug report.>
545
546     The storyteller
547        need not say "the end" aloud;
548     Silence is enough.
549
550 Automatically invoke C<log_warnings()> when constructing
551 via C<typical()>.
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<eryq@zeegee.com>).
589 President, ZeeGee Software Inc. (F<http://www.zeegee.com>).
590
591 Go to F<http://www.zeegee.com> 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__
602
603 my $T = new ExtUtils::TBone "testout/foo.tlog";
604 $T->begin(3);
605 $T->msg("before 1\nor 2");
606 $T->ok(1, "one");
607 $T->ok(2, "Two");
608 $T->ok(3, "Three", Roman=>'III', Arabic=>[3, '03'], Misc=>"3\nor 3");
609 $T->end;
610
611 1;
612