# selftest.pl - Will Mengarini - Th 10 Oct 96 # This is some support for my approach to self-testing code, which is # automated regression testing implemented in the script that does the # substantive work, rather than in a separate test script (which tends to get # lost, or to be difficult to name in a short-file-name environment, etc). # A self-testing script can be invoked with a single arg of '--selftest', & # will respond by ignoring all other args & input, & instead running a # regression test. The exit code will indicate success or failure, & there # usually will be diagnostic output as well. If $ARGV[0] != '--selftest', # requiring selftest.pl is a fast NOP. Self-test code goes after __END__, so # Perl stops parsing before it's reached, & it's only extracted by this # package if $ARGV[0] == '--selftest' # Because self-testing is a critical metasoftware capability, I want it to # remain runnable under either Perl4 or Perl5, so I can't use modules. Self- # testing code must require 'selftest.pl' before doing any other requires; # '--selftest' is coped with in here, & removed from @ARGV if found. Note # that when required code itself requires 'selftest.pl', nothing will happen, # since the require operator ensures that each require only happens once. # Because selftest.pl must be requireable before anything else & in any # context, it must use exclusively capabilities built in to Perl[45]. One # possible problem can arise when a non-self-testing package requires a self- # testing one; because the outer package won't have required 'selftest.pl', # it won't be in @INC. That'll still NOP as long as $ARGV[0] != '--selftest'. # The data after __END__ is readable using the DATA filehandle, & it's # preferable to leave that capability available for substantive code, rather # than preempting it for self-testing. This is accomplished by putting the # self-test code after a __TESTCODE__ line, which must follow the __END__ # line but need not do so immediately; everything in between __END__ & # __TESTCODE__ is readable from the DATA filehandle as before, except that # since Perl doesn't know about __TESTCODE__, instead of checking eof or # relying on a while( ) you need to check for /^__TESTCODE__$/. # Whether or not '--selftest' is found, &maybeSelfTest is defined; it's a NOP # unless self-testing is wanted, otherwise it contains any test code except # sub{}s. The code after __TESTCODE__ may contain sub{}s, but since they # can't be defined inside another sub (&maybeSelfTest), they're parsed out # & separately evalled into package main. Their definitions must match /^sub/ # & /^\}/ on their first & last lines; i.e., they need to be defined in # column 1. # If you're not sure whether the code in &maybeSelfTest was correctly # separated from the subs, you can see both by setting $selftest'verbose = 1 # before the require. Remember that under DOS on old keyboards, # is a synonym, which will probably be needed. # Remember that &maybeSelfTest is executed at the top of your # script. If it then returns normally, the rest of your substantive script # will be executed as if it had been invoked normally, which you probably # don't want. Therefore, your test code must use the exit operator; exit 0 if # the test detected no failures, nonzero if it detected some. # Since reading from the DATA filehandle is a useful technique, & the code # after __TESTCODE__ is essentially another script, it'd be nice for that # code to itself have the ability to read from something equivalent to DATA. # (DATA itself is reserved for the substantive script.) Therefore, a # __TESTDATA__ line is recognized as terminating the self-test script, & a # main'TESTDATA filehandle is left open pointing to the first line after it. # Your self-test code can use analogously to the way your # substantive code uses . (Of course, this extra filehandle is not left # open if $ARGV[0] ne '--selftest'.) # So this is the layout of a typical self-testing script: # # $selftest'verbose = 1; [only if desired] # require 'selftest.pl'; # [other requires if needed]; # [package-global variable initializations if needed]; # [other initialization code if needed]; # &maybeSelfTest; # # [substantive code] # # __END__ # [stuff you can read from , up to /^__TESTCODE__$/] # __TESTCODE__ # [the text of &maybeSelfTest, along with any subs it calls] # __TESTDATA__ # [stuff &maybeSelfTest can read from , up to eof TESTDATA] # # Since most self-testing code is relegated to beyond __TESTCODE__, it # doesn't even affect the parse, let alone the execution, of the script. # The reason for putting the "[other requires]" line where it is above is # that some requireable scripts may do more than just define subs; they may # initialize variables or do other setup work, and &maybeSelfTest should run # in an environment in which all initializations have been done. # An example of a self-testing package is my ask.pl. # That concludes user documentation; the rest documents implementation. # My first attempt at this tried to read from DATA to get the test code, # but that doesn't work from inside a require under Perl 4.036. (That's # probably a feature.) It's therefore necessary to reread the entire script # to find __END__. $0 contains a complete path under Windows 3.1 Perl 4.036. # This script will fail in a Perl in which $0 doesn't have a complete path. # It's likely some coders will forget to put __END__ before __TESTCODE__. # Most of the time Perl will catch this, but not always; consider # print "We're finished.\n"; # __TESTCODE__ # + print "Oops.\n"; # which may be silly, but runs, & demonstrates that there's probably some # pathological case which not only runs but could happen in real life. # Therefore, it's necessary for this code to verify that __END__ is present. # There were special problems with option handling. # * Because I want Perl[45] runnability, I can't use modules. The # only Perl4 package that supports long options, newgetop.pl, doesn't # support bundling of single-char options, which I consider more valuable. # * Scripts normally invoked with the require operator must also be command- # line executable with --selftest; but such scripts normally don't have # much code outside of subroutines. This scheme needs to work for, for # example, an outer app that self-tests, & requires 2 subordinate packages # that self-test; when --selftest is on the command line for the outer app, # it must induce the outer app's self-test, not that of any required # module, but typically requires are done before substantive code. # Because of these problems, I think it's best to not use any *getop*.p[lm] # to cope with "--selftest", but rather to do it "by hand", & to insist that # self-testing code require 'selftest.pl' before doing any other requires; # '--selftest' is coped with in here, & removed from @ARGV if found. package main; { package selftest; $code = $subs = ''; #needed only because of -w; must precede if() if( @ARGV && $ARGV[0] eq '--selftest' ){ shift; open( main'TESTDATA, $0 ) || die "can't open caller $0"; while( ){ last if /^__END__$/; eof && die "__END__ of $0 not found"; } while( ){ last if /^__TESTCODE__$/; eof && die "__TESTCODE__ of $0 not found"; } $inSub = 0; #whether we're in subroutine code while( ){ last if /^__TESTDATA__$/; $inSub = 1 if /^sub /; if( $inSub ){ $subs .= $_; }else{ $code .= $_; } $inSub = 0 if $inSub && /^\}/; } # The main'TESTDATA handle is now ready for reading the test data itself. } } $selftest'verbose = 0 unless defined $selftest'verbose; print "\$selftest'code-------\n$selftest'code\n\n" if $selftest'verbose; print "\$selftest'subs-------\n$selftest'subs\n\n" if $selftest'verbose; eval $selftest'subs; $@ && die "\$selftest'subs: $@"; eval <