From 3c1765559bf150e264a404e8e2f7bd97d6c49339 Mon Sep 17 00:00:00 2001 From: Paul Cochrane Date: Tue, 3 Oct 2017 17:44:01 +0200 Subject: [PATCH 1/6] Refactor indexer tests to allow for extension The way the indexer tests were skipping the tests if `Search::Indexer` wasn't available didn't allow further tests to be easily written. By putting the check for `Search::Indexer` into a `BEGIN` block, we can still skip all tests if `Search::Indexer` isn't available, however we can also more easily add tests to this test suite. --- t/indexer.t | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/t/indexer.t b/t/indexer.t index 5d27a3a..b7512b0 100644 --- a/t/indexer.t +++ b/t/indexer.t @@ -3,15 +3,16 @@ use strict; use warnings; -use Test::More tests => 1; +use Test::More; - -SKIP: { - eval {require Search::Indexer}; - skip "Search::Indexer does not seem to be installed", 1 - if $@; - use_ok( 'Pod::POM::Web::Indexer' ); +BEGIN { + eval {require Search::Indexer}; + plan skip_all => 'Search::Indexer does not seem to be installed' + if $@; } +plan tests => 1; + +use_ok( 'Pod::POM::Web::Indexer' ); # TODO ... more than just a compile test From 0887ba9f8d16416dbc9462676934af022f06630f Mon Sep 17 00:00:00 2001 From: Paul Cochrane Date: Tue, 3 Oct 2017 18:08:54 +0200 Subject: [PATCH 2/6] Add tests of uri_escape() function So that its behaviour is specified as part of the test suite. --- t/indexer.t | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/t/indexer.t b/t/indexer.t index b7512b0..0c575fa 100644 --- a/t/indexer.t +++ b/t/indexer.t @@ -11,8 +11,25 @@ BEGIN { if $@; } -plan tests => 1; +plan tests => 2; use_ok( 'Pod::POM::Web::Indexer' ); +subtest "uri escape" => sub { + plan tests => 3; + is(uri_escape(), undef, "An escaped, undefined URI stays undefined"); + + my $plain_uri = "http://example.com"; + is(uri_escape($plain_uri), $plain_uri, "Plain URI remains unchanged"); + + my $unescaped_uri = "http://example.com/^;/?:\@,Az0-_.!~*'()"; + my $escaped_uri = "http://example.com/%5E;/?:\@,Az0-_.!~*'()"; + is(uri_escape($unescaped_uri), $escaped_uri, "Non-standard characters escaped in URI"); +}; + +sub uri_escape { + my $uri = shift; + return Pod::POM::Web::Indexer::uri_escape($uri); +} + # TODO ... more than just a compile test From 66ab99a75a8664a1645db9332157d188af3cf0bc Mon Sep 17 00:00:00 2001 From: Paul Cochrane Date: Tue, 3 Oct 2017 20:42:43 +0200 Subject: [PATCH 3/6] Add explicit constructor for Indexer objects ... this will allow us to move some of the global variables into the object, thus making tests easier to control. --- lib/Pod/POM/Web/Indexer.pm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/Pod/POM/Web/Indexer.pm b/lib/Pod/POM/Web/Indexer.pm index 7fca9ae..fa4f2b6 100644 --- a/lib/Pod/POM/Web/Indexer.pm +++ b/lib/Pod/POM/Web/Indexer.pm @@ -81,6 +81,15 @@ my @stopwords = ( ); +sub new { + my ($class, $request, $response, $options) = @_; + + my $self = $class->SUPER::new($request, $response, $options); + + return $self; +} + + #---------------------------------------------------------------------- # RETRIEVING #---------------------------------------------------------------------- From 01a9d3c672844b210b143a79e090d6d395a02045 Mon Sep 17 00:00:00 2001 From: Paul Cochrane Date: Tue, 3 Oct 2017 20:43:42 +0200 Subject: [PATCH 4/6] Move global $index_dir var into Indexer constructor Now we can set this variable to e.g. a temporary directory in tests so that we can start testing the indexing code. --- lib/Pod/POM/Web/Indexer.pm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/Pod/POM/Web/Indexer.pm b/lib/Pod/POM/Web/Indexer.pm index fa4f2b6..df9d79d 100644 --- a/lib/Pod/POM/Web/Indexer.pm +++ b/lib/Pod/POM/Web/Indexer.pm @@ -28,8 +28,6 @@ my $ignore_headings = qr[ SYNOPSIS | DESCRIPTION | METHODS | FUNCTIONS | BUGS | AUTHOR | SEE\ ALSO | COPYRIGHT | LICENSE ]x; -(my $index_dir = __FILE__) =~ s[Indexer\.pm$][index]; - my $id_regex = qr/(?![0-9]) # don't start with a digit \w\w+ # start with 2 or more word chars .. (?:::\w+)* # .. and possibly ::some::more::components @@ -85,6 +83,7 @@ sub new { my ($class, $request, $response, $options) = @_; my $self = $class->SUPER::new($request, $response, $options); + ($self->{index_dir} = __FILE__) =~ s[Indexer\.pm$][index]; return $self; } @@ -99,7 +98,7 @@ sub full_text { my ($self, $search_string) = @_; my $indexer = eval { - new Search::Indexer(dir => $index_dir, + new Search::Indexer(dir => $self->{index_dir}, wregex => $wregex, preMatch => '[[', postMatch => ']]'); @@ -271,6 +270,7 @@ sub index { if grep {!/^-(from_scratch|max_size|positions)$/} keys %options; # make sure index dir exists + my $index_dir = $self->{index_dir}; -d $index_dir or mkdir $index_dir or die "mkdir $index_dir: $!"; # if -from_scratch, throw away old index @@ -413,6 +413,7 @@ sub index_file { sub _tie_docs { my ($self, $mode) = @_; + my $index_dir = $self->{index_dir}; # tie to docs.bdb, storing {$doc_id => "$mtime\t$pathname\t$description"} tie %{$self->{_docs}}, 'BerkeleyDB::Hash', -Filename => "$index_dir/docs.bdb", From 06f4d938487fa9a041cd1fdccfafed05166d12e1 Mon Sep 17 00:00:00 2001 From: Paul Cochrane Date: Tue, 3 Oct 2017 20:45:25 +0200 Subject: [PATCH 5/6] Add very basic tests of index() function Although this causes the test suite to slow down a lot, it at least tests the code and hence gives some support for later refactoring efforts. The indexing operation takes roughly a minute on my box, so this should not be a problem for other dev machines. The comment about the indexing operation taking about half an hour seems as if it could be outdated. --- t/indexer.t | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/t/indexer.t b/t/indexer.t index 0c575fa..9a1e6ae 100644 --- a/t/indexer.t +++ b/t/indexer.t @@ -4,6 +4,8 @@ use strict; use warnings; use Test::More; +use Capture::Tiny qw(capture_stderr); +use File::Temp qw(tempdir); BEGIN { eval {require Search::Indexer}; @@ -11,10 +13,27 @@ BEGIN { if $@; } -plan tests => 2; +plan tests => 3; use_ok( 'Pod::POM::Web::Indexer' ); +subtest "indexing" => sub { + plan tests => 2; + + my $ppwi = Pod::POM::Web::Indexer->new(); + + $ppwi->{index_dir} = tempdir(CLEANUP => 1); + my $output = capture_stderr { + $ppwi->index(); + }; + like($output, qr/INDEXING/, "Creating index creates individual indices"); + + $output = capture_stderr { + $ppwi->index(); + }; + unlike($output, qr/INDEXING/, "Rerunning index avoids recreation"); +}; + subtest "uri escape" => sub { plan tests => 3; is(uri_escape(), undef, "An escaped, undefined URI stays undefined"); From f6bd1433a9175efe9933d72114ade1a26e4f1ab1 Mon Sep 17 00:00:00 2001 From: Paul Cochrane Date: Tue, 3 Oct 2017 20:47:26 +0200 Subject: [PATCH 6/6] Add tests of the modlist() function This function requires the index to have been built, hence we build the index again in a new temporary directory to ensure a clean slate before starting the tests. This could be improved upon by not indexing an entire Perl installation as part of the tests, however the code needs to be refactored to allow that and these tests are a step to enabling such future refactorings. --- t/indexer.t | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/t/indexer.t b/t/indexer.t index 9a1e6ae..95fb2d6 100644 --- a/t/indexer.t +++ b/t/indexer.t @@ -4,7 +4,7 @@ use strict; use warnings; use Test::More; -use Capture::Tiny qw(capture_stderr); +use Capture::Tiny qw(capture_stdout capture_stderr); use File::Temp qw(tempdir); BEGIN { @@ -13,7 +13,7 @@ BEGIN { if $@; } -plan tests => 3; +plan tests => 4; use_ok( 'Pod::POM::Web::Indexer' ); @@ -34,6 +34,31 @@ subtest "indexing" => sub { unlike($output, qr/INDEXING/, "Rerunning index avoids recreation"); }; +subtest "modlist" => sub { + plan tests => 4; + my $ppwi = Pod::POM::Web::Indexer->new(); + $ppwi->{index_dir} = tempdir(CLEANUP => 1); + capture_stderr { + $ppwi->index(); # index needs to be created for modlist tests + }; + + eval { $ppwi->modlist(); }; + like($@, qr/module_list: arg too short/, "Error message with undef search string"); + + eval { $ppwi->modlist(); }; + like($@, qr/module_list: arg too short/, "Error message with empty search string"); + + my $output = capture_stdout { + $ppwi->modlist('nonexistent_search_string'); + }; + like($output, qr/\[\]/, "Empty list returned with unknown search string"); + + $output = capture_stdout { + $ppwi->modlist('devel'); + }; + like($output, qr/Devel::/, "Module list returned with known search string"); +}; + subtest "uri escape" => sub { plan tests => 3; is(uri_escape(), undef, "An escaped, undefined URI stays undefined");