Where should I put common utility functions for Perl .t tests? - perl

I am getting started with Test::More, already have a few .t test scripts. Now I'd like to define a function that will only be used for the tests, but across different .t files. Where's the best place to put such a function? Define another .t without any tests and require it where needed? (As a sidenote I use the module structure created by Module::Starter)

The best approach is to put your test functions, like any other set of functions, into a module. You can then use Test::Builder to have your test diagnostics/fail messages act as if the failure originated from the .t file, rather than your module.
Here is a simple example.
package Test::YourModule;
use Test::Builder;
use Sub::Exporter -setup => { exports => ['exitcode_ok'] }; # or 'use Exporter' etc.
my $Test = Test::Builder->new;
# Runs the command and makes sure its exit code is $expected_code. Contrived!
sub exitcode_ok {
my ($command, $expected_code, $name) = #_;
system($command);
my $exit = $? >> 8;
my $message = $!;
my $ok = $Test->is_num( $exit, $expected_code, $name );
if ( !$ok ) {
$Test->diag("$command exited incorrectly with the error '$message'");
}
return $ok;
}
In your script:
use Test::More plan => 1;
use Test::YourModule qw(exitcode_ok);
exitcode_ok('date', 0, 'date exits without errors');

Write a module as rjh has demonstrated. Put it in t/lib/Test/YourThing.pm, then it can be loaded as:
use lib 't/lib';
use Test::YourThing;
Or you can put it straight in t/Test/YourThing.pm, call it package t::Test::YourThing and load it as:
use t::Test::YourThing;
The upside is not having to write the use lib line in every test file, and clearly identifying it as a local test module. The down side is cluttering up t/, it won't work if "." is not in #INC (for example, if you run your tests in taint mode, but it can be worked around with use lib ".") and if you decide to move the .pm file out of your project you have to rewrite all the uses. Your choice.

Related

Anyway to tell if perl script is run via do?

I have a small script configuration file which is loaded from a main script.
#main.pl
package MYPACKAGE;
our $isMaster=1;
package main;
my config=do "./config.pl"
#config.pl
my $runViaDoFlag;
$runViaDoFlag=$0=~/main\.pl/; #Test if main.pl is the executing script
$runViaDoFlag=defined $MYPACKAGE::isMaster; #Test custom package variable
#Is there a 'built-in' way to do this?
die "Need to run from main script! " unless $runViaDoFlag;
{
options={
option1=>"some value",
option2=>"some value",
},
mySub=>sub {
# do interesting things here
}
}
In a more complicated config file it might not be so obvious that config.pl script is intended to only be executed by do. Hence I want to include a die with basic usage instructions.
Solutions:
test $0 for the main script name
have custom package variable defined in the main script and checked by the config script
simply have a comment in the config instructing the user how to use it.
These work, however is there some way of knowing if a script is executed via do via built-in variable/subs?
I'd offer a change in design: have that configuration in a normal module, in which you can then test whether it's been loaded by (out of) the main:: namespace or not. Then there is no need for any of that acrobatics with control variables etc.
One way to do that
use warnings;
use strict;
use feature 'say';
use FindBin qw($RealBin);
use lib $RealBin; # so to be able to 'use' from current directory
use ConfigPackage qw(load_config);
my $config = load_config();
# ...
and the ConfigPackage.pm (in the same directory)
package ConfigPackage;
use warnings;
use strict;
use feature 'say';
use Carp;
use Exporter qw(); # want our own import
our #EXPORT_OK = qw(load_config);
sub import {
#say "Loaded by: ", (caller)[0];
croak "Must only be loaded from 'main::'"
if not ( (caller)[0] eq 'main' );
# Now switch to Exporter::import to export symbols as needed
goto &Exporter::import;
}
sub load_config {
# ...
return 'Some config-related data structure';
}
1;
(Note that this use of goto is fine.)
This is just a sketch of course; adjust, develop further, and amend as needed. If this is lodaed out of a package other than main::, and so it fails, then that happens in the compile phase, since that's when import is called. I'd consider that a good thing.
If that config code need be able to run as well, as the question may indicate, then have a separate executable that loads this module and runs what need be run.
As for the question as stated, the title and the question's (apparent) quest differ a little, but both can be treated by using caller EXPR. It won't be a clean little "built-in" invocation though.
The thing about do as intended to be used is that
do './stat.pl' is largely like
eval `cat stat.pl`;
except that ...
(That stat.pl is introduced earlier in docs, merely to signify that do is invoked on a file.)
Then caller(0) will have clear hints to offer (see docs). It returns
my ($package, $filename, $line, $subroutine, $hasargs,
$wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash)
= caller($i);
In a call asked for, do './config.pl', apart from main (package) and the correct filename, the caller(0) in config.pl also returns:
(eval) for $subroutine
./config.pl for $evaltext
1 for $is_require
Altogether this gives plenty to decide whether the call was made as required.
However, I would not recommend this kind of involved analysis instead of just using a package, what is also incomparably more flexible.

In Perl, how do I extract out the declaration of variables into a wrapper script?

Background
I have a perl script, called main.pl that is currently in several branched states on clear case like so:
Branch 1:
my %hash
my $variable = "a"
my $variable2 = "c"
sub codeIsOtherwiseTheSame()
....
Branch 2:
my %hash2
my $variable = "b"
sub codeIsOtherwiseTheSame()
....
Branch 3
my %hash
my $variable2 = "d"
sub codeIsOtherwiseTheSame()
....
Right now, each branch of the script has the same code. The only differences are the kind of variables that are declared and what their initialized value is. What I want to do is extract these differing variables out to a wrapper script (for each variation) so that the main script does not have to be changed. I am doing this because several users will be using this script, but have only minor differences based on their use case. Thus I want each kind of user to have their own simplified interface. At the same time, I want the main script to still be aware of these variable once it is called. Below is an example of what I want:
Desired Solution
Wrapper Script 1:
my %hash;
my $variable = "a";
my $variable2 = "c";
system("main.pl");
Wrapper Script 2:
my %hash2;
my $variable = "b";
system("main.pl");
Wrapper Script 3:
my %hash;
my $variable2 = "d";
system("main.pl");
Main.pl
sub codeIsOtherwiseTheSame()
Question
How do I extract out a wrapper script to obtain the organization and behavior I want above?
Extract the common code into a module, not a script. Save it as e.g. MyCommon.pm.
Export a function from the module that does what you need:
package MyCommon;
use Exporter qw{ import };
our #EXPORT = qw{ common_code };
sub common_code {
my ($var1, $var2) = #_;
# Common code goes here...
}
Then, in various scripts, write
use MyCommon qw{ common_code };
common_code('a', 'b'); # <- insert the specific values here.
There are more advanced ways, e.g. you can use "object orientation": construct an object from the specific values, then run a method that implements the common code - but for simple use cases, you probably don't need it.
Desired behavior for simple case as yours can be achieved with with required function of perl
Put common code in a file, for example common.inc end the file with 1; (requirement for modules and include files)
sub commonFunction {
my $data = shift;
print "DATA: $data\n";
}
1;
Copy/move common.inc file into one of #INC directory (probably site directory best fit for this purpose).
Check your perl #INC configuration setting with following command
perl -e "print qw(#INC)"
Now you can reuse common.inc file in your user interface script
#!/usr/bin/perl
require 'common.inc';
my $a = 7;
commonFunction($a);
Already was suggested to place the common code which will be reused multiple times in form of .pm module.
By doing so you gain more control what functions/variables are visible (exported) to avoid namespace clash/collision [the modules can have functions/variables with same name].
Short tutorial how to create a module is available. Next natural step will be OOP programming.
Book: Object Oriented Perl
perlootut, Writing perl modules, Chapter Object Oriented Perl

Exporting subroutines from a module 'used' via a 'require'

I'm working with a set of perl scripts which our build system is written with. Unfortunately they were not written as a set of modules, but instead a bunch of .pl files which 'require' each other.
After making some changes to a 'LogOutput.pl' which was used by almost every other file, I started to suffer from some issues caused by the file being 'require'd multiple times.
In an effort to fix this, while not changing every file (some of which are not under my direct control), I did the following:
-Move everything in LogOutput.pl to a new file LogOutput.pm, this one having everything needed to make it a module (based on reading http://www.perlmonks.org/?node_id=102347 ).
-Replace the existing LogOutput.pl with the following
BEGIN
{
use File::Spec;
push #INC, File::Spec->catfile($BuildScriptsRoot, 'Modules');
}
use COMPANY::LogOutput qw(:DEFAULT);
1;
This works, except that I need to change calling code to prefix the sub names with the new package (i.e. COMPANY::LogOutput::OpenLog instead of just OpenLog)
Is there any way for me to export the new module's subroutine's from within LogOutput.pl?
The well named Import::Into can be used to export a module's symbols into another package.
use Import::Into;
# As if Some::Package did 'use COMPANY::LogOutput'
COMPANY::LogOutput->import::into("Some::Package");
However, this shouldn't be necessary. Since LogOutput.pl has no package, its code is in the package it was required from. use COMPANY::LogOutput will export into the package which required LogOutput.pl. Your code, as written, should work to emulate a bunch of functions in a .pl file.
Here's what I assume LogOutput.pl looked like (using the subroutine "pass" as a stand in for whatever subroutines you had in there)...
sub pass { print "pass called\n" }
1;
And what I assume LogOutput.pl and LogOutput.pm look like now...
# LogOutput.pl
BEGIN
{
use File::Spec;
push #INC, File::Spec->catfile($BuildScriptsRoot, 'Modules');
}
use COMPANY::LogOutput qw(:DEFAULT);
1;
# LogOutput.pm
package test;
use strict;
use warnings;
use Exporter "import";
our #EXPORT_OK = qw(pass);
our %EXPORT_TAGS = (
':DEFAULT' => [qw(pass)],
);
sub pass { print "pass called\n" }
1;
Note this will not change the basic nature of require. A module will still only be required once, after that requiring it again is a no-op. So this will still not work...
{
package Foo;
require "test.pl"; # this one will work
pass();
}
{
package Bar;
require "test.pl"; # this is a no-op
pass();
}
You can make it work. Perl stores the list of what files have been required in %INC. If you delete and entry, Perl will load the file again. However, you have to be careful that all the code in the .pl file is ok with this. That #INC hack has to make sure its only run once.
BEGIN
{
use File::Spec;
# Only run this code once, no matter how many times this
# file is loaded.
push #INC, File::Spec->catfile($BuildScriptsRoot, 'Modules')
if $LogOutput_pl::only_once++;
}
use COMPANY::LogOutput qw(:DEFAULT);
# Allow this to be required and functions imported more
# than once.
delete $INC{"LogOutput.pl"};
1;
This is one of the few cases that a global variable is justified. A lexical (my) variable must be declared and would be reset with each loading of the library. A global variable does not need to be declared and will persist between loading.
This turned out to just be a stupid mistake on my part, I didn't put the subs into the #EXPORT list, only into #EXPORT_OK.

How can I pass common arguments to Perl modules?

I'm not thrilled with the argument-passing architecture I'm evolving for the (many) Perl scripts that have been developed for some scripts that call various Hadoop MapReduce jobs.
There are currently 8 scripts (of the form run_something.pl) that are run from cron. (And more on the way ... we expect anywhere from 1 to 3 more for every function we add to hadoop.) Each of these have about 6 identical command-line parameters, and a couple command line parameters that are similar, all specified with Euclid.
The implementations are in a dozen .pm modules. Some of which are common, and others of which are unique....
Currently I'm passing the args globally to each module ...
Inside run_something.pl I have:
set_common_args (%ARGV);
set_something_args (%ARGV);
And inside Something.pm I have
sub set_something_args {
(%MYARGS) =#_;
}
So then I can do
if ( $MYARGS{'--needs_more_beer'} ) {
$beer++;
}
I'm seeing that I'm probably going to have additional "common" files that I'll want to pass args to, so I'll have three or four set_xxx_args calls at the top of each run_something.pl, and it just doesn't seem too elegant.
On the other hand, it beats passing the whole stupid argument array down the call chain, and choosing and passing individual elements down the call chain is (a) too much work (b) error-prone (c) doesn't buy much.
In lots of ways what I'm doing is just object-oriented design without the object-oriented language trappings, and it looks uglier without said trappings, but nonetheless ...
Anyone have thoughts or ideas?
In the same vein as Pedro's answer, but upgraded to use Moose and MooseX::Getopt, I present the SO community with... a Moose modulino*: a Moose module that can be included and run normally as a module, or separately as a command-line utility:
# this is all in one file, MyApp/Module/Foo.pm:
package MyApp::Module::Foo;
use Moose;
use MooseX::Getopt;
has [ qw(my config args here) ] => (
is => 'ro', isa => 'Int',
);
sub run { ... }
package main;
use strict;
use warnings;
sub run
{
my $module = MyApp::Module::Foo->new_with_options();
$module->run();
}
run() unless caller();
The module can be invoked using:
perl MyApp/Module/Foo.pm --my 0 --config 1 --args 2 --here 3
Using this pattern, you can collect command-line arguments using one module, which is used by all other modules and scripts that share the same options, and use standard Moose accessor methods for retrieving those options.
*modulinos are modules that can also be run as stand-alone scripts -- a Perl design pattern by SO's own brian d foy.
Have a look at import in Getopt::Long. You pass arguments to your module through use Module qw/.../ and grab those via the import subroutine.
# Override import.
sub import {
my $pkg = shift; # package
my #syms = (); # symbols to import
my #config = (); # configuration
my $dest = \#syms; # symbols first
for ( #_ ) {
if ( $_ eq ':config' ) {
$dest = \#config; # config next
next;
}
push(#$dest, $_); # push
}
# Hide one level and call super.
local $Exporter::ExportLevel = 1;
push(#syms, qw(&GetOptions)) if #syms; # always export GetOptions
$pkg->SUPER::import(#syms);
# And configure.
Configure(#config) if #config;
}

Is it possible use or require a Perl script without executing its statements?

I need to add unit testing to some old scripts, the scripts are all basically in the following form:
#!/usr/bin/perl
# Main code
foo();
bar();
# subs
sub foo {
}
sub bar {
}
If I try to 'require' this code in a unit test, the main section of the code will run, where as I want to be able to just test "foo" in isolation.
Is there any way to do this without moving foo,bar into a seperate .pm file?
Assuming you have no security concerns, wrap it in a sub { ... } and eval it:
use File::Slurp "read_file";
eval "package Script; sub {" . read_file("script") . "}";
is(Script::foo(), "foo");
(taking care that the eval isn't in scope of any lexicals that would be closed over by the script).
Another common trick for unit testing scripts is to wrap the body of their code into a 'caller' block:
#!/usr/bin/perl
use strict;
use warnings;
unless (caller) {
# startup code
}
sub foo { ... }
When run from the command line, cron, a bash script, etc., it runs normally. However, if you load it from another Perl program, the "unless (caller) {...}" code does not run. Then in your test program, declare a namespace (since the script is probably running code in package main::) and 'do' the script.
#!/usr/bin/perl
package Tests::Script; # avoid the Test:: namespace to avoid conflicts
# with testing modules
use strict;
use warnings;
do 'some_script' or die "Cannot (do 'some_script'): $!";
# write your tests
'do' is more efficient than eval and fairly clean for this.
Another trick for testing scripts is to use Expect. This is cleaner, but is also harder to use and it won't let you override anything within the script if you need to mock anything up.
Ahh, the old "how do I unit test a program" question. The simplest trick is to put this in your program before it starts doing things:
return 1 unless $0 eq __FILE__;
__FILE__ is the current source file. $0 is the name of the program being run. If they are the same, your code is being executed as a program. If they're different, it's being loaded as a library.
That's enough to let you start unit testing the subroutines inside your program.
require "some/program";
...and test...
Next step is to move all the code outside a subroutine into main, then you can do this:
main() if $0 eq __FILE__;
and now you can test main() just like any other subroutine.
Once that's done you can start contemplating moving the program's subroutines out into their own real libraries.