summaryrefslogtreecommitdiffstats
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/codespell.t76
-rw-r--r--t/codespell/stopwords34
-rw-r--r--t/cppcheck.t51
-rw-r--r--t/cppcheck/cppcheck.supp46
-rw-r--r--t/critic.t131
-rw-r--r--t/critic/perlcriticrc126
-rw-r--r--t/minimum-version.t32
-rw-r--r--t/module-version.t73
-rw-r--r--t/po.t51
-rw-r--r--t/pod-coverage.t77
-rw-r--r--t/pod-spell.t102
-rw-r--r--t/pod.t27
-rw-r--r--t/shellcheck.t74
-rw-r--r--t/strict.t34
-rw-r--r--t/synopsis.t32
-rw-r--r--t/syntax.t48
16 files changed, 1014 insertions, 0 deletions
diff --git a/t/codespell.t b/t/codespell.t
new file mode 100644
index 0000000..d09f49f
--- /dev/null
+++ b/t/codespell.t
@@ -0,0 +1,76 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_command('codespell');
+test_needs_srcdir_switch();
+
+plan tests => 1;
+
+my @codespell_skip = qw(
+ .git
+ *.po
+ *.pot
+ *.gmo
+ *.add
+ *.cache
+ *.policy
+ *~
+ .libs
+ .deps
+ ChangeLog
+ Makefile.in
+ Makefile
+ configure
+ config.*
+ libtool
+ libtool.m4
+ aclocal.m4
+ autom4te.cache
+ _build
+ build-aux
+ build-tree
+ tmp
+);
+my $codespell_skip = join ',', @codespell_skip;
+
+my @codespell_opts = (qw(
+ --ignore-words=t/codespell/stopwords
+), (
+ "--skip=$codespell_skip"
+));
+my $tags = qx(codespell @codespell_opts 2>&1);
+
+# Fixup the output:
+$tags =~ s/^WARNING: Binary file:.*\n//mg;
+$tags =~ s{^\./build-aux/.*\n}{}mg;
+$tags =~ s{^\./man/[a-zA-Z_]+/.*\n}{}mg;
+# XXX: Ignore python-3.8 runtime warnings:
+$tags =~ s{^.*: RuntimeWarning: line buffering .*\n}{}mg;
+$tags =~ s{^\s*file = builtins.open.*\n}{}mg;
+chomp $tags;
+
+my $ok = length $tags == 0;
+
+ok($ok, 'codespell');
+if (not $ok) {
+ diag($tags);
+}
diff --git a/t/codespell/stopwords b/t/codespell/stopwords
new file mode 100644
index 0000000..2360eae
--- /dev/null
+++ b/t/codespell/stopwords
@@ -0,0 +1,34 @@
+accreting
+ba
+bu
+cas
+chage
+chang
+claus
+cmo
+creat
+dof
+files'
+finitel
+fo
+iff
+isnt
+ist
+ists
+lowercased
+mitre
+msdos
+nickle
+objext
+od
+packages'
+process'
+rcall
+readded
+rela
+ro
+synopsys
+te
+thru
+ths
+troup
diff --git a/t/cppcheck.t b/t/cppcheck.t
new file mode 100644
index 0000000..b02661e
--- /dev/null
+++ b/t/cppcheck.t
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_command('cppcheck');
+test_needs_srcdir_switch();
+
+plan tests => 1;
+
+# XXX: We should add the following to @cppcheck_opts, but then cppcheck emits
+# tons of false positives due to not understanding non-returning functions.
+# -DLIBDPKG_VOLATILE_API=1
+# -Ilib
+my @cppcheck_opts = (qw(
+ --quiet --force --error-exitcode=2
+ --inline-suppr
+ --std=c99 --std=c++03
+ --suppressions-list=t/cppcheck/cppcheck.supp
+), (
+ '--enable=warning,performance,portability,style',
+ '--template=\'{file}:{line}: {severity} ({id}): {message}\''
+));
+my $tags = qx(cppcheck @cppcheck_opts . 2>&1);
+
+# Fixup the output:
+chomp $tags;
+
+my $ok = length $tags == 0;
+
+ok($ok, 'cppcheck');
+if (not $ok) {
+ diag($tags);
+}
diff --git a/t/cppcheck/cppcheck.supp b/t/cppcheck/cppcheck.supp
new file mode 100644
index 0000000..a62ae0a
--- /dev/null
+++ b/t/cppcheck/cppcheck.supp
@@ -0,0 +1,46 @@
+//
+// Suppression file for cppcheck.
+///
+
+// We assume that the system has a sane NULL definition.
+varFuncNullUB
+
+// TODO: While perhaps valid there are many to handle right away.
+variableScope
+
+// TODO: While perhaps valid there are many to handle right away.
+constParameter
+
+// TODO: Will fix in 1.22.x.
+badBitmaskCheck:lib/dpkg/fields.c
+identicalInnerCondition:lib/dpkg/fsys-hash.c
+knownConditionTrueFalse:lib/dpkg/tarfn.c
+
+// Ignore, this is an imported module.
+unusedStructMember:lib/compat/obstack.c
+nullPointerArithmetic:lib/compat/obstack.c
+
+// Ignore, the code is used only for its binary artifacts.
+uninitMemberVar:*/Dpkg_Shlibs/patterns.cpp
+
+// BUG: False positive.
+unusedStructMember:lib/dpkg/compress.c
+
+// BUG: False positive, it gets triggered even with -Ilib.
+unknownMacro:lib/dpkg/string.h
+unknownMacro:utils/*.c
+
+// BUG: False positive.
+syntaxError:lib/dpkg/t/*.c
+
+// BUG: False positive, does not understand non-returning functors.
+nullPointerArithmeticRedundantCheck:lib/dpkg/db-fsys-files.c
+nullPointerArithmeticRedundantCheck:lib/dpkg/db-fsys-override.c
+nullPointerRedundantCheck:lib/dpkg/db-fsys-override.c
+nullPointerRedundantCheck:lib/dpkg/ehandle.c
+nullPointerRedundantCheck:src/deb/build.c
+nullPointerRedundantCheck:src/main/enquiry.c
+nullPointerRedundantCheck:src/main/unpack.c
+
+// BUG: False positive, due to our local va_copy().
+va_list_usedBeforeStarted:lib/compat/vasprintf.c
diff --git a/t/critic.t b/t/critic.t
new file mode 100644
index 0000000..acb841f
--- /dev/null
+++ b/t/critic.t
@@ -0,0 +1,131 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_module('Test::Perl::Critic');
+test_needs_srcdir_switch();
+
+my @policies = qw(
+ BuiltinFunctions::ProhibitBooleanGrep
+ BuiltinFunctions::ProhibitLvalueSubstr
+ BuiltinFunctions::ProhibitReverseSortBlock
+ BuiltinFunctions::ProhibitSleepViaSelect
+ BuiltinFunctions::ProhibitStringySplit
+ BuiltinFunctions::ProhibitUniversalCan
+ BuiltinFunctions::ProhibitUniversalIsa
+ BuiltinFunctions::ProhibitUselessTopic
+ BuiltinFunctions::ProhibitVoidGrep
+ BuiltinFunctions::ProhibitVoidMap
+ BuiltinFunctions::RequireBlockGrep
+ BuiltinFunctions::RequireBlockMap
+ BuiltinFunctions::RequireGlobFunction
+ BuiltinFunctions::RequireSimpleSortBlock
+ ClassHierarchies::ProhibitAutoloading
+ ClassHierarchies::ProhibitExplicitISA
+ ClassHierarchies::ProhibitOneArgBless
+ CodeLayout::ProhibitHardTabs
+ CodeLayout::ProhibitQuotedWordLists
+ CodeLayout::ProhibitTrailingWhitespace
+ CodeLayout::RequireConsistentNewlines
+ ControlStructures::ProhibitCStyleForLoops
+ ControlStructures::ProhibitLabelsWithSpecialBlockNames
+ ControlStructures::ProhibitMutatingListFunctions
+ ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions
+ ControlStructures::ProhibitUntilBlocks
+ Documentation::RequirePackageMatchesPodName
+ Documentation::RequirePodSections
+ InputOutput::ProhibitBarewordFileHandles
+ InputOutput::ProhibitInteractiveTest
+ InputOutput::ProhibitJoinedReadline
+ InputOutput::ProhibitOneArgSelect
+ InputOutput::ProhibitReadlineInForLoop
+ InputOutput::ProhibitTwoArgOpen
+ InputOutput::RequireBracedFileHandleWithPrint
+ InputOutput::RequireCheckedOpen
+ InputOutput::RequireEncodingWithUTF8Layer
+ Miscellanea::ProhibitFormats
+ Miscellanea::ProhibitTies
+ Miscellanea::ProhibitUnrestrictedNoCritic
+ Miscellanea::ProhibitUselessNoCritic
+ Modules::ProhibitConditionalUseStatements
+ Modules::ProhibitEvilModules
+ Modules::RequireBarewordIncludes
+ Modules::RequireEndWithOne
+ Modules::RequireExplicitPackage
+ Modules::RequireFilenameMatchesPackage
+ NamingConventions::Capitalization
+ Objects::ProhibitIndirectSyntax
+ RegularExpressions::ProhibitSingleCharAlternation
+ RegularExpressions::ProhibitUnusedCapture
+ RegularExpressions::ProhibitUnusualDelimiters
+ RegularExpressions::ProhibitUselessTopic
+ RegularExpressions::RequireBracesForMultiline
+ RegularExpressions::RequireExtendedFormatting
+ Subroutines::ProhibitAmpersandSigils
+ Subroutines::ProhibitExplicitReturnUndef
+ Subroutines::ProhibitManyArgs
+ Subroutines::ProhibitNestedSubs
+ Subroutines::ProhibitReturnSort
+ Subroutines::ProhibitUnusedPrivateSubroutines
+ Subroutines::ProtectPrivateSubs
+ TestingAndDebugging::ProhibitNoStrict
+ TestingAndDebugging::ProhibitNoWarnings
+ TestingAndDebugging::RequireTestLabels
+ TestingAndDebugging::RequireUseStrict
+ TestingAndDebugging::RequireUseWarnings
+ ValuesAndExpressions::ProhibitCommaSeparatedStatements
+ ValuesAndExpressions::ProhibitComplexVersion
+ ValuesAndExpressions::ProhibitInterpolationOfLiterals
+ ValuesAndExpressions::ProhibitLongChainsOfMethodCalls
+ ValuesAndExpressions::ProhibitMismatchedOperators
+ ValuesAndExpressions::ProhibitMixedBooleanOperators
+ ValuesAndExpressions::ProhibitQuotesAsQuotelikeOperatorDelimiters
+ ValuesAndExpressions::ProhibitSpecialLiteralHeredocTerminator
+ ValuesAndExpressions::ProhibitVersionStrings
+ ValuesAndExpressions::RequireConstantVersion
+ ValuesAndExpressions::RequireQuotedHeredocTerminator
+ ValuesAndExpressions::RequireUpperCaseHeredocTerminator
+ Variables::ProhibitAugmentedAssignmentInDeclaration
+ Variables::ProhibitConditionalDeclarations
+ Variables::ProhibitLocalVars
+ Variables::ProhibitMatchVars
+ Variables::ProhibitPackageVars
+ Variables::ProhibitPerl4PackageNames
+ Variables::ProhibitUnusedVariables
+ Variables::ProtectPrivateVars
+ Variables::RequireLexicalLoopIterators
+ Variables::RequireNegativeIndices
+);
+
+Test::Perl::Critic->import(
+ -profile => 't/critic/perlcriticrc',
+ -verbose => 8,
+ -include => \@policies,
+ -only => 1,
+);
+
+my @files = Test::Dpkg::all_perl_files();
+
+plan tests => scalar @files;
+
+for my $file (@files) {
+ critic_ok($file);
+}
diff --git a/t/critic/perlcriticrc b/t/critic/perlcriticrc
new file mode 100644
index 0000000..e368954
--- /dev/null
+++ b/t/critic/perlcriticrc
@@ -0,0 +1,126 @@
+## Perl Critic configuration file
+severity = 1
+verbose = %f %l:%c (Severity: %s)\n %P (%s)\n near '%r'\n%d\n
+program-extensions = .pl .t
+
+##
+## Tune tests
+##
+
+[Documentation::RequirePodSections]
+lib_sections = NAME | DESCRIPTION | CHANGES
+script_sections = NAME | SYNOPSIS | DESCRIPTION
+
+[RegularExpressions::ProhibitUnusualDelimiters]
+allow_all_brackets = 1
+
+[RegularExpressions::RequireBracesForMultiline]
+allow_all_brackets = 1
+
+[RegularExpressions::RequireExtendedFormatting]
+minimum_regex_length_to_complain_about = 60
+
+[ValuesAndExpressions::ProhibitInterpolationOfLiterals]
+# TODO: switch these to q{} ?
+allow_if_string_contains_single_quote = 1
+
+##
+## Disable tests
+##
+
+# Here complex is meant as long, but long is not necessarily complex.
+[-BuiltinFunctions::ProhibitComplexMappings]
+
+# Needed when generating code.
+[-BuiltinFunctions::ProhibitStringyEval]
+
+# BUG: Bogus check.
+[-Documentation::PodSpelling]
+
+# While this might be good for performance, it is bad for keeping docs updated.
+[-Documentation::RequirePodAtEnd]
+
+# Too many false positives.
+[-CodeLayout::RequireTidyCode]
+
+# Forcing this just turns into noise (depending on the context, it makes sense).
+[-CodeLayout::RequireTrailingCommas]
+
+# These are fine.
+[-ControlStructures::ProhibitCascadingIfElse]
+
+# These are fine, too many in the code base anyway.
+[-ControlStructures::ProhibitPostfixControls]
+
+# These are fine, usually as long as they are not double negations.
+[-ControlStructures::ProhibitUnlessBlocks]
+
+# BUG: Too many false positives.
+[-ControlStructures::ProhibitUnreachableCode]
+
+# TODO: Check it out, add new Dpkg::Program module?
+[-InputOutput::ProhibitBacktickOperators]
+
+# Needed, using <>/<@ARGV> is not correct, Prompt is not a core module.
+[-InputOutput::ProhibitExplicitStdin]
+
+# TODO: Maybe, some of these are part of the public/current API.
+[-Modules::ProhibitAutomaticExportation]
+
+# Here complex is meant as long, but long is not necessarily complex.
+[-Modules::ProhibitExcessMainComplexity]
+
+# BUG: Too many false positives; non-modules all trigger.
+[-Modules::RequireVersionVar]
+
+# These are fine.
+[-NamingConventions::ProhibitAmbiguousNames]
+
+# We work primarily with ASCII, so we need to specify the exact characters
+# to match.
+[-RegularExpressions::ProhibitEnumeratedClasses]
+
+# When . is used in the code it means what it does.
+[-RegularExpressions::RequireDotMatchAnything]
+
+# When ^ or $ are used in the code they mean what they do.
+[-RegularExpressions::RequireLineBoundaryMatching]
+
+# TODO: While valid, these are part of the public/current API.
+[-Subroutines::ProhibitBuiltinHomonyms]
+
+# Needed.
+[-Subroutines::ProhibitSubroutinePrototypes]
+
+# Adding these seems like more noise.
+[-Subroutines::RequireFinalReturn]
+
+# Readers need to know perl, English module is worse.
+[-Variables::ProhibitPunctuationVars]
+
+# Readers need to know perl.
+[-Variables::RequireInitializationForLocalVars]
+
+# BUG: Too many false positives; on ::main and for $ENV, $SIG, $?, $a, $b.
+[-Variables::RequireLocalizedPunctuationVars]
+
+# Readonly is not a core module.
+[-ValuesAndExpressions::ProhibitConstantPragma]
+
+# TODO: Check it out, using other quotes might be less readable and uniform.
+[-ValuesAndExpressions::ProhibitEmptyQuotes]
+
+# Using charnames is less clear.
+[-ValuesAndExpressions::ProhibitEscapedCharacters]
+
+# Used for help output.
+[-ValuesAndExpressions::ProhibitImplicitNewlines]
+
+# Octals are fine.
+[-ValuesAndExpressions::ProhibitLeadingZeros]
+
+# TODO: Check it out, some magic numbers are fine, octals for example.
+[-ValuesAndExpressions::ProhibitMagicNumbers]
+
+# BUG: False positives on long hex numbers, even when separated.
+[-ValuesAndExpressions::RequireNumberSeparators]
diff --git a/t/minimum-version.t b/t/minimum-version.t
new file mode 100644
index 0000000..6511478
--- /dev/null
+++ b/t/minimum-version.t
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_module('Test::MinimumVersion');
+test_needs_srcdir_switch();
+
+my @files = Test::Dpkg::all_perl_files();
+
+plan tests => scalar @files;
+
+for my $file (@files) {
+ minimum_version_ok($file, '5.28.1');
+}
diff --git a/t/module-version.t b/t/module-version.t
new file mode 100644
index 0000000..401f373
--- /dev/null
+++ b/t/module-version.t
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+use Module::Metadata;
+
+test_needs_srcdir_switch();
+
+my @files = Test::Dpkg::all_perl_modules();
+
+plan tests => scalar @files;
+
+sub module_version_ok
+{
+ my $file = shift;
+
+ my $mod = Module::Metadata->new_from_file($file, collect_pod => 1);
+ my $modver = $mod->version();
+ my $podver;
+
+ SKIP: {
+ if ($mod->contains_pod()) {
+ my $in_changes = 0;
+
+ foreach my $sect ($mod->pod_inside) {
+ if ($sect eq 'CHANGES') {
+ $in_changes = 1;
+ next;
+ }
+
+ if ($in_changes and $sect =~ m/^Version ([0-9x.]+)/) {
+ $podver = $1;
+ last;
+ }
+ }
+
+ if (not $in_changes) {
+ fail("module $file does not contain a CHANGES POD section");
+ return;
+ }
+ } else {
+ skip("module $file does not contain POD", 1);
+ }
+
+ if (defined $podver and $podver eq '0.xx') {
+ ok($modver =~ m/^0.\d\d$/,
+ "module $file version $modver is POD version 0.xx");
+ } else {
+ ok($modver eq $podver,
+ "module $file version $modver == POD version $podver");
+ }
+ }
+}
+
+foreach my $file (@files) {
+ module_version_ok($file);
+};
diff --git a/t/po.t b/t/po.t
new file mode 100644
index 0000000..ce48395
--- /dev/null
+++ b/t/po.t
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_command('i18nspector');
+test_needs_srcdir_switch();
+
+my @files = Test::Dpkg::all_po_files();
+
+plan skip_all => 'no PO files distributed' if @files == 0;
+plan tests => scalar @files;
+
+sub po_ok {
+ my $file = shift;
+
+ my $tags = qx(i18nspector \"$file\" 2>&1);
+
+ # Fixup the output:
+ $tags =~ s/^.*\.pot: boilerplate-in-initial-comments .*\n//mg;
+ $tags =~ s/^.*\.po: duplicate-header-field X-POFile-SpellExtra\n//mg;
+ chomp $tags;
+
+ my $ok = length $tags == 0;
+
+ ok($ok, "PO check $file");
+ if (not $ok) {
+ diag($tags);
+ }
+}
+
+for my $file (@files) {
+ po_ok($file);
+}
diff --git a/t/pod-coverage.t b/t/pod-coverage.t
new file mode 100644
index 0000000..113feb2
--- /dev/null
+++ b/t/pod-coverage.t
@@ -0,0 +1,77 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use List::Util qw(any);
+use File::Find;
+use Module::Metadata;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_module('Test::Pod::Coverage');
+test_needs_srcdir_switch();
+
+sub all_pod_modules
+{
+ my @modules_todo = @_;
+ my @modules;
+ my $scan_perl_modules = sub {
+ my $module = $File::Find::name;
+
+ # Only check modules, scripts are documented in man pages.
+ return unless $module =~ s/\.pm$//;
+
+ my $mod = Module::Metadata->new_from_file($File::Find::name);
+
+ # As a first step just check public modules (version > 0.xx).
+ return if $mod->version() =~ m/^0\.\d\d$/;
+
+ $module =~ s{^\Q$File::Find::topdir\E/}{};
+ $module =~ s{/}{::}g;
+
+ return if any { $module eq $_ } @modules_todo;
+
+ push @modules, $module;
+ };
+
+ my %options = (
+ wanted => $scan_perl_modules,
+ no_chdir => 1,
+ );
+ find(\%options, Test::Dpkg::test_get_perl_dirs());
+
+ return @modules;
+}
+
+my @modules_todo = qw(Dpkg::Arch Dpkg::Source::Package);
+my @modules = all_pod_modules(@modules_todo);
+
+plan tests => scalar @modules + scalar @modules_todo;
+
+for my $module (@modules) {
+ pod_coverage_ok($module);
+}
+
+TODO: {
+ local $TODO = 'modules partially documented';
+
+ for my $module (@modules_todo) {
+ pod_coverage_ok($module);
+ }
+}
diff --git a/t/pod-spell.t b/t/pod-spell.t
new file mode 100644
index 0000000..b6bf46b
--- /dev/null
+++ b/t/pod-spell.t
@@ -0,0 +1,102 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_module('Test::Spelling');
+test_needs_command('aspell');
+
+if (qx(aspell dicts) !~ m/en_US/) {
+ plan skip_all => 'aspell en_US dictionary required for spell checking POD';
+}
+
+test_needs_srcdir_switch();
+
+my @files = Test::Dpkg::all_perl_files();
+
+plan tests => scalar @files;
+
+my @aspell_opts = qw(--encoding UTF-8 --lang en_US --personal /dev/null);
+set_spell_cmd("aspell list @aspell_opts");
+add_stopwords(<DATA>);
+
+for my $file (@files) {
+ pod_file_spelling_ok($file);
+}
+
+__DATA__
+CVS
+Devuan
+DSC
+Dpkg
+IPC
+ORed
+OpenPGP
+RCS
+XDG
+archqual
+backport
+buildinfo
+bzip2
+canonicalized
+checksum
+checksums
+cmdline
+debian
+decompressor
+dep
+deps
+dpkg
+dpkg-buildflags
+dpkg-buildpackage
+dpkg-checkbuilddeps
+dpkg-dev
+dpkg-genbuildinfo
+dpkg-genchanges
+dpkg-gencontrol
+dpkg-parsechangelog
+dpkg-mergechangelog
+dsc
+dup'ed
+env
+envvar
+fieldnames
+forceplugin
+ge
+getters
+gettext
+hurd
+keyrings
+le
+libdir
+lzma
+modelines
+multiarch
+nocheck
+qa
+quiesced
+reportfile
+rfc822
+sig
+substvar
+substvars
+unparsed
+update-buildflags
+xz
diff --git a/t/pod.t b/t/pod.t
new file mode 100644
index 0000000..680550a
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs :paths);
+
+test_needs_module('Test::Pod', '1.00');
+test_needs_srcdir_switch();
+
+my @files = Test::Dpkg::all_perl_files();
+
+all_pod_files_ok(@files);
diff --git a/t/shellcheck.t b/t/shellcheck.t
new file mode 100644
index 0000000..fdc6dcc
--- /dev/null
+++ b/t/shellcheck.t
@@ -0,0 +1,74 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_command('shellcheck');
+test_needs_srcdir_switch();
+
+my @todofiles = qw(
+ dselect/methods/disk/install.sh
+ dselect/methods/disk/setup.sh
+ dselect/methods/disk/update.sh
+ dselect/methods/media/install.sh
+ dselect/methods/media/setup.sh
+ dselect/methods/media/update.sh
+);
+my @files = qw(
+ autogen
+ build-aux/get-version
+ build-aux/run-script
+ debian/dpkg.cron.daily
+ debian/dpkg.postrm
+ src/dpkg-db-backup.sh
+ src/dpkg-maintscript-helper.sh
+ src/dpkg-realpath.sh
+);
+my @shellcheck_opts = (
+ '--external-sources', # Allow checking external source files.
+ '--exclude=SC1090', # Allow non-constant source.
+ '--exclude=SC2039', # Allow local keyword.
+ '--exclude=SC2166', # Allow -a and -o.
+ '--exclude=SC2034', # Allow unused variables for colors.
+ '--exclude=SC3043', # Allow local keyword.
+);
+
+plan tests => scalar @files;
+
+sub shell_syntax_ok
+{
+ my $file = shift;
+
+ my $tags = qx(shellcheck @shellcheck_opts $file 2>&1);
+
+ # Fixup the output:
+ chomp $tags;
+
+ my $ok = length $tags == 0;
+
+ ok($ok, 'shellcheck');
+ if (not $ok) {
+ diag($tags);
+ }
+}
+
+foreach my $file (@files) {
+ shell_syntax_ok($file);
+}
diff --git a/t/strict.t b/t/strict.t
new file mode 100644
index 0000000..62c792f
--- /dev/null
+++ b/t/strict.t
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_module('Test::Strict');
+test_needs_srcdir_switch();
+
+eval '$Test::Strict::TEST_WARNINGS = 1';
+
+my @files = Test::Dpkg::all_perl_files();
+
+plan tests => scalar @files * 2;
+
+for my $file (@files) {
+ strict_ok($file);
+ warnings_ok($file);
+}
diff --git a/t/synopsis.t b/t/synopsis.t
new file mode 100644
index 0000000..99835b6
--- /dev/null
+++ b/t/synopsis.t
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_author();
+test_needs_module('Test::Synopsis');
+test_needs_srcdir_switch();
+
+my @files = Test::Dpkg::all_perl_files();
+
+plan tests => scalar @files;
+
+for my $file (@files) {
+ synopsis_ok($file);
+}
diff --git a/t/syntax.t b/t/syntax.t
new file mode 100644
index 0000000..fc20f6c
--- /dev/null
+++ b/t/syntax.t
@@ -0,0 +1,48 @@
+#!/usr/bin/perl
+#
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Dpkg qw(:needs);
+
+test_needs_srcdir_switch();
+
+my @files = Test::Dpkg::all_perl_files();
+
+plan tests => scalar @files;
+
+my $PERL = $ENV{PERL} // $^X // 'perl';
+
+# Detect compilation warnings that are not found with just «use warnings»,
+# such as redefinition of symbols from multiple imports. We cannot use
+# Test::Strict::syntax_ok because it does not pass -w to perl, and does not
+# check for other issues whenever perl states the syntax is ok.
+sub syntax_ok {
+ my $file = shift;
+
+ my $eval = qx($PERL -cw \"$file\" 2>&1);
+ my $ok = ($eval =~ s{^\Q$file\E syntax OK\n$}{}ms) && length $eval == 0;
+
+ ok($ok, "Compilation check $file");
+ if (not $ok) {
+ diag($eval);
+ }
+}
+
+for my $file (@files) {
+ syntax_ok($file);
+}