use 5.008001;
use strict;
use warnings;
package Test::FailWarnings;
# ABSTRACT: Add test failures if warnings are caught
our $VERSION = '0.008'; # VERSION
use Test::More 0.86;
use Cwd qw/getcwd/;
use File::Spec;
use Carp;
our $ALLOW_DEPS = 0;
our @ALLOW_FROM = ();
my $ORIG_DIR = getcwd(); # cache in case handler runs after a chdir
sub import {
my ( $class, @args ) = @_;
croak("import arguments must be key/value pairs")
unless @args % 2 == 0;
my %opts = @args;
$ALLOW_DEPS = $opts{'-allow_deps'};
if ( $opts{'-allow_from'} ) {
@ALLOW_FROM =
ref $opts{'-allow_from'} ? @{ $opts{'-allow_from'} } : $opts{'-allow_from'};
}
$SIG{__WARN__} = \&handler;
}
sub handler {
my $msg = shift;
$msg = '' unless defined $msg;
chomp $msg;
my ( $package, $filename, $line ) = _find_source();
# shortcut if ignoring dependencies and warning did not
# come from something local
if ($ALLOW_DEPS) {
$filename = File::Spec->abs2rel( $filename, $ORIG_DIR )
if File::Spec->file_name_is_absolute($filename);
return if $filename !~ /^(?:t|xt|lib|blib)/;
}
return if grep { $package eq $_ } @ALLOW_FROM;
if ( $msg !~ m/at .*? line \d/ ) {
chomp $msg;
$msg = "'$msg' at $filename line $line.";
}
else {
$msg = "'$msg'";
}
my $builder = Test::More->builder;
$builder->ok( 0, "Test::FailWarnings should catch no warnings" )
or $builder->diag("Warning was $msg");
}
sub _find_source {
my $i = 1;
while (1) {
my ( $pkg, $filename, $line ) = caller( $i++ );
return caller( $i - 2 ) unless defined $pkg;
next if $pkg =~ /^(?:Carp|warnings)/;
return ( $pkg, $filename, $line );
}
}
1;
# vim: ts=4 sts=4 sw=4 et:
__END__
=pod
=encoding utf-8
=head1 NAME
Test::FailWarnings - Add test failures if warnings are caught
=head1 VERSION
version 0.008
=head1 SYNOPSIS
Test file:
use strict;
use warnings;
use Test::More;
use Test::FailWarnings;
ok( 1, "first test" );
ok( 1 + "lkadjaks", "add non-numeric" );
done_testing;
Output:
ok 1 - first test
not ok 2 - Test::FailWarnings should catch no warnings
# Failed test 'Test::FailWarnings should catch no warnings'
# at t/bin/main-warn.pl line 7.
# Warning was 'Argument "lkadjaks" isn't numeric in addition (+) at t/bin/main-warn.pl line 7.'
ok 3 - add non-numeric
1..3
# Looks like you failed 1 test of 3.
=head1 DESCRIPTION
This module hooks C<$SIG{__WARN__}> and converts warnings to L<Test::More>
C<fail()> calls. It is designed to be used with C<done_testing>, when you
don't need to know the test count in advance.
Just as with L<Test::NoWarnings>, this does not catch warnings if other things
localize C<$SIG{__WARN__}>, as this is designed to catch I<unhandled> warnings.
=for Pod::Coverage handler
=head1 USAGE
=head2 Overriding C<$SIG{__WARN__}>
On C<import>, C<$SIG{__WARN__}> is replaced with
C<Test::FailWarnings::handler>.
use Test::FailWarnings; # global
If you don't want global replacement, require the module instead and localize
in whatever scope you want.
require Test::FailWarnings;
{
local $SIG{__WARN__} = \&Test::FailWarnings::handler;
# ... warnings will issue fail() here
}
When the handler reports on the source of the warning, it will look past
any calling packages starting with C<Carp> or C<warnings> to try to detect
the real origin of the warning.
=head2 Allowing warnings from dependencies
If you want to ignore failures from outside your own code, you can set
C<$Test::FailWarnings::ALLOW_DEPS> to a true value. You can
do that on the C<use> line with C<< -allow_deps >>.
use Test::FailWarnings -allow_deps => 1;
When true, warnings will only be thrown if they appear to originate from a filename
matching C<< qr/^(?:t|xt|lib|blib)/ >>
=head2 Allowing warnings from specific modules
If you want to white-list specific modules only, you can add their package
names to C<@Test::NoWarnings::ALLOW_FROM>. You can do that on the C<use> line
with C<< -allow_from >>.
use Test::FailWarnings -allow_from => [ qw/Annoying::Module/ ];
=head1 SEE ALSO
=over 4
=item *
L<Test::NoWarnings> -- catches warnings and reports in an C<END> block. Not (yet) friendly with C<done_testing>.
=item *
L<Test::Warnings> -- a replacement for Test::NoWarnings that works with done_testing
=item *
L<Test::Warn> -- test for warnings without triggering failures from this modules
=back
=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
=head1 SUPPORT
=head2 Bugs / Feature Requests
Please report any bugs or feature requests through the issue tracker
at L<https://github.com/dagolden/Test-FailWarnings/issues>.
You will be notified automatically of any progress on your issue.
=head2 Source Code
This is open source software. The code repository is available for
public review and contribution under the terms of the license.
L<https://github.com/dagolden/Test-FailWarnings>
git clone https://github.com/dagolden/Test-FailWarnings.git
=head1 AUTHOR
David Golden <dagolden@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2013 by David Golden.
This is free software, licensed under:
The Apache License, Version 2.0, January 2004
=cut