| home / programming / awperl / 1 | [previous] [next] |
|
|
Did you spot that all these modules have a lot in common? Did you wonder how you’d add a Test:: module of your own, if you wanted to write one?
Then you’re already thinking lazily, and the testing guys are ahead of you. That common functionality lives in a superclass module called Test::Builder, seldom seen, but used to take the drudgery out of creating new test modules.
Suppose we want to write a module that checks whether mail messages conform
to RFC 822 syntax.3 We’ll call it Test::MailMessage, and it will export
a basic function, msg_ok(), that determines whether a message consists
of an optional set of header lines, optionally followed by a blank line and
any num¬ber of lines of text. (Yes, an empty message is legal according
to this syntax. Unfortunately, too few people who have nothing to say avail
themselves of this option.) Here’s the module:
Example 3.1 Using Test::Builder to Create Test::MailMessage
1 package Test::MailMessage; 2
use strict; 3 use warnings; 4
use Carp;
5 use Test::Builder;
6 use base qw(Exporter);
7 our @EXPORT = qw(msg_ok);
8
9 my $test = Test::Builder->new;
10
11 sub import 12
{
13 my $self = shift; 14 my $pack = caller;
15
16 $test->exported_to($pack);
17 $test->plan(@_);
3. http://www.faqs.org/rfcs/rfc822.html
18
19 $self->export_to_level(1, $self,
'msg_ok');
20 } 21
22 sub msg_ok
23 {
24 my $arg = shift;
25 my $tester = _new();
26 eval
27 {
28 if (defined(fileno($arg)))
29 {
30 while (<$arg>)
31 {
32 $tester->_validate($_);
33 }
34 }
35
elsif (ref $arg)
36 {
37 $tester->_validate($_)
for @$arg;
38 }
39 else
40 {
41
for ($arg =~ /(.*\n)/g)
42 {
43
$tester->_validate($_);
44 }
45 }
46 };
47 $test->ok(!$@, shift);
48 $test->diag($@) if $@;
49 } 50
51 sub _new
52 {
53 return bless { expect => "header"
};
54 } 55
56 sub _validate
57 {
58 my ($self, $line) =
@_;
59 return if $self->{expect} eq
"body";
|
|
|
|
|
60 |
|
if ($self->{expect} eq "header/continuation") |
|
61 |
|
{ |
|
62 |
|
/^\s+\S/
and return; |
|
63 |
|
} |
|
64 |
|
$self->{expect} = "body", return if /^$/; |
|
65 |
|
/^\S+:/ or croak "Invalid header"; |
|
66 |
|
$self->{expect} = "header/continuation"; |
|
67 |
} |
|
|
68 |
|
|
|
69 |
1; |
|
croak(), and the Test::Builder module
so we can create an instance of it. In lines 6 and 7 we declare this to be a subclass
of the Exporter module, exporting to the caller the subroutine msg_ok().
(Note that this is not a subclass of Test::Builder.)
In line 9 we create a Test::Builder object that will do the boring part of
testing for us. Lines 11 through 20 are copied right out of the Test::Builder
docu¬mentation; the import() routine is what allows us to say
how many tests we’re going to run when we use the module.
Lines 22 through 49 define the msg_ok() function itself. Its single
argument specifies the mail message, either via a scalar containing the message,
a reference to an array of lines in the message, or a filehandle from which
the message can be read. Rather than read all of the lines from that filehandle
into memory, we’re going to operate on them one at a time because it’s
not necessary to have the whole message in memory. That’s why we create
the object $tester in line 25 to handle each line: it will contain
a memory of its current state.
Then we call the _validate() method of $tester with each line
of the message. Because that method will croak() if the message is in error,
we wrap those loops in an eval block. This allows us easily to skip superfluous
scanning of a message after detecting an error.
Finally, we see whether an error occurred; if an exception was thrown by croak()inside
the eval block, $@ will contain its text; otherwise $@ will be empty. The ok()
method of the Test::Builder object we created is the same function we’re
used to using in Test::Simple; it takes a true or false value, and an optional
tag string, which we pass from our caller. If we had an exception, we pass its
text to Test::Builder’s diag() method, which causes it to be out¬put
as a comment during testing.
The _new() method in lines 50–53 is not called new() because it’s
not really a proper constructor; it’s really just creating a state object,
which is why we didn’t bother to make it inheritable. It starts out in
life expecting to see a mail header.
Lines 56–70 validate a line of a message. Because anything goes in a message body, if that’s what we’re expecting we have nothing to do. Otherwise, if we’re expecting a header or header continuation line, then first we check for a continuation line (which starts with white space; this is how a long message header “overflows”). If we have a blank line (line 67), that separates the header from the body, so we switch to expecting body text.
Finally, we must at this point be expecting a header line, and one of those
starts with non-white-space characters followed by a colon. If we don’t
have that, the message is bogus; but if we do, the next line could be either
a header line or a continuation of the current header (or the blank line separating
head¬ers from the body).
| home / programming / awperl / 1 | [previous] [next] |
Created: March 27, 2003
Revised: March 24, 2004
URL: http://webreference.com/programming/awperl/1