+ All Categories
Home > Documents > At8326GB Instalation Guide

At8326GB Instalation Guide

Date post: 02-Jun-2018
Category:
Upload: dragela
View: 233 times
Download: 0 times
Share this document with a friend
643
8/10/2019 At8326GB Instalation Guide http://slidepdf.com/reader/full/at8326gb-instalation-guide 1/643  my $thing = TAP::Whatever->new();  $thing->callback( event => sub {  # do something interesting  } ); =head1 DESCRIPTION C<TAP::Base> provides callback management. =head1 METHODS =head2 Class Methods =cut sub _initialize {  my ( $self, $arg_for, $ok_callback ) = @_;  my %ok_map = map { $_ => 1 } @$ok_callback;  $self->{ok_callbacks} = \%ok_map;  if ( my $cb = delete $arg_for->{callbacks} ) {  while ( my ( $event, $callback ) = each %$cb ) {  $self->callback( $event, $callback );  }  }  return $self; } =head3 C<callback> Install a callback for a named event. =cut sub callback {  my ( $self, $event, $callback ) = @_;  my %ok_map = %{ $self->{ok_callbacks} };  $self->_croak('No callbacks may be installed')  unless %ok_map;  $self->_croak( "Callback $event is not supported. Valid callbacks are "  . join( ', ', sort keys %ok_map ) )  unless exists $ok_map{$event};  push @{ $self->{code_for}{$event} }, $callback;  return; } sub _has_callbacks {  my $self = shift;  return keys %{ $self->{code_for} } != 0; }
Transcript
  • 8/10/2019 At8326GB Instalation Guide

    1/643

    my $thing = TAP::Whatever->new();

    $thing->callback( event => sub {

    # do something interesting } );

    =head1 DESCRIPTION

    C provides callback management.

    =head1 METHODS

    =head2 Class Methods

    =cut

    sub _initialize { my ( $self, $arg_for, $ok_callback ) = @_;

    my %ok_map = map { $_ => 1 } @$ok_callback;

    $self->{ok_callbacks} = \%ok_map;

    if ( my $cb = delete $arg_for->{callbacks} ) { while ( my ( $event, $callback ) = each %$cb ) { $self->callback( $event, $callback ); } }

    return $self;}

    =head3 C

    Install a callback for a named event.

    =cut

    sub callback { my ( $self, $event, $callback ) = @_;

    my %ok_map = %{ $self->{ok_callbacks} };

    $self->_croak('No callbacks may be installed') unless %ok_map;

    $self->_croak( "Callback $event is not supported. Valid callbacks are " . join( ', ', sort keys %ok_map ) ) unless exists $ok_map{$event};

    push @{ $self->{code_for}{$event} }, $callback;

    return;}

    sub _has_callbacks { my $self = shift; return keys %{ $self->{code_for} } != 0;}

  • 8/10/2019 At8326GB Instalation Guide

    2/643

    sub _callback_for { my ( $self, $event ) = @_; return $self->{code_for}{$event};}

    sub _make_callback { my $self = shift; my $event = shift;

    my $cb = $self->_callback_for($event); return unless defined $cb; return map { $_->(@_) } @$cb;}

    =head3 C

    Return the current time using Time::HiRes if available.

    =cut

    sub get_time { return time() }

    =head3 C

    Return true if the time returned by get_time is high resolution (i.e. if Time::HiRes is available).

    =cut

    sub time_is_hires { return GOT_TIME_HIRES }

    1;package TAP::Harness;

    use strict;use Carp;

    use File::Spec;use File::Path;use IO::Handle;

    use TAP::Base;

    use vars qw($VERSION @ISA);

    @ISA = qw(TAP::Base);

    =head1 NAME

    TAP::Harness - Run test scripts with statistics

    =head1 VERSION

    Version 3.17

    =cut

    $VERSION = '3.17';

  • 8/10/2019 At8326GB Instalation Guide

    3/643

    $ENV{HARNESS_ACTIVE} = 1;$ENV{HARNESS_VERSION} = $VERSION;

    END {

    # For VMS. delete $ENV{HARNESS_ACTIVE}; delete $ENV{HARNESS_VERSION};}

    =head1 DESCRIPTION

    This is a simple test harness which allows tests to be run and resultsautomatically aggregated and output to STDOUT.

    =head1 SYNOPSIS

    use TAP::Harness;my $harness = TAP::Harness->new( \%args );$harness->runtests(@tests);

    =cut

    my %VALIDATION_FOR;

    my @FORMATTER_ARGS;sub _error { my $self = shift; return $self->{error} unless @_; $self->{error} = shift;}

    BEGIN {

    @FORMATTER_ARGS = qw( directives verbosity timer failures comments errors stdout color show_count normalize

    );

    %VALIDATION_FOR = ( lib => sub { my ( $self, $libs ) = @_; $libs = [$libs] unless 'ARRAY' eq ref $libs;

    return [ map {"-I$_"} @$libs ]; }, switches => sub { shift; shift }, exec => sub { shift; shift }, merge => sub { shift; shift }, aggregator_class => sub { shift; shift },

    formatter_class => sub { shift; shift }, multiplexer_class => sub { shift; shift }, parser_class => sub { shift; shift }, scheduler_class => sub { shift; shift }, formatter => sub { shift; shift }, jobs => sub { shift; shift }, test_args => sub { shift; shift }, ignore_exit => sub { shift; shift }, rules => sub { shift; shift }, );

  • 8/10/2019 At8326GB Instalation Guide

    4/643

    for my $method ( sort keys %VALIDATION_FOR ) { no strict 'refs'; if ( $method eq 'lib' || $method eq 'switches' ) { *{$method} = sub { my $self = shift; unless (@_) { $self->{$method} ||= []; return wantarray ? @{ $self->{$method} } : $self->{$method}; } $self->_croak("Too many arguments to method '$method'") if @_ > 1; my $args = shift; $args = [$args] unless ref $args; $self->{$method} = $args; return $self; }; } else { *{$method} = sub { my $self = shift; return $self->{$method} unless @_;

    $self->{$method} = shift; }; } }

    for my $method (@FORMATTER_ARGS) { no strict 'refs'; *{$method} = sub { my $self = shift; return $self->formatter->$method(@_); }; }}

    ##############################################################################

    =head1 METHODS

    =head2 Class Methods

    =head3 C

    my %args = ( verbosity => 1, lib => [ 'lib', 'blib/lib', 'blib/arch' ],)

    my $harness = TAP::Harness->new( \%args );

    The constructor returns a new C object. It accepts anoptional hashref whose allowed keys are:

    =over 4

    =item * C

    Set the verbosity level:

  • 8/10/2019 At8326GB Instalation Guide

    5/643

    1 verbose Print individual test results to STDOUT. 0 normal -1 quiet Suppress some test output (mostly failures

    while tests are running). -2 really quiet Suppress everything but the tests summary. -3 silent Suppress everything.

    =item * C

    Append run time for each test to output. Uses L ifavailable.

    =item * C

    Show test failures (this is a no-op if C is selected).

    =item * C

    Show test comments (this is a no-op if C is selected).

    =item * C

    Update the running test count during testing.

    =item * C

    Set to a true value to normalize the TAP that is emitted in verbose modes.

    =item * C

    Accepts a scalar value or array ref of scalar values indicating whichpaths to allowed libraries should be included if Perl tests areexecuted. Naturally, this only makes sense in the context of testswritten in Perl.

    =item * C

    Accepts a scalar value or array ref of scalar values indicating whichswitches should be included if Perl tests are executed. Naturally, thisonly makes sense in the context of tests written in Perl.

    =item * C

    A reference to an C style array of arguments to be passed to eachtest program.

    =item * C

    Attempt to produce color output.

    =item * C

    Typically, Perl tests are run through this. However, anything whichspits out TAP is fine. You can use this argument to specify the name ofthe program (and optional switches) to run your tests with:

    exec => ['/usr/bin/ruby', '-w']

    You can also pass a subroutine reference in order to determine and

  • 8/10/2019 At8326GB Instalation Guide

    6/643

    return the proper program to run based on a given test script. Thesubroutine reference should expect the TAP::Harness object itself as thefirst argument, and the file name as the second argument. It shouldreturn an array reference containing the command to be run and includingthe test file name. It can also simply return C, in which caseTAP::Harness will fall back on executing the test script in Perl:

    exec => sub { my ( $harness, $test_file ) = @_;

    # Let Perl tests run. return undef if $test_file =~ /[.]t$/; return [ qw( /usr/bin/ruby -w ), $test_file ] if $test_file =~ /[.]rb$/; }

    If the subroutine returns a scalar with a newline or a filehandle, itwill be interpreted as raw TAP or as a TAP stream, respectively.

    =item * C

    If C is true the harness will create parsers that merge STDOUTand STDERR together for any processes they start.

    =item * CThe name of the class to use to aggregate test results. The default isL.

    =item * C

    The name of the class to use to format output. The default isL, or L if the outputisn't a TTY.

    =item * C

    The name of the class to use to multiplex tests during parallel testing.The default is L.

    =item * C

    The name of the class to use to parse TAP. The default isL.

    =item * C

    The name of the class to use to schedule test execution. The default isL.

    =item * C

    If set C must be an object that is capable of formatting theTAP output. See L for an example.

    =item * C

    If parse errors are found in the TAP output, a note of this will bemade in the summary report. To see all of the parse errors, set thisargument to true:

  • 8/10/2019 At8326GB Instalation Guide

    7/643

    errors => 1

    =item * C

    If set to a true value, only test results with directives will bedisplayed. This overrides other settings such as C orC.

    =item * C

    If set to a true value instruct C to ignore exit and waitstatus from test scripts.

    =item * C

    The maximum number of parallel tests to run at any time. Which testscan be run in parallel is controlled by C. The default is torun only one test at a time.

    =item * C

    A reference to a hash of rules that control which tests may beexecuted in parallel. This is an experimental feature and the

    interface may change. $harness->rules( { par => [ { seq => '../ext/DB_File/t/*' }, { seq => '../ext/IO_Compress_Zlib/t/*' }, { seq => '../lib/CPANPLUS/*' }, { seq => '../lib/ExtUtils/t/*' }, '*' ] } );

    =item * C

    A filehandle for catching standard output.

    =back

    Any keys for which the value is C will be ignored.

    =cut

    # new supplied by TAP::Base

    {

    my @legal_callback = qw( parser_args made_parser before_runtests after_runtests after_test );

    my %default_class = ( aggregator_class => 'TAP::Parser::Aggregator',

  • 8/10/2019 At8326GB Instalation Guide

    8/643

    formatter_class => 'TAP::Formatter::Console', multiplexer_class => 'TAP::Parser::Multiplexer', parser_class => 'TAP::Parser', scheduler_class => 'TAP::Parser::Scheduler', );

    sub _initialize { my ( $self, $arg_for ) = @_; $arg_for ||= {};

    $self->SUPER::_initialize( $arg_for, \@legal_callback ); my %arg_for = %$arg_for; # force a shallow copy

    for my $name ( sort keys %VALIDATION_FOR ) { my $property = delete $arg_for{$name}; if ( defined $property ) { my $validate = $VALIDATION_FOR{$name};

    my $value = $self->$validate($property); if ( $self->_error ) { $self->_croak; } $self->$name($value); }

    } $self->jobs(1) unless defined $self->jobs;

    local $default_class{formatter_class} = 'TAP::Formatter::File' unless -t ( $arg_for{stdout} || \*STDOUT ) && !$ENV{HARNESS_NOTTY};

    while ( my ( $attr, $class ) = each %default_class ) { $self->$attr( $self->$attr() || $class ); }

    unless ( $self->formatter ) {

    # This is a little bodge to preserve legacy behaviour. It's # pretty horrible that we know which args are destined for # the formatter. my %formatter_args = ( jobs => $self->jobs ); for my $name (@FORMATTER_ARGS) { if ( defined( my $property = delete $arg_for{$name} ) ) { $formatter_args{$name} = $property; } }

    $self->formatter( $self->_construct( $self->formatter_class, \%formatter_args ) );

    }

    if ( my @props = sort keys %arg_for ) { $self->_croak("Unknown arguments to TAP::Harness::new (@props)"); }

    return $self; }}

  • 8/10/2019 At8326GB Instalation Guide

    9/643

    ##############################################################################

    =head2 Instance Methods

    =head3 C

    $harness->runtests(@tests);

    Accepts and array of C to be run. This should generally be thenames of test files, but this is not required. Each element in Cwill be passed to C as a C. SeeL for more information.

    It is possible to provide aliases that will be displayed in place of thetest name by supplying the test as a reference to an array containingC>:

    $harness->runtests( [ 't/foo.t', 'Foo Once' ], [ 't/foo.t', 'Foo Twice' ] );

    Normally it is an error to attempt to run the same test twice. Aliasesallow you to overcome this limitation by giving each run of the test aunique name.

    Tests will be run in the order found.If the environment variable C is defined itshould name a directory into which a copy of the raw TAP for each testwill be written. TAP is written to files named for each test.Subdirectories will be created as needed.

    Returns a L containing the test results.

    =cut

    sub runtests { my ( $self, @tests ) = @_;

    my $aggregate = $self->_construct( $self->aggregator_class );

    $self->_make_callback( 'before_runtests', $aggregate ); $aggregate->start; $self->aggregate_tests( $aggregate, @tests ); $aggregate->stop; $self->summary($aggregate); $self->_make_callback( 'after_runtests', $aggregate );

    return $aggregate;}

    =head3 C

    Output the summary for a TAP::Parser::Aggregator.

    =cut

    sub summary { my ( $self, $aggregate ) = @_; $self->formatter->summary($aggregate);}

  • 8/10/2019 At8326GB Instalation Guide

    10/643

    sub _after_test { my ( $self, $aggregate, $job, $parser ) = @_;

    $self->_make_callback( 'after_test', $job->as_array_ref, $parser ); $aggregate->add( $job->description, $parser );}

    sub _bailout { my ( $self, $result ) = @_; my $explanation = $result->explanation; die "FAILED--Further testing stopped" . ( $explanation ? ": $explanation\n" : ".\n" );}

    sub _aggregate_parallel { my ( $self, $aggregate, $scheduler ) = @_;

    my $jobs = $self->jobs; my $mux = $self->_construct( $self->multiplexer_class );

    RESULT: {

    # Keep multiplexer topped up

    FILL: while ( $mux->parsers < $jobs ) { my $job = $scheduler->get_job;

    # If we hit a spinner stop filling and start running. last FILL if !defined $job || $job->is_spinner;

    my ( $parser, $session ) = $self->make_parser($job); $mux->add( $parser, [ $session, $job ] ); }

    if ( my ( $parser, $stash, $result ) = $mux->next ) { my ( $session, $job ) = @$stash;

    if ( defined $result ) { $session->result($result); $self->_bailout($result) if $result->is_bailout; } else {

    # End of parser. Automatically removed from the mux. $self->finish_parser( $parser, $session ); $self->_after_test( $aggregate, $job, $parser ); $job->finish; } redo RESULT; }

    }

    return;}

    sub _aggregate_single { my ( $self, $aggregate, $scheduler ) = @_;

    JOB: while ( my $job = $scheduler->get_job ) {

  • 8/10/2019 At8326GB Instalation Guide

    11/643

    next JOB if $job->is_spinner;

    my ( $parser, $session ) = $self->make_parser($job);

    while ( defined( my $result = $parser->next ) ) { $session->result($result); if ( $result->is_bailout ) {

    # Keep reading until input is exhausted in the hope # of allowing any pending diagnostics to show up. 1 while $parser->next; $self->_bailout($result); } }

    $self->finish_parser( $parser, $session ); $self->_after_test( $aggregate, $job, $parser ); $job->finish; }

    return;}

    =head3 C

    $harness->aggregate_tests( $aggregate, @tests );

    Run the named tests and display a summary of result. Tests will be runin the order found.

    Test results will be added to the supplied L.C may be called multiple times to run several sets oftests. Multiple C instances may be used to pass resultsto a single aggregator so that different parts of a complex test suitemay be run using different C settings. This is useful, forexample, in the case where some tests should run in parallel but othersare unsuitable for parallel execution.

    my $formatter = TAP::Formatter::Console->new; my $ser_harness = TAP::Harness->new( { formatter => $formatter } ); my $par_harness = TAP::Harness->new( { formatter => $formatter, jobs => 9 } ); my $aggregator = TAP::Parser::Aggregator->new;

    $aggregator->start(); $ser_harness->aggregate_tests( $aggregator, @ser_tests ); $par_harness->aggregate_tests( $aggregator, @par_tests );

    $aggregator->stop(); $formatter->summary($aggregator);

    Note that for simpler testing requirements it will often be possible toreplace the above code with a single call to C.

    Each elements of the @tests array is either

    =over

  • 8/10/2019 At8326GB Instalation Guide

    12/643

    =item * the file name of a test script to run

    =item * a reference to a [ file name, display name ] array

    =back

    When you supply a separate display name it becomes possible to run atest more than once; the display name is effectively the alias by whichthe test is known inside the harness. The harness doesn't care if itruns the same script more than once when each invocation uses adifferent name.

    =cut

    sub aggregate_tests { my ( $self, $aggregate, @tests ) = @_;

    my $jobs = $self->jobs; my $scheduler = $self->make_scheduler(@tests);

    # #12458 local $ENV{HARNESS_IS_VERBOSE} = 1 if $self->formatter->verbosity > 0;

    # Formatter gets only names. $self->formatter->prepare( map { $_->description } $scheduler->get_all );

    if ( $self->jobs > 1 ) { $self->_aggregate_parallel( $aggregate, $scheduler ); } else { $self->_aggregate_single( $aggregate, $scheduler ); }

    return;}

    sub _add_descriptions { my $self = shift;

    # Turn unwrapped scalars into anonymous arrays and copy the name as # the description for tests that have only a name. return map { @$_ == 1 ? [ $_->[0], $_->[0] ] : $_ } map { 'ARRAY' eq ref $_ ? $_ : [$_] } @_;}

    =head3 C

    Called by the harness when it needs to create aL. Override in a subclass to provide an

    alternative scheduler. C is passed the list of teststhat was passed to C.

    =cut

    sub make_scheduler { my ( $self, @tests ) = @_; return $self->_construct( $self->scheduler_class, tests => [ $self->_add_descriptions(@tests) ],

  • 8/10/2019 At8326GB Instalation Guide

    13/643

    rules => $self->rules );}

    =head3 C

    Gets or sets the number of concurrent test runs the harness ishandling. By default, this value is 1 -- for parallel testing, thisshould be set higher.

    =cut

    ##############################################################################

    =head1 SUBCLASSING

    C is designed to be (mostly) easy to subclass. If youdon't like how a particular feature functions, just override thedesired methods.

    =head2 Methods

    TODO: This is out of date

    The following methods are ones you may wish to override if you want tosubclass C.

    =head3 C

    $harness->summary( \%args );

    C prints the summary report after all tests are run. Theargument is a hashref with the following keys:

    =over 4

    =item * C

    This is created with Cnew >> and it the time the testsstarted. You can print a useful summary time, if desired, with:

    $self->output( timestr( timediff( Benchmark->new, $start_time ), 'nop' ) );

    =item * C

    This is an array reference of all test names. To get the Lobject for individual tests:

    my $aggregate = $args->{aggregate};

    my $tests = $args->{tests};

    for my $name ( @$tests ) { my ($parser) = $aggregate->parsers($test); ... do something with $parser}

    This is a bit clunky and will be cleaned up in a later release.

    =back

  • 8/10/2019 At8326GB Instalation Guide

    14/643

    =cut

    sub _get_parser_args { my ( $self, $job ) = @_; my $test_prog = $job->filename; my %args = (); my @switches; @switches = $self->lib if $self->lib; push @switches => $self->switches if $self->switches; $args{switches} = \@switches; $args{spool} = $self->_open_spool($test_prog); $args{merge} = $self->merge; $args{ignore_exit} = $self->ignore_exit;

    if ( my $exec = $self->exec ) { $args{exec} = ref $exec eq 'CODE' ? $exec->( $self, $test_prog ) : [ @$exec, $test_prog ]; if ( not defined $args{exec} ) { $args{source} = $test_prog; } elsif ( ( ref( $args{exec} ) || "" ) ne "ARRAY" ) {

    $args{source} = delete $args{exec}; } } else { $args{source} = $test_prog; }

    if ( defined( my $test_args = $self->test_args ) ) { $args{test_args} = $test_args; }

    return \%args;}

    =head3 C

    Make a new parser and display formatter session. Typically used and/oroverridden in subclasses.

    my ( $parser, $session ) = $harness->make_parser;

    =cut

    sub make_parser { my ( $self, $job ) = @_;

    my $args = $self->_get_parser_args($job); $self->_make_callback( 'parser_args', $args, $job->as_array_ref ); my $parser = $self->_construct( $self->parser_class, $args );

    $self->_make_callback( 'made_parser', $parser, $job->as_array_ref ); my $session = $self->formatter->open_test( $job->description, $parser );

    return ( $parser, $session );}

  • 8/10/2019 At8326GB Instalation Guide

    15/643

    =head3 C

    Terminate use of a parser. Typically used and/or overridden insubclasses. The parser isn't destroyed as a result of this.

    =cut

    sub finish_parser { my ( $self, $parser, $session ) = @_;

    $session->close_test; $self->_close_spool($parser);

    return $parser;}

    sub _open_spool { my $self = shift; my $test = shift;

    if ( my $spool_dir = $ENV{PERL_TEST_HARNESS_DUMP_TAP} ) {

    my $spool = File::Spec->catfile( $spool_dir, $test );

    # Make the directory my ( $vol, $dir, undef ) = File::Spec->splitpath($spool); my $path = File::Spec->catpath( $vol, $dir, '' ); eval { mkpath($path) }; $self->_croak($@) if $@;

    my $spool_handle = IO::Handle->new; open( $spool_handle, ">$spool" ) or $self->_croak(" Can't write $spool ( $! ) ");

    return $spool_handle; }

    return;}

    sub _close_spool { my $self = shift; my ($parser) = @_;

    if ( my $spool_handle = $parser->delete_spool ) { close($spool_handle) or $self->_croak(" Error closing TAP spool file( $! ) \n "); }

    return;

    }

    sub _croak { my ( $self, $message ) = @_; unless ($message) { $message = $self->_error; } $self->SUPER::_croak($message);

    return;

  • 8/10/2019 At8326GB Instalation Guide

    16/643

    }

    =head1 REPLACING

    If you like the C utility and L but you want yourown harness, all you need to do is write one and provide C andC methods. Then you can use the C utility like so:

    prove --harness My::Test::Harness

    Note that while C accepts a list of tests (or things to betested), C has a fairly rich set of arguments. You'll probably wantto read over this code carefully to see how all of them are being used.

    =head1 SEE ALSO

    L

    =cut

    1;

    # vim:ts=4:sw=4:et:stapackage TAP::Object;

    use strict;use vars qw($VERSION);

    =head1 NAME

    TAP::Object - Base class that provides common functionality to all C modules

    =head1 VERSION

    Version 3.17

    =cut

    $VERSION = '3.17';

    =head1 SYNOPSIS

    package TAP::Whatever;

    use strict; use vars qw(@ISA);

    use TAP::Object;

    @ISA = qw(TAP::Object);

    # new() implementation by TAP::Object sub _initialize { my ( $self, @args) = @_; # initialize your object return $self; }

    # ... later ...

  • 8/10/2019 At8326GB Instalation Guide

    17/643

    my $obj = TAP::Whatever->new(@args);

    =head1 DESCRIPTION

    C provides a default constructor and exception model for allC classes. Exceptions are raised using L.

    =head1 METHODS

    =head2 Class Methods

    =head3 C

    Create a new object. Any arguments passed to C will be passed on to theL method. Returns a new object.

    =cut

    sub new { my $class = shift; my $self = bless {}, $class; return $self->_initialize(@_);}

    =head2 Instance Methods=head3 C

    Initializes a new object. This method is a stub by default, you should overrideit as appropriate.

    I L expects you to return C or raise an exception. SeeL, and L.

    =cut

    sub _initialize {

    return $_[0];}

    =head3 C

    Raise an exception using C from L, eg:

    $self->_croak( 'why me?', 'aaarrgh!' );

    May also be called as a I method.

    $class->_croak( 'this works too' );

    =cut

    sub _croak { my $proto = shift; require Carp; Carp::croak(@_); return;}

    =head3 C

  • 8/10/2019 At8326GB Instalation Guide

    18/643

    Create a new instance of the specified class.

    =cut

    sub _construct { my ( $self, $class, @args ) = @_;

    $self->_croak("Bad module name $class") unless $class =~ /^ \w+ (?: :: \w+ ) *$/x;

    unless ( $class->can('new') ) { local $@; eval "require $class"; $self->_croak("Can't load $class") if $@; }

    return $class->new(@args);}

    =head3 C

    Create simple getter/setters.

    __PACKAGE__->mk_methods(@method_names);=cut

    sub mk_methods { my ( $class, @methods ) = @_; foreach my $method_name (@methods) { my $method = "${class}::$method_name"; no strict 'refs'; *$method = sub { my $self = shift; $self->{$method_name} = shift if @_; return $self->{$method_name};

    }; }}

    1;

    package TAP::Parser;

    use strict;use vars qw($VERSION @ISA);

    use TAP::Base ();use TAP::Parser::Grammar ();

    use TAP::Parser::Result ();use TAP::Parser::ResultFactory ();use TAP::Parser::Source ();use TAP::Parser::Source::Perl ();use TAP::Parser::Iterator ();use TAP::Parser::IteratorFactory ();

    use Carp qw( confess );

    =head1 NAME

  • 8/10/2019 At8326GB Instalation Guide

    19/643

    TAP::Parser - Parse L output

    =head1 VERSION

    Version 3.17

    =cut

    $VERSION = '3.17';

    my $DEFAULT_TAP_VERSION = 12;my $MAX_TAP_VERSION = 13;

    $ENV{TAP_VERSION} = $MAX_TAP_VERSION;

    END {

    # For VMS. delete $ENV{TAP_VERSION};}

    BEGIN { # making accessors @ISA = qw(TAP::Base);

    __PACKAGE__->mk_methods( qw( _stream _spool exec exit is_good_plan plan tests_planned tests_run wait version

    in_todo start_time end_time skip_all source_class perl_source_class grammar_class iterator_factory_class result_factory_class ) );} # done making accessors

    =head1 SYNOPSIS

    use TAP::Parser;

    my $parser = TAP::Parser->new( { source => $source } );

    while ( my $result = $parser->next ) { print $result->as_string; }

  • 8/10/2019 At8326GB Instalation Guide

    20/643

    =head1 DESCRIPTION

    C is designed to produce a proper parse of TAP output. Foran example of how to run tests through this module, see the simpleharnesses C.

    There's a wiki dedicated to the Test Anything Protocol:

    L

    It includes the TAP::Parser Cookbook:

    L

    =head1 METHODS

    =head2 Class Methods

    =head3 C

    my $parser = TAP::Parser->new(\%args);

    Returns a new C object.

    The arguments should be a hashref with I of the following keys:=over 4

    =item * C

    This is the preferred method of passing arguments to the constructor. Todetermine how to handle the source, the following steps are taken.

    If the source contains a newline, it's assumed to be a string of raw TAPoutput.

    If the source is a reference, it's assumed to be something to pass to

    the L constructor. This is usedinternally and you should not use it.

    Otherwise, the parser does a C check to see if the source exists. If so,it attempts to execute the source and read the output as a stream. This is byfar the preferred method of using the parser.

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

    =item * C

    The value should be the complete TAP output.

    =item * C

    If passed an array reference, will attempt to create the iterator bypassing a L object toL, using the array reference strings asthe command arguments to L:

  • 8/10/2019 At8326GB Instalation Guide

    21/643

    exec => [ '/usr/bin/ruby', 't/my_test.rb' ]

    Note that C and C are mutually exclusive.

    =back

    The following keys are optional.

    =over 4

    =item * C

    If present, each callback corresponding to a given result type will be calledwith the result as the argument if the C method is used:

    my %callbacks = ( test => \&test_callback, plan => \&plan_callback, comment => \&comment_callback, bailout => \&bailout_callback, unknown => \&unknown_callback,);

    my $aggregator = TAP::Parser::Aggregator->new;

    foreach my $file ( @test_files ) { my $parser = TAP::Parser->new( { source => $file, callbacks => \%callbacks, } ); $parser->run; $aggregator->add( $file, $parser );}

    =item * C

    If using a Perl file as a source, optional switches may be passed which willbe used when invoking the perl executable.

    my $parser = TAP::Parser->new( { source => $test_file, switches => '-Ilib',} );

    =item * C

    Used in conjunction with the C option to supply a reference toan C style array of arguments to pass to the test program.

    =item * C

    If passed a filehandle will write a copy of all parsed TAP to that handle.

    =item * C

    If false, STDERR is not captured (though it is 'relayed' to keep itsomewhat synchronized with STDOUT.)

    If true, STDERR and STDOUT are the same filehandle. This may cause

  • 8/10/2019 At8326GB Instalation Guide

    22/643

    breakage if STDERR contains anything resembling TAP format, but doesallow exact synchronization.

    Subtleties of this behavior may be platform-dependent and may change inthe future.

    =item * C

    This option was introduced to let you easily customize which I classthe parser should use. It defaults to L.

    See also L.

    =item * C

    This option was introduced to let you easily customize which Iclass the parser should use. It defaults to L.

    See also L.

    =item * C

    This option was introduced to let you easily customize which I classthe parser should use. It defaults to L.

    See also L.

    =item * C

    This option was introduced to let you easily customize which Ifactory class the parser should use. It defaults toL.

    See also L.

    =item * C

    This option was introduced to let you easily customize which Ifactory class the parser should use. It defaults toL.

    See also L.

    =back

    =cut

    # new() implementation supplied by TAP::Base

    # This should make overriding behaviour of the Parser in subclasses easier:

    sub _default_source_class {'TAP::Parser::Source'}sub _default_perl_source_class {'TAP::Parser::Source::Perl'}sub _default_grammar_class {'TAP::Parser::Grammar'}sub _default_iterator_factory_class {'TAP::Parser::IteratorFactory'}sub _default_result_factory_class {'TAP::Parser::ResultFactory'}

    ##############################################################################

    =head2 Instance Methods

  • 8/10/2019 At8326GB Instalation Guide

    23/643

    =head3 C

    my $parser = TAP::Parser->new( { source => $file } ); while ( my $result = $parser->next ) { print $result->as_string, "\n"; }

    This method returns the results of the parsing, one result at a time. Notethat it is destructive. You can't rewind and examine previous results.

    If callbacks are used, they will be issued before this call returns.

    Each result returned is a subclass of L. See thatmodule and related classes for more information on how to use them.

    =cut

    sub next { my $self = shift; return ( $self->{_iter} ||= $self->_iter )->();}

    ##############################################################################

    =head3 C $parser->run;

    This method merely runs the parser and parses all of the TAP.

    =cut

    sub run { my $self = shift; while ( defined( my $result = $self->next ) ) {

    # do nothing

    }}

    ##############################################################################

    =head3 C

    Make a new L object and return it. Passes through anyarguments given.

    The C can be customized, as described in L.

    =head3 C

    Make a new L object and return it. Passes throughany arguments given.

    The C can be customized, as described in L.

    =head3 C

    Make a new L object and return it. Passes through anyarguments given.

  • 8/10/2019 At8326GB Instalation Guide

    24/643

    The C can be customized, as described in L.

    =head3 C

    Make a new L object using the parser'sL, and return it. Passes through any argumentsgiven.

    The C can be customized, as described in L.

    =head3 C

    Make a new L object using the parser'sL, and return it. Passes through any argumentsgiven.

    The C can be customized, as described in L.

    =cut

    # This should make overriding behaviour of the Parser in subclasses easier:sub make_source { shift->source_class->new(@_); }sub make_perl_source { shift->perl_source_class->new(@_); }

    sub make_grammar { shift->grammar_class->new(@_); }sub make_iterator { shift->iterator_factory_class->make_iterator(@_); }sub make_result { shift->result_factory_class->make_result(@_); }

    sub _iterator_for_source { my ( $self, $source ) = @_;

    # If the source has a get_stream method then use it. This makes it # possible to pass a pre-existing source object to the parser's # constructor. if ( UNIVERSAL::can( $source, 'can' ) && $source->can('get_stream') ) { return $source->get_stream($self); }

    else { return $self->iterator_factory_class->make_iterator($source); }}

    {

    # of the following, anything beginning with an underscore is strictly # internal and should not be exposed. my %initialize = ( version => $DEFAULT_TAP_VERSION, plan => '', # the test plan (e.g., 1..3) tap => '', # the TAP

    tests_run => 0, # actual current test numbers results => [], # TAP parser results skipped => [], # todo => [], # passed => [], # failed => [], # actual_failed => [], # how many tests really failed actual_passed => [], # how many tests really passed todo_passed => [], # tests which unexpectedly succeed parse_errors => [], # perfect TAP should have none

  • 8/10/2019 At8326GB Instalation Guide

    25/643

    );

    # We seem to have this list hanging around all over the place. We could # probably get it from somewhere else to avoid the repetition. my @legal_callback = qw( test version plan comment bailout unknown yaml ALL ELSE EOF );

    my @class_overrides = qw( source_class perl_source_class grammar_class iterator_factory_class result_factory_class );

    sub _initialize { my ( $self, $arg_for ) = @_;

    # everything here is basically designed to convert any TAP source to a # stream.

    # Shallow copy my %args = %{ $arg_for || {} };

    $self->SUPER::_initialize( \%args, \@legal_callback );

    # get any class overrides out first:

    for my $key (@class_overrides) { my $default_method = "_default_$key"; my $val = delete $args{$key} || $self->$default_method(); $self->$key($val); }

    my $stream = delete $args{stream}; my $tap = delete $args{tap}; my $source = delete $args{source}; my $exec = delete $args{exec}; my $merge = delete $args{merge}; my $spool = delete $args{spool}; my $switches = delete $args{switches};

    my $ignore_exit = delete $args{ignore_exit}; my @test_args = @{ delete $args{test_args} || [] };

    if ( 1 < grep {defined} $stream, $tap, $source, $exec ) { $self->_croak( "You may only choose one of 'exec', 'stream', 'tap' or 'source'" ); }

    if ( my @excess = sort keys %args ) {

  • 8/10/2019 At8326GB Instalation Guide

    26/643

    $self->_croak("Unknown options: @excess"); }

    if ($tap) { $stream = $self->_iterator_for_source( [ split "\n" => $tap ] ); } elsif ($exec) { my $source = $self->make_source; $source->source( [ @$exec, @test_args ] ); $source->merge($merge); # XXX should just be arguments? $stream = $source->get_stream($self); } elsif ($source) { if ( $source =~ /\n/ ) { $stream = $self->_iterator_for_source( [ split "\n" => $source ] ); } elsif ( ref $source ) { $stream = $self->_iterator_for_source($source); } elsif ( -e $source ) { my $perl = $self->make_perl_source;

    $perl->switches($switches)

    if $switches; $perl->merge($merge); # XXX args to new()? $perl->source( [ $source, @test_args ] ); $stream = $perl->get_stream($self); } else { $self->_croak("Cannot determine source for $source"); } }

    unless ($stream) { $self->_croak('PANIC: could not determine stream');

    }

    while ( my ( $k, $v ) = each %initialize ) { $self->{$k} = 'ARRAY' eq ref $v ? [] : $v; }

    $self->_stream($stream); $self->_spool($spool); $self->ignore_exit($ignore_exit);

    return $self; }}

    =head1 INDIVIDUAL RESULTS

    If you've read this far in the docs, you've seen this:

    while ( my $result = $parser->next ) { print $result->as_string; }

    Each result returned is a L subclass, referred to as

  • 8/10/2019 At8326GB Instalation Guide

    27/643

    I.

    =head2 Result types

    Basically, you fetch individual results from the TAP. The six types, withexamples of each, are as follows:

    =over 4

    =item * Version

    TAP version 12

    =item * Plan

    1..42

    =item * Pragma

    pragma +strict

    =item * Test

    ok 3 - We should start with some foobar!

    =item * Comment

    # Hope we don't use up the foobar.

    =item * Bailout

    Bail out! We ran out of foobar!

    =item * Unknown

    ... yo, this ain't TAP! ...

    =back

    Each result fetched is a result object of a different type. There are commonmethods to each result object and different types may have methods unique totheir type. Sometimes a type method may be overridden in a subclass, but itsuse is guaranteed to be identical.

    =head2 Common type methods

    =head3 C

    Returns the type of result, such as C or C.

    =head3 C

    Prints a string representation of the token. This might not be the exactoutput, however. Tests will have test numbers added if not present, TODO andSKIP directives will be capitalized and, in general, things will be cleanedup. If you need the original text for the token, see the C method.

    =head3 C

    Returns the original line of text which was parsed.

  • 8/10/2019 At8326GB Instalation Guide

    28/643

    =head3 C

    Indicates whether or not this is the test plan line.

    =head3 C

    Indicates whether or not this is a test line.

    =head3 C

    Indicates whether or not this is a comment. Comments will generally onlyappear in the TAP stream if STDERR is merged to STDOUT. See theC option.

    =head3 C

    Indicates whether or not this is bailout line.

    =head3 C

    Indicates whether or not the current item is a YAML block.

    =head3 C

    Indicates whether or not the current line could be parsed.

    =head3 C

    if ( $result->is_ok ) { ... }

    Reports whether or not a given result has passed. Anything which is B atest result returns true. This is merely provided as a convenient shortcutwhich allows you to do this:

    my $parser = TAP::Parser->new( { source => $source } );while ( my $result = $parser->next ) {

    # only print failing results print $result->as_string unless $result->is_ok;}

    =head2 C methods

    if ( $result->is_plan ) { ... }

    If the above evaluates as true, the following methods will be available on theC object.

    =head3 C

    if ( $result->is_plan ) { print $result->plan; }

    This is merely a synonym for C.

    =head3 C

    my $directive = $result->directive;

  • 8/10/2019 At8326GB Instalation Guide

    29/643

    If a SKIP directive is included with the plan, this method will return it.

    1..0 # SKIP: why bother?

    =head3 C

    my $explanation = $result->explanation;

    If a SKIP directive was included with the plan, this method will return theexplanation, if any.

    =head2 C methods

    if ( $result->is_pragma ) { ... }

    If the above evaluates as true, the following methods will be available on theC object.

    =head3 C

    Returns a list of pragmas each of which is a + or - followed by thepragma name.=head2 C methods

    if ( $result->is_comment ) { ... }

    If the above evaluates as true, the following methods will be available on theC object.

    =head3 C

    if ( $result->is_comment ) { my $comment = $result->comment; print "I have something to say: $comment"; }

    =head2 C methods

    if ( $result->is_bailout ) { ... }

    If the above evaluates as true, the following methods will be available on theC object.

    =head3 C

    if ( $result->is_bailout ) { my $explanation = $result->explanation; print "We bailed out because ($explanation)"; }

    If, and only if, a token is a bailout token, you can get an "explanation" viathis method. The explanation is the text after the mystical "Bail out!" wordswhich appear in the tap output.

    =head2 C methods

    if ( $result->is_unknown ) { ... }

    There are no unique methods for unknown results.

  • 8/10/2019 At8326GB Instalation Guide

    30/643

    =head2 C methods

    if ( $result->is_test ) { ... }

    If the above evaluates as true, the following methods will be available on theC object.

    =head3 C

    my $ok = $result->ok;

    Returns the literal text of the C or C status.

    =head3 C

    my $test_number = $result->number;

    Returns the number of the test, even if the original TAP output did not supplythat number.

    =head3 C

    my $description = $result->description;

    Returns the description of the test, if any. This is the portion after thetest number but before the directive.

    =head3 C

    my $directive = $result->directive;

    Returns either C or C if either directive was present for a testline.

    =head3 C

    my $explanation = $result->explanation;

    If a test had either a C or C directive, this method will returnthe accompanying explantion, if present.

    not ok 17 - 'Pigs can fly' # TODO not enough acid

    For the above line, the explanation is I.

    =head3 C

    if ( $result->is_ok ) { ... }

    Returns a boolean value indicating whether or not the test passed. Rememberthat for TODO tests, the test always passes.

    B this was formerly C. The latter method is deprecated andwill issue a warning.

    =head3 C

    if ( $result->is_actual_ok ) { ... }

  • 8/10/2019 At8326GB Instalation Guide

    31/643

    Returns a boolean value indicating whether or not the test passed, regardlessof its TODO status.

    B this was formerly C. The latter method is deprecatedand will issue a warning.

    =head3 C

    if ( $test->is_unplanned ) { ... }

    If a test number is greater than the number of planned tests, this method willreturn true. Unplanned tests will I return false for C,regardless of whether or not the test C (seeL for more information about this).

    =head3 C

    if ( $result->has_skip ) { ... }

    Returns a boolean value indicating whether or not this test had a SKIPdirective.

    =head3 C

    if ( $result->has_todo ) { ... }Returns a boolean value indicating whether or not this test had a TODOdirective.

    Note that TODO tests I pass. If you need to know whether or notthey really passed, check the C method.

    =head3 C

    if ( $parser->in_todo ) { ... }

    True while the most recent result was a TODO. Becomes true before the

    TODO result is returned and stays true until just before the next non-TODO test is returned.

    =head1 TOTAL RESULTS

    After parsing the TAP, there are many methods available to let you dig throughthe results and determine what is meaningful to you.

    =head2 Individual Results

    These results refer to individual tests which are run.

    =head3 C

    my @passed = $parser->passed; # the test numbers which passedmy $passed = $parser->passed; # the number of tests which passed

    This method lets you know which (or how many) tests passed. If a test failedbut had a TODO directive, it will be counted as a passed test.

    =cut

    sub passed { @{ shift->{passed} } }

  • 8/10/2019 At8326GB Instalation Guide

    32/643

    =head3 C

    my @failed = $parser->failed; # the test numbers which failedmy $failed = $parser->failed; # the number of tests which failed

    This method lets you know which (or how many) tests failed. If a test passedbut had a TODO directive, it will B be counted as a failed test.

    =cut

    sub failed { @{ shift->{failed} } }

    =head3 C

    # the test numbers which actually passedmy @actual_passed = $parser->actual_passed;

    # the number of tests which actually passedmy $actual_passed = $parser->actual_passed;

    This method lets you know which (or how many) tests actually passed,regardless of whether or not a TODO directive was found.

    =cutsub actual_passed { @{ shift->{actual_passed} } }*actual_ok = \&actual_passed;

    =head3 C

    This method is a synonym for C.

    =head3 C

    # the test numbers which actually failedmy @actual_failed = $parser->actual_failed;

    # the number of tests which actually failedmy $actual_failed = $parser->actual_failed;

    This method lets you know which (or how many) tests actually failed,regardless of whether or not a TODO directive was found.

    =cut

    sub actual_failed { @{ shift->{actual_failed} } }

    ##############################################################################

    =head3 C

    my @todo = $parser->todo; # the test numbers with todo directivesmy $todo = $parser->todo; # the number of tests with todo directives

    This method lets you know which (or how many) tests had TODO directives.

    =cut

    sub todo { @{ shift->{todo} } }

  • 8/10/2019 At8326GB Instalation Guide

    33/643

    =head3 C

    # the test numbers which unexpectedly succeededmy @todo_passed = $parser->todo_passed;

    # the number of tests which unexpectedly succeededmy $todo_passed = $parser->todo_passed;

    This method lets you know which (or how many) tests actually passed but weredeclared as "TODO" tests.

    =cut

    sub todo_passed { @{ shift->{todo_passed} } }

    ##############################################################################

    =head3 C

    # deprecated in favor of 'todo_passed'. This method was horribly misnamed.

    This was a badly misnamed method. It indicates which TODO tests unexpectedlysucceeded. Will now issue a warning and call C.

    =cut

    sub todo_failed { warn '"todo_failed" is deprecated. Please use "todo_passed". See the docs.'; goto &todo_passed;}

    =head3 C

    my @skipped = $parser->skipped; # the test numbers with SKIP directivesmy $skipped = $parser->skipped; # the number of tests with SKIP directives

    This method lets you know which (or how many) tests had SKIP directives.

    =cut

    sub skipped { @{ shift->{skipped} } }

    =head2 Pragmas

    =head3 C

    Get or set a pragma. To get the state of a pragma:

    if ( $p->pragma('strict') ) { # be strict }

    To set the state of a pragma:

    $p->pragma('strict', 1); # enable strict mode

    =cut

  • 8/10/2019 At8326GB Instalation Guide

    34/643

    sub pragma { my ( $self, $pragma ) = splice @_, 0, 2;

    return $self->{pragma}->{$pragma} unless @_;

    if ( my $state = shift ) { $self->{pragma}->{$pragma} = 1; } else { delete $self->{pragma}->{$pragma}; }

    return;}

    =head3 C

    Get a list of all the currently enabled pragmas:

    my @pragmas_enabled = $p->pragmas;

    =cut

    sub pragmas { sort keys %{ shift->{pragma} || {} } }

    =head2 Summary Results

    These results are "meta" information about the total results of an individualtest program.

    =head3 C

    my $plan = $parser->plan;

    Returns the test plan, if found.

    =head3 C

    Deprecated. Use C instead.

    =cut

    sub good_plan { warn 'good_plan() is deprecated. Please use "is_good_plan()"'; goto &is_good_plan;}

    ##############################################################################

    =head3 C

    if ( $parser->is_good_plan ) { ... }

    Returns a boolean value indicating whether or not the number of tests plannedmatches the number of tests run.

    B this was formerly C. The latter method is deprecated andwill issue a warning.

    And since we're on that subject ...

  • 8/10/2019 At8326GB Instalation Guide

    35/643

    =head3 C

    print $parser->tests_planned;

    Returns the number of tests planned, according to the plan. For example, aplan of '1..17' will mean that 17 tests were planned.

    =head3 C

    print $parser->tests_run;

    Returns the number of tests which actually were run. Hopefully this willmatch the number of Ctests_planned >>.

    =head3 C

    Returns a true value (actually the reason for skipping) if all testswere skipped.

    =head3 C

    Returns the time when the Parser was created.

    =head3 CReturns the time when the end of TAP input was seen.

    =head3 C

    if ( $parser->has_problems ) { ... }

    This is a 'catch-all' method which returns true if any tests have currentlyfailed, any TODO tests unexpectedly succeeded, or any parse errors occurred.

    =cut

    sub has_problems { my $self = shift; return $self->failed || $self->parse_errors || ( !$self->ignore_exit && ( $self->wait || $self->exit ) );}

    =head3 C

    $parser->version;

    Once the parser is done, this will return the version number for theparsed TAP. Version numbers were introduced with TAP version 13 so if noversion number is found version 12 is assumed.

    =head3 C

    $parser->exit;

    Once the parser is done, this will return the exit status. If the parser ran

  • 8/10/2019 At8326GB Instalation Guide

    36/643

    an executable, it returns the exit status of the executable.

    =head3 C

    $parser->wait;

    Once the parser is done, this will return the wait status. If the parser ranan executable, it returns the wait status of the executable. Otherwise, thismererely returns the C status.

    =head2 C

    $parser->ignore_exit(1);

    Tell the parser to ignore the exit status from the test when determiningwhether the test passed. Normally tests with non-zero exit status areconsidered to have failed even if all individual tests passed. In caseswhere it is not possible to control the exit value of the test scriptuse this option to ignore it.

    =cut

    sub ignore_exit { shift->pragma( 'ignore_exit', @_ ) }

    =head3 Cmy @errors = $parser->parse_errors; # the parser errorsmy $errors = $parser->parse_errors; # the number of parser_errors

    Fortunately, all TAP output is perfect. In the event that it is not, thismethod will return parser errors. Note that a junk line which the parser doesnot recognize is C an error. This allows this parser to handle futureversions of TAP. The following are all TAP errors reported by the parser:

    =over 4

    =item * Misplaced plan

    The plan (for example, '1..5'), must only come at the beginning or end of theTAP output.

    =item * No plan

    Gotta have a plan!

    =item * More than one plan

    1..3ok 1 - input file openednot ok 2 - first line of the input valid # todo some data

    ok 3 read the rest of the file1..3

    Right. Very funny. Don't do that.

    =item * Test numbers out of sequence

    1..3ok 1 - input file openednot ok 2 - first line of the input valid # todo some data

  • 8/10/2019 At8326GB Instalation Guide

    37/643

    ok 2 read the rest of the file

    That last test line above should have the number '3' instead of '2'.

    Note that it's perfectly acceptable for some lines to have test numbers andothers to not have them. However, when a test number is found, it must be insequence. The following is also an error:

    1..3ok 1 - input file openednot ok - first line of the input valid # todo some dataok 2 read the rest of the file

    But this is not:

    1..3ok - input file openednot ok - first line of the input valid # todo some dataok 3 read the rest of the file

    =back

    =cut

    sub parse_errors { @{ shift->{parse_errors} } }sub _add_error { my ( $self, $error ) = @_; push @{ $self->{parse_errors} } => $error; return $self;}

    sub _make_state_table { my $self = shift; my %states; my %planned_todo = ();

    # These transitions are defaults for all states my %state_globals = ( comment => {}, bailout => {}, yaml => {}, version => { act => sub { $self->_add_error( 'If TAP version is present it must be the first line of output' ); }, },

    unknown => { act => sub { my $unk = shift; if ( $self->pragma('strict') ) { $self->_add_error( 'Unknown TAP token: "' . $unk->raw . '"' ); } }, }, pragma => {

  • 8/10/2019 At8326GB Instalation Guide

    38/643

    act => sub { my ($pragma) = @_; for my $pr ( $pragma->pragmas ) { if ( $pr =~ /^ ([-+])(\w+) $/x ) { $self->pragma( $2, $1 eq '+' ); } } }, }, );

    # Provides default elements for transitions my %state_defaults = ( plan => { act => sub { my ($plan) = @_; $self->tests_planned( $plan->tests_planned ); $self->plan( $plan->plan ); if ( $plan->has_skip ) { $self->skip_all( $plan->explanation || '(no reason given)' ); }

    $planned_todo{$_}++ for @{ $plan->todo_list };

    }, }, test => { act => sub { my ($test) = @_;

    my ( $number, $tests_run ) = ( $test->number, ++$self->{tests_run} );

    # Fake TODO state if ( defined $number && delete $planned_todo{$number} ) { $test->set_directive('TODO'); }

    my $has_todo = $test->has_todo;

    $self->in_todo($has_todo); if ( defined( my $tests_planned = $self->tests_planned ) ) { if ( $tests_run > $tests_planned ) { $test->is_unplanned(1); } }

    if ( defined $number ) { if ( $number != $tests_run ) { my $count = $tests_run;

    $self->_add_error( "Tests out of sequence. Found " . "($number) but expected ($count)" ); } } else { $test->_number( $number = $tests_run ); }

    push @{ $self->{todo} } => $number if $has_todo; push @{ $self->{todo_passed} } => $number

  • 8/10/2019 At8326GB Instalation Guide

    39/643

    if $test->todo_passed; push @{ $self->{skipped} } => $number if $test->has_skip;

    push @{ $self->{ $test->is_ok ? 'passed' : 'failed' } } => $number; push @{ $self->{ $test->is_actual_ok ? 'actual_passed' : 'actual_failed' } } => $number; }, }, yaml => { act => sub { }, }, );

    # Each state contains a hash the keys of which match a token type. For # each token # type there may be: # act A coderef to run # goto The new state to move to. Stay in this state if # missing

    # continue Goto the new state and run the new state for the # current token %states = ( INIT => { version => { act => sub { my ($version) = @_; my $ver_num = $version->version; if ( $ver_num _add_error( "Explicit TAP version must be at least " . "$ver_min. Got version $ver_num" );

    $ver_num = $DEFAULT_TAP_VERSION; } if ( $ver_num > $MAX_TAP_VERSION ) { $self->_add_error( "TAP specified version $ver_num but " . "we don't know about versions later " . "than $MAX_TAP_VERSION" ); $ver_num = $MAX_TAP_VERSION; } $self->version($ver_num); $self->_grammar->set_version($ver_num); }, goto => 'PLAN'

    }, plan => { goto => 'PLANNED' }, test => { goto => 'UNPLANNED' }, }, PLAN => { plan => { goto => 'PLANNED' }, test => { goto => 'UNPLANNED' }, }, PLANNED => { test => { goto => 'PLANNED_AFTER_TEST' },

  • 8/10/2019 At8326GB Instalation Guide

    40/643

    plan => { act => sub { my ($version) = @_; $self->_add_error( 'More than one plan found in TAP output'); }, }, }, PLANNED_AFTER_TEST => { test => { goto => 'PLANNED_AFTER_TEST' }, plan => { act => sub { }, continue => 'PLANNED' }, yaml => { goto => 'PLANNED' }, }, GOT_PLAN => { test => { act => sub { my ($plan) = @_; my $line = $self->plan; $self->_add_error( "Plan ($line) must be at the beginning " . "or end of the TAP output" ); $self->is_good_plan(0); }, continue => 'PLANNED'

    }, plan => { continue => 'PLANNED' }, }, UNPLANNED => { test => { goto => 'UNPLANNED_AFTER_TEST' }, plan => { goto => 'GOT_PLAN' }, }, UNPLANNED_AFTER_TEST => { test => { act => sub { }, continue => 'UNPLANNED' }, plan => { act => sub { }, continue => 'UNPLANNED' }, yaml => { goto => 'PLANNED' }, }, );

    # Apply globals and defaults to state table for my $name ( keys %states ) {

    # Merge with globals my $st = { %state_globals, %{ $states{$name} } };

    # Add defaults for my $next ( sort keys %{$st} ) { if ( my $default = $state_defaults{$next} ) { for my $def ( sort keys %{$default} ) { $st->{$next}->{$def} ||= $default->{$def}; }

    } }

    # Stuff back in table $states{$name} = $st; }

    return \%states;}

  • 8/10/2019 At8326GB Instalation Guide

    41/643

    =head3 C

    Get an a list of file handles which can be passed to C todetermine the readiness of this parser.

    =cut

    sub get_select_handles { shift->_stream->get_select_handles }

    sub _grammar { my $self = shift; return $self->{_grammar} = shift if @_;

    return $self->{_grammar} ||= $self->make_grammar( { stream => $self->_stream, parser => $self, version => $self->version } );}

    sub _iter { my $self = shift; my $stream = $self->_stream;

    my $grammar = $self->_grammar; my $spool = $self->_spool; my $state = 'INIT'; my $state_table = $self->_make_state_table;

    $self->start_time( $self->get_time );

    # Make next_state closure my $next_state = sub { my $token = shift; my $type = $token->type; TRANS: { my $state_spec = $state_table->{$state}

    or die "Illegal state: $state";

    if ( my $next = $state_spec->{$type} ) { if ( my $act = $next->{act} ) { $act->($token); } if ( my $cont = $next->{continue} ) { $state = $cont; redo TRANS; } elsif ( my $goto = $next->{goto} ) { $state = $goto; }

    } else { confess("Unhandled token type: $type\n"); } } return $token; };

    # Handle end of stream - which means either pop a block or finish my $end_handler = sub {

  • 8/10/2019 At8326GB Instalation Guide

    42/643

    $self->exit( $stream->exit ); $self->wait( $stream->wait ); $self->_finish; return; };

    # Finally make the closure that we return. For performance reasons # there are two versions of the returned function: one that handles # callbacks and one that does not. if ( $self->_has_callbacks ) { return sub { my $result = eval { $grammar->tokenize }; $self->_add_error($@) if $@;

    if ( defined $result ) { $result = $next_state->($result);

    if ( my $code = $self->_callback_for( $result->type ) ) { $_->($result) for @{$code}; } else { $self->_make_callback( 'ELSE', $result ); }

    $self->_make_callback( 'ALL', $result ); # Echo TAP to spool file print {$spool} $result->raw, "\n" if $spool; } else { $result = $end_handler->(); $self->_make_callback( 'EOF', $self ) unless defined $result; }

    return $result; };

    } # _has_callbacks else { return sub { my $result = eval { $grammar->tokenize }; $self->_add_error($@) if $@;

    if ( defined $result ) { $result = $next_state->($result);

    # Echo TAP to spool file print {$spool} $result->raw, "\n" if $spool; } else {

    $result = $end_handler->(); }

    return $result; }; } # no callbacks}

    sub _finish { my $self = shift;

  • 8/10/2019 At8326GB Instalation Guide

    43/643

    $self->end_time( $self->get_time );

    # Avoid leaks $self->_stream(undef); $self->_grammar(undef);

    # If we just delete the iter we won't get a fault if it's recreated. # Instead we set it to a sub that returns an infinite # stream of undef. This segfaults on 5.5.4, presumably because # we're still executing the closure that gets replaced and it hasn't # been protected with a refcount. $self->{_iter} = sub {return} if $] >= 5.006;

    # sanity checks if ( !$self->plan ) { $self->_add_error('No plan found in TAP output'); } else { $self->is_good_plan(1) unless defined $self->is_good_plan; } if ( $self->tests_run != ( $self->tests_planned || 0 ) ) { $self->is_good_plan(0);

    if ( defined( my $planned = $self->tests_planned ) ) { my $ran = $self->tests_run; $self->_add_error( "Bad plan. You planned $planned tests but ran $ran."); } } if ( $self->tests_run != ( $self->passed + $self->failed ) ) {

    # this should never happen my $actual = $self->tests_run; my $passed = $self->passed; my $failed = $self->failed; $self->_croak( "Panic: planned test count ($actual) did not equal "

    . "sum of passed ($passed) and failed ($failed) tests!" ); }

    $self->is_good_plan(0) unless defined $self->is_good_plan; return $self;}

    =head3 C

    Delete and return the spool.

    my $fh = $parser->delete_spool;

    =cut

    sub delete_spool { my $self = shift;

    return delete $self->{_spool};}

    ##############################################################################

  • 8/10/2019 At8326GB Instalation Guide

    44/643

  • 8/10/2019 At8326GB Instalation Guide

    45/643

    Invoked if Cis_yaml >> returns true.

    =item * C

    Invoked if Cis_unknown >> returns true.

    =item * C

    If a result does not have a callback defined for it, this callback willbe invoked. Thus, if all of the previous result types are specified ascallbacks, this callback will I be invoked.

    =item * C

    This callback will always be invoked and this will happen for eachresult after one of the above callbacks is invoked. For example, ifL is loaded, you could use the following to color yourtest output:

    my %callbacks = ( test => sub { my $test = shift; if ( $test->is_ok && not $test->directive ) { # normal passing test

    print color 'green'; } elsif ( !$test->is_ok ) { # even if it's TODO print color 'white on_red'; } elsif ( $test->has_skip ) { print color 'white on_blue';

    } elsif ( $test->has_todo ) { print color 'white'; } },

    ELSE => sub { # plan, comment, and so on (anything which isn't a test line) print color 'black on_white'; }, ALL => sub { # now print them print shift->as_string; print color 'reset'; print "\n"; },);

    =item * C

    Invoked when there are no more lines to be parsed. Since there is noaccompanying L object the C object ispassed instead.

    =back

    =head1 TAP GRAMMAR

    If you're looking for an EBNF grammar, see L.

  • 8/10/2019 At8326GB Instalation Guide

    46/643

    =head1 BACKWARDS COMPATABILITY

    The Perl-QA list attempted to ensure backwards compatability withL. However, there are some minor differences.

    =head2 Differences

    =over 4

    =item * TODO plans

    A little-known feature of L is that it supported TODOlists in the plan:

    1..2 todo 2ok 1 - We have liftoffnot ok 2 - Anti-gravity device activated

    Under L, test number 2 would I because it waslisted as a TODO test on the plan line. However, we are not aware ofanyone actually using this feature and hard-coding test numbers isdiscouraged because it's very easy to add a test and break the testnumber sequence. This makes test suites very fragile. Instead, the

    following should be used:1..2ok 1 - We have liftoffnot ok 2 - Anti-gravity device activated # TODO

    =item * 'Missing' tests

    It rarely happens, but sometimes a harness might encounter'missing tests:

    ok 1ok 2

    ok 15ok 16ok 17

    L would report tests 3-14 as having failed. For theC, these tests are not considered failed because they'venever run. They're reported as parse failures (tests out of sequence).

    =back

    =head1 SUBCLASSING

    If you find you need to provide custom functionality (as you would have using

    L), you're in luck: C and friends aredesigned to be easily subclassed.

    Before you start, it's important to know a few things:

    =over 2

    =item 1

    All C objects inherit from L.

  • 8/10/2019 At8326GB Instalation Guide

    47/643

    =item 2

    Most C classes have a I section to guide you.

    =item 3

    Note that C is designed to be the central 'maker' - ie: it isresponsible for creating new objects in the C namespace.

    This makes it possible for you to have a single point of configuring whatsubclasses should be used, which in turn means that in many cases you'll findyou only need to sub-class one of the parser's components.

    =item 4

    By subclassing, you may end up overriding undocumented methods. That's nota bad thing per se, but be forewarned that undocumented methods may changewithout warning from one release to the next - we cannot guarantee backwardscompatability. If any I method needs changing, it will bedeprecated first, and changed in a later release.

    =back

    =head2 Parser Components=head3 Sources

    A TAP parser consumes input from a I. There are currently two typesof sources: L for general non-perl commands, andL. You can subclass both of them. You'll need tocustomize your parser by setting the C & Cparameters. See L for more details.

    If you need to customize the objects on creation, subclass L andoverride L or L.

    =head3 Iterators

    A TAP parser uses I to loop through the I provided by theparser's I. There are quite a few types of Iterators available.Choosing which class to use is the responsibility of the I.

    To create your own iterators you'll have to subclassL and L. Then you'llneed to customize the class used by your parser by setting theC parameter. See L for more details.

    If you need to customize the objects on creation, subclass L andoverride L.

    =head3 Results

    A TAP parser creates Ls as it iterates through theinput I. There are quite a few result types available; choosingwhich class to use is the responsibility of the I.

    To create your own result types you have two options:

    =over 2

  • 8/10/2019 At8326GB Instalation Guide

    48/643

    =item option 1

    Subclass L and register your new result type/class withthe default L.

    =item option 2

    Subclass L itself and implement your ownL creation logic. Then you'll need to customize theclass used by your parser by setting the C parameter.See L for more details.

    =back

    If you need to customize the objects on creation, subclass L andoverride L.

    =head3 Grammar

    L is the heart of the parser - it tokenizes the TAPinput I and produces results. If you need to customize its behaviouryou should probably familiarize yourself with the source first. Enoughlecturing.

    Subclass L and customize your parser by setting theC parameter. See L for more details.

    If you need to customize the objects on creation, subclass L andoverride L

    =head1 ACKNOWLEDGEMENTS

    All of the following have helped. Bug reports, patches, (im)moralsupport, or just words of encouragement have all been forthcoming.

    =over 4

    =item * Michael Schwern

    =item * Andy Lester

    =item * chromatic

    =item * GEOFFR

    =item * Shlomi Fish

    =item * Torsten Schoenfeld

    =item * Jerry Gay

    =item * Aristotle

    =item * Adam Kennedy

    =item * Yves Orton

    =item * Adrian Howard

  • 8/10/2019 At8326GB Instalation Guide

    49/643

    =item * Sean & Lil

    =item * Andreas J. Koenig

    =item * Florian Ragwitz

    =item * Corion

    =item * Mark Stosberg

    =item * Matt Kraai

    =item * David Wheeler

    =item * Alex Vandiver

    =back

    =head1 AUTHORS

    Curtis "Ovid" Poe

    Andy Armstong

    Eric Wilhelm @ Michael Peters

    Leif Eriksen

    Steve Purkis

    Nicholas Clark

    =head1 BUGS

    Please report any bugs or feature requests to

    C, or through the web interface atL.We will be notified, and then you'll automatically be notified ofprogress on your bug as we make changes.

    Obviously, bugs which include patches are best. If you prefer, you canpatch against bleed by via anonymous checkout of the latest version:

    svn checkout http://svn.hexten.net/tapx

    =head1 COPYRIGHT & LICENSE

    Copyright 2006-2008 Curtis "Ovid" Poe, all rights reserved.

    This program is free software; you can redistribute it and/or modify itunder the same terms as Perl itself.

    =cut

    1;package TAP::Formatter::Base;

    use strict;

  • 8/10/2019 At8326GB Instalation Guide

    50/643

    use TAP::Base ();use POSIX qw(strftime);

    use vars qw($VERSION @ISA);

    my $MAX_ERRORS = 5;my %VALIDATION_FOR;

    BEGIN { @ISA = qw(TAP::Base);

    %VALIDATION_FOR = ( directives => sub { shift; shift }, verbosity => sub { shift; shift }, normalize => sub { shift; shift }, timer => sub { shift; shift }, failures => sub { shift; shift }, comments => sub { shift; shift }, errors => sub { shift; shift }, color => sub { shift; shift }, jobs => sub { shift; shift }, show_count => sub { shift; shift }, stdout => sub { my ( $self, $ref ) = @_;

    $self->_croak("option 'stdout' needs a filehandle") unless ( ref $ref || '' ) eq 'GLOB' or eval { $ref->can('print') }; return $ref; }, );

    my @getter_setters = qw( _longest _printed_summary_header _colorizer );

    __PACKAGE__->mk_methods( @getter_setters, keys %VALIDATION_FOR );}

    =head1 NAME

    TAP::Formatter::Console - Harness output delegate for default console output

    =head1 VERSION

    Version 3.17

    =cut

    $VERSION = '3.17';

    =head1 DESCRIPTION

    This provides console orientated output formatting for TAP::Harness.

    =head1 SYNOPSIS

    use TAP::Formatter::Console;my $harness = TAP::Formatter::Console->new( \%args );

  • 8/10/2019 At8326GB Instalation Guide

    51/643

    =cut

    sub _initialize { my ( $self, $arg_for ) = @_; $arg_for ||= {};

    $self->SUPER::_initialize($arg_for); my %arg_for = %$arg_for; # force a shallow copy

    $self->verbosity(0);

    for my $name ( keys %VALIDATION_FOR ) { my $property = delete $arg_for{$name}; if ( defined $property ) { my $validate = $VALIDATION_FOR{$name}; $self->$name( $self->$validate($property) ); } }

    if ( my @props = keys %arg_for ) { $self->_croak( "Unknown arguments to " . __PACKAGE__ . "::new (@props)" ); }

    $self->stdout( \*STDOUT ) unless $self->stdout;

    if ( $self->color ) { require TAP::Formatter::Color; $self->_colorizer( TAP::Formatter::Color->new ); }

    return $self;}

    sub verbose { shift->verbosity >= 1 }sub quiet { shift->verbosity verbosity verbosity 1,)my $harness = TAP::Formatter::Console->new( \%args );

    The constructor returns a new C object. Ifa L is created with no C aC is automatically created. If any of thefollowing options were given to TAP::Harness->new they well be passed tothis constructor which accepts an optional hashref whose allowed keys are:

    =over 4

    =item * C

  • 8/10/2019 At8326GB Instalation Guide

    52/643

    Set the verbosity level.

    =item * C

    Printing individual test results to STDOUT.

    =item * C

    Append run time for each test to output. Uses L if available.

    =item * C

    Show test failures (this is a no-op if C is selected).

    =item * C

    Show test comments (this is a no-op if C is selected).

    =item * C

    Suppressing some test output (mostly failures while tests are running).

    =item * C

    Suppressing everything but the tests summary.

    =item * C

    Suppressing all output.

    =item * C

    If parse errors are found in the TAP output, a note of this will be madein the summary report. To see all of the parse errors, set this argument totrue:

    errors => 1

    =item * C

    If set to a true value, only test results with directives will be displayed.This overrides other settings such as C, C, or C.

    =item * C

    A filehandle for catching standard output.

    =item * C

    If defined specifies whether color output is desired. If C is notdefined it will default to color output if color support is available onthe current platform and output is not being redirected.

    =item * C

    The number of concurrent jobs this formatter will handle.

    =item * C

  • 8/10/2019 At8326GB Instalation Guide

    53/643

    Boolean value. If false, disables the C test count which shows up whiletests are running.

    =back

    Any keys for which the value is C will be ignored.

    =cut

    # new supplied by TAP::Base

    =head3 C

    Called by Test::Harness before any test output is generated.

    This is an advisory and may not be called in the case where tests arebeing supplied to Test::Harness by an iterator.

    =cut

    sub prepare { my ( $self, @tests ) = @_;

    my $longest = 0;

    foreach my $test (@tests) { $longest = length $test if length $test > $longest; }

    $self->_longest($longest);}

    sub _format_now { strftime "[%H:%M:%S]", localtime }

    sub _format_name { my ( $self, $test ) = @_; my $name = $test;

    my $periods = '.' x ( $self->_longest + 2 - length $test ); $periods = " $periods ";

    if ( $self->timer ) { my $stamp = $self->_format_now(); return "$stamp $name$periods"; } else { return "$name$periods"; }

    }

    =head3 C

    Called to create a new test session. A test session looks like this:

    my $session = $formatter->open_test( $test, $parser ); while ( defined( my $result = $parser->next ) ) { $session->result($result); exit 1 if $result->is_bailout; } $session->close_test;

  • 8/10/2019 At8326GB Instalation Guide

    54/643

    =cut

    sub open_test { die "Unimplemented.";}

    sub _output_success { my ( $self, $msg ) = @_; $self->_output($msg);}

    =head3 C

    $harness->summary( $aggregate );

    C prints the summary report after all tests are run. The argument isan aggregate.

    =cut

    sub summary { my ( $self, $aggregate ) = @_;

    return if $self->silent; my @t = $aggregate->descriptions; my $tests = \@t;

    my $runtime = $aggregate->elapsed_timestr;

    my $total = $aggregate->total; my $passed = $aggregate->passed;

    if ( $self->timer ) { $self->_output( $self->_format_now(), "\n" ); }

    # TODO: Check this condition still works when all subtests pass but # the exit status is nonzero

    if ( $aggregate->all_passed ) { $self->_output_success("All tests successful.\n"); }

    # ~TODO option where $aggregate->skipped generates reports if ( $total != $passed or $aggregate->has_problems ) { $self->_output("\nTest Summary Report"); $self->_output("\n-------------------\n"); foreach my $test (@$tests) {

    $self->_printed_summary_header(0); my ($parser) = $aggregate->parsers($test); $self->_output_summary_failure( 'failed', [ ' Failed test: ', ' Failed tests: ' ], $test, $parser ); $self->_output_summary_failure( 'todo_passed', " TODO passed: ", $test, $parser

  • 8/10/2019 At8326GB Instalation Guide

    55/643

    );

    # ~TODO this cannot be the default #$self->_output_summary_failure( 'skipped', " Tests skipped: " );

    if ( my $exit = $parser->exit ) { $self->_summary_test_header( $test, $parser ); $self->_failure_output(" Non-zero exit status: $exit\n"); } elsif ( my $wait = $parser->wait ) { $self->_summary_test_header( $test, $parser ); $self->_failure_output(" Non-zero wait status: $wait\n"); }

    if ( my @errors = $parser->parse_errors ) { my $explain; if ( @errors > $MAX_ERRORS && !$self->errors ) { $explain = "Displayed the first $MAX_ERRORS of " . scalar(@errors) . " TAP syntax errors.\n" . "Re-run prove with the -p option to see them all.\n"; splice @errors, $MAX_ERRORS; }

    $self->_summary_test_header( $test, $parser ); $self->_failure_output( sprintf " Parse errors: %s\n", shift @errors ); foreach my $error (@errors) { my $spaces = ' ' x 16; $self->_failure_output("$spaces$error\n"); } $self->_failure_output($explain) if $explain; } } }

    my $files = @$tests; $self->_output("Files=$files, Tests=$total, $runtime\n"); my $status = $aggregate->get_status; $self->_output("Result: $status\n");}

    sub _output_summary_failure { my ( $self, $method, $name, $test, $parser ) = @_;

    # ugly hack. Must rethink this :( my $output = $method eq 'failed' ? '_failure_output' : '_output';

    if ( my @r = $parser->$method() ) {

    $self->_summary_test_header( $test, $parser ); my ( $singular, $plural ) = 'ARRAY' eq ref $name ? @$name : ( $name, $name ); $self->$output( @r == 1 ? $singular : $plural ); my @results = $self->_balanced_range( 40, @r ); $self->$output( sprintf "%s\n" => shift @results ); my $spaces = ' ' x 16; while (@results) { $self->$output( sprintf "$spaces%s\n" => shift @results ); }

  • 8/10/2019 At8326GB Instalation Guide

    56/643

    }}

    sub _summary_test_header { my ( $self, $test, $parser ) = @_; return if $self->_printed_summary_header; my $spaces = ' ' x ( $self->_longest - length $test ); $spaces = ' ' unless $spaces; my $output = $self->_get_output_method($parser); $self->$output( sprintf "$test$spaces(Wstat: %d Tests: %d Failed: %d)\n", $parser->wait, $parser->tests_run, scalar $parser->failed ); $self->_printed_summary_header(1);}

    sub _output { my $self = shift;

    print { $self->stdout } @_;}

    sub _failure_output { my $self = shift;

    $self->_output(@_);}

    sub _balanced_range { my ( $self, $limit, @range ) = @_; @range = $self->_range(@range); my $line = ""; my @lines; my $curr = 0; while (@range) { if ( $curr < $limit ) { my $range = ( shift @range ) . ", ";

    $line .= $range; $curr += length $range; } elsif (@range) { $line =~ s/, $//; push @lines => $line; $line = ''; $curr = 0; } } if ($line) { $line =~ s/, $//; push @lines => $line;

    } return @lines;}

    sub _range { my ( $self, @numbers ) = @_;

    # shouldn't be needed, but subclasses might call this @numbers = sort { $a $b } @numbers; my ( $min, @range );

  • 8/10/2019 At8326GB Instalation Guide

    57/643

    foreach my $i ( 0 .. $#numbers ) { my $num = $numbers[$i]; my $next = $numbers[ $i + 1 ]; if ( defined $next && $next == $num + 1 ) { if ( !defined $min ) { $min = $num; } } elsif ( defined $min ) { push @range => "$min-$num"; undef $min; } else { push @range => $num; } } return @range;}

    sub _get_output_method { my ( $self, $parser ) = @_; return $parser->has_problems ? '_failure_output' : '_output';}

    1;package TAP::Formatter::Color;

    use strict;use vars qw($VERSION @ISA);

    use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );

    @ISA = qw(TAP::Object);

    my $NO_COLOR;

    BEGIN { $NO_COLOR = 0;

    if (IS_WIN32) { eval 'use Win32::Console'; if ($@) { $NO_COLOR = $@; } else { my $console = Win32::Console->new( STD_OUTPUT_HANDLE() );

    # eval here because we might not know about these variables my $fg = eval '$FG_LIGHTGRAY';

    my $bg = eval '$BG_BLACK';

    *set_color = sub { my ( $self, $output, $color ) = @_;

    my $var; if ( $color eq 'reset' ) { $fg = eval '$FG_LIGHTGRAY'; $bg = eval '$BG_BLACK'; }

  • 8/10/2019 At8326GB Instalation Guide

    58/643

    elsif ( $color =~ /^on_(.+)$/ ) { $bg = eval '$BG_' . uc($1); } else { $fg = eval '$FG_' . uc($color); }

    # In case of colors that aren't defined $self->set_color('reset') unless defined $bg && defined $fg;

    $console->Attr( $bg | $fg ); }; } } else { eval 'use Term::ANSIColor'; if ($@) { $NO_COLOR = $@; } else { *set_color = sub { my ( $self, $output, $color ) = @_; $output->( color($color) );

    }; } }

    if ($NO_COLOR) { *set_color = sub { }; }}

    =head1 NAME

    TAP::Formatter::Color - Run Perl test scripts with color

    =head1 VERSION

    Version 3.17

    =cut

    $VERSION = '3.17';

    =head1 DESCRIPTION

    Note that this harness is I. You may not like the colors I'vechosen and I haven't yet provided an easy way to override them.

    This test harness is the same as L, but test results are outputin color. Passing tests are printed in green. Failing tests are in red.Skipped tests are blue on a white background and TODO tests are printed inwhite.

    If L cannot be found (or L if runningunder Windows) tests will be run without color.

    =head1 SYNOPSIS

  • 8/10/2019 At8326GB Instalation Guide

    59/643

    use TAP::Formatter::Color;my $harness = TAP::Formatter::Color->new( \%args );$harness->runtests(@tests);

    =head1 METHODS

    =head2 Class Methods

    =head3 C

    The constructor returns a new C object. IfL is not installed, returns undef.

    =cut

    # new() implementation supplied by TAP::Object

    sub _initialize { my $self = shift;

    if ($NO_COLOR) {

    # shorten that message a bit ( my $error = $NO_COLOR ) =~ s/ in \@INC .*//s;

    warn "Note: Cannot run tests in color: $error\n"; return; # abort object construction }

    return $self;}

    ##############################################################################

    =head3 C

    Test::Formatter::Color->can_color()

    Returns a boolean indicating whether or not this module can actuallygenerate colored output. This will be false if it could not load themodules needed for the current platform.

    =cut

    sub can_color { return !$NO_COLOR;}

    =head3 C

    Set the output color.

    =cut

    1;package TAP::Formatter::Console;

    use strict;use TAP::Formatter::Base ();use POSIX qw(strftime);

  • 8/10/2019 At8326GB Instalation Guide

    60/643

    use vars qw($VERSION @ISA);

    @ISA = qw(TAP::Formatter::Base);

    =head1 NAME

    TAP::Formatter::Console - Harness output delegate for default console output

    =head1 VERSION

    Version 3.17

    =cut

    $VERSION = '3.17';

    =head1 DESCRIPTION

    This provides console orientated output formatting for TAP::Harness.

    =head1 SYNOPSIS

    use TAP::Formatter::Console;my $harness = TAP::Formatter::Console->new( \%args );

    =head2 C>

    See L

    =cut

    sub open_test { my ( $self, $test, $parser ) = @_;

    my $class = $self->jobs > 1 ? 'TAP::Formatter::Console::ParallelSession'

    : 'TAP::Formatter::Console::Session';

    eval "require $class"; $self->_croak($@) if $@;

    my $session = $class->new( { name => $test, formatter => $self, parser => $parser, show_count => $self->show_count, } );

    $session->header;

    return $session;}

    # Use _colorizer delegate to set output color. NOP if we have no delegatesub _set_colors { my ( $self, @colors ) = @_; if ( my $colorizer = $self->_colorizer ) { my $output_func = $self->{_output_func} ||= sub {

  • 8/10/2019 At8326GB Instalation Guide

    61/643

    $self->_output(@_); }; $colorizer->set_color( $output_func, $_ ) for @colors; }}

    sub _output_success { my ( $self, $msg ) = @_; $self->_set_colors('green'); $self->_output($msg); $self->_set_colors('reset');}

    sub _failure_output { my $self = shift; $self->_set_colors('red'); my $out = join '', @_; my $has_newline = chomp $out; $self->_output($out); $self->_set_colors('reset'); $self->_output($/) if $has_newline;}

    1;package TAP::Formatter::File;

    use strict;use TAP::Formatter::Base ();use TAP::Formatter::File::Session;use POSIX qw(strftime);

    use vars qw($VERSION @ISA);

    @ISA = qw(TAP::Formatter::Base);

    =head1 NAME

    TAP::Formatter::File - Harness output delegate for file output

    =head1 VERSION

    Version 3.17

    =cut

    $VERSION = '3.17';

    =head1 DESCRIPTION

    This provides file orientated output formatting for TAP::Harness.

    =head1 SYNOPSIS

    use TAP::Formatter::File;my $harness = TAP::Formatter::File->new( \%args );

    =head2 C>

    See L

  • 8/10/2019 At8326GB Instalation Guide

    62/643

    =cut

    sub open_test { my ( $self, $test, $parser ) = @_;

    my $session = TAP::Formatter::File::Session->new( { name => $test, formatter => $self, parser => $parser, } );

    $session->header;

    return $session;}

    sub _should_show_count { return 0;}

    1;package TAP::Formatter::Session;

    use strict;use TAP::Base;

    use vars qw($VERSION @ISA);

    @ISA = qw(TAP::Base);

    my @ACCESSOR;

    BEGIN {

    @ACCESSOR = qw( name formatter parser show_count );

    for my $method (@ACCESSOR) { no strict 'refs'; *$method = sub { shift->{$method} }; }}

    =head1 NAME

    TAP::Formatter::Session - Abstract base class for harness output delegate

    =head1 VERSION

    Version 3.17

    =cut

    $VERSION = '3.17';

    =head1 METHODS

    =head2 Class Methods

  • 8/10/2019 At8326GB Instalation Guide

    63/643

  • 8/10/2019 At8326GB Instalation Guide

    64/643

    =head3 C

    Called by C to clear the line showing test progress, or the paralleltest ruler, prior to printing the final test result.

    =cut

    sub header { }

    sub result { }

    sub close_test { }

    sub clear_for_close { }

    sub _should_show_count { my $self = shift; return !$self->formatter->verbose && -t $self->formatter->stdout && !$ENV{HARNESS_NOTTY};}

    sub _format_for_output { my ( $self, $result ) = @_; return $self->formatter->normalize ? $result->as_string : $result->raw;}

    sub _output_test_failure { my ( $self, $parser ) = @_; my $formatter = $self->formatter; return if $formatter->really_quiet;

    my $tests_run = $parser->tests_run; my $tests_planned = $parser->tests_planned;

    my $total = defined $tests_planned ? $tests_planned : $tests_run;

    my $passed = $parser->passed;

    # The total number of fails includes any tests that were planned but # didn't run my $failed = $parser->failed + $total - $tests_run; my $exit = $parser->exit;

    if ( my $exit = $parser->exit ) {

    my $wstat = $parser->wait; my $status = sprintf( "%d (wstat %d, 0x%x)", $exit, $wstat, $wstat ); $formatter->_failure_output("Dubious, test returned $status\n"); }

    if ( $failed == 0 ) { $formatter->_failure_output( $total ? "All $total subtests passed " : 'No subtests run '

  • 8/10/2019 At8326GB Instalation Guide

    65/643

    ); } else { $formatter->_failure_output("Failed $failed/$total subtests "); if ( !$total ) { $formatter->_failure_output("\nNo tests run!"); } }

    if ( my $skipped = $parser->skipped ) { $passed -= $skipped; my $test = 'subtest' . ( $skipped != 1 ? 's' : '' ); $formatter->_output( "\n\t(less $skipped skipped $test: $passed okay)"); }

    if ( my $failed = $parser->todo_passed ) { my $test = $failed > 1 ? 'tests' : 'test'; $formatter->_output( "\n\t($failed TODO $test unexpectedly succeeded)"); }

    $formatter->_output("\n");}

    1;package TAP::Formatter::Console::ParallelSession;

    use strict;use File::Spec;use File::Path;use TAP::Formatter::Console::Session;use Carp;

    use constant WIDTH => 72; # Because Eric saysuse vars qw($VERSION @ISA);

    @ISA = qw(TAP::Formatter::Console::Session);

    my %shared;

    sub _initialize { my ( $self, $arg_for ) = @_;

    $self->SUPER::_initialize($arg_for); my $formatter = $self->formatter;

    # Horrid bodge. This creates our shared context per harness. Maybe # TAP::Harness should give us this? my $context = $shared{$formatter} ||= $self->_create_shared_context;

    push @{ $context->{active} }, $self;

    return $self;}

    sub _create_shared_context { my $self = shift; return { active => [], tests => 0,

  • 8/10/2019 At8326GB Instalation Guide

    66/643

    fails => 0, };}

    =head1 NAME


Recommended