SlideShare a Scribd company logo
1 of 68
Download to read offline
Getting There from Here:
Unit testing a few generations of code.
Steven Lembark
Workhorse Computing
lembark@wrkhors.com
perl 5.8
Fun isn’t it? A generation of nothing.
No improvements in Perl.
No advances in CPAN.
No reason to upgrade.
20 years of nada...
Ask all the programmers stuck with 5.8.
perl 5.8
Q: Why are we stuck with 5.8?
A: /usr/bin/perl is 5.8
Q: Why are we stuck with /usr/bin/perl?
A: ???
perl 5.8
Q: Why are we stuck with 5.8?
A: /usr/bin/perl is 5.8
Q: Why are we stuck with /usr/bin/perl?
A: Because “it worked”.
perl 5.8
Q: Why are we stuck with 5.8?
A: /usr/bin/perl is 5.8
Q: Why are we stuck with /usr/bin/perl?
A: Because something else “might not” work.
perl 5.8
Q: Why are we stuck with 5.8?
A: /usr/bin/perl is 5.8
Q: Why are we stuck with /usr/bin/perl?
A: Because we can’t show that 5.X works.
(for some definition of Christmas > 5.8)
perl 5.X
Q: Why can’t we show that 5.X works?
A: Because Christmas came after 2002.
perl 5.X
Q: Why can’t we show that 5.X works?
A: Because we stopped testing our code.
perl 5.X
Q: Why can’t we show that 5.X works?
A: Because they stopped testing our code.
perl 5.X
Q: Why can’t we show that 5.X works?
A: Because we have worked around it for way too long.
perl 5.X
Q: Why can’t we show that 5.X works?
A: Because we have worked around it for way too long.
perlbrew, all of the other approaches to dodging
/usr/bin/perl.
OK how do we fix it?
Prove that “it works”.
With testing.
In one client’s case: Lots and lots of testing.
OK how do we fix it?
Prove that “it works”.
With testing.
In one client’s case: Lots and lots of testing.
75_000 modules worth of testing.
OK how do we fix it?
Prove that “it works”.
With testing.
In one client’s case: Lots and lots of testing.
75_000 modules worth of testing.
Q: But how did you write 75_000 unit tests?
OK how do we fix it?
Prove that “it works”.
With testing.
In one client’s case: Lots and lots of testing.
75_000 modules worth of automated testing.
A: Lazyness.
Syntax checks are the first pass.
Unit tests get a bad rap.
“They don’t really test anything.”
Syntax checks are the first pass.
Unit tests get a bad rap.
“They don’t really test anything.”
Except whether 75_000 modules compile.
Code that was written on 5.8.
That now to run on 5.20+.
How do we test that much code?
Metadata driven testing:
Encode data into a test.
The standard test is data-driven.
Tests each validate one small part of a module.
How do we test that much code?
Q: How do you write that many tests?
A: Don’t.
How do we test that much code?
Q: How do you write that many tests?
A: Don’t.
Symlinks are your friends.
Simple basic test: require_ok
Run “require_ok $module” for every *.pm.
Simple, basic, obvious test for syntax errors.
… missing modules.
… botched system paths.
… all sorts of things.
Simple basic test: require_ok
Run “require_ok $module” for every *.pm.
Simple, basic, obvious test for syntax errors.
Nice thing: $module can be a path.
Even an absolute path.
Passing a path
“require_ok $path”
One argument.
Q: How do we pass it?
A: In the basename of a test.
Basic unit test
use v5.30;
use Test::More;
use File::Basename;
my $base0 = basename $0, '.t';
my $s = substr $base0, 0, 1;
my $path = join ‘/’, split m{[$s]}, $base0;
require_ok $path;
done_testing
__END__
Basic unit test
Install
them
using
shell.
#!/bin/bash
dir=$(cd $(dirname $0)/../..; pwd -L);
cd “$dir/t/01-units”;
find $dir/lib -name ‘*pm’ |
while read i
do
base=”$(echo $i | tr ‘/’ ‘~’).t”;
ln -fs ../bin/01-unit_t ./$base;
done
Basic unit test
Install
them
using
shell.
From
./t/bin
#!/bin/bash
dir=$(cd $(dirname $0)/../..; pwd -L);
cd “$dir/t/01-units”;
find $dir/lib -name ‘*pm’ |
while read i
do
base=”$(echo $i | tr ‘/’ ‘~’).t”;
ln -fs ../bin/01-unit_t ./$base;
done
Basic unit test
Install
them
using
shell.
Path to
basename.
#!/bin/bash
dir=$(cd $(dirname $0)/../..; pwd -L);
cd “$dir/t/01-units”;
find $dir/lib -name ‘*pm’ |
while read i
do
base=”$(echo $i | tr ‘/’ ‘~’).t”;
ln -fs ../bin/01-unit_t ./$base;
done
Basic unit test
Now you
know if it
compiles. prove t/01-units;
Basic unit test
Now you
know if it
compiles.
A bit
faster.
prove --jobs=8 t/01-units;
Basic unit test
Now you
know if it
compiles.
At 3am
without
watching.
args=(--jobs=8 --state=save );
prove ${args[*]} t/01-units;
Basic unit test
Now you
know if it
compiles.
At 8am
knowing
what
failed.
args=(--jobs=8 --state=save,failed );
prove ${args[*]} t/01-units;
Basic unit test
Now you
know if it
compiles.
Missing
module
anyone?
args=(--jobs=8 --state=save,failed );
prove ${args[*]} t/01-units
Can't locate Foo/Bar.pm in @INC (you
may need to install the Foo::Bar
module) (@INC contains:
/opt/perl/5.30/lib/site_perl/5.30.1/x8
6_64-linux
Basic unit test
Now you
know if it
compiles.
Fine:
Install
them.
args=(--jobs=8 --state=save,failed );
prove ${args[*]} t/01-units |
grep ‘you may need to install the’ |
cut -d’(‘ -f2 |
cut -d’ ‘ -f7 |
sort -d |
uniq |
cpanm ;
Basic unit test
Now you
know if it
compiles.
Local
mirror
provides
pre-
verified
modules.
args=(--jobs=8 --state=save,failed );
prove ${args[*]} t/01-units |
grep ‘you may need to install the’ |
cut -d’(‘ -f2 |
cut -d’ ‘ -f7 |
sort -d |
uniq |
cpanm -M$local ;
Basic unit test
Now you
know if it
compiles.
Local
library
simplifies
auto-
install.
args=(--jobs=8 --state=save,failed );
prove ${args[*]} t/01-units |
grep ‘you may need to install the’ |
cut -d’(‘ -f2 |
cut -d’ ‘ -f7 |
sort -d |
uniq |
cpanm 
-M$local -l$repo --self-contained ;
Basic unit test
Start with
a virgin
/opt/perl.
Keep
testing
until it’s
all use-
able.
args=(--jobs=8 --state=save,failed );
prove ${args[*]} t/01-units |
grep ‘you may need to install the’ |
cut -d’(‘ -f2 |
cut -d’ ‘ -f7 |
sort -d |
uniq |
cpanm 
-M$local -l$repo --self-contained ;
Version control what you install
git submodule add blah://.../site_perl;
perl Makefile.PL INSTALL_BASE=$PWD/site_perl;
cpanm --local-library=$PWD/site_perl;
If anyone asks, you know what non-core modules have
been installed.
If anything breaks, check out the last commit.
What else can we do with a path?
Ever fat-finger
a package?
package AcmeWigdit::Config;
use v5.30;
What else can we do with a path?
Ever fat-finger
a package?
Paths define
packages.
require_ok $path or skip ... ;
my ($sub)
= $path
=~ m{^.+?/lib/(.+?) [.]pm}x;
my $pkg = $sub =~ s{/}{::}g;
isa_ok $pkg, ‘UNIVERSAL’
or skip “$pkg not ‘UNIVERSAL’”;
$pkg->can( ‘VERSION’ )
or skip “$path missing $pkg”, 1;
What else can we do with a path?
Ever fat-finger
a package?
Paths define
packages.
All defined
packages are
UNIVERSAL.
require_ok $path or skip ... ;
my ($sub)
= $path
=~ m{^.+?/lib/(.+?) [.]pm}x;
my $pkg = $sub =~ s{/}{::}g;
isa_ok $pkg, ‘UNIVERSAL’
or skip “$pkg not ‘UNIVERSAL’”;
$pkg->can( ‘VERSION’ )
or skip “$path missing $pkg”, 1;
What else can we do with a path?
Ever fat-finger
a package?
Paths define
packages.
VERSION is
UNIVERSAL.
require_ok $path or skip ... ;
my ($sub)
= $path
=~ m{^.+?/lib/(.+?) [.]pm}x;
my $pkg = $sub =~ s{/}{::}g;
is_ok $pkg, ‘UNIVERSAL’
or skip “$pkg not ‘UNIVERSAL’”;
$pkg->can( ‘VERSION’ )
or skip “$path missing $pkg”, 1;
Minor issue with paths
Packages are related to paths.
We know that now.
In 2000 not everyone got that.
Minor issue with paths
What about:
package <basename>;
use lib <every directory everywhere>;
It works, but there are better ways...
Minor issue with paths
One pattern: Overlapping product of evolution.
./lib/AcmeWig/
./lib/AcmeWig/Config.pm AcmeWig::Config
Minor issue with paths
One pattern: Overlapping product of evolution.
./lib/AcmeWig/
./lib/AcmeWig/Config.pm AcmeWig::Config
Minor issue with paths
One pattern: Overlapping product of evolution.
./lib/AcmeWig/
./lib/AcmeWig/Config.pm AcmeWig::Config
./lib/AcmeWig/Acme/Config.pm Acme::Config
Minor issue with paths
One pattern: Overlapping product of evolution.
./lib/AcmeWig/
./lib/AcmeWig/Config.pm AcmeWig::Config
./lib/AcmeWig/Acme/Config.pm Acme::Config
What’s the expected package?
Solving nested paths
Say we figure out the paths:
qw
(
./lib/AcmeWig
./lib/AcmeWig/Acme
./lib/AcmeWig/Acme/Plastic/External
./lib/AcmeWig/Acme/Plastic/Internal
./lib/AcmeWig/Acme/Plastic/Wrappers
./lib/AcmeWig/Plastic
./lib/AcmeWig/AcmeWig/External/Plastic
)
Solving nested paths
We can obviously iterate them...
qw
(
./lib/AcmeWig
./lib/AcmeWig/Acme
./lib/AcmeWig/Acme/Plastic/External
./lib/AcmeWig/Acme/Plastic/Internal
./lib/AcmeWig/Acme/Plastic/Wrappers
./lib/AcmeWig/Plastic
./lib/AcmeWig/AcmeWig/External/Plastic
)
Solving nested paths
Q: But how do we avoid duplicate tests?
qw
(
./lib/AcmeWig
./lib/AcmeWig/Acme
./lib/AcmeWig/Acme/Plastic/External
./lib/AcmeWig/Acme/Plastic/Internal
./lib/AcmeWig/Acme/Plastic/Wrappers
./lib/AcmeWig/Plastic
./lib/AcmeWig/AcmeWig/External/Plastic
)
Solving nested paths
A: Sort them by length.
qw
(
./lib/AcmeWig/AcmeWig/External/Plastic
./lib/AcmeWig/Acme/Plastic/External
./lib/AcmeWig/Acme/Plastic/Internal
./lib/AcmeWig/Acme/Plastic/Wrappers
./lib/AcmeWig/Plastic
./lib/AcmeWig/Acme
./lib/AcmeWig
)
Solving nested paths
A: Sort them by length & exclude known paths.
qw
(
./lib/AcmeWig/AcmeWig/External/Plastic
./lib/AcmeWig/Acme/Plastic/External
./lib/AcmeWig/Acme/Plastic/Internal
./lib/AcmeWig/Acme/Plastic/Wrappers
./lib/AcmeWig/Plastic
./lib/AcmeWig/Acme
./lib/AcmeWig
)
Solving nested paths
my %prune = ();
my $wanted = sub
{
-d && exists $prune{ $File::Find::dir }
? $File::Find::prune = 1
: $File::Find::Path->$install_test_symlink
};
for my $dir ( @dirs_sorted_by_length )
{
find $wanted, $dir;
$prune{ $dir } = ();
}
Solving nested paths
my %prune = ();
my $wanted = sub
{
-d && exists $prune{ $File::Find::dir }
? $File::Find::prune = 1
: $File::Find::Path->$install_test_symlink
};
for my $dir ( @dirs_sorted_by_length )
{
find $wanted, $dir;
$prune{ $dir } = (); # don’t revisit
}
Testing nested paths
Catch: The test needs to know its ‘root’ directory.
Has to subtract that to get the expected package.
Testing nested paths
Catch: The test needs to know its ‘root’ directory.
Has to subtract that to get the expected package.
Fix: Double the separator.
~path~to~repo~lib~AcmeWig~Acme~Config.pm.t
Testing nested paths
Catch: The test needs to know its ‘root’ directory.
Has to subtract that to get the expected package.
Fix: Double the separator.
~path~to~repo~lib~AcmeWig~~Acme~Config.pm.t
Testing nested paths
Catch: The test needs to know its ‘root’ directory.
Has to subtract that to get the expected package.
Fix: ‘//’ works fine with require_ok.
~path~to~repo~lib~AcmeWig~~Acme~Config.pm.t
/path/to/repo/lib/AcmeWig//Acme/Config.pm.t
Testing nested paths
Catch: The test needs to know its ‘root’ directory.
Has to subtract that to get the expected package.
Fix: Gives sub-path for module.
~path~to~repo~lib~AcmeWig~~Acme~Config.pm.t
Acme~Config.pm.t
Testing nested paths
Catch: The test needs to know its ‘root’ directory.
Has to subtract that to get the expected package.
Fix: basename with s{~}{::}.
~path~to~repo~lib~AcmeWig~~Acme~Config.pm.t
Acme::Config
Testing nested paths
Catch: The test needs to know its ‘root’ directory.
Has to subtract that to get the expected package.
Save the search dir’s length and substr in a ‘~’:
my $l = length $dir;
...
substr $symlink, $l, 0, ‘~’;
Skipping test groups
We started with 75_000 files.
Found some dirs we didn’t want to test.
Fix:
my @no_test = qw( … );
my %prune = ();
@prune{ @no_test } = ();
Net result
We were able to test 45_000+ files each night.
Found missing modules.
Found outdated syntax.
Managed to get it all working.
Largey with “require_ok”.
Knowledge is power
Unit tests are useful.
Provide automated, reproducable results.
No excuse for “It may not work in v5.30.”
We can know.
And fix whatever “it” is.
For some definition of Perl5
Perl7 is coming.
Adopting it requires showing that “it works.”
Which means finding where it doesn’t.
Unit testing all of CPAN.
Unit testing all of everything.
For some definition of Perl5
Perl7 is coming.
Adopting it requires showing that “it works.”
Which means finding where it doesn’t.
Unit testing all of CPAN.
Unit testing all of everything.
We just have to be lazy about it.
Units of your very own!
In case you don’t like pasting from PDF:
https://gitlab.com/lembark/perl5-unit-tests
If anyone wants to work on this we can release it as
App::Testify or Test2::Unitz …
Be nice to have a migration suite for Perl7.
Units of your very own!
A wider range of unit tests is shown at:
https://www.slideshare.net/lembark/path-toknowlege
With a little bit of work we could get them all on CPAN.

More Related Content

What's hot

The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.Workhorse Computing
 
BSDM with BASH: Command Interpolation
BSDM with BASH: Command InterpolationBSDM with BASH: Command Interpolation
BSDM with BASH: Command InterpolationWorkhorse Computing
 
Shared Object images in Docker: What you need is what you want.
Shared Object images in Docker: What you need is what you want.Shared Object images in Docker: What you need is what you want.
Shared Object images in Docker: What you need is what you want.Workhorse Computing
 
Replacing "exec" with a type and provider: Return manifests to a declarative ...
Replacing "exec" with a type and provider: Return manifests to a declarative ...Replacing "exec" with a type and provider: Return manifests to a declarative ...
Replacing "exec" with a type and provider: Return manifests to a declarative ...Puppet
 
Puppet camp chicago-automated_testing2
Puppet camp chicago-automated_testing2Puppet camp chicago-automated_testing2
Puppet camp chicago-automated_testing2nottings
 
Puppet: What _not_ to do
Puppet: What _not_ to doPuppet: What _not_ to do
Puppet: What _not_ to doPuppet
 
Test-Driven Puppet Development - PuppetConf 2014
Test-Driven Puppet Development - PuppetConf 2014Test-Driven Puppet Development - PuppetConf 2014
Test-Driven Puppet Development - PuppetConf 2014Puppet
 
Puppet loves RSpec, why you should, too
Puppet loves RSpec, why you should, tooPuppet loves RSpec, why you should, too
Puppet loves RSpec, why you should, tooDennis Rowe
 
What you need to remember when you upload to CPAN
What you need to remember when you upload to CPANWhat you need to remember when you upload to CPAN
What you need to remember when you upload to CPANcharsbar
 
Better detection of what modules are used by some Perl 5 code
Better detection of what modules are used by some Perl 5 codeBetter detection of what modules are used by some Perl 5 code
Better detection of what modules are used by some Perl 5 codecharsbar
 
Can you upgrade to Puppet 4.x?
Can you upgrade to Puppet 4.x?Can you upgrade to Puppet 4.x?
Can you upgrade to Puppet 4.x?Martin Alfke
 
Release with confidence
Release with confidenceRelease with confidence
Release with confidenceJohn Congdon
 
SDPHP - Percona Toolkit (It's Basically Magic)
SDPHP - Percona Toolkit (It's Basically Magic)SDPHP - Percona Toolkit (It's Basically Magic)
SDPHP - Percona Toolkit (It's Basically Magic)Robert Swisher
 
Working with databases in Perl
Working with databases in PerlWorking with databases in Perl
Working with databases in PerlLaurent Dami
 
Test Driven Development with Puppet - PuppetConf 2014
Test Driven Development with Puppet - PuppetConf 2014Test Driven Development with Puppet - PuppetConf 2014
Test Driven Development with Puppet - PuppetConf 2014Puppet
 
PL/Perl - New Features in PostgreSQL 9.0
PL/Perl - New Features in PostgreSQL 9.0PL/Perl - New Features in PostgreSQL 9.0
PL/Perl - New Features in PostgreSQL 9.0Tim Bunce
 

What's hot (20)

The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.The $path to knowledge: What little it take to unit-test Perl.
The $path to knowledge: What little it take to unit-test Perl.
 
Get your teeth into Plack
Get your teeth into PlackGet your teeth into Plack
Get your teeth into Plack
 
BSDM with BASH: Command Interpolation
BSDM with BASH: Command InterpolationBSDM with BASH: Command Interpolation
BSDM with BASH: Command Interpolation
 
Shared Object images in Docker: What you need is what you want.
Shared Object images in Docker: What you need is what you want.Shared Object images in Docker: What you need is what you want.
Shared Object images in Docker: What you need is what you want.
 
Replacing "exec" with a type and provider: Return manifests to a declarative ...
Replacing "exec" with a type and provider: Return manifests to a declarative ...Replacing "exec" with a type and provider: Return manifests to a declarative ...
Replacing "exec" with a type and provider: Return manifests to a declarative ...
 
Puppet camp chicago-automated_testing2
Puppet camp chicago-automated_testing2Puppet camp chicago-automated_testing2
Puppet camp chicago-automated_testing2
 
Puppet: What _not_ to do
Puppet: What _not_ to doPuppet: What _not_ to do
Puppet: What _not_ to do
 
Test-Driven Puppet Development - PuppetConf 2014
Test-Driven Puppet Development - PuppetConf 2014Test-Driven Puppet Development - PuppetConf 2014
Test-Driven Puppet Development - PuppetConf 2014
 
Troubleshooting Puppet
Troubleshooting PuppetTroubleshooting Puppet
Troubleshooting Puppet
 
Puppet loves RSpec, why you should, too
Puppet loves RSpec, why you should, tooPuppet loves RSpec, why you should, too
Puppet loves RSpec, why you should, too
 
What you need to remember when you upload to CPAN
What you need to remember when you upload to CPANWhat you need to remember when you upload to CPAN
What you need to remember when you upload to CPAN
 
Better detection of what modules are used by some Perl 5 code
Better detection of what modules are used by some Perl 5 codeBetter detection of what modules are used by some Perl 5 code
Better detection of what modules are used by some Perl 5 code
 
Can you upgrade to Puppet 4.x?
Can you upgrade to Puppet 4.x?Can you upgrade to Puppet 4.x?
Can you upgrade to Puppet 4.x?
 
Release with confidence
Release with confidenceRelease with confidence
Release with confidence
 
SDPHP - Percona Toolkit (It's Basically Magic)
SDPHP - Percona Toolkit (It's Basically Magic)SDPHP - Percona Toolkit (It's Basically Magic)
SDPHP - Percona Toolkit (It's Basically Magic)
 
Lies, Damn Lies, and Benchmarks
Lies, Damn Lies, and BenchmarksLies, Damn Lies, and Benchmarks
Lies, Damn Lies, and Benchmarks
 
Working with databases in Perl
Working with databases in PerlWorking with databases in Perl
Working with databases in Perl
 
Puppet modules for Fun and Profit
Puppet modules for Fun and ProfitPuppet modules for Fun and Profit
Puppet modules for Fun and Profit
 
Test Driven Development with Puppet - PuppetConf 2014
Test Driven Development with Puppet - PuppetConf 2014Test Driven Development with Puppet - PuppetConf 2014
Test Driven Development with Puppet - PuppetConf 2014
 
PL/Perl - New Features in PostgreSQL 9.0
PL/Perl - New Features in PostgreSQL 9.0PL/Perl - New Features in PostgreSQL 9.0
PL/Perl - New Features in PostgreSQL 9.0
 

Similar to Unit Testing Lots of Perl

Continuous Integration Testing in Django
Continuous Integration Testing in DjangoContinuous Integration Testing in Django
Continuous Integration Testing in DjangoKevin Harvey
 
Ansible roles done right
Ansible roles done rightAnsible roles done right
Ansible roles done rightDan Vaida
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everythingnoelrap
 
Can you upgrade to Puppet 4.x? (Beginner) Can you upgrade to Puppet 4.x? (Beg...
Can you upgrade to Puppet 4.x? (Beginner) Can you upgrade to Puppet 4.x? (Beg...Can you upgrade to Puppet 4.x? (Beginner) Can you upgrade to Puppet 4.x? (Beg...
Can you upgrade to Puppet 4.x? (Beginner) Can you upgrade to Puppet 4.x? (Beg...Puppet
 
Puppet Camp Duesseldorf 2014: Martin Alfke - Can you upgrade to puppet 4.x?
Puppet Camp Duesseldorf 2014: Martin Alfke - Can you upgrade to puppet 4.x?Puppet Camp Duesseldorf 2014: Martin Alfke - Can you upgrade to puppet 4.x?
Puppet Camp Duesseldorf 2014: Martin Alfke - Can you upgrade to puppet 4.x?NETWAYS
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl TechniquesDave Cross
 
How I hack on puppet modules
How I hack on puppet modulesHow I hack on puppet modules
How I hack on puppet modulesKris Buytaert
 
Drupal Camp Brighton 2015: Ansible Drupal Medicine show
Drupal Camp Brighton 2015: Ansible Drupal Medicine showDrupal Camp Brighton 2015: Ansible Drupal Medicine show
Drupal Camp Brighton 2015: Ansible Drupal Medicine showGeorge Boobyer
 
modern module development - Ken Barber 2012 Edinburgh Puppet Camp
modern module development - Ken Barber 2012 Edinburgh Puppet Campmodern module development - Ken Barber 2012 Edinburgh Puppet Camp
modern module development - Ken Barber 2012 Edinburgh Puppet CampPuppet
 
Angular JS in 2017
Angular JS in 2017Angular JS in 2017
Angular JS in 2017Ayush Sharma
 
Ansible with oci
Ansible with ociAnsible with oci
Ansible with ociDonghuKIM2
 
Automate Yo'self -- SeaGL
Automate Yo'self -- SeaGL Automate Yo'self -- SeaGL
Automate Yo'self -- SeaGL John Anderson
 

Similar to Unit Testing Lots of Perl (20)

Metadata-driven Testing
Metadata-driven TestingMetadata-driven Testing
Metadata-driven Testing
 
Continuous Integration Testing in Django
Continuous Integration Testing in DjangoContinuous Integration Testing in Django
Continuous Integration Testing in Django
 
Ansible roles done right
Ansible roles done rightAnsible roles done right
Ansible roles done right
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Ansible testing
Ansible   testingAnsible   testing
Ansible testing
 
Can you upgrade to Puppet 4.x? (Beginner) Can you upgrade to Puppet 4.x? (Beg...
Can you upgrade to Puppet 4.x? (Beginner) Can you upgrade to Puppet 4.x? (Beg...Can you upgrade to Puppet 4.x? (Beginner) Can you upgrade to Puppet 4.x? (Beg...
Can you upgrade to Puppet 4.x? (Beginner) Can you upgrade to Puppet 4.x? (Beg...
 
Puppet Camp Duesseldorf 2014: Martin Alfke - Can you upgrade to puppet 4.x?
Puppet Camp Duesseldorf 2014: Martin Alfke - Can you upgrade to puppet 4.x?Puppet Camp Duesseldorf 2014: Martin Alfke - Can you upgrade to puppet 4.x?
Puppet Camp Duesseldorf 2014: Martin Alfke - Can you upgrade to puppet 4.x?
 
Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
 
How I hack on puppet modules
How I hack on puppet modulesHow I hack on puppet modules
How I hack on puppet modules
 
Drupal Camp Brighton 2015: Ansible Drupal Medicine show
Drupal Camp Brighton 2015: Ansible Drupal Medicine showDrupal Camp Brighton 2015: Ansible Drupal Medicine show
Drupal Camp Brighton 2015: Ansible Drupal Medicine show
 
modern module development - Ken Barber 2012 Edinburgh Puppet Camp
modern module development - Ken Barber 2012 Edinburgh Puppet Campmodern module development - Ken Barber 2012 Edinburgh Puppet Camp
modern module development - Ken Barber 2012 Edinburgh Puppet Camp
 
Angular JS in 2017
Angular JS in 2017Angular JS in 2017
Angular JS in 2017
 
Autotesting rails app
Autotesting rails appAutotesting rails app
Autotesting rails app
 
Testing Ansible
Testing AnsibleTesting Ansible
Testing Ansible
 
Docker perl build
Docker perl buildDocker perl build
Docker perl build
 
IntroTestMore
IntroTestMoreIntroTestMore
IntroTestMore
 
IntroTestMore
IntroTestMoreIntroTestMore
IntroTestMore
 
Ansible with oci
Ansible with ociAnsible with oci
Ansible with oci
 
Automate Yo' Self
Automate Yo' SelfAutomate Yo' Self
Automate Yo' Self
 
Automate Yo'self -- SeaGL
Automate Yo'self -- SeaGL Automate Yo'self -- SeaGL
Automate Yo'self -- SeaGL
 

More from Workhorse Computing

Wheels we didn't re-invent: Perl's Utility Modules
Wheels we didn't re-invent: Perl's Utility ModulesWheels we didn't re-invent: Perl's Utility Modules
Wheels we didn't re-invent: Perl's Utility ModulesWorkhorse Computing
 
Paranormal statistics: Counting What Doesn't Add Up
Paranormal statistics: Counting What Doesn't Add UpParanormal statistics: Counting What Doesn't Add Up
Paranormal statistics: Counting What Doesn't Add UpWorkhorse Computing
 
Generating & Querying Calendar Tables in Posgresql
Generating & Querying Calendar Tables in PosgresqlGenerating & Querying Calendar Tables in Posgresql
Generating & Querying Calendar Tables in PosgresqlWorkhorse Computing
 
Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!Workhorse Computing
 
The W-curve and its application.
The W-curve and its application.The W-curve and its application.
The W-curve and its application.Workhorse Computing
 
Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Workhorse Computing
 
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6Workhorse Computing
 
Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Workhorse Computing
 
Selenium Sandwich Part 1: Data driven Selenium
Selenium Sandwich Part 1: Data driven Selenium Selenium Sandwich Part 1: Data driven Selenium
Selenium Sandwich Part 1: Data driven Selenium Workhorse Computing
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Workhorse Computing
 

More from Workhorse Computing (18)

Wheels we didn't re-invent: Perl's Utility Modules
Wheels we didn't re-invent: Perl's Utility ModulesWheels we didn't re-invent: Perl's Utility Modules
Wheels we didn't re-invent: Perl's Utility Modules
 
mro-every.pdf
mro-every.pdfmro-every.pdf
mro-every.pdf
 
Paranormal statistics: Counting What Doesn't Add Up
Paranormal statistics: Counting What Doesn't Add UpParanormal statistics: Counting What Doesn't Add Up
Paranormal statistics: Counting What Doesn't Add Up
 
Generating & Querying Calendar Tables in Posgresql
Generating & Querying Calendar Tables in PosgresqlGenerating & Querying Calendar Tables in Posgresql
Generating & Querying Calendar Tables in Posgresql
 
Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!
 
Findbin libs
Findbin libsFindbin libs
Findbin libs
 
The W-curve and its application.
The W-curve and its application.The W-curve and its application.
The W-curve and its application.
 
Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.
 
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
 
Neatly folding-a-tree
Neatly folding-a-treeNeatly folding-a-tree
Neatly folding-a-tree
 
Light my-fuse
Light my-fuseLight my-fuse
Light my-fuse
 
Paranormal stats
Paranormal statsParanormal stats
Paranormal stats
 
Putting some "logic" in LVM.
Putting some "logic" in LVM.Putting some "logic" in LVM.
Putting some "logic" in LVM.
 
Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.
 
Selenium sandwich-2
Selenium sandwich-2Selenium sandwich-2
Selenium sandwich-2
 
Selenium Sandwich Part 1: Data driven Selenium
Selenium Sandwich Part 1: Data driven Selenium Selenium Sandwich Part 1: Data driven Selenium
Selenium Sandwich Part 1: Data driven Selenium
 
Designing net-aws-glacier
Designing net-aws-glacierDesigning net-aws-glacier
Designing net-aws-glacier
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6
 

Recently uploaded

presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businesspanagenda
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesBoston Institute of Analytics
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...apidays
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Principled Technologies
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyKhushali Kathiriya
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 

Recently uploaded (20)

presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 

Unit Testing Lots of Perl

  • 1. Getting There from Here: Unit testing a few generations of code. Steven Lembark Workhorse Computing lembark@wrkhors.com
  • 2. perl 5.8 Fun isn’t it? A generation of nothing. No improvements in Perl. No advances in CPAN. No reason to upgrade. 20 years of nada... Ask all the programmers stuck with 5.8.
  • 3. perl 5.8 Q: Why are we stuck with 5.8? A: /usr/bin/perl is 5.8 Q: Why are we stuck with /usr/bin/perl? A: ???
  • 4. perl 5.8 Q: Why are we stuck with 5.8? A: /usr/bin/perl is 5.8 Q: Why are we stuck with /usr/bin/perl? A: Because “it worked”.
  • 5. perl 5.8 Q: Why are we stuck with 5.8? A: /usr/bin/perl is 5.8 Q: Why are we stuck with /usr/bin/perl? A: Because something else “might not” work.
  • 6. perl 5.8 Q: Why are we stuck with 5.8? A: /usr/bin/perl is 5.8 Q: Why are we stuck with /usr/bin/perl? A: Because we can’t show that 5.X works. (for some definition of Christmas > 5.8)
  • 7. perl 5.X Q: Why can’t we show that 5.X works? A: Because Christmas came after 2002.
  • 8. perl 5.X Q: Why can’t we show that 5.X works? A: Because we stopped testing our code.
  • 9. perl 5.X Q: Why can’t we show that 5.X works? A: Because they stopped testing our code.
  • 10. perl 5.X Q: Why can’t we show that 5.X works? A: Because we have worked around it for way too long.
  • 11. perl 5.X Q: Why can’t we show that 5.X works? A: Because we have worked around it for way too long. perlbrew, all of the other approaches to dodging /usr/bin/perl.
  • 12. OK how do we fix it? Prove that “it works”. With testing. In one client’s case: Lots and lots of testing.
  • 13. OK how do we fix it? Prove that “it works”. With testing. In one client’s case: Lots and lots of testing. 75_000 modules worth of testing.
  • 14. OK how do we fix it? Prove that “it works”. With testing. In one client’s case: Lots and lots of testing. 75_000 modules worth of testing. Q: But how did you write 75_000 unit tests?
  • 15. OK how do we fix it? Prove that “it works”. With testing. In one client’s case: Lots and lots of testing. 75_000 modules worth of automated testing. A: Lazyness.
  • 16. Syntax checks are the first pass. Unit tests get a bad rap. “They don’t really test anything.”
  • 17. Syntax checks are the first pass. Unit tests get a bad rap. “They don’t really test anything.” Except whether 75_000 modules compile. Code that was written on 5.8. That now to run on 5.20+.
  • 18. How do we test that much code? Metadata driven testing: Encode data into a test. The standard test is data-driven. Tests each validate one small part of a module.
  • 19. How do we test that much code? Q: How do you write that many tests? A: Don’t.
  • 20. How do we test that much code? Q: How do you write that many tests? A: Don’t. Symlinks are your friends.
  • 21. Simple basic test: require_ok Run “require_ok $module” for every *.pm. Simple, basic, obvious test for syntax errors. … missing modules. … botched system paths. … all sorts of things.
  • 22. Simple basic test: require_ok Run “require_ok $module” for every *.pm. Simple, basic, obvious test for syntax errors. Nice thing: $module can be a path. Even an absolute path.
  • 23. Passing a path “require_ok $path” One argument. Q: How do we pass it? A: In the basename of a test.
  • 24. Basic unit test use v5.30; use Test::More; use File::Basename; my $base0 = basename $0, '.t'; my $s = substr $base0, 0, 1; my $path = join ‘/’, split m{[$s]}, $base0; require_ok $path; done_testing __END__
  • 25. Basic unit test Install them using shell. #!/bin/bash dir=$(cd $(dirname $0)/../..; pwd -L); cd “$dir/t/01-units”; find $dir/lib -name ‘*pm’ | while read i do base=”$(echo $i | tr ‘/’ ‘~’).t”; ln -fs ../bin/01-unit_t ./$base; done
  • 26. Basic unit test Install them using shell. From ./t/bin #!/bin/bash dir=$(cd $(dirname $0)/../..; pwd -L); cd “$dir/t/01-units”; find $dir/lib -name ‘*pm’ | while read i do base=”$(echo $i | tr ‘/’ ‘~’).t”; ln -fs ../bin/01-unit_t ./$base; done
  • 27. Basic unit test Install them using shell. Path to basename. #!/bin/bash dir=$(cd $(dirname $0)/../..; pwd -L); cd “$dir/t/01-units”; find $dir/lib -name ‘*pm’ | while read i do base=”$(echo $i | tr ‘/’ ‘~’).t”; ln -fs ../bin/01-unit_t ./$base; done
  • 28. Basic unit test Now you know if it compiles. prove t/01-units;
  • 29. Basic unit test Now you know if it compiles. A bit faster. prove --jobs=8 t/01-units;
  • 30. Basic unit test Now you know if it compiles. At 3am without watching. args=(--jobs=8 --state=save ); prove ${args[*]} t/01-units;
  • 31. Basic unit test Now you know if it compiles. At 8am knowing what failed. args=(--jobs=8 --state=save,failed ); prove ${args[*]} t/01-units;
  • 32. Basic unit test Now you know if it compiles. Missing module anyone? args=(--jobs=8 --state=save,failed ); prove ${args[*]} t/01-units Can't locate Foo/Bar.pm in @INC (you may need to install the Foo::Bar module) (@INC contains: /opt/perl/5.30/lib/site_perl/5.30.1/x8 6_64-linux
  • 33. Basic unit test Now you know if it compiles. Fine: Install them. args=(--jobs=8 --state=save,failed ); prove ${args[*]} t/01-units | grep ‘you may need to install the’ | cut -d’(‘ -f2 | cut -d’ ‘ -f7 | sort -d | uniq | cpanm ;
  • 34. Basic unit test Now you know if it compiles. Local mirror provides pre- verified modules. args=(--jobs=8 --state=save,failed ); prove ${args[*]} t/01-units | grep ‘you may need to install the’ | cut -d’(‘ -f2 | cut -d’ ‘ -f7 | sort -d | uniq | cpanm -M$local ;
  • 35. Basic unit test Now you know if it compiles. Local library simplifies auto- install. args=(--jobs=8 --state=save,failed ); prove ${args[*]} t/01-units | grep ‘you may need to install the’ | cut -d’(‘ -f2 | cut -d’ ‘ -f7 | sort -d | uniq | cpanm -M$local -l$repo --self-contained ;
  • 36. Basic unit test Start with a virgin /opt/perl. Keep testing until it’s all use- able. args=(--jobs=8 --state=save,failed ); prove ${args[*]} t/01-units | grep ‘you may need to install the’ | cut -d’(‘ -f2 | cut -d’ ‘ -f7 | sort -d | uniq | cpanm -M$local -l$repo --self-contained ;
  • 37. Version control what you install git submodule add blah://.../site_perl; perl Makefile.PL INSTALL_BASE=$PWD/site_perl; cpanm --local-library=$PWD/site_perl; If anyone asks, you know what non-core modules have been installed. If anything breaks, check out the last commit.
  • 38. What else can we do with a path? Ever fat-finger a package? package AcmeWigdit::Config; use v5.30;
  • 39. What else can we do with a path? Ever fat-finger a package? Paths define packages. require_ok $path or skip ... ; my ($sub) = $path =~ m{^.+?/lib/(.+?) [.]pm}x; my $pkg = $sub =~ s{/}{::}g; isa_ok $pkg, ‘UNIVERSAL’ or skip “$pkg not ‘UNIVERSAL’”; $pkg->can( ‘VERSION’ ) or skip “$path missing $pkg”, 1;
  • 40. What else can we do with a path? Ever fat-finger a package? Paths define packages. All defined packages are UNIVERSAL. require_ok $path or skip ... ; my ($sub) = $path =~ m{^.+?/lib/(.+?) [.]pm}x; my $pkg = $sub =~ s{/}{::}g; isa_ok $pkg, ‘UNIVERSAL’ or skip “$pkg not ‘UNIVERSAL’”; $pkg->can( ‘VERSION’ ) or skip “$path missing $pkg”, 1;
  • 41. What else can we do with a path? Ever fat-finger a package? Paths define packages. VERSION is UNIVERSAL. require_ok $path or skip ... ; my ($sub) = $path =~ m{^.+?/lib/(.+?) [.]pm}x; my $pkg = $sub =~ s{/}{::}g; is_ok $pkg, ‘UNIVERSAL’ or skip “$pkg not ‘UNIVERSAL’”; $pkg->can( ‘VERSION’ ) or skip “$path missing $pkg”, 1;
  • 42. Minor issue with paths Packages are related to paths. We know that now. In 2000 not everyone got that.
  • 43. Minor issue with paths What about: package <basename>; use lib <every directory everywhere>; It works, but there are better ways...
  • 44. Minor issue with paths One pattern: Overlapping product of evolution. ./lib/AcmeWig/ ./lib/AcmeWig/Config.pm AcmeWig::Config
  • 45. Minor issue with paths One pattern: Overlapping product of evolution. ./lib/AcmeWig/ ./lib/AcmeWig/Config.pm AcmeWig::Config
  • 46. Minor issue with paths One pattern: Overlapping product of evolution. ./lib/AcmeWig/ ./lib/AcmeWig/Config.pm AcmeWig::Config ./lib/AcmeWig/Acme/Config.pm Acme::Config
  • 47. Minor issue with paths One pattern: Overlapping product of evolution. ./lib/AcmeWig/ ./lib/AcmeWig/Config.pm AcmeWig::Config ./lib/AcmeWig/Acme/Config.pm Acme::Config What’s the expected package?
  • 48. Solving nested paths Say we figure out the paths: qw ( ./lib/AcmeWig ./lib/AcmeWig/Acme ./lib/AcmeWig/Acme/Plastic/External ./lib/AcmeWig/Acme/Plastic/Internal ./lib/AcmeWig/Acme/Plastic/Wrappers ./lib/AcmeWig/Plastic ./lib/AcmeWig/AcmeWig/External/Plastic )
  • 49. Solving nested paths We can obviously iterate them... qw ( ./lib/AcmeWig ./lib/AcmeWig/Acme ./lib/AcmeWig/Acme/Plastic/External ./lib/AcmeWig/Acme/Plastic/Internal ./lib/AcmeWig/Acme/Plastic/Wrappers ./lib/AcmeWig/Plastic ./lib/AcmeWig/AcmeWig/External/Plastic )
  • 50. Solving nested paths Q: But how do we avoid duplicate tests? qw ( ./lib/AcmeWig ./lib/AcmeWig/Acme ./lib/AcmeWig/Acme/Plastic/External ./lib/AcmeWig/Acme/Plastic/Internal ./lib/AcmeWig/Acme/Plastic/Wrappers ./lib/AcmeWig/Plastic ./lib/AcmeWig/AcmeWig/External/Plastic )
  • 51. Solving nested paths A: Sort them by length. qw ( ./lib/AcmeWig/AcmeWig/External/Plastic ./lib/AcmeWig/Acme/Plastic/External ./lib/AcmeWig/Acme/Plastic/Internal ./lib/AcmeWig/Acme/Plastic/Wrappers ./lib/AcmeWig/Plastic ./lib/AcmeWig/Acme ./lib/AcmeWig )
  • 52. Solving nested paths A: Sort them by length & exclude known paths. qw ( ./lib/AcmeWig/AcmeWig/External/Plastic ./lib/AcmeWig/Acme/Plastic/External ./lib/AcmeWig/Acme/Plastic/Internal ./lib/AcmeWig/Acme/Plastic/Wrappers ./lib/AcmeWig/Plastic ./lib/AcmeWig/Acme ./lib/AcmeWig )
  • 53. Solving nested paths my %prune = (); my $wanted = sub { -d && exists $prune{ $File::Find::dir } ? $File::Find::prune = 1 : $File::Find::Path->$install_test_symlink }; for my $dir ( @dirs_sorted_by_length ) { find $wanted, $dir; $prune{ $dir } = (); }
  • 54. Solving nested paths my %prune = (); my $wanted = sub { -d && exists $prune{ $File::Find::dir } ? $File::Find::prune = 1 : $File::Find::Path->$install_test_symlink }; for my $dir ( @dirs_sorted_by_length ) { find $wanted, $dir; $prune{ $dir } = (); # don’t revisit }
  • 55. Testing nested paths Catch: The test needs to know its ‘root’ directory. Has to subtract that to get the expected package.
  • 56. Testing nested paths Catch: The test needs to know its ‘root’ directory. Has to subtract that to get the expected package. Fix: Double the separator. ~path~to~repo~lib~AcmeWig~Acme~Config.pm.t
  • 57. Testing nested paths Catch: The test needs to know its ‘root’ directory. Has to subtract that to get the expected package. Fix: Double the separator. ~path~to~repo~lib~AcmeWig~~Acme~Config.pm.t
  • 58. Testing nested paths Catch: The test needs to know its ‘root’ directory. Has to subtract that to get the expected package. Fix: ‘//’ works fine with require_ok. ~path~to~repo~lib~AcmeWig~~Acme~Config.pm.t /path/to/repo/lib/AcmeWig//Acme/Config.pm.t
  • 59. Testing nested paths Catch: The test needs to know its ‘root’ directory. Has to subtract that to get the expected package. Fix: Gives sub-path for module. ~path~to~repo~lib~AcmeWig~~Acme~Config.pm.t Acme~Config.pm.t
  • 60. Testing nested paths Catch: The test needs to know its ‘root’ directory. Has to subtract that to get the expected package. Fix: basename with s{~}{::}. ~path~to~repo~lib~AcmeWig~~Acme~Config.pm.t Acme::Config
  • 61. Testing nested paths Catch: The test needs to know its ‘root’ directory. Has to subtract that to get the expected package. Save the search dir’s length and substr in a ‘~’: my $l = length $dir; ... substr $symlink, $l, 0, ‘~’;
  • 62. Skipping test groups We started with 75_000 files. Found some dirs we didn’t want to test. Fix: my @no_test = qw( … ); my %prune = (); @prune{ @no_test } = ();
  • 63. Net result We were able to test 45_000+ files each night. Found missing modules. Found outdated syntax. Managed to get it all working. Largey with “require_ok”.
  • 64. Knowledge is power Unit tests are useful. Provide automated, reproducable results. No excuse for “It may not work in v5.30.” We can know. And fix whatever “it” is.
  • 65. For some definition of Perl5 Perl7 is coming. Adopting it requires showing that “it works.” Which means finding where it doesn’t. Unit testing all of CPAN. Unit testing all of everything.
  • 66. For some definition of Perl5 Perl7 is coming. Adopting it requires showing that “it works.” Which means finding where it doesn’t. Unit testing all of CPAN. Unit testing all of everything. We just have to be lazy about it.
  • 67. Units of your very own! In case you don’t like pasting from PDF: https://gitlab.com/lembark/perl5-unit-tests If anyone wants to work on this we can release it as App::Testify or Test2::Unitz … Be nice to have a migration suite for Perl7.
  • 68. Units of your very own! A wider range of unit tests is shown at: https://www.slideshare.net/lembark/path-toknowlege With a little bit of work we could get them all on CPAN.