tags:

views:

43

answers:

3

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  A: 

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.

ysth
The code in App::Prove hands the stuff in -M to App::Prove->_load_extension() which is used to load prove plug ins. Any importing is explicitly done inside App::Prove and it even looks for a special load() method. Its a badly documented feature.
Schwern
@Schwern: but -P is for loading plugins. And I can't believe it an accident that -M applies to not just prove, but also the tests run. Though I guess stranger accidents have happened.
ysth
@ysth: having done some tests printing out `%INC`, it seems that prove -M does *not* load modules into the tests run.
Philip Potter
@ysth Yes, -P is for loading prove "plugins". -M is for loading prove "modules". `prove -M` does not apply the module to the tests, only to prove. The only difference between a prove "module" and a prove "plugin" appears to be that prove will prepend "App::Prove::Plugin::" to a plugin name for you. The code in App::Prove->run is pretty clear on the matter. I agree its designed for maximum confusion.
Schwern
@Schwern: thanks, for some reason I had myself convinced -M was propagating into the run tests, but I was clearly mistaken somehow
ysth
+6  A: 

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.

Schwern
okay, you're right, this is *also* true. Missed the part where prove's `-M` overrides the harness class. Nonetheless, the bug I pointed out seems to exist. :)
hobbs
hmm, investigation shows you are right. `prove -MMoose foo.pl` doesn't load Moose (according to `%INC`), while the `PERL5OPT` trick does.
Philip Potter
+2  A: 

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 :)

hobbs
While this may be a problem with Carp, it doesn't bear on the OP's problem. `prove -MFoo` simply causes Foo to export into App::Prove.
Schwern
Well, OP's problem as posted is that Prove is blowing up because its `verbose` method was trashed. Although I suppose if that didn't happen, it would have blown up differently shortly after.
hobbs
@hobbs if it didn't blow up, it would have silently not worked. You can see this with, say, `prove -MMoose` which will simply not load Moose and run the test regardless. So I was lucky that it blew up spectacularly rather than failing silently :)
Philip Potter