Recently, in Re: Testing in Perl, I said I was working on a script that automates multiple test builds of a module against a number of Perlbrew instances. Below are the brew_build.pl (brew control) script and the test.pl (test runner) script, and here's the git repo.
This works on all platforms I've tested it on (FreeBSD, Linux and Windows). For *nix, you need to have Perlbrew installed. On Windows, Berrybrew is required. You'll also require cpanm from App::cpanminus.
The reasoning behind this creation is due to the fact Travis CI only performs builds on Linux machines, and I wanted an easy way to perform release candidate builds on Windows as well in much the same manner.
It was rather quickly slapped together, but it's simply a prototype. Now that I know it works pretty well, I'm going to turn it into a proper Test module.
In your module, create a build directory in the root, and drop these two files into it. Here are some usage examples:
Run unit tests on all currently installed perl versions:
perl build/brew_build.pl
Remove all currently installed perl instances (except the one you're using), and install three new random versions, and run tests on those pristine instances (short forms for args (eg: -c for --count) are available:
perl build/brew_build.pl --reload 1 --count 3
Install all versions of perl available to Perlbrew, without removing existing instances, and enable verbose output:
perl build/brew_build.pl -d 1 -c -1
Install a specific version of perl, and run tests on all installed versions:
perl build/brew_build.pl -v 5.20.1
Example output (note that if one perlbrew instance fails tests, all processing stops (exit;) and the actual test output for the failed build is displayed along with the perl version so you can further investigate. Otherwise, on success:
% perl build/brew_build.pl
perl-5.23.7
perl-5.22.1
perl-5.20.3
perl-5.18.4
perl-5.14.4
perl-5.12.5
perl-5.12.5 :: PASS
perl-5.14.4 :: PASS
perl-5.18.4 :: PASS
perl-5.20.3 :: PASS
perl-5.22.1 :: PASS
perl-5.23.7 :: PASS
brew_build.pl
#!/usr/bin/perl
use warnings;
use strict;
use Cwd;
use Getopt::Long;
my ($debug, $count, $reload, $version, $help);
GetOptions(
"debug=i" => \$debug,
"count=i" => \$count,
"reload=i" => \$reload,
"version=s" => \$version,
"help" => \$help,
);
if ($help){
print <<EOF;
Usage: perl build/brewbuild.pl [options]
Options:
--debug | -d: Bool, enable verbosity
--count | -c: Integer, how many random versions of perl to insta
+ll.
Send in -1 to install all available versions.
--reload | -r: Bool, remove all installed perls (less the current
+ one)
before installation of new ones
--verion | -v: String, the number portion of an available perl ve
+rsion
according to "perlbrew available" Note that only o
+ne is
allowed at this time
--help | -h: print this help message
EOF
exit;
}
my $cwd = getcwd();
my $is_win = 0;
$is_win = 1 if $^O =~ /Win/;
run($count);
sub perls_available {
my $brew_info = shift;
my @perls_available = $is_win
? $brew_info =~ /(\d\.\d+\.\d+_\d+)/g
: $brew_info =~ /(perl-\d\.\d+\.\d+)/g;
if ($is_win){
for (@perls_available){
s/perl-//;
}
}
return @perls_available;
}
sub perls_installed {
my $brew_info = shift;
return $is_win
? $brew_info =~ /(\d\.\d{2}\.\d(?:_\d{2}))(?!=_)\s+\[installed
+\]/ig
: $brew_info =~ /i.*?(perl-\d\.\d+\.\d+)/g;
}
sub instance_remove {
my @perls_installed = @_;
if ($debug) {
print "$_\n" for @perls_installed;
print "\nremoving previous installs...\n";
}
my $remove_cmd = $is_win
? 'berrybrew remove'
: 'perlbrew uninstall';
for (@perls_installed){
my $ver = $^V;
$ver =~ s/v//;
if ($_ =~ /$ver$/){
print "skipping version we're using, $_\n" if $debug;
next;
}
`$remove_cmd $_`;
}
print "\nremoval of existing perl installs complete...\n" if $debu
+g;
}
sub instance_install {
my $count = shift;
my @perls_available = @_;
my $install_cmd = $is_win
? 'berrybrew install'
: 'perlbrew install --notest -j 4';
my @new_installs;
if ($version){
$version = $is_win ? $version : "perl-$version";
push @new_installs, $version;
}
else {
if ($count) {
while ($count > 0){
push @new_installs, $perls_available[rand @perls_avail
+able];
$count--;
}
}
}
if (@new_installs){
for (@new_installs){
print "\ninstalling $_...\n";
`$install_cmd $_`;
}
}
else {
print "\nusing existing versions only\n" if $debug;
}
}
sub results {
my $exec_cmd = $is_win
? "berrybrew exec perl $cwd\\build\\test.pl"
: "perlbrew exec perl $cwd/build/test.pl 2>/dev/null";
my $debug_exec_cmd = $is_win
? "berrybrew exec perl $cwd\\build\\test.pl"
: "perlbrew exec perl $cwd/build/test.pl";
my $result;
print "\n...executing\n" if $debug;
if ($is_win){
$result = `$exec_cmd`;
}
else {
if ($debug){
$result = `$debug_exec_cmd`;
}
else {
$result = `$exec_cmd`;
}
}
my @ver_results = split /\n\n\n/, $result;
print "\n\n";
for (@ver_results){
my $ver;
if (/^([Pp]erl-\d\.\d+\.\d+)/){
$ver = $1;
}
my $res;
if (/Result:\s+(PASS)/){
$res = $1;
}
else {
print $_;
exit;
}
print "$ver :: $res\n";
}
}
sub run {
my $count = shift // 0;
my $brew_info = $is_win
? `berrybrew available`
: `perlbrew available`;
my @perls_available = perls_available($brew_info);
$count = scalar @perls_available if $count < 0;
my @perls_installed = perls_installed($brew_info);
print "$_\n" for @perls_installed;
if ($debug){
print "$_ installed\n" for @perls_installed;
print "\n";
}
my %perl_vers;
instance_remove(@perls_installed) if $reload;
instance_install($count, @perls_available);
results();
}
test.pl
#!/usr/bin/perl
use warnings;
use strict;
use Cwd;
my $cwd = getcwd();
if ($^O ne 'MSWin32'){
system "cpanm --installdeps . && make && make test";
}
else {
system "cpanm --installdeps . && dmake && dmake test";
}