The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

# Copyright 2011, 2012, 2014 Kevin Ryde

# This file is part of Test-VariousBits.
#
# Test-VariousBits is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 3, or (at your option) any
# later version.
#
# Test-VariousBits is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Test-VariousBits.  If not, see <http://www.gnu.org/licenses/>.

use strict;

# uncomment this to run the ### lines
use Smart::Comments;

my $verbose = 0;
my $ignore_alpha_version_gt = 1;
my $warning_count;
my $stacktraces;
my $stacktraces_count = 0;

sub import {
  my $class = shift;
  foreach my $arg (@_) {
    if ($arg eq '-verbose') {
      $verbose++;
    } else {
      diag("WarnFail unrecognised option: ",$arg);
    }
  }
  $class->install;
}
sub unimport {
  my ($class) = @_;
  $class->uninstall;
}

my $installed = 0;
my $old_warn_handler;

sub install {
  my ($class) = @_;
  if (! $installed) {
    $installed = 1;
    if ($verbose) {
      $class->diag("WarnFail: install __WARN__ handler");
    }
    $old_warn_handler = $SIG{'__WARN__'};
    $SIG{'__WARN__'} = $class->can('warn_handler');
  }
}

sub uninstall {
  my ($class) = @_;
  if ($installed) {
    $installed = 0;

    if (defined $SIG{'__WARN__'}
        && $SIG{'__WARN__'} == $class->can('warn_handler')) {
      if ($verbose) {
        $class->diag("WarnFail restore __WARN__ handler");
      }
      $SIG{'__WARN__'} = $old_warn_handler;
    } else {
      if ($verbose) {
        $class->diag("WarnFail \$SIG{__WARN__} has changed again, cannot restore");
      }
    }
  }
}

sub warn_handler {
  my ($msg) = @_;
  # don't error out for cpan alpha version number warnings
  unless ($ignore_alpha_version_gt
          && defined $msg
          && $msg =~ /^Argument "[0-9._]+" isn't numeric in numeric gt/) {
    $warning_count++;
    if ($stacktraces_count < 3 && eval { require Devel::StackTrace }) {
      $stacktraces_count++;
      $stacktraces .= "\n" . Devel::StackTrace->new->as_string() . "\n";
    }
  }
  if ($old_warn_handler) {
    goto &$old_warn_handler;
  } else {
    warn @_;
  }
}

END {
  __PACKAGE__->end;
}
sub end {
  my ($class) = @_;

  if ($warning_count) {
    $class->diag("Saw $warning_count warning(s):");
    if (defined $stacktraces) {
      $class->diag($stacktraces);
    } else {
      $class->diag('(no backtrace, Devel::StackTrace not available)');
    }
    $class->diag('Exit code 1 for warnings');
    $? ||= 1;

    $warning_count = 0;
    undef $stacktraces;
  }
}

# diag($str, $str, ...)
sub diag {
  if (eval { Test::More->can('diag') }) {
    Test::More::diag (@_);
  } else {
    my $msg = join('', map {defined($_)?$_:'[undef]'} @_)."\n";
    $msg =~ s/^/# /mg;
    print STDERR $msg;
  }
}

1;
__END__