-
-
05 Jul 2008 14:25:26 UTC
- Distribution: TAP-Filter
- Module version: 0.04
- Source (raw)
- Browse (raw)
- Changes
- How to Contribute
- Issues
- Testers (559 / 19 / 13)
- Kwalitee
Bus factor: 1- 97.60% Coverage
- License: perl_5
- Activity
24 month- Tools
- Download (14.34KB)
- MetaCPAN Explorer
- Permissions
- Subscribe to distribution
- Permalinks
- This version
- Latest version
and 1 contributors-
Andy Armstrong
- Dependencies
- Data::Dumper
- File::Spec
- List::Util
- Storable
- TAP::Harness
- TAP::Parser
- TAP::Parser::Aggregator
- TAP::Parser::Result
- Test::Deep
- Test::More
- and possibly others
- Reverse dependencies
- CPAN Testers List
- Dependency graph
Why not adopt me?
This distribution is up for adoption! If you're interested then please contact the PAUSE module admins via email.- NAME
- VERSION
- SYNOPSIS
- DESCRIPTION
- INTERFACE
- Utility methods
- Accessors
- Implementation details and caveats
- CONFIGURATION AND ENVIRONMENT
- DEPENDENCIES
- INCOMPATIBILITIES
- BUGS AND LIMITATIONS
- AUTHOR
- LICENCE AND COPYRIGHT
NAME
TAP::Filter::Iterator - A TAP filter
VERSION
This document describes TAP::Filter::Iterator version 0.04
SYNOPSIS
use TAP::Parser; use TAP::Filter::Iterator; my $parser = TAP::Parser->new({ source => 'test.t' }); my $filter = TAP::Filter::Iterator->new; $filter->add_to_parser( $parser );
DESCRIPTION
TAP::Filter
allows arbitrary filters to be placed in the TAP processing pipeline of TAP::Harness. Installed filters see the parsed TAP stream a line at a time and can modify the stream byreplacing a result
injecting extra results
removing results
An individual filter in the processing pipeline is a
TAP::Filter::Iterator
or a subclass of it. Here is a simple filter:package MyFilter; use strict; use warnings; use base qw( TAP::Filter::Iterator ); sub inspect { my ( $self, $result ) = @_; # Perform some manipulation here... return $result; } 1;
The
inspect
method is called for each line of TAP. The$result
argument is an instance of TAP::Parser::Result, the class that represents TAP tokens within TAP::Parser. The return value ofinspect
is a list of results that will replace the result being processed.Here's a simple
inspect
implementation that flags an error for any test that has no description:sub inspect { my ( $self, $result ) = @_; if ( $result->is_test ) { my $description = $result->description; unless ( defined $description && $description =~ /\S/ ) { return ( $result, TAP::Filter->ok( ok => 0, description => 'Preceding test has no description' ) ); } } return $result; }
Note that
inspect
sees all TAP tokens; not just those that represent test results. In this case I'm only interested in test results so I callis_test
to check the type of the result.If I have a test I then call
description
to get its descriptive text. If the description is undefined or contains no non-blank characters I return the original$result
followed by a new, failed test result that I synthesize by callingTAP::Filter->ok
.By returning a pair of values I'm adding an extra result to the TAP stream. The filter automatically adjust's
TAP::Parser
's notion of how many tests have been planned and renumbers subsequent test results to account for the additional result.Any number of additional tests may be injected into the TAP stream in this way. It is not necessary to return the original
$result
as part of the list; the returned list can consist solely of new, synthetic tokens. If$result
is present it need not be the first item in the list; that is, it is legal to inject additional results before or after the original$result
.Note that the result tokens you return may be modified by
TAP::Filter::Iterator
; for example tests may be renumbered. For this reason you should not retain a reference to the returned results and expect them to remain unaltered and should not use the same result instance more than once.To remove a token from the TAP stream return an empty list from
inspect
.Filter lifecycle
When a filter is loaded by TAP::Filter the same filter instance may be used to process the output of multiple test files. If a filter has state that it would like to reset before each file it should override the
init
method:sub init { my $self = shift; $self->{_test_count} = 0; # for example }
Similarly a filter that needs to clean up at the end of each file may override
done
:sub done { my $self = shift; close $self->{_log_file}; # for example }
An alternative to subclassing
Instead of subclassing
TAP::Filter::Iterator
you may use it directly as a filter by supplying one, two or three closures that correspond to theinspect
,init
anddone
methods:my $filter = TAP::Filter::Iterator->new( sub { # inspect my $result = shift; return $result; }, sub { # init $count = 0; }, sub { # done close $log_file; } );
Note that unlike the corresponding methods the anonymous subroutines are not passed a
$self
reference. In all other ways their interface is the same.INTERFACE
new
Create a new
TAP::Filter::Iterator
. You may optionally supply one, two or three subroutine references that provide handlers forinspect
,init
anddone
.Subclasses that wish to provide their own constructor should look like this:
package MyFilter; use base qw( TAP::Filter::Iterator ); sub new { my $class = shift; my $self = $class->SUPER::new; # Perform our own initialisation # Return instance return $self; }
add_to_parser
Add this filter to the specified
TAP::Parser
. Filters must be added after the parser is created but before the first TAP is read through it.$filter->add_to_parser( $parser );
When filters are loaded by TAP::Filter
add_to_parser
is called automatically at the appropriate time.tokenize
TAP::Filter::Iterator
s implementtokenize
so that they can stand in for a TAP::Parser::Grammar.TAP::Parser
callstokenize
to read the next token from the TAP stream. If you wish to use a filter directly you may calltokenize
repeatedly to read tokens. At the end of the TAP token streamtokenize
returnsundef
.inspect
Override
inspect
in a subclass to filter the TAP stream. Called for each token in the TAP stream. Returns a list of tokens to replace the input token. See the example implementation ofinspect
above.It is not necessary for subclasses to call the superclass
inspect
.init
Called before the first TAP token in each test's output is passed to
inspect
. Override in a subclass to perform custom initialisation.done
Called after the last token in a TAP stream has been read. Override to perform custom cleanup.
Utility methods
ok
A convenience method for creating new test results to inject into the TAP stream. This method is an alias for
TAP::Filter::ok
provided here for convenient use in subclasses. See TAP::Filter for full documentation.Accessors
A
TAP::Filter::Iterator
has a number of attributes which may be retrieved or set using the following accessors. To read a value call the accessor with no arguments:my $parser = $filter->parser;
To set the value pass it as an argument:
$filter->parser( $new_parser );
In many cases it will not be necessary to use these accessors.
inspect_hook
Get or set the closure that the default implementation of
inspect
delegates to. This is only relevant if you are using the default implementation ofinspect
. Normally closures are passed tonew
; see the documentation fornew
above for more details.init_hook
Get or set the
init
closure.done_hook
Get or set the
done
closure.next_iterator
Multiple
TAP::Filter::Iterator
s may be chained together. The parser's originalTAP::Parser::Grammar
tokeniser is at the end of the iterator chain. An iterator'snext_iterator
attribute contains a reference to the next iterator in the chain.parser
A
TAP::Filter::Iterator
has a reference, stored in theparser
attribute, to the parser to which it is attached so that it can update the parser's test count dynamically.Implementation details and caveats
A filter may vary the number of tests that appear in a TAP stream. To avoid a plan error it must dynamically adjust the
TAP::Parser
's test count. This is normally effective but may interract badly with otherTAP::Parser
features in certain cases.In particular if you are spooling TAP to a file (by passing the
spool
option toTAP::Parser
) the plan line that is output to the file will be incorrect if the filter adjusts the number of tests. Without buffering the entire TAP stream this is hard to avoid; the plan token will already have been spooled to disk when the test count adjustments are applied.CONFIGURATION AND ENVIRONMENT
TAP::Filter::Iterator requires no configuration files or environment variables.
DEPENDENCIES
TAP::Filter::Iterator
requires Test::Harness version 3.11 or later.INCOMPATIBILITIES
None reported.
BUGS AND LIMITATIONS
No bugs have been reported.
Please report any bugs or feature requests to
bug-tap-filter@rt.cpan.org
, or through the web interface at http://rt.cpan.org.AUTHOR
Andy Armstrong
<andy.armstrong@messagesystems.com>
LICENCE AND COPYRIGHT
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.
Copyright (c) 2008, Message Systems, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Message Systems, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Module Install Instructions
To install TAP::Filter, copy and paste the appropriate command in to your terminal.
cpanm TAP::Filter
perl -MCPAN -e shell install TAP::Filter
For more information on module installation, please visit the detailed CPAN module installation guide.