tags:

views:

190

answers:

5

I'm building a regression system (not unit testing) for some Perl scripts.

A core component of the system is

  `perl script.pl @params 1>stdoutfile 2>stderrfile`;

However, in the course of actually working on the scripts, they sometimes don't compile(Shock!). But perl itself will execute correctly. However, I don't know how to detect on stderr whether Perl failed to compile (and therefore wrote to stderr), or my script barfed on input (and therefore wrote to stderr).

How do I detect whether a program executed or not, without exhaustively finding Perl error messages and grepping the stderr file?

+1  A: 

Take a look at the $? variable.

From perldoc perlvar:

The status returned by the last pipe close, backtick ("``") command, successful call to wait() or waitpid(), or from the system() operator. This is just the 16-bit status word returned by the traditional Unix wait() system call (or else is made up to look like it). Thus, the exit value of the subprocess is really ("$? >> 8"), and "$? & 127" gives which signal, if any, the process died from, and "$? & 128" reports whether there was a core dump.

innaM
But does $? return the result from *perl* or the *script*? And what are the Perl return values - I wasn't able to find it when I googled.
Paul Nathan
It could be either. If you want to check that the script compiles, run `perl -c script.pl` instead and test for $? == 0.
Michael Carman
A: 

It sounds like you need IPC::Open3.

Chas. Owens
+4  A: 

It might be easiest to do this in two steps:

system('$^X -c script.pl');
if ($? == 0) {
    # it compiled, now let's see if it runs
    system('$^X script.pl', @params, '1>stdoutfile', '2>stderrfile');
    # check $?
}
else {
    warn "script.pl didn't compile";
}

Note the use of $^X instead of perl. This is more flexible and robust. It ensures that you're running from the same installation instead of whatever interpreter shows up first in your path. The system call will inherit your environment (including PERL5LIB), so spawning a different version of perl could result in hard-to-diagnose compatibility errors.

Michael Carman
You should always use $^X. I've been bitten by a hard-coded 'perl' almost every time I've used it. The problem is that that the child process inherits your PERL5LIB, which might be wrong for the first perl in the PATH. You end up with odd library mismatch errors that way.
brian d foy
Heh. That's ugly. While it won't always matter (Windows users are unlikely to have multiple versions of Perl installed) it should never be harmful to use $^X. I'll update the answer accordingly.
Michael Carman
@Michael Carman Ah-em ... I have Cygwin, Strawberry and ActiveState Perl as well as one I compiled from scratch using VC Express.
Sinan Ünür
@Sinan Unur: I have several versions as well, but I think that puts us squarely in the minority.
Michael Carman
+1  A: 
brian d foy
Test::More has a BAIL_OUT() function.
Schwern
A: 

Scripts are notoriously hard to test. You have to run them and then scrape their output. You can't unit test their guts... or can you?

#!/usr/bin/perl -w

# Only run if we're the file being executed by Perl
main() if $0 eq __FILE__;

sub main {
    ...your code here...
}

1;

Now you can load the script like any other library.

#!/usr/bin/perl -w

use Test::More;

require_ok("./script.pl");

You can even run and test main(). Test::Output is handy for capturing the output. You can say local @ARGV to control arguments or you can change main() to take @ARGV as an argument (recommended).

Then you can start splitting main() up into smaller routines which you can easily unit test.

Schwern
The thing is, I don't want to edit the script. This is a black-box test approach, not a unit test approach(which has its own time and place).
Paul Nathan
You're not doing just blackbox. By testing the program through the command line you're doing high level acceptance testing. JUST high level acceptance testing. These tests are harder to write since you have to test everything through lots of intermediate code. And when something goes wrong it can be any one of 9381 things. You lose much of the benefit of testing while coding. And you already said you're working on the scripts, so its not like that's set in stone. Finally, I wanted to give you another approach rather than just a variant on everyone else's answer.
Schwern