Why won't prove accept -MCarp=verbose?

460 Views Asked by At

I ran this test script:

use strict;
use warnings;
use Test::More tests => 3;
use Carp;

ok(1<2);
pass();
fail();
croak "example";

using the command line prove -MCarp=verbose -v foo.pl, and got the following errors:

Subroutine App::Prove::verbose redefined at /opt/ActivePerl-5.12/lib/App/Prove.pm line 407
        App::Prove::_load_extension('App::Prove=HASH(0x683718)', 'Carp=verbose') called at /opt/ActivePerl-5.12/lib/App/Prove.pm line 419
        App::Prove::_load_extensions('App::Prove=HASH(0x683718)', 'ARRAY(0x683850)') called at /opt/ActivePerl-5.12/lib/App/Prove.pm line 481
        App::Prove::run('App::Prove=HASH(0x683718)') called at /opt/ActivePerl-5.12/bin/prove line 11
Undefined subroutine &Carp::verbose called at /opt/ActivePerl-5.12/lib/App/Prove.pm line 484.

If I run it using perl -MCarp=verbose foo.pl there's no problem. What is causing prove to reject verbose Carp? How can I get a full callstack from my tests when they croak without global replacing croak to confess?

3

There are 3 best solutions below

2
On BEST ANSWER

prove -M does not appear to be equivalent to perl -M. It appears to load a prove extension, not load a module into your tests. The docs are totally unclear on this point, but the code in App::Prove is not. So prove -MCarp=verbose imports Carp::verbose into App::Prove causing the problem above.

A simple way to do what you want is to use the PERL5OPT environment variable plus Carp::Always which will turn all warns and dies (and carps and croaks) into stack traces.

PERL5OPT=-MCarp::Always prove ...

This has the added benefit of working in any situation, with or without prove.

5
On

prove has a very different set of command line arguments than perl, being a completely different program?

prove's -M is, I believe, intended for enabling pragmas; Carp actually exports a forward reference to a verbose() subroutine, which interferes with prove's inner workings.

You can create a small module like this:

# Verbme.pm
use Carp;
$Carp::Verbose = 1;

and enable it from prove:

prove -MVerbme -v foo.pl

though.

2
On

Carp uses Exporter's EXPORT_FAIL mechanism to handle the verbose "option" to import, which is pretty much wrong, as Exporter::Heavy will still try to assign *Carp::verbose to *{"$callerpkg::verbose"} despite the fact that it was "failed". Unfortunately, App::Prove has a verbose sub that it depends on to work, and your -M option causes the import to happen within App::Prove. I'm not sure who is to blame here -- Carp for (ab)using EXPORT_FAIL in this way, or Exporter::Heavy for not removing stuff from @imports if it's in the @failed list, but together they're breaking it :)