—package
Test::Kwalitee::Extra;
use
strict;
use
warnings;
# ABSTRACT: Run Kwalitee tests including optional indicators, especially, prereq_matches_use
our
$VERSION
=
'v0.4.0'
;
# VERSION
use
version 0.77;
use
Cwd;
use
Carp;
use
File::Find;
use
File::Spec;
use
Test::Builder;
use
MetaCPAN::Client;
use
Module::CPANTS::Analyse 0.87;
use
Module::CoreList;
sub
_exclude_proper_libs
{
my
$target_ver
= version->parse(
$Module::CPANTS::Analyse::VERSION
);
return
$target_ver
== version->parse(
'0.88'
) ||
$target_ver
> version->parse(
'0.89'
);
}
sub
_init
{
return
{
builder
=> Test::Builder->new,
exclude
=> {
# can not apply already unpacked dist
extractable
=> 1,
extracts_nicely
=> 1,
has_version
=> 1,
has_proper_version
=> 1,
_exclude_proper_libs() ? (
proper_libs
=> 1) : (),
# already dirty in test phase
no_generated_files
=> 1,
manifest_matches_dist
=> 1,
},
include
=> {},
core
=> 1,
optional
=> 1,
experimental
=> 0,
analyser
=> Module::CPANTS::Analyse->new({
distdir
=> cwd(),
dist
=> cwd(),
}),
retry
=> 5,
};
}
sub
_pmu_error_desc
{
my
(
$error
,
$remedy
,
$berror
,
$bremedy
);
my
$ref
= Module::CPANTS::Kwalitee::Prereq->kwalitee_indicators;
foreach
my
$val
(
@$ref
) {
(
$error
,
$remedy
) = @{
$val
}{
qw(error remedy)
}
if
$val
->{name} eq
'prereq_matches_use'
;
(
$berror
,
$bremedy
) = @{
$val
}{
qw(error remedy)
}
if
$val
->{name} eq
'build_prereq_matches_use'
;
}
$error
||=
q{This distribution uses a module or a dist that's not listed as a prerequisite.}
;
$remedy
||=
q{List all used modules in META.yml requires}
;
$berror
||=
q{This distribution uses a module or a dist in its test suite that's not listed as a build prerequisite.}
;
$bremedy
||=
q{List all modules used in the test suite in META.yml build_requires}
;
return
(
$error
,
$remedy
,
$berror
,
$bremedy
);
}
sub
_check_ind
{
my
(
$env
,
$ind
) =
@_
;
return
1
if
$env
->{include}{
$ind
->{name}};
return
0
if
$env
->{exclude}{
$ind
->{name}};
if
(
$ind
->{is_experimental}) {
# experimental
return
$env
->{experimental};
}
elsif
(
$ind
->{is_extra}) {
# optional
return
$env
->{optional};
}
else
{
# core
return
$env
->{core};
}
}
sub
_is_core
{
my
(
$module
,
$minperlver
) =
@_
;
return
0
if
defined
Module::CoreList->removed_from(
$module
);
my
$fr
= Module::CoreList->first_release(
$module
);
return
0
if
!
defined
$fr
;
return
1
if
version->parse(
$minperlver
) >= version->parse(
$fr
);
return
0;
}
sub
_do_test_one
{
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
(
$test
,
$ok
,
$name
,
$error
,
$remedy
,
$more
) =
@_
;
$test
->ok(
$ok
,
$name
);
if
(!
$ok
) {
$test
->diag(
' Detail: '
,
$error
);
$test
->diag(
' Detail: '
,
ref
(
$more
) ?
join
(
', '
,
@$more
) :
$more
)
if
defined
$more
;
$test
->diag(
' Remedy: '
,
$remedy
);
}
}
sub
_is_missing_check_for_old
{
my
(
$uses
,
$prereq
,
$build_prereq
,
$minperlver
,
$mcpan
,
$is_old
,
$packages_not_indexed
,
$missing
,
$bmissing
,
$qerror
) =
@_
;
while
(
my
(
$key
,
$val
) =
each
%$uses
) {
next
if
version::is_lax(
$key
);
# perl version
# Skip packages provided by the distribution but not indexed by CPAN.
next
if
scalar
(
grep
{
$key
eq
$_
}
@$packages_not_indexed
) != 0;
next
if
_is_core(
$key
,
$minperlver
);
next
if
$key
=~ m
'[$@%*&]'
;
# ignore entry including sigil
my
$result
=
eval
{
$mcpan
->module(
$key
) };
if
($@ || !
$result
->distribution) {
$qerror
->{
$key
} = 1;
next
;
}
my
$dist
=
$result
->distribution;
push
@$missing
,
$key
.
' in '
.
$dist
if
$val
->{in_code} &&
$val
->{in_code} != (
$val
->{evals_in_code} || 0) && !
exists
$prereq
->{
$dist
};
push
@$bmissing
,
$key
.
' in '
.
$dist
if
$val
->{in_tests} &&
$val
->{in_tests} != (
$val
->{evals_in_tests} || 0) && !
exists
$build_prereq
->{
$dist
};
}
}
my
%uses_keys
= (
used_in_code
=>
''
,
required_in_code
=>
''
,
used_in_tests
=>
'build'
,
required_in_tests
=>
'build'
);
sub
_is_missing_check_for_new
{
my
(
$uses
,
$prereq
,
$build_prereq
,
$minperlver
,
$mcpan
,
$is_old
,
$packages_not_indexed
,
$missing
,
$bmissing
,
$qerror
) =
@_
;
foreach
my
$uses_keys
(
keys
%uses_keys
) {
while
(
my
(
$key
,
$val
) =
each
%{
$uses
->{
$uses_keys
}}) {
next
if
version::is_lax(
$key
);
# perl version
# Skip packages provided by the distribution but not indexed by CPAN.
next
if
scalar
(
grep
{
$key
eq
$_
}
@$packages_not_indexed
) != 0;
next
if
_is_core(
$key
,
$minperlver
);
next
if
$key
=~ m
'[$@%*&]'
;
# ignore entry including sigil
my
$result
=
eval
{
$mcpan
->module(
$key
) };
if
($@ || !
$result
->distribution) {
$qerror
->{
$key
} = 1;
next
;
}
my
$dist
=
$result
->distribution;
if
(
$uses_keys
{
$uses_keys
} ne
'build'
) {
push
@$missing
,
$key
.
' in '
.
$dist
if
!
exists
$prereq
->{
$dist
};
}
else
{
# build
push
@$bmissing
,
$key
.
' in '
.
$dist
if
!
exists
$build_prereq
->{
$dist
};
}
}
}
}
sub
_do_test_pmu
{
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
(
$env
) =
@_
;
my
(
$error
,
$remedy
,
$berror
,
$bremedy
) = _pmu_error_desc();
my
(
$test
,
$analyser
) = @{
$env
}{
qw(builder analyser)
};
return
if
! _check_ind(
$env
, {
name
=>
'prereq_matches_use'
,
is_extra
=> 1 }) &&
! _check_ind(
$env
, {
name
=>
'build_prereq_matches_use'
,
is_experimental
=> 1 });
my
$minperlver
;
if
(
exists
$env
->{minperlver}) {
$minperlver
=
$env
->{minperlver};
}
else
{
$minperlver
= $];
for
my
$val
(@{
$analyser
->d->{prereq}}) {
if
(
$val
->{requires} eq
'perl'
) {
$minperlver
=
$val
->{version};
last
;
}
}
}
my
$mcpan
= MetaCPAN::Client->new;
my
%qerror
;
my
(
%build_prereq
,
%prereq
);
# NOTE: prereq part is kept in new stash layout of Module::CPANTS::Analyse since 0.93_01
foreach
my
$val
(@{
$analyser
->d->{prereq}}) {
next
if
_is_core(
$val
->{requires},
$minperlver
);
my
$retry
= 0;
my
$result
;
while
(
$retry
<
$env
->{retry}) {
$result
=
eval
{
$mcpan
->module(
$val
->{requires}) };
if
($@ || !
$result
->distribution) {
++
$retry
;
}
else
{
last
;
}
}
if
(
$retry
==
$env
->{retry}) {
$qerror
{
$val
->{requires}} = 1;
next
;
}
$prereq
{
$result
->distribution} = 1
if
$val
->{is_prereq} ||
$val
->{is_optional_prereq};
$build_prereq
{
$result
->{distribution}} = 1
if
$val
->{is_prereq} ||
$val
->{is_build_prereq} ||
$val
->{is_optional_prereq};
}
# NOTE: uses part is changed in new stash layout of Module::CPANTS::Analyse since 0.93_01
my
$is_old
=
grep
{
exists
$analyser
->d->{uses}{
$_
}{module} }
keys
%{
$analyser
->d->{uses}};
# Look at META.yml to determine if the author specified modules provided
# by the distribution that should not be indexed by CPAN.
my
$packages_not_indexed
= _get_packages_not_indexed(
d
=>
$analyser
->d,
distdir
=>
$analyser
->distdir,
is_old
=>
$is_old
,
);
my
(
@missing
,
@bmissing
);
if
(
$is_old
) {
_is_missing_check_for_old(
$analyser
->d->{uses}, \
%prereq
, \
%build_prereq
,
$minperlver
,
$mcpan
,
$is_old
,
$packages_not_indexed
, \
@missing
, \
@bmissing
, \
%qerror
);
}
else
{
_is_missing_check_for_new(
$analyser
->d->{uses}, \
%prereq
, \
%build_prereq
,
$minperlver
,
$mcpan
,
$is_old
,
$packages_not_indexed
, \
@missing
, \
@bmissing
, \
%qerror
);
}
if
(
%qerror
) {
$remedy
=
$bremedy
=
'Fix query error(s) to MetaCPAN.'
;
}
_do_test_one(
$test
, !
%qerror
&&
@missing
== 0,
'prereq_matches_use by '
.__PACKAGE__,
$error
,
$remedy
,
!
%qerror
?
'Missing: '
.
join
(
', '
,
sort
@missing
) :
'Query error: '
.
join
(
' '
,
sort
keys
%qerror
))
if
_check_ind(
$env
, {
name
=>
'prereq_matches_use'
,
is_extra
=> 1 });
_do_test_one(
$test
, !
%qerror
&&
@bmissing
== 0,
'build_prereq_matches_use by '
.__PACKAGE__,
$berror
,
$bremedy
,
!
%qerror
?
'Missing: '
.
join
(
', '
,
sort
@bmissing
) :
'Query error: '
.
join
(
' '
,
sort
keys
%qerror
))
if
_check_ind(
$env
, {
name
=>
'build_prereq_matches_use'
,
is_experimental
=> 1 });
}
# Look at META.yml to determine if the author specified modules provided
# by the distribution that should not be indexed by CPAN.
sub
_get_packages_not_indexed
{
my
(
%args
) =
@_
;
my
$d
=
delete
$args
{
'd'
};
my
$distdir
=
delete
$args
{
'distdir'
};
my
$is_old
=
delete
$args
{
'is_old'
};
# Check if no_index exists in META.yml
my
$meta_yml
=
$d
->{
'meta_yml'
};
return
[]
if
!
defined
$meta_yml
;
my
$no_index
=
$meta_yml
->{
'no_index'
};
return
[]
if
!
defined
$no_index
;
# Get the uses, to determine which ones are no-index internals.
my
$uses
=
$d
->{
'uses'
};
return
[]
if
!
defined
$uses
;
# NOTE: uses part is changed in new stash layout of Module::CPANTS::Analyse since 0.93_01
if
(!
$is_old
) {
my
@uses
;
push
@uses
,
keys
%{
$uses
->{
$_
}}
for
qw[used_in_code required_in_code used_in_tests required_in_tests]
;
$uses
= {
map
{ (
$_
=>
undef
) }
@uses
};
}
my
$packages_not_indexed
= {};
# Find all the files corresponding to the 'file' and 'directory'
# sections of 'no_index'.
my
@files
= ();
if
(
defined
$no_index
->{
'file'
}) {
push
@files
,
map
{ File::Spec->catdir(
$distdir
,
$_
) } @{
$no_index
->{
'file'
}};
}
if
(
defined
$no_index
->{
'directory'
}) {
my
$filter_pm_files
=
sub
{
return
if
$File::Find::name
!~ /\.pm$/;
push
(
@files
,
$File::Find::name
);
};
foreach
my
$directory
(@{
$no_index
->{
'directory'
}}) {
my
$no_meta_directory
= File::Spec->catdir(
$distdir
,
$directory
);
if
(-d
$no_meta_directory
) {
File::Find::find(
$filter_pm_files
,
$no_meta_directory
,
);
}
}
}
# Extract the namespaces from those files.
foreach
my
$file
(
@files
) {
my
@namespaces
= Module::Extract::Namespaces->from_file(
$file
);
foreach
my
$namespace
(
@namespaces
) {
next
if
!
exists
$uses
->{
$namespace
};
$packages_not_indexed
->{
$namespace
} =
undef
;
}
}
# 'package' section of no_index.
if
(
defined
$no_index
->{
'package'
}) {
foreach
my
$package
(@{
$no_index
->{
'package'
}}) {
next
if
!
exists
$uses
->{
$package
};
$packages_not_indexed
->{
$package
} =
undef
;
}
}
# 'namespace' section of no_index.
if
(
defined
$no_index
->{
'namespace'
}) {
foreach
my
$use
(
keys
%$uses
) {
foreach
my
$namespace
(@{
$no_index
->{
'namespace'
}}) {
next
if
$use
!~ /^\Q
$namespace
\E(?:::|$)/;
$packages_not_indexed
->{
$use
} =
undef
;
}
}
}
return
[
sort
keys
%$packages_not_indexed
];
}
sub
_count_tests
{
my
(
$env
) =
@_
;
my
(
$test
,
$analyser
) = @{
$env
}{
qw(builder analyser)
};
my
$count
= 0;
foreach
my
$mod
(@{
$analyser
->mck->generators}) {
foreach
my
$ind
(@{
$mod
->kwalitee_indicators}) {
next
if
$ind
->{needs_db};
next
if
! _check_ind(
$env
,
$ind
);
++
$count
;
}
}
# overrides needs_db
++
$count
if
_check_ind(
$env
, {
name
=>
'prereq_matches_use'
,
is_extra
=> 1 });
++
$count
if
_check_ind(
$env
, {
name
=>
'build_prereq_matches_use'
,
is_experimental
=> 1 });
return
$count
;
}
sub
_do_test
{
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
(
$env
) =
@_
;
my
(
$test
,
$analyser
) = @{
$env
}{
qw(builder analyser)
};
if
(!
$env
->{no_plan}) {
$test
->plan(
tests
=> _count_tests(
@_
));
}
foreach
my
$mod
(@{
$analyser
->mck->generators}) {
$mod
->analyse(
$analyser
);
foreach
my
$ind
(@{
$mod
->kwalitee_indicators}) {
next
if
$ind
->{needs_db};
next
if
! _check_ind(
$env
,
$ind
);
_do_test_one(
$test
,
$ind
->{code}(
$analyser
->d,
$ind
),
$ind
->{name}.
' by '
.
$mod
,
$ind
->{error},
$ind
->{remedy},
$analyser
->d->{error}{
$ind
->{name}}
);
}
}
_do_test_pmu(
$env
);
}
my
%class
= (
core
=> 1,
optional
=> 1,
experimental
=> 1 );
sub
import
{
my
(
$pkg
,
@arg
) =
@_
;
my
$env
= _init();
my
$ind_seen
= 0;
while
(
my
$arg
=
shift
@arg
) {
if
(
$arg
eq
':no_plan'
) {
$env
->{no_plan} = 1;
}
elsif
(
$arg
eq
':minperlver'
) {
$env
->{minperlver} =
shift
@arg
;
}
elsif
(
$arg
eq
':retry'
) {
$env
->{retry} =
shift
@arg
;
}
elsif
(
$arg
=~ /^!:/) {
warn
"Tag $arg appears after indicator"
if
$ind_seen
;
$arg
=~ s/^!://;
if
(
$arg
eq
'all'
) {
$env
->{core} =
$env
->{optional} =
$env
->{experimental} = 1;
}
elsif
(
$arg
eq
'none'
) {
$env
->{core} =
$env
->{optional} =
$env
->{experimental} = 0;
}
elsif
(
$class
{
$arg
}) {
$env
->{
$arg
} = 0;
}
else
{
warn
"Unknown tag :$arg is used"
;
}
}
elsif
(
$arg
=~ /^:/) {
warn
"Tag $arg appears after indicator"
if
$ind_seen
;
$arg
=~ s/^://;
if
(
$arg
eq
'all'
) {
$env
->{core} =
$env
->{optional} =
$env
->{experimental} = 0;
}
elsif
(
$arg
eq
'none'
) {
$env
->{core} =
$env
->{optional} =
$env
->{experimental} = 1;
}
elsif
(
$class
{
$arg
}) {
$env
->{
$arg
} = 1;
}
else
{
warn
"Unknown tag :$arg is used"
;
}
}
elsif
(
$arg
=~ /^!/) {
$ind_seen
= 1;
$arg
=~ s/^!//;
$env
->{exclude}{
$arg
} = 1;
delete
$env
->{include}{
$arg
};
}
else
{
$ind_seen
= 1;
$env
->{include}{
$arg
} = 1;
delete
$env
->{exclude}{
$arg
};
}
}
_do_test(
$env
);
}
1;
__END__
=pod
=head1 NAME
Test::Kwalitee::Extra - Run Kwalitee tests including optional indicators, especially, prereq_matches_use
=head1 VERSION
version v0.4.0
=head1 SYNOPSIS
# Simply use, with excluding indicators
use Test::Kwalitee::Extra qw(!has_example !metayml_declares_perl_version);
# Use with eval guard, with excluding class
use Test::More;
eval { require Test::Kwalitee::Extra; Test::Kwalitee::Extra->import(qw(!:optional)); };
plan( skip_all => "Test::Kwalitee::Extra not installed: $@; skipping") if $@;
# Typically, this test is limited to author test or release test
BEGIN { # limited to release test
unless ($ENV{RELEASE_TESTING}) { # or $ENV{AUTHOR_TESTING} for author test
require Test::More;
Test::More::plan(skip_all => 'these tests are for release candidate testing');
}
}
use Test::More;
eval { require Test::Kwalitee::Extra; Test::Kwalitee::Extra->import(qw(!:optional)); };
plan( skip_all => "Test::Kwalitee::Extra not installed: $@; skipping") if $@;
# Avoid network access
use Test::Kwalitee::Extra qw(!prereq_matches_use);
# or, when experimental enabled
use Test::Kwalitee::Extra qw(:experimental !prereq_matches_use !build_prereq_matches_use);
=head1 DESCRIPTION
L<CPANTS|http://cpants.cpanauthors.org/> checks Kwalitee indicators, which is not quality but automatically-measurable indicators how good your distribution is. L<Module::CPANTS::Analyse> calcluates Kwalitee but it is not directly applicable to your module test. CPAN has already had L<Test::Kwalitee> for the test module of Kwalitee. It is, however, impossible to calculate C<prereq_matches_use> indicator, because dependent module L<Module::CPANTS::Analyse> itself cannot calculate C<prereq_matches_use> indicator. It is marked as C<needs_db> which means pre-calculated module database is necessary, but only limited information is needed to calculate the indicator. This module calculate C<prereq_matches_use> to query needed information to L<MetaCPAN site|https://metacpan.org/> online.
For available indicators, see L</INDICATORS> section.
=head1 OPTIONS
You can specify including or excluding an indicator or a tag like L<Exporter>. Valid tags are C<core>, C<optional> and C<experimental>. For indicators, see L<Module::CPANTS::Analyse>.
Please NOTE that to specify tags are handled a bit differently from L<Exporter>. First, specifying an indicator is always superior to specifying tags, even though specifying an indicator is prior to specifying tags.
For example,
use Test::Kwalitee::Extra qw(!has_example :optional);
C<!has_example> is in effect, that is C<has_example> is excluded, even though C<has_example> is an C<optional> indicator.
Second, default excluded indicators mentioned in L</INDICATORS> section are not included by specifying tags. For example, in the above example, C<:optional> does not enable C<is_prereq>. You can override it by explicitly specifying the indicator:
use Test::Kwalitee::Extra qw(manifest_matches_dist);
=head2 SPECIAL TAGS
Some tags have special meanings.
=head2 C<:no_plan>
If specified, do not call C<Test::Builder::plan>. You may need to specify it, if this test is embedded into other tests.
=head2 C<:minperlver> <C<version>>
C<prereq_matches_use> indicator ignores core modules. What modules are in core, however, is different among perl versions. If minimum perl version is specified in META.yml or such a meta information, it is used as minimum perl version. Otherewise, C<$]>, the version of the current perl interpreter, is used.
If specified, this option overrides them.
=head2 C<:retry> <C<count>>
The number of retry to query to MetaCPAN. This is related with C<prereq_matches_use> and C<build_prereq_matches_use> indicators only.
Defaults to 5.
=head1 CAVEATS
An optional indicator C<prereq_matches_use> and an experimental indicator C<build_prereq_matches_use> require HTTP access to L<MetaCPAN site|https://metacpan.org/>. If you want to avoid it, you can specify excluded indicators like
# Avoid network access
use Test::Kwalitee::Extra qw(!prereq_matches_use);
# or, when experimental enabled
use Test::Kwalitee::Extra qw(:experimental !prereq_matches_use !build_prereq_matches_use);
Or mitigate wait by tentative failures to reduce retry counts like
# Try just one time for each query
use Test::Kwalitee::Extra qw(:retry 1);
=head1 INDICATORS
In L<Module::CPANTS::Analyse>, C<prereq_matches_use> requires CPANTS DB setup by L<Module::CPANTS::ProcessCPAN>. C<is_prereq> really requires information of prereq of other modules but C<prereq_matches_use> only needs mappings between modules and dists. So, this module query the mappings to MetaCPAN by using L<MetaCPAN::Client>.
Recently, L<Module::CPANTS::Analyse> has been changed much. For actual available indicators, please consult C<Module::CPANTS::Kwalitee::*> documentation. For default configuration, indicators are treated as follows:
=over 4
=item NOTES
=over 4
=item B<(+)>
No longer available for L<Module::CPANTS::Analyse> 0.88 or 0.90+.
=item B<(++)>
No longer available for L<Module::CPANTS::Analyse> 0.90+.
=item B<(+++)>
No longer available for L<Module::CPANTS::Analyse> 0.88 or 0.90+, moved to L<Module::CPANTS::SiteKwalitee|https://github.com/cpants/Module-CPANTS-SiteKwalitee>.
=item B<(++++)>
No longer available for L<Module::CPANTS::Analyse> 0.88 or 0.90+, moved to L<Module::CPANTS::SiteKwalitee|https://github.com/cpants/Module-CPANTS-SiteKwalitee> but supported by this module.
=back
=item Available indicators in core
=over 4
=item *
has_readme
=item *
has_manifest
=item *
has_meta_yml
=item *
has_buildtool
=item *
has_changelog
=item *
no_symlinks
=item *
has_tests
=item *
buildtool_not_executable B<(++)>
=item *
metayml_is_parsable
=item *
metayml_has_license B<(optional for 0.88 or 0.90+)>
=item *
metayml_conforms_to_known_spec
=item *
proper_libs B<(for 0.87 or 0.89)>
=item *
no_pod_errors B<(+)>
=item *
has_working_buildtool B<(+)>
=item *
has_better_auto_install B<(+)>
=item *
use_strict
=item *
valid_signature B<(+++)>
=item *
has_humanreadable_license B<(for 0.87 or 0.89)> | has_human_redable_license B<(for 0.88 or 0.90+)>
=item *
no_cpants_errors B<(+)>
=back
=item Available indicators in optional
=over 4
=item *
has_tests_in_t_dir
=item *
has_example B<(+)>
=item *
no_stdin_for_prompting
=item *
metayml_conforms_spec_current
=item *
metayml_declares_perl_version
=item *
prereq_matches_use B<(++++)>
=item *
use_warnings
=item *
has_test_pod B<(+)>
=item *
has_test_pod_coverage B<(+)>
=back
=item Excluded indicators in core
=over 4
=item Can not apply already unpacked dist
=over 4
=item *
extractable B<(+)>
=item *
extracts_nicely B<(+)>
=item *
has_version B<(+)>
=item *
has_proper_version B<(+)>
=back
=item Already dirty in test phase
=over 4
=item *
manifest_matches_dist
=item *
no_generated_files B<(++)>
=back
=back
=item Excluded indicators in optional
=over 4
=item Can not apply already unpacked dist
=over 4
=item *
proper_libs B<(for 0.88 or 0.90+)>
=back
=item Needs CPANTS DB
=over 4
=item *
is_prereq B<(+++)>
=back
=back
=item Indicators with special note in experimental
=over 4
=item *
build_prereq_matches_use B<(++++)>
=back
=back
=head1 SEE ALSO
=over 4
=item *
L<Module::CPANTS::Analyse> - Kwalitee indicators, except for prereq_matches_use, are calculated by this module.
=item *
L<Test::Kwalitee> - Another test module for Kwalitee indicators.
=item *
L<Dist::Zilla::Plugin::Test::Kwalitee::Extra> - Dist::Zilla plugin for this module.
=back
=head1 AUTHOR
Yasutaka ATARASHI <yakex@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by Yasutaka ATARASHI.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut