summaryrefslogtreecommitdiff
path: root/t/99_perlcritic.t
blob: 5bed531dd1eda86a3bd1bc3a54e062e3594659f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!perl

## Run Perl::Critic against the source code and the tests
## This is highly customized, so take with a grain of salt

use 5.006;
use strict;
use warnings;
use Test::More;
use Data::Dumper;

my @testfiles;

if (!$ENV{RELEASE_TESTING}) {
    plan (skip_all =>  'Test skipped unless environment variable RELEASE_TESTING is set');
}
elsif (!eval { require Perl::Critic; 1 }) {
    plan skip_all => 'Could not find Perl::Critic';
}
elsif ($Perl::Critic::VERSION < 0.23) {
    plan skip_all => 'Perl::Critic must be version 0.23 or higher';
}
else {
    opendir my $dir, 't' or die qq{Could not open directory 't': $!\n};
    @testfiles = map { "t/$_" } grep { /^.+\.(t|pl)$/ } readdir $dir;
    closedir $dir;
    plan tests => 5+@testfiles;
}
ok(@testfiles, 'Found files in test directory');

## Check some non-test files
my $critic = Perl::Critic->new(-severity => 1, '-profile-strictness' => 'quiet');

for my $filename (qw{Makefile.PL check_postgres.pl t/CP_Testing.pm}) {

    if ($ENV{TEST_CRITIC_SKIPNONTEST}) {
        pass qq{Skipping non-test file "$filename"};
        next;
    }

    -e $filename or die qq{Could not find "$filename"!};
    open my $oldstderr, '>&', \*STDERR or die 'Could not dupe STDERR';
    close STDERR or die qq{Could not close STDERR: $!};
    my @vio;
    my $ranok = 0;
    eval {
        @vio = $critic->critique($filename);
        $ranok = 1;
    };
    if (! $ranok) {
        pass "Perl::Critic failed for file $filename. Error was: $@\n";
        $@ = undef;
        next;
    }
    open STDERR, '>&', $oldstderr or die 'Could not recreate STDERR'; ## no critic
    close $oldstderr or die qq{Could not close STDERR copy: $!};
    my $vios = 0;
  VIO: for my $v (@vio) {
        my $d = $v->description();
        (my $policy = $v->policy()) =~ s/Perl::Critic::Policy:://;
        my $source = $v->source();

        if ($filename =~ /test/io) {
            next VIO if $policy =~ /RequireArgUnpacking/o
                or $policy =~ /RequireVersionVar/o;
        }

        if ($filename =~ /Makefile/o) {
            next VIO if $policy =~ /RequireVersionVar/o
                or $policy =~ /ProhibitBooleanGrep/o
                or $policy =~ /RequireInterpolationOfMetachars/o;
        }

        $vios++;
        my $f = $v->filename();
        my $l = $v->location();
        my $line = $l->[0];
        diag "\nFile: $f (line $line)\n";
        diag "Vio: $d\n";
        diag "Policy: $policy\n";
        diag "Source: $source\n\n";
    }
    if ($vios) {
        fail qq{ Failed Perl::Critic tests for file "$filename": $vios};
    }
    else {
        pass qq{ File "$filename" passed all Perl::Critic tests};
    }

}

## Specific exclusions for test scripts:
my %ok =
    (yaml => {
              sub => 'meta_spec_ok',
              },
     pod => {
             sub => 'pod_file_ok pod_coverage_ok',
             },
     signature => {
             sub => 'verify SIGNATURE_OK',
             },
);
for my $f (keys %ok) {
    for my $ex (keys %{$ok{$f}}) {
        if ($ex eq 'sub') {
            for my $foo (split /\s+/ => $ok{$f}{sub}) {
                push @{$ok{$f}{OK}} => qr{Subroutine "$foo" (?:is neither|not exported)};
            }
        }
        else {
            die "Unknown exception '$ex'\n";
        }
    }
}

## Allow Test::More subroutines
my $tm = join '|' => (qw/skip plan pass fail is ok diag BAIL_OUT/);
my $testmoreok = qr{Subroutine "$tm" is neither};

## Create a new critic for the tests
$critic = Perl::Critic->new(-severity => 1, '-profile-strictness' => 'quiet');

my $count = 1;
for my $filename (sort @testfiles) {
    -e $filename or die qq{Could not find "$filename"!};

    my @vio = $critic->critique($filename);
    my $vios = 0;
  VIO: for my $v (@vio) {
        my $d = $v->description();
        (my $policy = $v->policy()) =~ s/Perl::Critic::Policy:://;
        my $source = $v->source();
        my $f = $v->filename();

        ## Skip common Test::More subroutines:
        next if $d =~ $testmoreok;

        ## Specific 'test files' exceptions
        next if $policy =~ /Modules::RequireVersionVar/o;

        ## Skip other specific items:
        for my $k (sort keys %ok) {
            next unless $f =~ /$k/;
            for (@{$ok{$k}{OK}}) {
                next VIO if $d =~ $_;
            }
        }

        $vios++;
        my $l = $v->location();
        my $line = $l->[0];
        my $pbp = $v->explanation();
        diag "\nFile: $f (line $line)\n";
        diag "Vio: $d\n";
        diag "Policy: $policy\n";
        diag "Source: $source\n";
        diag "PBP says: $pbp\n\n";
    }
    my $SPACE = ++$count < 9 ? ' ' : '';
    if ($vios) {
        fail qq{${SPACE}Failed Perl::Critic tests for file "$filename": $vios};
    }
    else {
        pass qq{${SPACE}File "$filename" passed all Perl::Critic tests};
    }
}

pass('Finished Perl::Critic testing');

exit;