File Coverage

blib/lib/Wiki/Toolkit/TestLib.pm
Criterion Covered Total %
statement 35 92 38.0
branch 2 22 9.0
condition n/a
subroutine 12 13 92.3
pod 4 4 100.0
total 53 131 40.4


line stmt bran cond sub pod time code
1             package Wiki::Toolkit::TestLib;
2              
3 39     39   79459 use 5.006; #by perlver
  39         343  
4 39     39   204 use strict;
  39         75  
  39         889  
5 39     39   2400 use Carp "croak";
  39         70  
  39         3164  
6 39     39   19367 use Wiki::Toolkit;
  39         108  
  39         1268  
7 39     39   18295 use Wiki::Toolkit::TestConfig;
  39         145  
  39         1306  
8 39     39   58523 use DBI;
  39         647696  
  39         2738  
9              
10 39     39   378 use vars qw( $VERSION @wiki_info );
  39         76  
  39         29892  
11             $VERSION = '0.05';
12              
13             =head1 NAME
14              
15             Wiki::Toolkit::TestLib - Utilities for writing Wiki::Toolkit tests.
16              
17             =head1 DESCRIPTION
18              
19             When 'perl Makefile.PL' is run on a Wiki::Toolkit distribution,
20             information will be gathered about test databases etc that can be used
21             for running tests. Wiki::Toolkit::TestLib gives convenient access to this
22             information.
23              
24             =head1 SYNOPSIS
25              
26             use strict;
27             use Wiki::Toolkit::TestLib;
28             use Test::More;
29              
30             my $iterator = Wiki::Toolkit::TestLib->new_wiki_maker;
31             plan tests => ( $iterator->number * 6 );
32              
33             while ( my $wiki = $iterator->new_wiki ) {
34             # put some test data in
35             # run six tests
36             }
37              
38             Each time you call C<< ->next >> on your iterator, you will get a
39             fresh blank wiki object. The iterator will iterate over all configured
40             search and storage backends.
41              
42             The Lucy search backend will be configured to index three metadata fields:
43             address, category, and locale.
44              
45             =cut
46              
47             my %configured = %Wiki::Toolkit::TestConfig::config;
48              
49             my %datastore_info;
50              
51             foreach my $dbtype (qw( MySQL Pg SQLite )) {
52             if ( $configured{$dbtype}{dbname} ) {
53             my %config = %{ $configured{$dbtype} };
54             my $store_class = "Wiki::Toolkit::Store::$dbtype";
55             my $setup_class = "Wiki::Toolkit::Setup::$dbtype";
56             eval "require $store_class";
57             if ( $@ ) {
58             warn "Couldn't require $store_class: $@\n";
59             warn "Will skip $dbtype tests.\n";
60             next;
61             }
62             my $dsn = $store_class->_dsn( @config{ qw( dbname dbhost dbport ) } );
63             my $err;
64             if ( $err = _test_dsn( $dsn, $config{dbuser}, $config{dbpass} ) ) {
65             warn "connecting to test $dbtype database failed: $err\n";
66             warn "will skip $dbtype tests\n";
67             next;
68             }
69             $datastore_info{$dbtype} = {
70             class => $store_class,
71             setup_class => $setup_class,
72             params => {
73             dbname => $config{dbname},
74             dbuser => $config{dbuser},
75             dbpass => $config{dbpass},
76             dbhost => $config{dbhost},
77             },
78             dsn => $dsn
79             };
80             }
81             }
82              
83             my %dbixfts_info;
84             # DBIxFTS only works with MySQL.
85             if ( $configured{dbixfts} && $configured{MySQL}{dbname} ) {
86             my %config = %{ $configured{MySQL} };
87             $dbixfts_info{MySQL} = {
88             db_params => {
89             dbname => $config{dbname},
90             dbuser => $config{dbuser},
91             dbpass => $config{dbpass},
92             dbhost => $config{dbhost},
93             },
94             };
95             }
96              
97             my %sii_info;
98             # Test the MySQL SII backend, if we can.
99             if ( $configured{search_invertedindex} && $configured{MySQL}{dbname} ) {
100             my %config = %{ $configured{MySQL} };
101             $sii_info{MySQL} = {
102             db_class => "Search::InvertedIndex::DB::Mysql",
103             db_params => {
104             -db_name => $config{dbname},
105             -username => $config{dbuser},
106             -password => $config{dbpass},
107             -hostname => $config{dbhost} || "",
108             -table_name => 'siindex',
109             -lock_mode => 'EX',
110             },
111             };
112             }
113              
114             # Test the Pg SII backend, if we can. It's not in the main S::II package.
115             eval { require Search::InvertedIndex::DB::Pg; };
116             my $sii_pg = $@ ? 0 : 1;
117             if ( $configured{search_invertedindex}
118             && $configured{Pg}{dbname}
119             && $sii_pg
120             ) {
121             my %config = %{ $configured{Pg} };
122             $sii_info{Pg} = {
123             db_class => "Search::InvertedIndex::DB::Pg",
124             db_params => {
125             -db_name => $config{dbname},
126             -username => $config{dbuser},
127             -password => $config{dbpass},
128             -hostname => $config{dbhost},
129             -table_name => 'siindex',
130             -lock_mode => 'EX',
131             },
132             };
133             }
134              
135             # Also test the default DB_File backend, if we have S::II installed at all.
136             if ( $configured{search_invertedindex} ) {
137             $sii_info{DB_File} = {
138             db_class => "Search::InvertedIndex::DB::DB_File_SplitHash",
139             db_params => {
140             -map_name => 't/sii-db-file-test.db',
141             -lock_mode => 'EX',
142             },
143             };
144             }
145              
146             my ( $plucene_path, $lucy_path );
147             # Test with Plucene and Lucy if possible.
148             if ( $configured{plucene} ) {
149             $plucene_path = "t/plucene";
150             }
151             if ( $configured{lucy} ) {
152             $lucy_path = "t/lucy";
153             }
154              
155             # @wiki_info describes which searches work with which stores.
156              
157             # Database-specific searchers.
158             push @wiki_info, { datastore_info => $datastore_info{MySQL},
159             dbixfts_info => $dbixfts_info{MySQL} }
160             if ( $datastore_info{MySQL} and $dbixfts_info{MySQL} );
161             push @wiki_info, { datastore_info => $datastore_info{MySQL},
162             sii_info => $sii_info{MySQL} }
163             if ( $datastore_info{MySQL} and $sii_info{MySQL} );
164             push @wiki_info, { datastore_info => $datastore_info{Pg},
165             sii_info => $sii_info{Pg} }
166             if ( $datastore_info{Pg} and $sii_info{Pg} );
167              
168             # All stores are compatible with the default S::II search, and with Plucene,
169             # and with Lucy, and with no search.
170             foreach my $dbtype ( qw( MySQL Pg SQLite ) ) {
171             push @wiki_info, { datastore_info => $datastore_info{$dbtype},
172             sii_info => $sii_info{DB_File} }
173             if ( $datastore_info{$dbtype} and $sii_info{DB_File} );
174             push @wiki_info, { datastore_info => $datastore_info{$dbtype},
175             plucene_path => $plucene_path }
176             if ( $datastore_info{$dbtype} and $plucene_path );
177             push @wiki_info, { datastore_info => $datastore_info{$dbtype},
178             lucy_path => $lucy_path }
179             if ( $datastore_info{$dbtype} and $lucy_path );
180             push @wiki_info, { datastore_info => $datastore_info{$dbtype} }
181             if $datastore_info{$dbtype};
182             }
183              
184             =head1 METHODS
185              
186             =over 4
187              
188             =item B
189              
190             my $iterator = Wiki::Toolkit::TestLib->new_wiki_maker;
191              
192             =cut
193              
194             sub new_wiki_maker {
195 2     2 1 171 my $class = shift;
196 2         6 my $count = 0;
197 2         5 my $iterator = \$count;
198 2         6 bless $iterator, $class;
199 2         5 return $iterator;
200             }
201              
202             =item B
203              
204             use Test::More;
205             my $iterator = Wiki::Toolkit::TestLib->new_wiki_maker;
206             plan tests => ( $iterator->number * 6 );
207              
208             Returns the number of new wikis that your iterator will be able to give you.
209              
210             =cut
211              
212             sub number {
213 1     1 1 11 return scalar @wiki_info;
214             }
215              
216             =item B
217              
218             my $wiki = $iterator->new_wiki;
219              
220             Returns a fresh blank wiki object, or false if you've used up all the
221             configured search and storage backends.
222              
223             =cut
224              
225             sub new_wiki {
226 1     1 1 253 my $self = shift;
227 1 50       9 return undef if $$self > $#wiki_info;
228 0         0 my $details = $wiki_info[$$self];
229 0         0 my %wiki_config;
230              
231             # Set up and clear datastore.
232 0         0 my %datastore_info = %{ $details->{datastore_info } };
  0         0  
233 0         0 my $setup_class = $datastore_info{setup_class};
234 0         0 eval "require $setup_class";
235             {
236 39     39   345 no strict "refs";
  39         100  
  39         32926  
  0         0  
237 0         0 &{"$setup_class\:\:cleardb"}( $datastore_info{params} );
  0         0  
238 0         0 &{"$setup_class\:\:setup"}( $datastore_info{params} );
  0         0  
239             }
240 0         0 my $class = $datastore_info{class};
241 0         0 eval "require $class";
242 0         0 $wiki_config{store} = $class->new( %{ $datastore_info{params} } );
  0         0  
243              
244             # Set up and clear search object (if required).
245 0 0       0 if ( $details->{dbixfts_info} ) {
    0          
    0          
    0          
246 0         0 my %fts_info = %{ $details->{dbixfts_info} };
  0         0  
247 0         0 require Wiki::Toolkit::Store::MySQL;
248 0         0 my %dbconfig = %{ $fts_info{db_params} };
  0         0  
249             my $dsn = Wiki::Toolkit::Store::MySQL->_dsn( $dbconfig{dbname},
250 0         0 $dbconfig{dbhost} );
251             my $dbh = DBI->connect( $dsn, $dbconfig{dbuser}, $dbconfig{dbpass},
252 0 0       0 { PrintError => 0, RaiseError => 1, AutoCommit => 1 } )
253             or croak "Can't connect to $dbconfig{dbname} using $dsn: " . DBI->errstr;
254 0         0 require Wiki::Toolkit::Setup::DBIxFTSMySQL;
255             Wiki::Toolkit::Setup::DBIxFTSMySQL::setup(
256 0         0 @dbconfig{ qw( dbname dbuser dbpass dbhost ) }
257             );
258 0         0 require Wiki::Toolkit::Search::DBIxFTS;
259 0         0 $wiki_config{search} = Wiki::Toolkit::Search::DBIxFTS->new( dbh => $dbh );
260             } elsif ( $details->{sii_info} ) {
261 0         0 my %sii_info = %{ $details->{sii_info} };
  0         0  
262 0         0 my $db_class = $sii_info{db_class};
263 0         0 eval "use $db_class";
264 0         0 my %db_params = %{ $sii_info{db_params} };
  0         0  
265 0         0 my $indexdb = $db_class->new( %db_params );
266 0         0 require Wiki::Toolkit::Setup::SII;
267 0         0 Wiki::Toolkit::Setup::SII::setup( indexdb => $indexdb );
268 0         0 $wiki_config{search} = Wiki::Toolkit::Search::SII->new(indexdb =>$indexdb);
269             } elsif ( $details->{plucene_path} ) {
270 0         0 require Wiki::Toolkit::Search::Plucene;
271 0         0 my $dir = $details->{plucene_path};
272 0         0 unlink <$dir/*>; # don't die if false since there may be no files
273 0 0       0 if ( -d $dir ) {
274 0 0       0 rmdir $dir or die $!;
275             }
276 0 0       0 mkdir $dir or die $!;
277 0         0 $wiki_config{search} = Wiki::Toolkit::Search::Plucene->new( path => $dir );
278             } elsif ( $details->{lucy_path} ) {
279 0         0 require Wiki::Toolkit::Search::Lucy;
280 0         0 require File::Path;
281 0         0 my $dir = $details->{lucy_path};
282 0         0 File::Path::rmtree( $dir, 0, 1 ); # 0 = verbose, 1 = safe
283 0 0       0 mkdir $dir or die $!;
284 0         0 $wiki_config{search} = Wiki::Toolkit::Search::Lucy->new(
285             path => $dir,
286             metadata_fields => [ "address", "category", "locale" ] );
287             }
288              
289             # Make a wiki.
290 0         0 my $wiki = Wiki::Toolkit->new( %wiki_config );
291 0         0 $$self++;
292 0         0 return $wiki;
293             }
294              
295             =item B
296              
297             my @configured_databases = $iterator->configured_databases;
298              
299             Returns the @configured_databases array detailing configured test databases.
300             Useful for very low-level testing only.
301              
302             =cut
303              
304             sub configured_databases {
305 1     1 1 4 my @configured_databases;
306 1         4 foreach my $dbtype (qw( MySQL Pg SQLite )) {
307             push @configured_databases, $datastore_info{$dbtype}
308 3 50       9 if $datastore_info{$dbtype};
309             }
310 1         3 return @configured_databases;
311             }
312              
313             sub _test_dsn {
314 0     0     my ( $dsn, $dbuser, $dbpass ) = @_;
315 0           my $dbh = eval {
316 0           DBI->connect($dsn, $dbuser, $dbpass, {RaiseError => 1});
317             };
318 0           return $@;
319             }
320              
321             =back
322              
323             =head1 SEE ALSO
324              
325             L
326              
327             =head1 AUTHOR
328              
329             Kake Pugh (kake@earth.li).
330              
331             =head1 COPYRIGHT
332              
333             Copyright (C) 2003-2004 Kake Pugh. All Rights Reserved.
334             Copyright (C) 2008 the Wiki::Toolkit team. All Rights Reserved.
335              
336             This module is free software; you can redistribute it and/or modify it
337             under the same terms as Perl itself.
338              
339             =head1 CAVEATS
340              
341             If you have the L backend configured (see
342             L) then your tests will raise warnings like
343              
344             (in cleanup) Search::InvertedIndex::DB::Mysql::lock() -
345             testdb is not open. Can't lock.
346             at /usr/local/share/perl/5.6.1/Search/InvertedIndex.pm line 1348
347              
348             or
349              
350             (in cleanup) Can't call method "sync" on an undefined value
351             at /usr/local/share/perl/5.6.1/Tie/DB_File/SplitHash.pm line 331
352             during global destruction.
353              
354             in unexpected places. I don't know whether this is a bug in me or in
355             L.
356              
357             =cut
358              
359             1;