2 # BioPerl module for Bio::Root::Test
4 # Please direct questions and support issues to <bioperl-l@bioperl.org>
6 # Cared for by Sendu Bala <bix@sendu.me.uk>
10 # You may distribute this module under the same terms as perl itself
12 # POD documentation - main docs before the code
16 Bio::Root::Test - A common base for all Bioperl test scripts.
20 use lib '.'; # (for core package tests only)
23 test_begin(-tests => 20,
24 -requires_modules => [qw(IO::String XML::Parser)],
25 -requires_networking => 1);
27 my $do_network_tests = test_network();
28 my $output_debugging = test_debug();
30 # carry out tests with Test::More, Test::Exception and Test::Warn syntax
33 # these tests need version 2.6 of Optional::Module to work
34 test_skip(-tests => 10, -requires_module => 'Optional::Module 2.6');
35 use_ok('Optional::Module');
37 # 9 other optional tests that need Optional::Module
41 test_skip(-tests => 10, -requires_networking => 1);
43 # 10 optional tests that require internet access (only makes sense in the
44 # context of a script that doesn't use -requires_networking in the call to
48 # in unix terms, we want to test with a file t/data/input_file.txt
49 my $input_file = test_input_file('input_file.txt');
51 # we want the name of a file we can write to, that will be automatically
52 # deleted when the test script finishes
53 my $output_file = test_output_file();
55 # we want the name of a directory we can store files in, that will be
56 # automatically deleted when the test script finishes
57 my $output_dir = test_output_dir();
61 This provides a common base for all BioPerl test scripts. It safely handles the
62 loading of Test::More, Test::Exception and Test::Warn (actually, a subclass
63 compatible with Bioperl warnings) prior to tests being run. It also presents an
64 interface to common needs such as skipping all tests if required modules aren't
65 present or if network tests haven't been enabled. See test_begin().
67 In the same way, it allows you to skip just a subset of tests for those same
68 reasons, in addition to requiring certain executables and environment variables.
71 It also has two further methods that let you decide if network tests should be
72 run, and if debugging information should be printed. See test_network() and
75 Finally, it presents a consistent way of getting the path to input and output
76 files. See test_input_file(), test_output_file() and test_output_dir().
82 User feedback is an integral part of the evolution of this and other
83 Bioperl modules. Send your comments and suggestions preferably to
84 the Bioperl mailing list. Your participation is much appreciated.
86 bioperl-l@bioperl.org - General discussion
87 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
91 Please direct usage questions or support issues to the mailing list:
93 I<bioperl-l@bioperl.org>
95 rather than to the module maintainer directly. Many experienced and
96 reponsive experts will be able look at the problem and quickly
97 address it. Please include a thorough description of the problem
98 with code and data examples if at all possible.
100 =head2 Reporting Bugs
102 Report bugs to the Bioperl bug tracking system to help us keep track
103 of the bugs and their resolution. Bug reports can be submitted via
106 https://redmine.open-bio.org/projects/bioperl/
108 =head1 AUTHOR - Sendu Bala
110 Email bix@sendu.me.uk
114 The rest of the documentation details each of the object methods.
115 Internal methods are usually preceded with a _
119 package Bio
::Root
::Test
;
124 use File
::Temp
qw(tempdir);
126 use Exporter
qw(import);
129 # For prototyping reasons, we have to load Test::More's methods now, even
130 # though theoretically in future the user may use a different Test framework
132 # We want to load Test::More, Test::Exception and Test::Warn. Preferably the
133 # users own versions, but if they don't have them, the ones in t/lib.
134 # However, this module is in t/lib so t/lib is already in @INC so Test::* in
135 # t/lib will be used first, which we don't want: get rid of t/lib in @INC
137 eval { require Test
::More
;
138 require Test
::Exception
;
139 require Test
::Warn
; };
141 eval "use lib 't/lib';";
143 eval "use Test::More;
144 use Test::Exception;";
147 # now that the users' Test::Warn has been loaded if they had it, we can
148 # use Bio::Root::TestWarn
149 eval "use Bio::Root::Test::Warn;";
153 # re-export Test::More, Test::Exception and Test::Warn methods and export our own
154 our @EXPORT = qw(ok use_ok require_ok
155 is isnt like unlike is_deeply
159 eq_array eq_hash eq_set
187 if (Test
::More
->can('done_testing')) {
188 push @EXPORT, 'done_testing';
191 our $GLOBAL_FRAMEWORK = 'Test::More';
197 Usage : test_begin(-tests => 20);
198 Function: Begin your test script, setting up the plan (skip all tests, or run
200 Returns : True if tests should be run.
201 Args : -tests => int (REQUIRED, the number of tests that will
203 -requires_modules => [] (array ref of module names that are
204 required; if any don't load, all tests
205 will be skipped. To specify a required
206 version of a module, include the version
207 number after the module name, separated
209 -requires_module => str (as above, but for just one module)
210 -requires_networking => 1|0 (default 0, if true all tests will be
211 skipped if network tests haven't been
213 -requires_email => 1 (if true the desired number of tests will
214 be skipped if either network tests
215 haven't been enabled in Build.PL or an
216 email hasn't been entered)
217 -excludes_os => str (default none, if OS suppied, all tests
218 will skip if running on that OS (eg.
220 -framework => str (default 'Test::More', the Test module
221 to load. NB: experimental, avoid using)
223 Note, supplying -tests => 0 is possible, allowing you to skip all
224 tests in the case that a test script is testing deprecated modules
225 that have yet to be removed from the distribution
230 my ($skip_all, $tests, $framework) = _skip
(@_);
231 $GLOBAL_FRAMEWORK = $framework;
233 if ($framework eq 'Test::More') {
234 # ideally we'd delay loading Test::More until this point, but see BEGIN
238 eval "plan skip_all => '$skip_all';";
240 elsif (defined $tests && $tests == 0) {
241 eval "plan skip_all => 'All tests are being skipped, probably because the module(s) being tested here are now deprecated';";
244 eval "plan tests => $tests;";
249 # go ahead and add support for other frameworks here
251 die "Only Test::More is supported at the current time\n";
261 test_skip(-tests => 10,
262 -requires_module => 'Optional::Module 2.01');
264 # 10 tests that need v2.01 of Optional::Module
266 Function: Skip a subset of tests for one of several common reasons: missing one
267 or more optional modules, network tests haven't been enabled, a
268 required binary isn't present, or an environmental variable isn't set
270 Args : -tests => int (REQUIRED, the number of tests that are
271 to be skipped in the event one of the
272 following options isn't satisfied)
273 -requires_modules => [] (array ref of module names that are
274 required; if any don't load, the desired
275 number of tests will be skipped. To
276 specify a required version of a module,
277 include the version number after the
278 module name, separated by a space)
279 -requires_module => str (as above, but for just one module)
280 -requires_executable => Bio::Tools::Run::WrapperBase instance
281 (checks WrapperBase::executable for the
282 presence of a binary, skips if absent)
283 -requires_env => str (checks %ENV for a specific env. variable,
285 -excludes_os => str (default none, if OS suppied, desired num
286 of tests will skip if running on that OS
288 -requires_networking => 1 (if true the desired number of tests will
289 be skipped if network tests haven't been
291 -requires_email => 1 (if true the desired number of tests will
292 be skipped if either network tests
293 haven't been enabled in Build.PL or an
294 email hasn't been entered)
299 my ($skip, $tests, $framework) = _skip
(@_);
300 $tests || die "-tests must be a number greater than 0";
302 if ($framework eq 'Test::More') {
304 eval "skip('$skip', $tests);";
307 # go ahead and add support for other frameworks here
309 die "Only Test::More is supported at the current time\n";
313 =head2 test_output_file
315 Title : test_output_file
316 Usage : my $output_file = test_output_file();
317 Function: Get the full path of a file suitable for writing to.
318 When your test script ends, the file will be automatically deleted.
319 Returns : string (file path)
324 sub test_output_file
{
325 die "test_output_file takes no args\n" if @_;
328 my $tmp = File
::Temp
->new();
329 push(@TEMP_FILES, $tmp);
330 close($tmp); # Windows needs this
331 return $tmp->filename;
334 =head2 test_output_dir
336 Title : test_output_dir
337 Usage : my $output_dir = test_output_dir();
338 Function: Get the full path of a directory suitable for storing temporary files
340 When your test script ends, the directory and its contents will be
341 automatically deleted.
342 Returns : string (path)
347 sub test_output_dir
{
348 die "test_output_dir takes no args\n" if @_;
350 return tempdir
(CLEANUP
=> 1);
353 =head2 test_input_file
355 Title : test_input_file
356 Usage : my $input_file = test_input_file();
357 Function: Get the path of a desired input file stored in the standard location
358 (currently t/data), but correct for all platforms.
359 Returns : string (file path)
360 Args : list of strings (ie. at least the input filename, preceded by the
361 names of any subdirectories within t/data)
362 eg. for the file t/data/in.file pass 'in.file', for the file
363 t/data/subdir/in.file, pass ('subdir', 'in.file')
367 sub test_input_file
{
368 return File
::Spec
->catfile('t', 'data', @_);
374 Usage : my $do_network_tests = test_network();
375 Function: Ask if network tests should be run.
382 require Module
::Build
;
383 my $build = Module
::Build
->current();
384 return $build->notes('network');
390 Usage : my $do_network_tests = test_email();
391 Function: Ask if email address provided
398 require Module
::Build
;
399 my $build = Module
::Build
->current();
400 # this should not be settable unless the network tests work
401 return $build->notes('email');
407 Usage : my $output_debugging = test_debug();
408 Function: Ask if debugging information should be output.
415 return $ENV{'BIOPERLDEBUG'} || 0;
421 Usage : float_is($val1, $val2);
422 Function: test two floating point values for equality
423 Returns : Boolean based on test (can use in combination with diag)
424 Args : two scalar values (floating point numbers) (required via prototype)
425 test message (optional)
429 sub float_is
($$;$) {
430 my ($val1, $val2, $message) = @_;
431 # catch any potential undefined values and directly compare
432 if (!defined $val1 || !defined $val2) {
433 is
($val1, $val2 ,$message);
435 is
(sprintf("%g",$val1), sprintf("%g",$val2),$message);
439 # decide if should skip and generate skip message
443 # handle input strictly
444 my $tests = $args{'-tests'};
445 #(defined $tests && $tests =~ /^\d+$/) || die "-tests must be supplied and be an int\n";
446 delete $args{'-tests'};
448 my $req_mods = $args{'-requires_modules'};
449 delete $args{'-requires_modules'};
452 ref($req_mods) eq 'ARRAY' || die "-requires_modules takes an array ref\n";
453 @req_mods = @
{$req_mods};
455 my $req_mod = $args{'-requires_module'};
456 delete $args{'-requires_module'};
458 ref($req_mod) && die "-requires_module takes a string\n";
459 push(@req_mods, $req_mod);
462 my $req_net = $args{'-requires_networking'};
463 delete $args{'-requires_networking'};
465 my $req_email = $args{'-requires_email'};
466 delete $args{'-requires_email'};
468 my $req_env = $args{'-requires_env'};
469 delete $args{'-requires_env'};
471 # strip any leading $ in case someone passes $FOO instead of 'FOO'
472 $req_env =~ s{^\$}{} if $req_env;
474 my $req_exe = $args{'-requires_executable'};
475 delete $args{'-requires_executable'};
477 if ($req_exe && (!ref($req_exe) || !$req_exe->isa('Bio::Tools::Run::WrapperBase'))) {
478 die "-requires_exe takes an argument of type Bio::Tools::Run::WrapperBase";
481 my $os = $args{'-excludes_os'};
482 delete $args{'-excludes_os'};
484 my $framework = $args{'-framework'} || $GLOBAL_FRAMEWORK;
485 delete $args{'-framework'};
487 # catch user mistakes
488 while (my ($key, $val) = each %args) {
489 die "unknown argument '$key' supplied, did you mistake 'required...' for 'requires...'?\n";
492 # test user requirments and return
495 return ('Not compatible with your Operating System', $tests, $framework);
499 foreach my $mod (@req_mods) {
500 my $skip = _check_module
($mod);
502 return ($skip, $tests, $framework);
506 if ($req_net && ! test_network
()) {
507 return ('Network tests have not been requested', $tests, $framework);
510 if ($req_email && ! test_email
()) {
511 return ('Valid email not provided; required for tests', $tests, $framework);
514 if ($req_exe && !$req_exe->executable) {
515 my $msg = 'Required executable for '.ref($req_exe).' is not present';
517 return ($msg, $tests, $framework);
520 if ($req_env && !exists $ENV{$req_env}) {
521 my $msg = 'Required environment variable $'.$req_env. ' is not set';
523 return ($msg, $tests, $framework);
526 return ('', $tests, $framework);
533 if ($mod =~ /(\S+)\s+(\S+)/) {
535 $desired_version = $2;
538 eval "require $mod;";
541 if ($@
=~ /Can't locate/) {
542 return "The optional module $mod (or dependencies thereof) was not installed";
545 return "The optional module $mod generated the following error: \n$@";
548 elsif ($desired_version) {
550 unless (defined ${"${mod}::VERSION"}) {
551 return "The optional module $mod didn't have a version, but we want v$desired_version";
553 elsif (${"${mod}::VERSION"} < $desired_version) {
554 return "The optional module $mod was out of date (wanted v$desired_version)";