diff options
author | gregor herrmann <gregoa@debian.org> | 2023-07-29 17:14:22 +0200 |
---|---|---|
committer | gregor herrmann <gregoa@debian.org> | 2023-07-29 17:14:22 +0200 |
commit | 9163869e79db8a666d430b7a48de1855e02ca6e4 (patch) | |
tree | 582309ce05b4c2a7bbaf4cbc5efe2b3f4c768b61 | |
parent | e8d4d78c9499b332006ffc8ed9b5060a9b99135e (diff) |
New upstream version 0.12
-rw-r--r-- | .gitignore | 22 | ||||
-rwxr-xr-x | Changes | 29 | ||||
-rw-r--r--[-rwxr-xr-x] | MANIFEST | 91 | ||||
-rwxr-xr-x | MANIFEST.SKIP (renamed from MANIFEST.skip) | 2 | ||||
-rw-r--r-- | META.json | 46 | ||||
-rw-r--r-- | META.yml | 33 | ||||
-rwxr-xr-x | Makefile.PL | 243 | ||||
-rwxr-xr-x | README | 99 | ||||
-rw-r--r-- | README.mkdn | 146 | ||||
-rwxr-xr-x | lib/Test/HTML/Content.pm | 14 | ||||
-rwxr-xr-x | lib/Test/HTML/Content/NoXPath.pm | 2 | ||||
-rwxr-xr-x | lib/Test/HTML/Content/XPathExtensions.pm | 2 | ||||
-rw-r--r-- | t/00-load.t | 20 | ||||
-rwxr-xr-x | t/09-errors.xpath.t | 172 | ||||
-rwxr-xr-x | t/99-manifest.t | 22 | ||||
-rw-r--r-- | testrules.yml | 5 | ||||
-rw-r--r-- | xt/99-changes.t (renamed from t/99-changes.t) | 58 | ||||
-rw-r--r-- | xt/99-compile.t | 60 | ||||
-rw-r--r-- | xt/99-manifest.t | 35 | ||||
-rw-r--r-- | xt/99-minimumversion.t | 18 | ||||
-rw-r--r--[-rwxr-xr-x] | xt/99-pod.t (renamed from t/99-Pod.t) | 11 | ||||
-rw-r--r-- | xt/99-synopsis.t | 58 | ||||
-rw-r--r-- | xt/99-test-prerequisites.t | 122 | ||||
-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.t | 95 | ||||
-rw-r--r-- | xt/meta-lint.t | 48 |
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 @@ -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 @@ -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$ @@ -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 } @@ -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': $!"; + }; + }; +} @@ -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)"; +}; |