peters has asked for the wisdom of the Perl Monks concerning the following question:

A friend has written some monitor-type tests (is machine X pingable, etc) using Test::More, and now his customer wants the output to be more "user friendly", say, like, "Test 6 of foo.t failed: POP server foo doesn't respond" etc. He asked how best to do this and I'm toying with a couple of ideas for intercepting the TAP output so I can munge it to the customer's desire.

First is to tie STDOUT and STDERR and grab the output that way. I haven't tried that yet because I experimented instead with redefining Test::Builder::_print() as follows via a module that would replace Test::More in my friend's test. Any suggestions for the best approach to take?

package Testicle; use Test::Builder::Module; require Test::More; our @ISA = qw(Test::More); use base 'Exporter'; our @EXPORT = @Test::More::EXPORT; Test::More->import; my $builder; BEGIN { $builder = Test::Builder::Module->builder; my $class = ref $builder; no strict 'refs'; no warnings 'redefine'; *{ "${class}::_print" } = sub { shift; "Have my way with @_" }; }
Peter Scott
Perl Medic

Replies are listed 'Best First'.
Re: Intercepting TAP output
by kyle (Abbot) on May 08, 2008 at 03:53 UTC

    I'd look at TAP::Parser first. Rather than rewire the original test program, just open a pipe from it and read its natural TAP output. Feed that to the parser and present the results however you like.

Re: Intercepting TAP output
by tachyon-II (Chaplain) on May 08, 2008 at 04:15 UTC

    It would appear to me that a poor initial choice has been made. Test::More would seem ill suited to being the framework for what seems like a network monitoring app. Can you explain the rationale for using Test::More?

    I would suggest using Tap::Parser if you wish to persist with this approach:

    use TAP::Parser; for my $file ( @test_files ) { my $parser = TAP::Parser->new( { source => $file } ); # do stuff with the parser }

    Alternatively (and seeing you appear to want to do things the hard way) because Test::More is built on top of Test::Builder all you need to do to redirect the output is modify the underlying T::B object using the published interface:

    use Test::More 'no_plan'; my $ok_fh = 'ok.txt'; my $fail_fh = 'fail.txt'; my $Test = Test::More->builder; $Test->output($ok_fh); $Test->failure_output($fail_fh); ok(1); ok(0,'bar');

    Unfortuantely Test::Builder is "broken" in that while it will accept a FH or filename to redirect to it will not accept an IO::String object, so it constrains you to writing to a file.

      Test::More would seem ill suited to being the framework for what seems like a network monitoring app.

      Both Randal Schwartz (here) and David N. Blank-Edelman (here, p. 522) consider it a plausible idea.

      Well done is better than well said. -- Benjamin Franklin

      It would appear to me that a poor initial choice has been made. Test::More would seem ill suited to being the framework for what seems like a network monitoring app. Can you explain the rationale for using Test::More?

      It wasn't my choice, but it has a certain elegance. At the point where the customer said that they wanted different output it lost some of that elegance.

      TAP::Parser totally looks like the way to go. Thanks!

      Peter Scott
      Perl Medic
        I have boiled it down to this demo. TAP::Harness looks like the best driver and it has a callback that lets me get at the parser before it would otherwise be used. Silencing the harness means that it never gets to find out that its parsers have been drained.
        use TAP::Harness; my $harness = TAP::Harness->new( { verbosity => -3, merge => 1 } ); my @tests = glob "t/*.t"; $harness->callback( made_parser => \&hijack_parser ); $harness->runtests( @tests ); sub hijack_parser { my ($parser, $test_ref) = @_; while ( my $result = $parser->next ) { $result->is_test or next; $result->is_actual_ok and next; (my $description = $result->description) =~ s/- //; print "I seem to have failed $description in $test_ref->[1]\n"; } }
        Peter Scott
        Perl Medic
      Test::More would seem ill suited to being the framework for what seems like a network monitoring app.
      O RLY
Re: Intercepting TAP output
by hesco (Deacon) on May 08, 2008 at 05:50 UTC
    I spent quite a bit of time in February 2007 trying to figure out how to manipulate Test::More output. What I learned and what others taught me is documented here: Redirecting STDOUT, Controlling STDOUT, Testing a Test:: Module and ultimately in the module, Test::MonitorSites, which might actually already handle what your friend is looking for. Its late at the moment, and I'm now fourteen months removed from that project, so I don't know that I can comment on a "best approach to take" at this moment. But if I were to, it would start by reviewing what I learned last year writing that Test::MonitorSites module. You might consider reading the code and test suite for that module. As I recall it wasn't very long.

    -- Hugh

    if( $lal && $lol ) { $life++; }