* Adriano R. Ferreira <[EMAIL PROTECTED]> [2004-02-09 01:56]:
> While writing tests for some of my code, I was faced with the issue
> of capturing what the code sends to STDOUT and STDERR. As I have not
> found a module to make it easy, I wrote a trivial code to do it. It
> is used like this:
> 
>       use Test::More tests => 2;
>       use Seize qw(seize release);
> 
>       my ($out, $err);
> 
>       seize \$out, \$err; # from here on, STDOUT and STDERR are seized
> 
>       print "1\n";
>       warn "2\n";
>       print "3\n";
>       eval { die "4\n"; }
> 
>       release; # here STDOUT and STDERR are released
> 
>       is($out, "1\n3\n");
>       is($err, "2\n3\n");
> 
> My doubt is if this deserves a module: maybe it would be better
> to use idioms commonly employed to this kind of task. Maybe
> there is some module I am overlooking that already does that.

For Perl 5.8 (and 5.6 with IO::String? I think it works there)
it's trivial:

    use Test::More tests => 2;
    use Carp;

    sub string_fh {
        open my $fh, '>', $_[0]
            or croak "Couldn't open scalar as filehandle: $!\n";
        return $fh;
    }

    my ($out, err);

    {
        local STDOUT, STDERR;
        *STDOUT = string_fh \$out;
        *STDERR = string_fh \$err;

        print "1\n";
        warn "2\n";
        print "3\n";
        eval { die "4\n"; }
    }

    is($out, "1\n3\n");
    is($err, "2\n3\n");

I believe this is a lot more robust than a tie() solution as
well. But it isn't really modularizable due to the local() and
there's not much anyway to stick in a module. Another approach I
only have the time to sketch here:

    use Test::More tests => 2;
    use Carp;

    sub redir_fh {
        my ($fh, $mode, $file);

        my $saved_fh;
        # dup $fh in here

        open $fh, $mode, $file
            # not sure this msg makes sense..
            or croak "Couldn't dup $fh to $file: $!\n";

        return bless [ $fh, $saved_fh ], 'RestoreHandle';
    }

    sub RestoreHandle::DESTROY {
        # use $self to dup the original FH back into the used one
    }

    my ($out, err);

    {
        my $s_out = redir_fh \*STDOUT, '>', \$out;
        my $s_err = redir_fh \*STDERR, '>', \$err;

        print "1\n";
        warn "2\n";
        print "3\n";
        eval { die "4\n"; }

    }
    # $s_* went out of scope here..

    is($out, "1\n3\n");
    is($err, "2\n3\n");

which uses lexical variables to implement dynamic scoping. :)

-- 
Regards,
Aristotle
 
"If you can't laugh at yourself, you don't take life seriously enough."

Reply via email to