summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgregor herrmann <gregoa@debian.org>2023-07-29 17:14:22 +0200
committergregor herrmann <gregoa@debian.org>2023-07-29 17:14:22 +0200
commit9163869e79db8a666d430b7a48de1855e02ca6e4 (patch)
tree582309ce05b4c2a7bbaf4cbc5efe2b3f4c768b61
parente8d4d78c9499b332006ffc8ed9b5060a9b99135e (diff)
New upstream version 0.12
-rw-r--r--.gitignore22
-rwxr-xr-xChanges29
-rw-r--r--[-rwxr-xr-x]MANIFEST91
-rwxr-xr-xMANIFEST.SKIP (renamed from MANIFEST.skip)2
-rw-r--r--META.json46
-rw-r--r--META.yml33
-rwxr-xr-xMakefile.PL243
-rwxr-xr-xREADME99
-rw-r--r--README.mkdn146
-rwxr-xr-xlib/Test/HTML/Content.pm14
-rwxr-xr-xlib/Test/HTML/Content/NoXPath.pm2
-rwxr-xr-xlib/Test/HTML/Content/XPathExtensions.pm2
-rw-r--r--t/00-load.t20
-rwxr-xr-xt/09-errors.xpath.t172
-rwxr-xr-xt/99-manifest.t22
-rw-r--r--testrules.yml5
-rw-r--r--xt/99-changes.t (renamed from t/99-changes.t)58
-rw-r--r--xt/99-compile.t60
-rw-r--r--xt/99-manifest.t35
-rw-r--r--xt/99-minimumversion.t18
-rw-r--r--[-rwxr-xr-x]xt/99-pod.t (renamed from t/99-Pod.t)11
-rw-r--r--xt/99-synopsis.t58
-rw-r--r--xt/99-test-prerequisites.t122
-rw-r--r--[-rwxr-xr-x]xt/99-todo.t (renamed from t/99-todo.t)25
-rw-r--r--[-rwxr-xr-x]xt/99-unix-text.t (renamed from t/99-unix-text.t)23
-rw-r--r--xt/99-versions.t (renamed from t/99-versions.t)122
-rw-r--r--xt/copyright.t95
-rw-r--r--xt/meta-lint.t48
28 files changed, 1282 insertions, 341 deletions
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 8a5d777..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,22 +0,0 @@
-Thumbs.db
-_Inline
-ccv-src/
-out.png
-blib/
-*.bak
-Makefile
-Makefile.old
-pm_to_blib
-CCV.def
-CCV.inl
-CCV.c
-*.bs
-*.old
-*.o
-dll.base
-dll.exp
-.releaserc
-Test-HTML-Content-*.tar
-Test-HTML-Content-*.tar.gz
-Test-HTML-Content-*/
-MYMETA.* \ No newline at end of file
diff --git a/Changes b/Changes
index 120d455..19723dd 100755
--- a/Changes
+++ b/Changes
@@ -1,6 +1,19 @@
Revision history for Perl extension Test::HTML::Content.
-0.09 20130206
+0.12 2023-07-23
+ - Fix test output/diagnostics between XML::LibXML and XML::Parser
+
+0.11 2023-07-21
+ - Various test suite updates, no code changes, no need to upgrade
+
+0.10 2023-01-20
+ - Add a XML::LibXML version number if we find it installed
+ This is to hunt down spurious test failures with an unknown version
+ of XML::LibXML
+ - Upgrade package/distribution infrastructure
+ - No code changes, no need to upgrade
+
+0.09 2013-02-06
- Apply patch from RT 70099, by gregor herrmann and dom
This fixes bugs in the test suite
<http://bugs.debian.org/cgi-bin /bugreport.cgi?bug=636520>
@@ -8,33 +21,33 @@ Revision history for Perl extension Test::HTML::Content.
- Apply patch from RT 42072 by gyles19@visi.com
This fixes the crash when using XML::XPath instead of XML::LibXML
-0.08 20081112
+0.08 2008-11-12
???
-0.07 20031230
+0.07 2003-12-30
- Fixed test bug reported by Kate Pugh (KAKE):
t/09-errors.xpath.t was missing a SKIP: label
- still no resolution on the other reported errors, as XML::XPath "works"
on my Win32 machine, but dosen't work elsewhere :-(
-
+
Most of the failures seem to be failures due to different text output
of the tests:
-
+
# # <p foo="bar" />
vs.
# # <p foo="bar"/>
but as I can't replicate them here, it's hard to fix those :-(
-0.08 20081112
+0.08 2008-11-12
+ Added $parsing_method to allow XML to be tested as well.
-0.06 20031222
+0.06 2003-12-22
- Fixed tests against XML::XPath
- added another test testing the internal abstraction
API and differences between XML::XPath and XML::LibXML
-0.05 20031204
+0.05 2003-12-04
- Added XPath functionality (xpath_ok, no_xpath, xpath_count)
- Added fallback to old functionality if neither XML::LibXML nor
XML::XPath are available
diff --git a/MANIFEST b/MANIFEST
index 501a4bb..ecd2ae6 100755..100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,41 +1,50 @@
-.gitignore
-Changes
-lib/Test/HTML/Content.pm
-lib/Test/HTML/Content/NoXPath.pm
-lib/Test/HTML/Content/XPathExtensions.pm
-Makefile.PL
-MANIFEST This list of files
-MANIFEST.skip
-META.yml
-README
-t/00-prerequisites.t
-t/01-fallback-libxml.t
-t/01-fallback-pureperl.t
-t/01-fallback-xpath.t
-t/01-internal-api.t
-t/01-libxml-xpath-abstraction.t
-t/01-xpath-query-builder.t
-t/02-tags.t
-t/03-links.t
-t/04-comments.t
-t/05-doctype.t
-t/06-text.t
-t/07-errors.link.t
-t/08-errors.comment.t
-t/09-errors.declaration.t
-t/09-errors.xpath.t
-t/10-errors.text.t
-t/12-title-fallback.t
-t/12-title.t
-t/13-xpath-gracefull-errors.t
-t/99-changes.t
-t/99-manifest.t
-t/99-Pod.t
-t/99-todo.t
-t/99-unix-text.t
-t/99-versions.t
-t/embedded-Test-HTML-Content-NoXPath.t
-t/embedded-Test-HTML-Content-XPathExtensions.t
-t/embedded-Test-HTML-Content.t
-t/testlib.pm
-META.json Module JSON meta-data (added by MakeMaker)
+.gitignore
+Changes
+lib/Test/HTML/Content.pm
+lib/Test/HTML/Content/NoXPath.pm
+lib/Test/HTML/Content/XPathExtensions.pm
+Makefile.PL
+MANIFEST This list of files
+MANIFEST.SKIP
+META.json
+META.yml
+README
+README.mkdn
+t/00-load.t
+t/00-prerequisites.t
+t/01-fallback-libxml.t
+t/01-fallback-pureperl.t
+t/01-fallback-xpath.t
+t/01-internal-api.t
+t/01-libxml-xpath-abstraction.t
+t/01-xpath-query-builder.t
+t/02-tags.t
+t/03-links.t
+t/04-comments.t
+t/05-doctype.t
+t/06-text.t
+t/07-errors.link.t
+t/08-errors.comment.t
+t/09-errors.declaration.t
+t/09-errors.xpath.t
+t/10-errors.text.t
+t/12-title-fallback.t
+t/12-title.t
+t/13-xpath-gracefull-errors.t
+t/embedded-Test-HTML-Content-NoXPath.t
+t/embedded-Test-HTML-Content-XPathExtensions.t
+t/embedded-Test-HTML-Content.t
+t/testlib.pm
+testrules.yml
+xt/99-changes.t
+xt/99-compile.t
+xt/99-manifest.t
+xt/99-minimumversion.t
+xt/99-pod.t
+xt/99-synopsis.t
+xt/99-test-prerequisites.t
+xt/99-todo.t
+xt/99-unix-text.t
+xt/99-versions.t
+xt/copyright.t
+xt/meta-lint.t
diff --git a/MANIFEST.skip b/MANIFEST.SKIP
index eb2e0ef..0b06aea 100755
--- a/MANIFEST.skip
+++ b/MANIFEST.SKIP
@@ -1,3 +1,5 @@
+^\.prove
+^\.github
^blib/
^Makefile$
^Makefile\.old$
diff --git a/META.json b/META.json
index 41d8db8..9498fd1 100644
--- a/META.json
+++ b/META.json
@@ -1,16 +1,16 @@
{
- "abstract" : "unknown",
+ "abstract" : "Perl extension for testing HTML output",
"author" : [
- "unknown"
+ "Max Maischein <corion@cpan.org>"
],
- "dynamic_config" : 1,
- "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150",
+ "dynamic_config" : 0,
+ "generated_by" : "ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010",
"license" : [
- "unknown"
+ "perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
- "version" : "2"
+ "version" : 2
},
"name" : "Test-HTML-Content",
"no_index" : {
@@ -22,22 +22,44 @@
"prereqs" : {
"build" : {
"requires" : {
- "ExtUtils::MakeMaker" : 0
+ "ExtUtils::MakeMaker" : "0"
}
},
"configure" : {
"requires" : {
- "ExtUtils::MakeMaker" : 0
+ "ExtUtils::MakeMaker" : "0"
}
},
"runtime" : {
"requires" : {
- "HTML::TokeParser" : 0,
- "Test::Builder" : 0,
- "Test::More" : 0
+ "HTML::TokeParser" : "0",
+ "Test::Builder" : "0",
+ "Test::More" : "0",
+ "XML::LibXML" : "2.0133",
+ "perl" : "5.006"
+ }
+ },
+ "test" : {
+ "requires" : {
+ "Test::More" : "0"
}
}
},
"release_status" : "stable",
- "version" : "0.09"
+ "resources" : {
+ "bugtracker" : {
+ "web" : "https://github.com/Corion/Test-HTML-Content/issues"
+ },
+ "license" : [
+ "https://dev.perl.org/licenses/"
+ ],
+ "repository" : {
+ "type" : "git",
+ "url" : "git://github.com/Corion/Test-HTML-Content.git",
+ "web" : "https://github.com/Corion/Test-HTML-Content"
+ }
+ },
+ "version" : "0.12",
+ "x_serialization_backend" : "JSON::PP version 4.11",
+ "x_static_install" : 1
}
diff --git a/META.yml b/META.yml
index 2c1d560..79437f7 100644
--- a/META.yml
+++ b/META.yml
@@ -1,24 +1,33 @@
---
-abstract: unknown
+abstract: 'Perl extension for testing HTML output'
author:
- - unknown
+ - 'Max Maischein <corion@cpan.org>'
build_requires:
- ExtUtils::MakeMaker: 0
+ ExtUtils::MakeMaker: '0'
+ Test::More: '0'
configure_requires:
- ExtUtils::MakeMaker: 0
-dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150'
-license: unknown
+ ExtUtils::MakeMaker: '0'
+dynamic_config: 0
+generated_by: 'ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010'
+license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
- version: 1.4
+ version: '1.4'
name: Test-HTML-Content
no_index:
directory:
- t
- inc
requires:
- HTML::TokeParser: 0
- Test::Builder: 0
- Test::More: 0
-version: 0.09
+ HTML::TokeParser: '0'
+ Test::Builder: '0'
+ Test::More: '0'
+ XML::LibXML: '2.0133'
+ perl: '5.006'
+resources:
+ bugtracker: https://github.com/Corion/Test-HTML-Content/issues
+ license: https://dev.perl.org/licenses/
+ repository: git://github.com/Corion/Test-HTML-Content.git
+version: '0.12'
+x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
+x_static_install: 1
diff --git a/Makefile.PL b/Makefile.PL
index 7eb97e5..0a30bda 100755
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -1,23 +1,238 @@
-use ExtUtils::MakeMaker;
+# -*- mode: perl; c-basic-offset: 4; indent-tabs-mode: nil; -*-
+
+use strict;
+use 5.006000;
+use ExtUtils::MakeMaker qw(WriteMakefile);
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
-WriteMakefile(
- 'NAME' => 'Test::HTML::Content',
- 'VERSION_FROM' => 'lib/Test/HTML/Content.pm', # finds $VERSION
- 'PREREQ_PM' => {
- 'Test::Builder' => 0.0,
- 'Test::More' => 0.0,
- 'HTML::TokeParser' => 0.0}, # e.g., Module::Name => 1.1
-);
-use vars qw($have_test_inline);
+# Normalize version strings like 6.30_02 to 6.3002,
+# so that we can do numerical comparisons on it.
+my $eumm_version = $ExtUtils::MakeMaker::VERSION;
+$eumm_version =~ s/_//;
+
+our $have_xml_libxml;
+our @libxml;
BEGIN {
- eval { require Test::Inline;
- $have_test_inline = 1 };
+ eval { require XML::LibXML;
+ $have_xml_libxml = $XML::LibXML::VERSION };
undef $@;
- if (! $have_test_inline) {
- print "Test::Inline is nice for testing the examples, but not necessary\n"
+ if ($have_xml_libxml) {
+ push @libxml, "XML::LibXML" => '2.0133'; # a random recent-ish version
};
};
+my $module = 'Test::HTML::Content';
+(my $main_file = "lib/$module.pm" ) =~ s!::!/!g;
+(my $distbase = $module) =~ s!::!-!g;
+my $distlink = $distbase;
+
+my @tests = map { glob $_ } 't/*.t', 't/*/*.t';
+
+my %module = (
+ NAME => $module,
+ AUTHOR => q{Max Maischein <corion@cpan.org>},
+ VERSION_FROM => $main_file,
+ ABSTRACT_FROM => $main_file,
+ META_MERGE => {
+ "meta-spec" => { version => 2 },
+ resources => {
+ repository => {
+ web => "https://github.com/Corion/$distlink",
+ url => "git://github.com/Corion/$distlink.git",
+ type => 'git',
+ },
+ bugtracker => {
+ web => "https://github.com/Corion/$distbase/issues",
+ # mailto => 'meta-bugs@example.com',
+ },
+ license => "https://dev.perl.org/licenses/",
+ },
+ dynamic_config => 0, # we promise to keep META.* up-to-date
+ x_static_install => 1, # we are pure Perl and don't do anything fancy
+ },
+
+ MIN_PERL_VERSION => '5.006', # I use // in some places
+
+ 'LICENSE'=> 'perl',
+
+ PL_FILES => {},
+ BUILD_REQUIRES => {
+ 'ExtUtils::MakeMaker' => 0,
+ },
+
+ PREREQ_PM => {
+ 'Test::Builder' => 0,
+ 'Test::More' => 0,
+ 'HTML::TokeParser' => 0,
+ @libxml,
+ },
+ TEST_REQUIRES => {
+ 'Test::More' => 0,
+ },
+
+ dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+ clean => { FILES => "$distbase-*" },
+
+ test => { TESTS => join( ' ', @tests ) },
+);
+
+# This is so that we can do
+# require 'Makefile.PL'
+# and then call get_module_info
+
+sub get_module_info { %module }
+
+if( ! caller ) {
+ # I should maybe use something like Shipwright...
+ regen_README($main_file);
+ regen_EXAMPLES() if -d 'examples';
+ WriteMakefile1(get_module_info);
+};
+
1;
+
+sub WriteMakefile1 { #Written by Alexandr Ciornii, version 0.21. Added by eumm-upgrade.
+ my %params=@_;
+ my $eumm_version=$ExtUtils::MakeMaker::VERSION;
+ $eumm_version=eval $eumm_version;
+ die "EXTRA_META is deprecated" if exists $params{EXTRA_META};
+ die "License not specified" if not exists $params{LICENSE};
+ if ($params{BUILD_REQUIRES} and $eumm_version < 6.5503) {
+ #EUMM 6.5502 has problems with BUILD_REQUIRES
+ $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{BUILD_REQUIRES}} };
+ delete $params{BUILD_REQUIRES};
+ }
+ if ($params{TEST_REQUIRES} and $eumm_version < 6.64) {
+ $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{TEST_REQUIRES}} };
+ delete $params{TEST_REQUIRES};
+ }
+ delete $params{CONFIGURE_REQUIRES} if $eumm_version < 6.52;
+ delete $params{MIN_PERL_VERSION} if $eumm_version < 6.48;
+ delete $params{META_MERGE} if $eumm_version < 6.46;
+ delete $params{META_ADD} if $eumm_version < 6.46;
+ delete $params{LICENSE} if $eumm_version < 6.31;
+ delete $params{AUTHOR} if $] < 5.005;
+ delete $params{ABSTRACT_FROM} if $] < 5.005;
+ delete $params{BINARY_LOCATION} if $] < 5.005;
+
+ WriteMakefile(%params);
+}
+
+sub regen_README {
+ # README is the short version that just tells people what this is
+ # and how to install it
+ eval {
+ # Get description
+ my $readme = join "\n",
+ pod_section($_[0], 'NAME', 'no heading' ),
+ pod_section($_[0], 'DESCRIPTION' ),
+ <<INSTALL,
+
+INSTALLATION
+
+This is a Perl module distribution. It should be installed with whichever
+tool you use to manage your installation of Perl, e.g. any of
+
+ cpanm .
+ cpan .
+ cpanp -i .
+
+Consult https://www.cpan.org/modules/INSTALL.html for further instruction.
+Should you wish to install this module manually, the procedure is
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+INSTALL
+ pod_section($_[0], 'REPOSITORY'),
+ pod_section($_[0], 'SUPPORT'),
+ pod_section($_[0], 'TALKS'),
+ pod_section($_[0], 'KNOWN ISSUES'),
+ pod_section($_[0], 'BUG TRACKER'),
+ pod_section($_[0], 'CONTRIBUTING'),
+ pod_section($_[0], 'SEE ALSO'),
+ pod_section($_[0], 'AUTHOR'),
+ pod_section($_[0], 'LICENSE' ),
+ pod_section($_[0], 'COPYRIGHT' ),
+ ;
+ update_file( 'README', $readme );
+ };
+ # README.mkdn is the documentation that will be shown as the main
+ # page of the repository on Github. Hence we recreate the POD here
+ # as Markdown
+ eval {
+ require Pod::Markdown;
+
+ my $parser = Pod::Markdown->new();
+
+ # Read POD from Module.pm and write to README
+ $parser->parse_from_file($_[0]);
+ my $readme_mkdn = <<STATUS . $parser->as_markdown;
+
+[![Windows](https://github.com/Corion/$distbase/workflows/windows/badge.svg)](https://github.com/Corion/$distbase/actions?query=workflow%3Awindows)
+[![MacOS](https://github.com/Corion/$distbase/workflows/macos/badge.svg)](https://github.com/Corion/$distbase/actions?query=workflow%3Amacos)
+[![Linux](https://github.com/Corion/$distbase/workflows/linux/badge.svg)](https://github.com/Corion/$distbase/actions?query=workflow%3Alinux)
+
+STATUS
+ update_file( 'README.mkdn', $readme_mkdn );
+ };
+}
+
+sub pod_section {
+ my( $filename, $section, $remove_heading ) = @_;
+ open my $fh, '<', $filename
+ or die "Couldn't read '$filename': $!";
+
+ my @section =
+ grep { /^=head1\s+$section/.../^=/ } <$fh>;
+
+ # Trim the section
+ if( @section ) {
+ pop @section if $section[-1] =~ /^=/;
+ shift @section if $remove_heading;
+
+ pop @section
+ while @section and $section[-1] =~ /^\s*$/;
+ shift @section
+ while @section and $section[0] =~ /^\s*$/;
+ };
+
+ @section = map { $_ =~ s!^=\w+\s+!!; $_ } @section;
+ return join "", @section;
+}
+
+sub regen_EXAMPLES {
+ my $perl = $^X;
+ if ($perl =~/\s/) {
+ $perl = qq{"$perl"};
+ };
+ (my $example_file = $main_file) =~ s!\.pm$!/Examples.pm!;
+ my $examples = `$perl -w examples/gen_examples_pod.pl`;
+ if ($examples) {
+ warn "(Re)Creating $example_file\n";
+ $examples =~ s/\r\n/\n/g;
+ update_file( $example_file, $examples );
+ };
+};
+
+sub update_file {
+ my( $filename, $new_content ) = @_;
+ my $content;
+ if( -f $filename ) {
+ open my $fh, '<:raw:encoding(UTF-8)', $filename
+ or die "Couldn't read '$filename': $!";
+ local $/;
+ $content = <$fh>;
+ };
+
+ if( $content ne $new_content ) {
+ if( open my $fh, '>:raw:encoding(UTF-8)', $filename ) {
+ print $fh $new_content;
+ } else {
+ warn "Couldn't (re)write '$filename': $!";
+ };
+ };
+}
diff --git a/README b/README
index c99a485..cdf8466 100755
--- a/README
+++ b/README
@@ -1,26 +1,73 @@
-Test::HTML::Content version 0.09
-=========================
-
-This module provides an easy way to test elements of
-generated HTML. It is intended for tests of templating
-systems or generally generated HTML.
-
-INSTALLATION
-
-To install this module type the following:
-
- perl Makefile.PL
- make
- make test
- make install
-
-DEPENDENCIES
-
-HTML::TokeParser to parse the HTML
-Test::Builder to implement test functionality
-
-COPYRIGHT AND LICENCE
-
-This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
-
-Copyright (C) 2002-2013 Max Maischein, corion@cpan.org \ No newline at end of file
+Test::HTML::Content - Perl extension for testing HTML output
+
+DESCRIPTION
+
+This is a module to test the HTML output of your programs in simple
+test scripts. It can test a scalar (presumably containing HTML) for
+the presence (or absence, or a specific number) of tags having (or
+lacking) specific attributes. Unspecified attributes are ignored,
+and the attribute values can be specified as either scalars (meaning
+a match succeeds if the strings are identical) or regular expressions
+(meaning that a match succeeds if the actual attribute value is matched
+by the given RE) or undef (meaning that the attribute must not
+be present).
+
+If you want to specify or test the deeper structure
+of the HTML (for example, META tags within the BODY) or the (textual)
+content of tags, you will have to resort to C<xpath_ok>,C<xpath_count>
+and C<no_xpath>, which take an XPath expression. If you find yourself crafting
+very complex XPath expression to verify the structure of your output, it is
+time to rethink your testing process and maybe use a template based solution
+or simply compare against prefabricated files as a whole.
+
+The used HTML parser is HTML::TokeParser, the used XPath module
+is XML::XPath or XML::LibXML. XML::XPath needs valid xHTML, XML::LibXML
+will try its best to force your code into xHTML, but it is best to
+supply valid xHTML (snippets) to the test functions.
+
+If no XPath parsers/interpreters are available, the tests will automatically
+skip, so your users won't need to install XML::XPath or XML::LibXML. The module
+then falls back onto a crude implementation of the core functions for tags,
+links, comments and text, and the diagnostic output of the tests varies a bit.
+
+The test functionality is derived from L<Test::Builder>, and the export
+behaviour is the same. When you use Test::HTML::Content, a set of
+HTML testing functions is exported into the namespace of the caller.
+
+
+INSTALLATION
+
+This is a Perl module distribution. It should be installed with whichever
+tool you use to manage your installation of Perl, e.g. any of
+
+ cpanm .
+ cpan .
+ cpanp -i .
+
+Consult https://www.cpan.org/modules/INSTALL.html for further instruction.
+Should you wish to install this module manually, the procedure is
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+
+
+
+
+
+
+
+SEE ALSO
+
+perl(1), L<Test::Builder>,L<Test::Simple>,L<Test::HTML::Lint>.
+
+AUTHOR
+
+Max Maischein E<lt>corion@cpan.orgE<gt>
+
+LICENSE
+
+This code may be distributed under the same terms as Perl itself.
+
diff --git a/README.mkdn b/README.mkdn
new file mode 100644
index 0000000..b8a095a
--- /dev/null
+++ b/README.mkdn
@@ -0,0 +1,146 @@
+
+[![Windows](https://github.com/Corion/Test-HTML-Content/workflows/windows/badge.svg)](https://github.com/Corion/Test-HTML-Content/actions?query=workflow%3Awindows)
+[![MacOS](https://github.com/Corion/Test-HTML-Content/workflows/macos/badge.svg)](https://github.com/Corion/Test-HTML-Content/actions?query=workflow%3Amacos)
+[![Linux](https://github.com/Corion/Test-HTML-Content/workflows/linux/badge.svg)](https://github.com/Corion/Test-HTML-Content/actions?query=workflow%3Alinux)
+
+# NAME
+
+Test::HTML::Content - Perl extension for testing HTML output
+
+# SYNOPSIS
+
+ use Test::HTML::Content( tests => 13 );
+
+ $HTML = "<html><title>A test page</title><body><p>Home page</p>
+ <img src='http://www.perl.com/camel.png' alt='camel'>
+ <a href='http://www.perl.com'>Perl</a>
+ <img src='http://www.perl.com/camel.png' alt='more camel'>
+ <!--Hidden message--></body></html>";
+
+ link_ok($HTML,"http://www.perl.com","We link to Perl");
+ no_link($HTML,"http://www.pearl.com","We have no embarassing typos");
+ link_ok($HTML,qr"http://[a-z]+\.perl.com","We have a link to perl.com");
+
+ title_count($HTML,1,"We have one title tag");
+ title_ok($HTML,qr/test/);
+
+ tag_ok($HTML,"img", {src => "http://www.perl.com/camel.png"},
+ "We have an image of a camel on the page");
+ tag_count($HTML,"img", {src => "http://www.perl.com/camel.png"}, 2,
+ "In fact, we have exactly two camel images on the page");
+ no_tag($HTML,"blink",{}, "No annoying blink tags ..." );
+
+ # We can check the textual contents
+ text_ok($HTML,"Perl");
+
+ # We can also check the contents of comments
+ comment_ok($HTML,"Hidden message");
+
+ # Advanced stuff
+
+ # Using a regular expression to match against
+ # tag attributes - here checking there are no ugly styles
+ no_tag($HTML,"p",{ style => qr'ugly$' }, "No ugly styles" );
+
+ # REs also can be used for substrings in comments
+ comment_ok($HTML,qr"[hH]idden\s+mess");
+
+ # and if you have XML::LibXML or XML::XPath, you can
+ # even do XPath queries yourself:
+ xpath_ok($HTML,'/html/body/p','HTML is somewhat wellformed');
+ no_xpath($HTML,'/html/head/p','HTML is somewhat wellformed');
+
+# DESCRIPTION
+
+This is a module to test the HTML output of your programs in simple
+test scripts. It can test a scalar (presumably containing HTML) for
+the presence (or absence, or a specific number) of tags having (or
+lacking) specific attributes. Unspecified attributes are ignored,
+and the attribute values can be specified as either scalars (meaning
+a match succeeds if the strings are identical) or regular expressions
+(meaning that a match succeeds if the actual attribute value is matched
+by the given RE) or undef (meaning that the attribute must not
+be present).
+
+If you want to specify or test the deeper structure
+of the HTML (for example, META tags within the BODY) or the (textual)
+content of tags, you will have to resort to `xpath_ok`,`xpath_count`
+and `no_xpath`, which take an XPath expression. If you find yourself crafting
+very complex XPath expression to verify the structure of your output, it is
+time to rethink your testing process and maybe use a template based solution
+or simply compare against prefabricated files as a whole.
+
+The used HTML parser is HTML::TokeParser, the used XPath module
+is XML::XPath or XML::LibXML. XML::XPath needs valid xHTML, XML::LibXML
+will try its best to force your code into xHTML, but it is best to
+supply valid xHTML (snippets) to the test functions.
+
+If no XPath parsers/interpreters are available, the tests will automatically
+skip, so your users won't need to install XML::XPath or XML::LibXML. The module
+then falls back onto a crude implementation of the core functions for tags,
+links, comments and text, and the diagnostic output of the tests varies a bit.
+
+The test functionality is derived from [Test::Builder](https://metacpan.org/pod/Test%3A%3ABuilder), and the export
+behaviour is the same. When you use Test::HTML::Content, a set of
+HTML testing functions is exported into the namespace of the caller.
+
+## EXPORT
+
+Exports the bunch of test functions :
+
+ link_ok() no_link() link_count()
+ tag_ok() no_tag() tag_count()
+ text_ok no_text() text_count()
+ comment_ok() no_comment() comment_count()
+ xpath_ok() no_xpath() xpath_count()
+ has_declaration() no_declaration()
+
+## CONSIDERATIONS
+
+The module reparses the HTML string every time a test function is called.
+This will make running many tests over the same, large HTML stream relatively
+slow. A possible speedup could be simple minded caching mechanism that keeps the most
+recent HTML stream in a cache.
+
+## CAVEATS
+
+The test output differs between XPath and HTML parsing, because XML::XPath
+delivers the complete node including the content, where my HTML parser only
+delivers the start tag. So don't make your tests depend on the \_exact\_
+output of my tests. It was a pain to do so in my test scripts for this module
+and if you really want to, take a look at the included test scripts.
+
+The title functions `title_ok` and `no_title` rely on the XPath functionality
+and will thus skip if XPath functionality is unavailable.
+
+## BUGS
+
+Currently, if there is text split up by comments, the text will be seen
+as two separate entities, so the following dosen't work :
+
+ is_text( "Hello<!-- brave new--> World", "Hello World" );
+
+Whether this is a real bug or not, I don't know at the moment - most likely,
+I'll modify text\_ok() and siblings to ignore embedded comments.
+
+## TODO
+
+My things on the todo list for this module. Patches are welcome !
+
+- Refactor the code to fold some of the internal routines
+- Implement a cache for the last parsed tree / token sequence
+- Possibly diag() the row/line number for failing tests
+- Allow RE instead of plain strings in the functions (for tags themselves). This
+one is most likely useless.
+
+# LICENSE
+
+This code may be distributed under the same terms as Perl itself.
+
+# AUTHOR
+
+Max Maischein <corion@cpan.org>
+
+# SEE ALSO
+
+perl(1), [Test::Builder](https://metacpan.org/pod/Test%3A%3ABuilder),[Test::Simple](https://metacpan.org/pod/Test%3A%3ASimple),[Test::HTML::Lint](https://metacpan.org/pod/Test%3A%3AHTML%3A%3ALint).
diff --git a/lib/Test/HTML/Content.pm b/lib/Test/HTML/Content.pm
index a08c414..e3778c1 100755
--- a/lib/Test/HTML/Content.pm
+++ b/lib/Test/HTML/Content.pm
@@ -37,7 +37,7 @@ use vars qw( $tidy );
xpath_ok no_xpath xpath_count
);
-$VERSION = '0.09';
+$VERSION = '0.12';
my $Test = Test::Builder->new;
@@ -355,7 +355,11 @@ sub __tag_diag {
for sort keys %$attrs;
if (@$found) {
$Test->diag("Got");
- $Test->diag(" " . $_) for @$found;
+ for my $tag (@$found) {
+ my $vis = "$tag";
+ $vis =~ s!\s*/>\s*$!/>!; # canonicalize between XML::Parser and XML::LibXML
+ $Test->diag(" " . $vis);
+ };
} else {
$Test->diag("Got none");
};
@@ -512,7 +516,11 @@ sub __xpath_diag {
my $phrase = "Expected to find $num nodes matching on '$query'";
if (@$found) {
$Test->diag("Got");
- $Test->diag(" $_") for @$found;
+ for my $tag (@$found) {
+ my $vis = "$tag";
+ $vis =~ s!\s*/>$!/>!; # canonicalize between XML::Parser and XML::LibXML
+ $Test->diag(" $vis");
+ }
} else {
$Test->diag("Got none");
};
diff --git a/lib/Test/HTML/Content/NoXPath.pm b/lib/Test/HTML/Content/NoXPath.pm
index 081a8c3..27f42cc 100755
--- a/lib/Test/HTML/Content/NoXPath.pm
+++ b/lib/Test/HTML/Content/NoXPath.pm
@@ -10,7 +10,7 @@ use HTML::TokeParser;
eval 'use warnings;' if ($] >= 5.006);
use vars qw( $HTML_PARSER_StripsTags $VERSION @exports );
-$VERSION = '0.09';
+$VERSION = '0.12';
BEGIN {
# Check whether HTML::Parser is v3 and delivers the comments starting
diff --git a/lib/Test/HTML/Content/XPathExtensions.pm b/lib/Test/HTML/Content/XPathExtensions.pm
index b620453..6bf5977 100755
--- a/lib/Test/HTML/Content/XPathExtensions.pm
+++ b/lib/Test/HTML/Content/XPathExtensions.pm
@@ -10,7 +10,7 @@ use HTML::TokeParser;
eval 'use warnings;' if ($] >= 5.006);
use vars qw( $HTML_PARSER_StripsTags $VERSION @exports );
-$VERSION = '0.09';
+$VERSION = '0.12';
@exports = qw( matches comment );
diff --git a/t/00-load.t b/t/00-load.t
new file mode 100644
index 0000000..21488f2
--- /dev/null
+++ b/t/00-load.t
@@ -0,0 +1,20 @@
+#!perl
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+require './Makefile.PL';
+my %module = get_module_info();
+
+my $module = $module{ NAME };
+
+require_ok( $module );
+
+diag( sprintf "Testing %s %s, Perl %s", $module, $module->VERSION, $] );
+
+for (sort grep /\.pm\z/, keys %INC) {
+ s/\.pm\z//;
+ s!/!::!g;
+ eval { diag(join(' ', $_, $_->VERSION || '<unknown>')) };
+}
diff --git a/t/09-errors.xpath.t b/t/09-errors.xpath.t
index ecfe4ba..1c33275 100755
--- a/t/09-errors.xpath.t
+++ b/t/09-errors.xpath.t
@@ -1,86 +1,86 @@
-# Test script to test the failure modes of Test::HTML::Content
-use Test::More;
-BEGIN{
- eval {
- require Test::Builder::Tester;
- Test::Builder::Tester->import;
- };
-
- if ($@) {
- plan skip_all => "Test::Builder::Tester required for testing error messages";
- }
-};
-
-BEGIN {
- plan tests => 7;
- use_ok('Test::HTML::Content');
-};
-
-SKIP: {
-
-if (! $Test::HTML::Content::can_xpath) {
- skip "Need XPath functionality to test it", 6;
- exit;
-};
-
-my $HTML = q{<html><head><title>Test</title></head>
-<body>
-<p foo="bar"></p>
-<p foo="foo">1</p>
-<p foo="baz">2</p>
-</body>
-</html>
-};
-
-test_out("not ok 1 - no XPath results found");
-test_fail(+5);
-test_diag(q{Got},
- q{ <p foo="bar"/>},
- q{ <p foo="foo">1</p>},
- q{ <p foo="baz">2</p>});
-xpath_ok($HTML,'//p[@boo]','//p',"no XPath results found");
-test_test("Finding no xpath results where some should be outputs the fallback");
-
-test_out("not ok 1 - no XPath results found");
-test_fail(+2);
-test_diag(q{Got none});
-xpath_ok($HTML,'//p[@boo]',"no XPath results found");
-test_test("Finding no xpath results (implicit)");
-
-test_out("not ok 1 - no XPath results found");
-test_fail(+5);
-test_diag(q{Got},
- q{ <p foo="bar"/>},
- q{ <p foo="foo">1</p>},
- q{ <p foo="baz">2</p>});
-no_xpath($HTML,'//p[@foo]','//p',"no XPath results found");
-test_test("Finding xpath results where none should be outputs the fallback");
-
-test_out("not ok 1 - no XPath results found");
-test_fail(+5);
-test_diag(q{Got},
- q{ <p foo="bar"/>},
- q{ <p foo="foo">1</p>},
- q{ <p foo="baz">2</p>});
-no_xpath($HTML,'//p',"no XPath results found");
-test_test("Finding xpath results (implicit fallback)");
-
-test_out("not ok 1 - no XPath results found");
-test_fail(+5);
-test_diag(q{Got},
- q{ <p foo="bar"/>},
- q{ <p foo="foo">1</p>},
- q{ <p foo="baz">2</p>});
-xpath_count($HTML,'//p',4,"no XPath results found");
-test_test("Too few hits get reported");
-
-test_out("not ok 1 - no XPath results found");
-test_fail(+5);
-test_diag(q{Got},
- q{ <p foo="bar"/>},
- q{ <p foo="foo">1</p>},
- q{ <p foo="baz">2</p>});
-xpath_count($HTML,'//p',2,"no XPath results found");
-test_test("Too many hits get reported");
-
-}; \ No newline at end of file
+# Test script to test the failure modes of Test::HTML::Content
+use Test::More;
+BEGIN{
+ eval {
+ require Test::Builder::Tester;
+ Test::Builder::Tester->import;
+ };
+
+ if ($@) {
+ plan skip_all => "Test::Builder::Tester required for testing error messages";
+ }
+};
+
+BEGIN {
+ plan tests => 7;
+ use_ok('Test::HTML::Content');
+};
+
+SKIP: {
+
+if (! $Test::HTML::Content::can_xpath) {
+ skip "Need XPath functionality to test it", 6;
+ exit;
+};
+
+my $HTML = q{<html><head><title>Test</title></head>
+<body>
+<p foo="bar"></p>
+<p foo="foo">1</p>
+<p foo="baz">2</p>
+</body>
+</html>
+};
+
+test_out("not ok 1 - no XPath results found");
+test_fail(+5);
+test_diag(q{Got},
+ q{ <p foo="bar"/>},
+ q{ <p foo="foo">1</p>},
+ q{ <p foo="baz">2</p>});
+xpath_ok($HTML,'//p[@boo]','//p',"no XPath results found");
+test_test("Finding no xpath results where some should be outputs the fallback");
+
+test_out("not ok 1 - no XPath results found");
+test_fail(+2);
+test_diag(q{Got none});
+xpath_ok($HTML,'//p[@boo]',"no XPath results found");
+test_test("Finding no xpath results (implicit)");
+
+test_out("not ok 1 - no XPath results found");
+test_fail(+5);
+test_diag(q{Got},
+ q{ <p foo="bar"/>},
+ q{ <p foo="foo">1</p>},
+ q{ <p foo="baz">2</p>});
+no_xpath($HTML,'//p[@foo]','//p',"no XPath results found");
+test_test("Finding xpath results where none should be outputs the fallback");
+
+test_out("not ok 1 - no XPath results found");
+test_fail(+5);
+test_diag(q{Got},
+ q{ <p foo="bar"/>},
+ q{ <p foo="foo">1</p>},
+ q{ <p foo="baz">2</p>});
+no_xpath($HTML,'//p',"no XPath results found");
+test_test("Finding xpath results (implicit fallback)");
+
+test_out("not ok 1 - no XPath results found");
+test_fail(+5);
+test_diag(q{Got},
+ q{ <p foo="bar"/>},
+ q{ <p foo="foo">1</p>},
+ q{ <p foo="baz">2</p>});
+xpath_count($HTML,'//p',4,"no XPath results found");
+test_test("Too few hits get reported");
+
+test_out("not ok 1 - no XPath results found");
+test_fail(+5);
+test_diag(q{Got},
+ q{ <p foo="bar"/>},
+ q{ <p foo="foo">1</p>},
+ q{ <p foo="baz">2</p>});
+xpath_count($HTML,'//p',2,"no XPath results found");
+test_test("Too many hits get reported");
+
+};
diff --git a/t/99-manifest.t b/t/99-manifest.t
deleted file mode 100755
index 7d10e17..0000000
--- a/t/99-manifest.t
+++ /dev/null
@@ -1,22 +0,0 @@
-use strict;
-use Test::More;
-
-# Check that MANIFEST and MANIFEST.skip are sane :
-
-use File::Find;
-use File::Spec;
-
-my @files = qw( MANIFEST MANIFEST.skip );
-plan tests => scalar @files * 4;
-
-for my $file (@files) {
- ok(-f $file, "$file exists");
- open F, "<$file"
- or die "Couldn't open $file : $!";
- my @lines = <F>;
- is_deeply([grep(/^$/, @lines)],[], "No empty lines in $file");
- is_deeply([grep(/^\s+$/, @lines)],[], "No whitespace-only lines in $file");
- is_deeply([grep(/^\s*\S\s+$/, @lines)],[],"No trailing whitespace on lines in $file");
- close F;
-};
-
diff --git a/testrules.yml b/testrules.yml
new file mode 100644
index 0000000..b85d163
--- /dev/null
+++ b/testrules.yml
@@ -0,0 +1,5 @@
+---
+# This test suite can be run fully in parallel
+par:
+ - t/*.t
+ - xt/*.t
diff --git a/t/99-changes.t b/xt/99-changes.t
index c4726f5..6f17d78 100644
--- a/t/99-changes.t
+++ b/xt/99-changes.t
@@ -1,28 +1,30 @@
-#!perl -w
-use warnings;
-use strict;
-use File::Find;
-use Test::More tests => 2;
-
-=head1 PURPOSE
-
-This test ensures that the Changes file
-mentions the current version and that a
-release date is mentioned as well
-
-=cut
-
-my $module = 'Test::HTML::Content';
-
-(my $file = $module) =~ s!::!/!g;
-require "$file.pm";
-
-my $version = sprintf '%0.2f', $module->VERSION;
-diag "Checking for version " . $version;
-
-my $changes = do { local $/; open my $fh, 'Changes' or die $!; <$fh> };
-
-ok $changes =~ /^(.*$version.*)$/m, "We find version $version";
-my $changes_line = $1;
-ok $changes_line =~ /$version\s+20\d{6}/, "We find a release date on the same line"
- or diag $changes_line;
+#!perl -w
+use warnings;
+use strict;
+use File::Find;
+use Test::More tests => 2;
+
+=head1 PURPOSE
+
+This test ensures that the Changes file
+mentions the current version and that a
+release date is mentioned as well
+
+=cut
+
+require './Makefile.PL';
+# Loaded from Makefile.PL
+our %module = get_module_info();
+my $module = $module{NAME};
+
+(my $file = $module) =~ s!::!/!g;
+require "$file.pm";
+
+my $version = sprintf '%0.2f', $module->VERSION;
+
+my $changes = do { local $/; open my $fh, 'Changes' or die $!; <$fh> };
+
+ok $changes =~ /^(.*$version.*)$/m, "We find version $version for $module";
+my $changes_line = $1;
+ok $changes_line =~ /$version\s+20\d\d-[01]\d-[0123]\d\b/, "We find a release date on the same line"
+ or diag $changes_line;
diff --git a/xt/99-compile.t b/xt/99-compile.t
new file mode 100644
index 0000000..45dc86f
--- /dev/null
+++ b/xt/99-compile.t
@@ -0,0 +1,60 @@
+#!perl
+use warnings;
+use strict;
+use File::Find;
+use Test::More;
+BEGIN {
+ eval 'use Capture::Tiny ":all"; 1';
+ if ($@) {
+ plan skip_all => "Capture::Tiny needed for testing";
+ exit 0;
+ };
+};
+
+plan 'no_plan';
+
+require './Makefile.PL';
+# Loaded from Makefile.PL
+our %module = get_module_info();
+
+my $last_version = undef;
+
+sub check {
+ #return if (! m{(\.pm|\.pl) \z}xmsi);
+
+ my ($stdout, $stderr, $exit) = capture(sub {
+ system( $^X, '-Mblib', '-c', $_ );
+ });
+
+ s!\s*\z!!
+ for ($stdout, $stderr);
+
+ if( $exit ) {
+ diag $stderr;
+ diag "Exit code: ", $exit;
+ fail($_);
+ } elsif( $stderr ne "$_ syntax OK") {
+ diag $stderr;
+ fail($_);
+ } else {
+ pass($_);
+ };
+}
+
+my @files;
+find({wanted => \&wanted, no_chdir => 1},
+ grep { -d $_ }
+ 'blib/lib', 'examples', 'lib'
+ );
+
+if( my $exe = $module{EXE_FILES}) {
+ push @files, @$exe;
+};
+
+for (@files) {
+ check($_)
+}
+
+sub wanted {
+ push @files, $File::Find::name if /\.p(l|m|od)$/;
+}
diff --git a/xt/99-manifest.t b/xt/99-manifest.t
new file mode 100644
index 0000000..b3ea80a
--- /dev/null
+++ b/xt/99-manifest.t
@@ -0,0 +1,35 @@
+use strict;
+use Test::More;
+
+# Check that MANIFEST and MANIFEST.skip are sane :
+
+use File::Find;
+use File::Spec;
+
+my @files = qw( MANIFEST MANIFEST.SKIP );
+plan tests => scalar @files * 4
+ +1 # MANIFEST existence check
+ +1 # MYMETA.* non-existence check
+ ;
+
+for my $file (@files) {
+ ok(-f $file, "$file exists");
+ open my $fh, '<', $file
+ or die "Couldn't open $file : $!";
+ my @lines = <$fh>;
+ is_deeply([grep(/^$/, @lines)],[], "No empty lines in $file");
+ is_deeply([grep(/^\s+$/, @lines)],[], "No whitespace-only lines in $file");
+ is_deeply([grep(/^\s*\S\s+$/, @lines)],[],"No trailing whitespace on lines in $file");
+
+ if ($file eq 'MANIFEST') {
+ chomp @lines;
+ is_deeply([grep { s/\s.*//; ! -f } @lines], [], "All files in $file exist")
+ or do { diag "$_ is mentioned in $file but doesn't exist on disk" for grep { ! -f } @lines };
+
+ # Exclude some files from shipping
+ is_deeply([grep(/^MYMETA\.(yml|json)$/, @lines)],[],"We don't try to ship MYMETA.* $file");
+ };
+
+ close $fh;
+};
+
diff --git a/xt/99-minimumversion.t b/xt/99-minimumversion.t
new file mode 100644
index 0000000..5100e99
--- /dev/null
+++ b/xt/99-minimumversion.t
@@ -0,0 +1,18 @@
+#!perl -w
+use strict;
+use Test::More;
+
+eval {
+ #require Test::MinimumVersion::Fast;
+ require Test::MinimumVersion;
+ Test::MinimumVersion->import;
+};
+
+my @files;
+
+if ($@) {
+ plan skip_all => "Test::MinimumVersion required for testing minimum Perl version";
+}
+else {
+ all_minimum_version_from_metajson_ok();
+}
diff --git a/t/99-Pod.t b/xt/99-pod.t
index f9bfde6..8bc060a 100755..100644
--- a/t/99-Pod.t
+++ b/xt/99-pod.t
@@ -14,6 +14,10 @@ eval {
Test::Pod->import;
};
+require './Makefile.PL';
+# Loaded from Makefile.PL
+our %module = get_module_info();
+
my @files;
if ($@) {
@@ -24,7 +28,12 @@ elsif ($Test::Pod::VERSION < 0.95) {
}
else {
my $blib = File::Spec->catfile(qw(blib lib));
- find(\&wanted, grep { -d $_ } ($blib, 'bin'));
+ find(\&wanted, grep { -d } ($blib));
+
+ if( my $exe = $module{EXE_FILES}) {
+ push @files, @$exe;
+ };
+
plan tests => scalar @files;
foreach my $file (@files) {
pod_file_ok($file);
diff --git a/xt/99-synopsis.t b/xt/99-synopsis.t
new file mode 100644
index 0000000..a63c84c
--- /dev/null
+++ b/xt/99-synopsis.t
@@ -0,0 +1,58 @@
+use strict;
+use Test::More;
+use File::Spec;
+use File::Find;
+use File::Temp 'tempfile';
+
+require './Makefile.PL';
+# Loaded from Makefile.PL
+our %module = get_module_info();
+
+my @files;
+my $blib = File::Spec->catfile(qw(blib lib));
+find(\&wanted, grep { -d } ($blib));
+
+#if( my $exe = $module{EXE_FILES}) {
+# push @files, @$exe;
+#};
+
+plan tests => scalar @files;
+foreach my $file (@files) {
+ synopsis_file_ok($file);
+}
+
+sub wanted {
+ push @files, $File::Find::name if /\.p(l|m|od)$/
+ and $_ !~ /\bDSL\.pm$/; # we skip that one as it initializes immediately
+}
+
+sub synopsis_file_ok {
+ my( $file ) = @_;
+ my $name = "SYNOPSIS in $file compiles";
+ open my $fh, '<', $file
+ or die "Couldn't read '$file': $!";
+ my @synopsis = map { s!^\s\s!!; $_ } # outdent all code for here-docs
+ grep { /^\s\s/ } # extract all verbatim (=code) stuff
+ grep { /^=head1\s+SYNOPSIS$/.../^=/ } # extract Pod synopsis
+ <$fh>;
+ if( @synopsis ) {
+ my($tmpfh,$tempname) = tempfile();
+ print {$tmpfh} join '', @synopsis;
+ close $tmpfh; # flush it
+ my $output = `$^X -Ilib -c $tempname 2>&1`;
+ if( $output =~ /\ssyntax OK$/ ) {
+ pass $name;
+ } else {
+ fail $name;
+ diag $output;
+ diag $_ for @synopsis;
+ };
+ unlink $tempname
+ or warn "Couldn't clean up $tempname: $!";
+ } else {
+ SKIP: {
+ skip "$file has no SYNOPSIS section", 1;
+ };
+ };
+
+}
diff --git a/xt/99-test-prerequisites.t b/xt/99-test-prerequisites.t
new file mode 100644
index 0000000..74e3d0d
--- /dev/null
+++ b/xt/99-test-prerequisites.t
@@ -0,0 +1,122 @@
+#!perl -w
+
+use warnings;
+use strict;
+use Test::More;
+use Data::Dumper;
+use File::Find;
+
+=head1 DESCRIPTION
+
+This test checks whether all tests still pass when the optional test
+prerequisites for the test are not present.
+
+This is done by using L<Test::Without::Module> to rerun the test while excluding
+the optional prerequisite.
+
+=cut
+
+BEGIN {
+ eval {
+ require CPAN::Meta::Prereqs;
+ require Parse::CPAN::Meta;
+ require Perl::PrereqScanner::Lite;
+ require Module::CoreList;
+ require Test::Without::Module;
+ require Capture::Tiny;
+ Capture::Tiny->import('capture');
+ require Path::Class;
+ Path::Class->import('dir');
+ };
+ if (my $err = $@) {
+ warn "# $err";
+ plan skip_all => "Prerequisite needed for testing is missing";
+ exit 0;
+ };
+};
+
+my @tests;
+if( @ARGV ) {
+ @tests = @ARGV;
+} else {
+ open my $manifest, '<', 'MANIFEST'
+ or die "Couldn't read MANIFEST: $!";
+ @tests = grep { -f $_ } grep { m!^(t/.*\.t|scripts/.*\.pl)$! } map { s!\s*$!!; $_ } <$manifest>
+}
+plan tests => 0+@tests;
+
+my $meta = Parse::CPAN::Meta->load_file('META.json');
+
+# Find what META.* declares
+my $explicit_test_prereqs = CPAN::Meta::Prereqs->new( $meta->{prereqs} )->merged_requirements->as_string_hash;
+my $minimum_perl = $meta->{prereqs}->{runtime}->{requires}->{perl} || 5.006;
+
+sub distributed_packages {
+ my @modules;
+ for( @_ ) {
+ dir($_)->recurse( callback => sub {
+ my( $child ) = @_;
+ if( !$child->is_dir and $child =~ /\.pm$/) {
+ push @modules, ((scalar $child->slurp()) =~ m/^\s*package\s+(?:#.*?\n\s+)*(\w+(?:::\w+)*)\b/msg);
+ }
+ });
+ };
+ map { $_ => $_ } @modules;
+}
+
+# Find what we distribute:
+my %distribution = distributed_packages('blib','t');
+
+my $scanner = Perl::PrereqScanner::Lite->new;
+for my $test_file (@tests) {
+ my $implicit_test_prereqs = $scanner->scan_file($test_file)->as_string_hash;
+ my %missing = %{ $implicit_test_prereqs };
+ #use Data::Dumper;
+ #warn Dumper \%missing;
+
+ for my $p ( keys %missing ) {
+ # remove core modules
+ if( Module::CoreList::is_core( $p, undef, $minimum_perl)) {
+ delete $missing{ $p };
+ #diag "$p is core for $minimum_perl";
+ } else {
+ #diag "$p is not in core for $minimum_perl";
+ };
+ };
+
+ # remove explicit (test) prerequisites
+ for my $k (keys %$explicit_test_prereqs) {
+ delete $missing{ $k };
+ };
+ #warn Dumper $explicit_test_prereqs->as_string_hash;
+
+ # Remove stuff from our distribution
+ for my $k (keys %distribution) {
+ delete $missing{ $k };
+ };
+
+ # If we have no apparent missing prerequisites, we're good
+ my @missing = sort keys %missing;
+
+ # Rerun the test without these modules and see whether it crashes
+ my @failed;
+ for my $candidate (@missing) {
+ diag "Checking that $candidate is not essential";
+ my @cmd = ($^X, "-MTest::Without::Module=$candidate", "-Mblib", '-w', $test_file);
+ my $cmd = join " ", @cmd;
+
+ my ($stdout, $stderr, $exit) = capture {
+ system( @cmd );
+ };
+ if( $exit != 0 ) {
+ push @failed, [ $candidate, [@cmd]];
+ } elsif( $? != 0 ) {
+ push @failed, [ $candidate, [@cmd]];
+ };
+ };
+ is 0+@failed, 0, $test_file
+ or diag Dumper \@failed;
+
+};
+
+done_testing;
diff --git a/t/99-todo.t b/xt/99-todo.t
index 6efd075..cc29dbf 100755..100644
--- a/t/99-todo.t
+++ b/xt/99-todo.t
@@ -3,17 +3,25 @@ use File::Spec;
use File::Find;
use strict;
-# Check that all files do not contain any
+# Check that all files do not contain any
# lines with "XXX" - such markers should
# either have been converted into Todo-stuff
-# or have been resolved.
+# or have been resolved.
# The test was provided by Andy Lester.
+require './Makefile.PL';
+# Loaded from Makefile.PL
+our %module = get_module_info();
+
my @files;
my $blib = File::Spec->catfile(qw(blib lib));
-find(\&wanted, grep { -d $_ } ($blib, 'bin'));
+find(\&wanted, grep { -d } ($blib));
+
+if( my $exe = $module{EXE_FILES}) {
+ push @files, @$exe;
+};
-plan tests => scalar @files;
+plan tests => 2* @files;
foreach my $file (@files) {
source_file_ok($file);
}
@@ -25,7 +33,7 @@ sub wanted {
sub source_file_ok {
my $file = shift;
- open( my $fh, "<", $file ) or die "Can't open $file: $!";
+ open( my $fh, '<', $file ) or die "Can't open $file: $!";
my @lines = <$fh>;
close $fh;
@@ -40,4 +48,9 @@ sub source_file_ok {
if ( !is( scalar @x, 0, "Looking for XXXes in $file" ) ) {
diag( $_ ) for @x;
}
-}
+ @x = grep /<<<|>>>/, @lines;
+
+ if ( !is( scalar @x, 0, "Looking for <<<<|>>>> in $file" ) ) {
+ diag( $_ ) for @x;
+ }
+}
diff --git a/t/99-unix-text.t b/xt/99-unix-text.t
index e99dc2d..f8b6028 100755..100644
--- a/t/99-unix-text.t
+++ b/xt/99-unix-text.t
@@ -7,10 +7,19 @@ use File::Spec;
use File::Find;
use strict;
-my @files;
+my @files = ('Makefile.PL', 'MANIFEST', 'MANIFEST.SKIP', glob 't/*.t');
+
+require './Makefile.PL';
+# Loaded from Makefile.PL
+our %module = get_module_info();
+my @files;
my $blib = File::Spec->catfile(qw(blib lib));
-find(\&wanted, grep { -d $_ } ($blib, 'bin', 't', 'lib'));
+find(\&wanted, grep { -d } ($blib));
+
+if( my $exe = $module{EXE_FILES}) {
+ push @files, @$exe;
+};
plan tests => scalar @files;
foreach my $file (@files) {
@@ -24,15 +33,15 @@ sub wanted {
sub unix_file_ok {
my ($filename) = @_;
local $/;
- open F, "< $filename"
+ open my $fh, '<', $filename
or die "Couldn't open '$filename' : $!\n";
- binmode F;
- my $content = <F>;
-
+ binmode $fh;
+ my $content = <$fh>;
+
my $i;
my @lines = grep { /\x0D\x0A$/sm } map { sprintf "%s: %s\x0A", $i++, $_ } split /\x0A/, $content;
unless (is(scalar @lines, 0,"'$filename' contains no windows newlines")) {
diag $_ for @lines;
};
- close F;
+ close $fh;
};
diff --git a/t/99-versions.t b/xt/99-versions.t
index 49da187..182c956 100644
--- a/t/99-versions.t
+++ b/xt/99-versions.t
@@ -1,51 +1,71 @@
-#!perl -w
-
-# Stolen from ChrisDolan on use.perl.org
-# http://use.perl.org/comments.pl?sid=29264&cid=44309
-
-use warnings;
-use strict;
-use File::Find;
-use Test::More;
-BEGIN {
- eval 'use File::Slurp; 1';
- if ($@) {
- plan skip_all => "File::Slurp needed for testing";
- exit 0;
- };
-};
-
-plan 'no_plan';
-
-my $last_version = undef;
-
-sub check {
- return if (! m{blib/script/}xms && ! m{\.pm \z}xms);
-
- my $content = read_file($_);
-
- # only look at perl scripts, not sh scripts
- return if (m{blib/script/}xms && $content !~ m/\A \#![^\r\n]+?perl/xms);
-
- my @version_lines = $content =~ m/ ( [^\n]* \$VERSION \s* = [^=] [^\n]* ) /gxms;
- if (@version_lines == 0) {
- fail($_);
- }
- for my $line (@version_lines) {
- $line =~ s/^\s+//;
- $line =~ s/\s+$//;
- if (!defined $last_version) {
- $last_version = shift @version_lines;
- diag "Checking for $last_version";
- pass($_);
- } else {
- is($line, $last_version, $_);
- }
- }
-}
-
-find({wanted => \&check, no_chdir => 1}, 'blib');
-
-if (! defined $last_version) {
- fail('Failed to find any files with $VERSION');
-}
+#!perl -w
+
+# Stolen from ChrisDolan on use.perl.org
+# http://use.perl.org/comments.pl?sid=29264&cid=44309
+
+use warnings;
+use strict;
+use File::Find;
+use Test::More;
+
+require './Makefile.PL';
+# Loaded from Makefile.PL
+our %module = get_module_info();
+
+my @files;
+my $blib = File::Spec->catfile(qw(blib lib));
+find(\&wanted, grep { -d } ($blib));
+
+if( my $exe = $module{EXE_FILES}) {
+ push @files, @$exe;
+};
+
+sub read_file {
+ open my $fh, '<', $_[0]
+ or die "Couldn't read '$_[0]': $!";
+ binmode $fh;
+ local $/;
+ <$fh>
+}
+
+sub wanted {
+ push @files, $File::Find::name if /\.p(l|m|od)$/;
+}
+
+plan tests => 0+@files;
+
+my $last_version = undef;
+
+sub check {
+ my $content = read_file($_);
+
+ # only look at perl scripts, not sh scripts
+ return if (m{blib/script/}xms && $content !~ m/\A \#![^\r\n]+?perl/xms);
+
+ # what my version numbers look like
+ my $version = qr/\d+\.\d+/;
+ my @version_lines = grep { defined }
+ $content =~ m/ [^\n]* \$VERSION \s* = \s* ["']($version)['"] | package \s+ \S+ \s+ ($version) \s* ; /gxms;
+ if (@version_lines == 0) {
+ fail($_);
+ }
+ for my $line (@version_lines) {
+ $line =~ s/^\s+//;
+ $line =~ s/\s+$//;
+ if (!defined $last_version) {
+ $last_version = shift @version_lines;
+ diag "Checking for $last_version";
+ pass($_);
+ } else {
+ is($line, $last_version, $_);
+ }
+ }
+}
+
+for (@files) {
+ check();
+};
+
+if (! defined $last_version) {
+ fail('Failed to find any files with $VERSION');
+}
diff --git a/xt/copyright.t b/xt/copyright.t
new file mode 100644
index 0000000..b009058
--- /dev/null
+++ b/xt/copyright.t
@@ -0,0 +1,95 @@
+#!perl
+use warnings;
+use strict;
+use File::Find;
+use Test::More tests => 1;
+use POSIX 'strftime';
+
+my $this_year = strftime '%Y', localtime;
+
+my $last_modified_year = 0;
+
+my $is_checkout = -d '.git';
+
+require './Makefile.PL';
+# Loaded from Makefile.PL
+our %module = get_module_info();
+
+my @files;
+#my $blib = File::Spec->catfile(qw(blib lib));
+find(\&wanted, grep { -d } ('lib'));
+
+if( my $exe = $module{EXE_FILES}) {
+ push @files, @$exe;
+};
+
+sub wanted {
+ push @files, $File::Find::name if /\.p(l|m|od)$/;
+}
+
+sub collect {
+ my( $file ) = @_;
+ note $file;
+ my $modified_ts;
+ if( $is_checkout ) {
+ # diag `git log -1 --pretty="format:%ct" "$file"`;
+ $modified_ts = `git log -1 --pretty="format:%ct" "$file"`;
+ } else {
+ $modified_ts = (stat($_))[9];
+ }
+
+ my $modified_year;
+ if( $modified_ts ) {
+ $modified_year = strftime('%Y', localtime($modified_ts));
+ } else {
+ $modified_year = 1970;
+ };
+
+ open my $fh, '<', $file
+ or die "Couldn't read $file: $!";
+ my @copyright = map {
+ /\bcopyright\b.*?\d{4}-(\d{4})\b/i
+ ? [ $_ => $1 ]
+ : ()
+ }
+ <$fh>;
+ my $copyright = 0;
+ for (@copyright) {
+ $copyright = $_->[1] > $copyright ? $_->[1] : $copyright;
+ };
+
+ return {
+ file => $file,
+ copyright_lines => \@copyright,
+ copyright => $copyright,
+ modified => $modified_year,
+ };
+};
+
+my @results;
+for my $file (@files) {
+ push @results, collect($file);
+};
+
+for my $file (@results) {
+ $last_modified_year = $last_modified_year < $file->{modified}
+ ? $file->{modified}
+ : $last_modified_year;
+};
+
+note "Distribution was last modified in $last_modified_year";
+
+my @out_of_date = grep { $_->{copyright} and $_->{copyright} < $last_modified_year } @results;
+
+if(! is 0+@out_of_date, 0, "All files have a current copyright year ($last_modified_year)") {
+ for my $file (@out_of_date) {
+ diag sprintf "%s modified %d, but copyright is %d", $file->{file}, $file->{modified}, $file->{copyright};
+ diag $_ for map {@$_} @{ $file->{copyright_lines}};
+ };
+ diag q{To fix (in a rough way, please review) run};
+ diag sprintf q{ perl -i -ple 's!(\bcopyright\b.*?\d{4}-)(\d{4})\b!${1}%s!i' %s},
+ $this_year,
+ join ' ',
+ map { $_->{file} } @out_of_date;
+};
+
diff --git a/xt/meta-lint.t b/xt/meta-lint.t
new file mode 100644
index 0000000..4dcbfe7
--- /dev/null
+++ b/xt/meta-lint.t
@@ -0,0 +1,48 @@
+#!perl -w
+
+# Stolen from ChrisDolan on use.perl.org
+# http://use.perl.org/comments.pl?sid=29264&cid=44309
+
+use warnings;
+use strict;
+use File::Find;
+use Test::More;
+
+eval {
+ #require Test::MinimumVersion::Fast;
+ require Parse::CPAN::Meta;
+ Parse::CPAN::Meta->import();
+ require CPAN::Meta::Validator;
+ CPAN::Meta::Validator->VERSION(2.15);
+};
+if ($@) {
+ plan skip_all => "CPAN::Meta::Validator version 2.15 required for testing META files";
+}
+else {
+ plan tests => 4;
+}
+
+use lib '.';
+our %module;
+require 'Makefile.PL';
+# Loaded from Makefile.PL
+%module = get_module_info();
+my $module = $module{NAME};
+
+(my $file = $module) =~ s!::!/!g;
+require "$file.pm";
+
+my $version = sprintf '%0.2f', $module->VERSION;
+
+for my $meta_file ('META.yml', 'META.json') {
+ my $meta = Parse::CPAN::Meta->load_file($meta_file);
+
+ my $cmv = CPAN::Meta::Validator->new( $meta );
+
+ if(! ok $cmv->is_valid, "$meta_file is valid" ) {
+ diag $_ for $cmv->errors;
+ };
+
+ # Also check that the declared version matches the version in META.*
+ is $meta->{version}, $version, "$meta_file version matches module version ($version)";
+};