[SCM] Debian package checker branch, master, updated. 2.5.11-16-g3889107
The following commit has been merged in the master branch:
commit 38891070f95d64060a0b124e71272af6e0e5395d
Author: Niels Thykier <niels@thykier.net>
Date: Sat Dec 29 19:42:21 2012 +0100
Test::Lintian: Support finding checks in a dir
Extend some of the tests to accept directory arguments (or omit check
name arguments). In such cases, all checks in those directories will
be processed.
Signed-off-by: Niels Thykier <niels@thykier.net>
diff --git a/lib/Test/Lintian.pm b/lib/Test/Lintian.pm
index 92db686..e6632a5 100644
--- a/lib/Test/Lintian.pm
+++ b/lib/Test/Lintian.pm
@@ -74,14 +74,17 @@ my %known_html_tags = map { $_ => 1 } qw(a em i tt);
=over 4
-=item test_check_desc ([OPTS, ]DESCFILES...)
+=item test_check_desc ([OPTS, ]CHECKS...)
Test check desc files (and the tags in them) for common errors.
-OPTS is an optional HASHREF that determines if some of the tests
-are optional or not. Currently it is unused.
+OPTS is an optional HASHREF containing key/value pairs, which are
+described below.
-DESCFILES is a list of paths in which to check desc files.
+CHECKS is a list of paths in which to check desc files. Any given
+element in CHECKS can be either a file or a dir. Files are assumed to
+be check desc file. Directories are searched and all I<.desc> files
+in those dirs are processed.
As the number of tests depends on the number of tags in desc, it is
difficult to "plan ahead" when using this test. It is therefore
@@ -102,6 +105,19 @@ Lintian itself.
If set to C<undef>, the test of Needs-Info containing only existing
collections will be skipped.
+=item filter
+
+If defined, it is a filter function that examines $_ (or its first
+argument) and returns a truth value if C<$_> should be considered or
+false otherwise. C<$_> will be the path to the current file (or dir)
+in question; it may be relative or absolute.
+
+NB: I<all> elements in CHECKS are subject to the filter.
+
+CAVEAT: If the filter rejects a directory, none of the files in it will be
+considered either. Even if the filter accepts a file, that file will
+only be processed if it has the proper extension (i.e. with I<.desc>).
+
=back
=cut
@@ -110,16 +126,21 @@ sub test_check_desc {
my ($opts, @descs);
my $builder = $CLASS->builder;
my $colldir = '/usr/share/lintian/collection';
+ my $find_opt = {
+ 'filter' => undef,
+ };
+ my $tested = 0;
if (ref $_[0] eq 'HASH') {
$opts = shift;
$colldir = $opts->{'coll-dir'}//'' if exists $opts->{'coll-dir'};
+ $find_opt->{'filter'} = $opts->{'filter'} if exists $opts->{'filter'};
}
$opts //= {};
@descs = @_;
load_profile_for_test ();
- foreach my $desc_file (@descs) {
+ foreach my $desc_file (map { _find_check ($find_opt, $_) } @descs) {
my ($header, @tagpara) = read_dpkg_control ($desc_file);
my $cname = $header->{'check-script'}//'';
my $ctype = $header->{'type'} // '';
@@ -128,6 +149,7 @@ sub test_check_desc {
my $i = 1; # paragraph counter.
$builder->isnt_eq ($cname, '', "Check has a name ($desc_file)");
$cname = '<missing>' if $cname eq '';
+ $tested++;
if ($cname eq 'lintian') {
# skip these two tests for this special case...
@@ -206,6 +228,9 @@ sub test_check_desc {
# TODO: Implement check of Ref (?)
}
}
+
+ $builder->cmp_ok ($tested, '>', 0, 'Tested at least one desc file')
+ if @descs;
}
=item test_load_profiles (ROOT, INC...)
@@ -256,28 +281,73 @@ sub test_load_profiles {
File::Find::find (\%opt, $absdir);
}
-=item test_load_checks (DIR, CHECKNAMES...)
+=item test_load_checks ([OPTS, ]DIR[, CHECKNAMES...])
Test that the Perl module implementation of the checks can be loaded
and has a run sub.
+OPTS is an optional HASHREF containing key/value pairs, which are
+described below.
+
DIR is the directory where the checks can be found.
-CHECKNAMES is a list of check names.
+CHECKNAMES is a list of check names. If CHECKNAMES is given, only the
+checks in this list will be processed. Otherwise, all the checks in
+DIR will be processed.
-For planning purposes, every element in CHECKNAMES counts for 2 tests.
+For planning purposes, every check processed counts for 2 tests and
+the call itself does on additional check. So if CHECKNAMES contains
+10 elements, then 21 tests will be done (2 * 10 + 1). Filtered out
+checks will I<not> be counted.
NB: This will load a profile if one hasn't been loaded already. This
is done to avoid issues loading L<data files|Lintian::Data> in the
package scope of the checks. (see
L</load_profile_for_test ([PROFNAME[, INC...]])>)
+OPTS may contain the following key/value pairs:
+
+=over 4
+
+=item filter
+
+If defined, it is a filter function that examines $_ (or its first
+argument) and returns a truth value if C<$_> should be considered or
+false otherwise. C<$_> will be the path to the current file (or dir)
+in question; it may be relative or absolute.
+
+NB: filter is I<not> used if CHECKNAMES is given.
+
+CAVEAT: If the filter rejects a directory, none of the files in it will be
+considered either. Even if the filter accepts a file, that file will
+only be processed if it has the proper extension (i.e. with I<.desc>).
+
+=back
+
=cut
sub test_load_checks {
- my ($dir, @checknames) = @_;
+ my ($opts, $dir, @checknames);
my $builder = $CLASS->builder;
+ if ($_[0] and ref $_[0] eq 'HASH') {
+ ($opts, $dir, @checknames) = @_;
+ } else {
+ $opts = {};
+ ($dir, @checknames) = @_;
+ }
+
+ unless (@checknames) {
+ my $find_opt = {
+ 'want-check-name' => 1,
+ };
+ $find_opt->{'filter'} = $opts->{'filter'} if exists $opts->{'filter'};
+ @checknames = _find_check ($find_opt, $dir);
+ $builder->cmp_ok (scalar @checknames, '>', 0, 'Found checks to test');
+ } else {
+ $builder->skip ('Given an explicit list of checks');
+ }
+
load_profile_for_test ();
foreach my $checkname (@checknames) {
@@ -314,11 +384,12 @@ sub test_load_checks {
}
}
-=item test_tags_implemented ([OPTS, ], DIR, CHECKNAMES...)
+=item test_tags_implemented ([OPTS, ], DIR[, CHECKNAMES...])
Test a given check implements all the tags listed in its desc file.
-For planning purposes, each check listed in CHECKNAMES counts as one
-test.
+For planning purposes, each check counts as one test and the call
+itself do one additional check. So if 10 checks are tested, the plan
+should account for 11 tests.
This is a simple scan of the source code looking asserting that the
tag names I<appear> (in the actual code part). For a vast majority of
@@ -327,7 +398,10 @@ false-positives and false-negatives - the former can be handled via
"exclude-pattern" (see below).
The DIR argument is the directory in which to find the checks.
-CHECKNAMES is a list of the check names.
+
+CHECKNAMES is a list of the check names. If CHECKNAMES is given, only
+the checks in this list will be processed. Otherwise, all the checks
+in DIR will be processed.
The optional parameter OPTS is a hashref. If passed it must be the
first argument. The following key/value pairs are defined:
@@ -347,6 +421,19 @@ This is useful for avoiding false-positives with cases like:
unless f($x);
}
+=item filter
+
+If defined, it is a filter function that examines $_ (or its first
+argument) and returns a truth value if C<$_> should be considered or
+false otherwise. C<$_> will be the path to the current file (or dir)
+in question; it may be relative or absolute.
+
+NB: filter is I<not> used if CHECKNAMES is given.
+
+CAVEAT: If the filter rejects a directory, none of the files in it will be
+considered either. Even if the filter accepts a file, that file will
+only be processed if it has the proper extension (i.e. with I<.desc>).
+
=back
As mentioned, this test assert that the tag name appears in the code.
@@ -369,6 +456,9 @@ sub test_tags_implemented {
my ($opts, $dir, @checknames);
my $pattern;
my $builder = $CLASS->builder;
+ my $find_opt = {
+ 'want-check-name' => 1,
+ };
if ($_[0] and ref $_[0] eq 'HASH') {
($opts, $dir, @checknames) = @_;
@@ -377,6 +467,17 @@ sub test_tags_implemented {
($dir, @checknames) = @_;
}
+ unless (@checknames) {
+ my $find_opt = {
+ 'want-check-name' => 1,
+ };
+ $find_opt->{'filter'} = $opts->{'filter'} if exists $opts->{'filter'};
+ @checknames = _find_check ($find_opt, $dir);
+ $builder->cmp_ok (scalar @checknames, '>', 0, 'Found checks to test');
+ } else {
+ $builder->skip ('Given an explicit list of checks');
+ }
+
if (exists $opts->{'exclude-pattern'}) {
if (ref $opts->{'exclude-pattern'} eq 'Regexp') {
$pattern = $opts->{'exclude-pattern'};
@@ -464,6 +565,51 @@ sub load_profile_for_test {
}
+sub _find_check {
+ my ($find_opt, $input) = @_;
+ $find_opt//= {};
+ my $filter = $find_opt->{'filter'};
+
+ if ($filter) {
+ local $_ = $input;
+ # filtered out?
+ return () unless $filter->($_);
+ }
+
+ if ( -d $input) {
+ my @result = ();
+ my $regex = undef;
+ if ($find_opt->{'want-check-name'}) {
+ $regex = qr,^\Q$input\E/*,
+ }
+ my $wanted = sub {
+ if (defined $filter) {
+ local $_ = $_;
+ if (not $filter->($_)) {
+ # filtered out; if a dir - filter the
+ # entire dir.
+ $File::Find::prune = 1 if -d $_;
+ return;
+ }
+ }
+ return unless m/\.desc$/ and -f $_;
+ if ($regex) {
+ s/$regex//;
+ s/\.desc$//;
+ }
+ push @result, $_;
+ };
+ my $opt = {
+ 'wanted' => $wanted,
+ 'no_chdir' => 1,
+ };
+ File::Find::find ($opt, $input);
+ return @result;
+ }
+
+ return ($input);
+}
+
=back
=cut
diff --git a/t/scripts/check-descs.t b/t/scripts/check-descs.t
index 5c5c3a5..6e9ac0f 100755
--- a/t/scripts/check-descs.t
+++ b/t/scripts/check-descs.t
@@ -30,6 +30,6 @@ my $opts = {
'coll-dir' => "$ENV{'LINTIAN_ROOT'}/collection",
};
-test_check_desc ($opts, <$ENV{'LINTIAN_ROOT'}/checks/*.desc>);
+test_check_desc ($opts, "$ENV{'LINTIAN_ROOT'}/checks");
done_testing;
diff --git a/t/scripts/check-load.t b/t/scripts/check-load.t
index 93b276e..839f36d 100755
--- a/t/scripts/check-load.t
+++ b/t/scripts/check-load.t
@@ -21,17 +21,20 @@
use strict;
use warnings;
-use Test::More import => ['plan'];
+use Test::More import => ['done_testing'];
use Test::Lintian;
# Test that all checks can be loaded (except lintian.desc, which is
# a special case).
-our @CHECKNAMES = map {
- s,^\Q$ENV{'LINTIAN_ROOT'}\E/checks/(.+)\.desc$,$1,;
- $_
- } (grep { !m,/lintian.desc$, } <$ENV{LINTIAN_ROOT}/checks/*.desc>);
+sub accept_filter {
+ !m,/lintian\.desc$,;
+}
-plan tests => 2 * scalar @CHECKNAMES;
+my $opts = {
+ 'filter' => \&accept_filter,
+};
-test_load_checks ("$ENV{'LINTIAN_ROOT'}/checks", @CHECKNAMES);
+test_load_checks ($opts, "$ENV{'LINTIAN_ROOT'}/checks");
+
+done_testing;
diff --git a/t/scripts/implemented-tags.t b/t/scripts/implemented-tags.t
index a6c0ce4..704fc52 100755
--- a/t/scripts/implemented-tags.t
+++ b/t/scripts/implemented-tags.t
@@ -44,16 +44,17 @@ our $EXCLUDE =
^hardening-.*$
));
-# Find all of the check description files. We'll do one check per
-# description. Exclude "lintian.desc" as it does not have a perl
-# module like other checks.
-our @CHECKNAMES = map {
- s,^\Q$ENV{'LINTIAN_ROOT'}\E/checks/(.+)\.desc$,$1,;
- $_
- } (grep {!m,/lintian\.desc$, } <$ENV{LINTIAN_ROOT}/checks/*.desc>);
+# Exclude "lintian.desc" as it does not have a perl module like other
+# checks.
+sub accept_filter {
+ !m,/lintian\.desc$,;
+}
-plan tests => scalar @CHECKNAMES;
+my $opts = {
+ 'exclude-pattern' => $EXCLUDE,
+ 'filter' => \&accept_filter,
+};
-test_tags_implemented ( {'exclude-pattern' => $EXCLUDE},
- "$ENV{LINTIAN_ROOT}/checks", @CHECKNAMES);
+test_tags_implemented ($opts, "$ENV{LINTIAN_ROOT}/checks");
+done_testing;
--
Debian package checker
Reply to: