I was debugging some test failures earlier this morning but they were in the middle of a long test script. I thought it'd be nice if I could step through the test with the debugger and just stop whenever a test failed.
Here's the result. A test failure immediately followed by being in the debugger:
not ok 236 14: return $result; DB<1>
DB<1> T . = main::ok(undef) called from file `t/02_methods.t' line 796 . = main::check_tar_extract(ref(Archive::Tar), ref(ARRAY)) called from file `t/02_methods.t' line 507
# Wrap Test::More functions with debugger breakpoints BEGIN { use B;
# Enumerate over all symbols in the main:: symbol table. SYMBOL: for my $symbol ( sort keys %main:: ) {
# Choose only things that are functions. { no strict 'refs'; next SYMBOL if ! defined &$symbol; }
# Get a reference to the function. my $code = do { no strict 'refs'; \&$symbol; };
# Get a B::CV object so I can get at meta-data about the # function. my $cv = B::svref_2object( $code );
# Get the path to the compilation file. Will often be a path like # '/usr/share/perl/5.10/Test/More.pm'. # # To visually inspect other meta-data possibly available about this function: # # use Devel::Peek; # Dump( $code ); # my $src_file = $cv->FILE;
# Select only functions coming from files named like # .../Test/More.pm. next SYMBOL if $src_file !~ m{/Test/More\.pm$};
# Propagate the old function's prototype to the new function. my $prototype = prototype $code; my $new_prototype = defined $prototype ? "($prototype)" : '';
# Generate the new function and replace the old function. The new function # has access to the original via the closed-over $old variable. my $src = qq{ no warnings 'redefine'; sub $symbol $new_prototype {
# Call the original function and get the test pass/fail. my \$result = \$code->( \@_ );
# Debugger single-stepping mode if the test failed. if ( ! \$result ) { \$DB::single = 1; }
# Return the test/failure. return \$result; }
# Compilation of the new function succeeded? 1; }; eval $src or die $@; } }
I've wished I had that sort of module in the past, often when debugging testing modules. Unfortunately, I'm not to the point of being able to pull out the magic B wand and make it do what I want to do yet.
When you DO make it into a module, make the regexp {/Test/(*.)\.pm\z}
, OK? That way it covers any testing module, not just Test::More.
Re:I like, I like...
jjore on 2010-05-09T21:19:33
How about this?
use Debugger::OnTestFailures
-src_rx => qr/^Test::/;Re:I like, I like...
DiamondInTheRough on 2010-05-09T21:41:01
That would be great, thanks!
Thanks, this is a nicely commented way of doing mass-wrapping of stuff. I'll have to keep it in mind for other Evil Things to do...