So here's why I just fixed that bug in Test::Builder.... The situation is thus: I've got a little script that's used to start/restart and stop the ssh daemon. I want to test that it can in fact stop the ssh daemon. Unfortunately, I ssh'd into that machine! So stopping it would be Bad. [1]
A TCL test engineer at TPC last year gave me a technique to handle this. It takes advantage of Perl's ability to replace existing functions at run-time. The sshd-stop script basically does its work like so: use utils; # this calls /etc/init.d/sshd stop serviceControl( NAME => 'sshd', ACTION => 'stop' ); # this then kills all existing connections. killall('HUP', 'sshd'); so the plan is to literally replace serviceControl() and killall() with stub functions that do nothing but check it got the right arguments. So write up a little module and run the ssh-stop script system( $^X, '-It/lib', '-Mreload_overrides', $Original_File ); is( $?, 0, 'ran myself ok' ); the clever thing there is we're loading a little module, reload_overrides.pm, that plants our stub functions. It looks like this... use Test::More 'no_plan'; # Here we tell the test to not use any numbers (because there were # probably tests output'd before us) and to not do end-of-test # checks. my $TB = Test::More->builder; $TB->use_numbers(0); $TB->no_ending(1); first thing that's going on here is we're telling the underlying Test::Builder object to not use numbers and don't do any extra processing at the end of the test. This is because it will be run in a seperate process from the test script itself. use util; use util::system; we load our victims so that when the sshd-stop script goes to load them it won't blow over our overrides. package util; ::can_ok('util', 'serviceControl'); no warnings 'redefine'; sub serviceControl { my(%params) = @_; ::pass('service control called'); ::is( $params{NAME}, 'sshd', 'serviceControl NAME == sshd' ); ::is( $params{ACTION}, 'stop', ' ACTION == stop' ); return 1; } so this is a simple override. First we check to make sure that serviceControl() is already loaded. Then we tell perl to not warn us about redefining the function. The function itself is very simple. It just reads in the arguments and makes sure its what we expect them to be. We assume that the real serviceControl() works, so all we care about is how its called. package util::system; ::can_ok('esmith::util::system', 'killall'); no warnings 'redefine'; sub killall { my($sig, @commands) = @_; ::pass('killall called'); ::is( $sig, 'HUP', ' with a HUP' ); ::is( @commands, 1, ' one command' ); ::is( $commands[0], 'sshd', ' for sshd' ); return 1; } and here's the same for killall(). It has to be called with some care, making sure that we take into account that the subprocess will be running some tests of its own to make the test counters all work out. # this will run 9 tests. my $tb = Test::More->builder; $tb->current_test($tb->current_test + 9); system( $^X, '-It/lib', -Mreload_overrides', $Original_File ); is( $?, 0, 'ran myself ok' ); and the result looks something like this: ok 1 - ran myself ok ok 2 - sshd lockfile touched ok - esmith::util->can(serviceControl) ok - esmith::util::system->can(killall) ok - service control called ok - serviceControl NAME == sshd ok - ACTION == stop ok - killall called ok - with a HUP ok - one command ok - for sshd ok 12 - ran myself ok ok 13 - ran myself ok ok 14 - sshd lockfile untouched 1..14 the unnumbered tests are coming from the subprocess. They're surrounded by the rest of the normal test script. And that's how you can test something that's otherwise destructive. Questions? :) [1] Its not just stop. Its "stop with extreme prejudice". It kills all existing connections. -- Michael G. Schwern <[EMAIL PROTECTED]> http://www.pobox.com/~schwern/ Perl Quality Assurance <[EMAIL PROTECTED]> Kwalitee Is Job One Beer still cheaper than crack!