Building simplicidade.org: notes, projects, and occasional rants

Perl testing and git pre-commit hooks

An article on Colin's blog mentioned a technique that I also use, using your test suite as a git pre-commit hook. I'll expand on some of his ideas, showing my own setup.

Before each commit you should run the full test-suite of your code. If your test-suite has grown so much that it takes a long time to run, it makes more sense to run a smaller part of it, and let the continuous integration system (you do have one, right?) report back any problems with your tree later.

At the very least you should make sure that all your modules compile correctly. And that means checking the 00-compile.t test file.

To make the whole process automatic, I use the following pre-commit-hook:

#!/bin/sh

## Set any ENV vars that your test suite requires here

exec prove -l -Q t/00*

I usually write a script called my-git-pre-commit-hook and exec that one from the .git/hook/pre-commit file, this way I can run it by hand if I need to.

I also assume that all tests named 00-* are important enough to run before each commit, which lets me add more specific tests at any point.

This solves half of the problem. I also don't want to keep editing the 00-compile.t file each time I add another module to my project. So I wrote it like this:

#!perl

use strict;
use warnings;
use Test::More;
use Path::Class;
use File::Find;

my $lib = dir('lib')->absolute->resolve;
find({
  bydepth => 1,
  no_chdir => 1,
  wanted => sub {
    my $m = $_;
    return unless $m =~ s/[.]pm$//;

    $m =~ s{^.*/lib/}{};
    $m =~ s{/}{::}g;
    use_ok($m) || BAIL_OUT("***** PROBLEMS LOADING FILE '$m'");
  },
}, $lib);

done_testing();

This code uses File::Find to list all the .pm files in your lib/ directory, and then tries to use them. It also use submodules before using the main module, courtesy of the bydepth switch.

If a problem is found, it immediately bail out of the entire test suite, mentioning the file that has problems. On previous versions it would test all modules and only bail out at the end if any had problems. I found that version less productive because the same problem would be reported multiple times.

This works out fine for my projects. It adds at most a couple of seconds to each commit (in the current project it adds less than 4 seconds) but I find that acceptable.

It would be interesting to write a 00-test-commited-files.pl file that would look at the files being updated by the commit and run the tests that cover them. This could be done with the help from Devel::CoverX::Covered but I haven't done it yet.