1package TAP::Harness; 2 3use strict; 4use Carp; 5 6use File::Spec; 7use File::Path; 8use IO::Handle; 9 10use TAP::Base; 11 12use vars qw($VERSION @ISA); 13 14@ISA = qw(TAP::Base); 15 16=head1 NAME 17 18TAP::Harness - Run test scripts with statistics 19 20=head1 VERSION 21 22Version 3.26 23 24=cut 25 26$VERSION = '3.26'; 27 28$ENV{HARNESS_ACTIVE} = 1; 29$ENV{HARNESS_VERSION} = $VERSION; 30 31END { 32 33 # For VMS. 34 delete $ENV{HARNESS_ACTIVE}; 35 delete $ENV{HARNESS_VERSION}; 36} 37 38=head1 DESCRIPTION 39 40This is a simple test harness which allows tests to be run and results 41automatically aggregated and output to STDOUT. 42 43=head1 SYNOPSIS 44 45 use TAP::Harness; 46 my $harness = TAP::Harness->new( \%args ); 47 $harness->runtests(@tests); 48 49=cut 50 51my %VALIDATION_FOR; 52my @FORMATTER_ARGS; 53 54sub _error { 55 my $self = shift; 56 return $self->{error} unless @_; 57 $self->{error} = shift; 58} 59 60BEGIN { 61 62 @FORMATTER_ARGS = qw( 63 directives verbosity timer failures comments errors stdout color 64 show_count normalize 65 ); 66 67 %VALIDATION_FOR = ( 68 lib => sub { 69 my ( $self, $libs ) = @_; 70 $libs = [$libs] unless 'ARRAY' eq ref $libs; 71 72 return [ map {"-I$_"} @$libs ]; 73 }, 74 switches => sub { shift; shift }, 75 exec => sub { shift; shift }, 76 merge => sub { shift; shift }, 77 aggregator_class => sub { shift; shift }, 78 formatter_class => sub { shift; shift }, 79 multiplexer_class => sub { shift; shift }, 80 parser_class => sub { shift; shift }, 81 scheduler_class => sub { shift; shift }, 82 formatter => sub { shift; shift }, 83 jobs => sub { shift; shift }, 84 test_args => sub { shift; shift }, 85 ignore_exit => sub { shift; shift }, 86 rules => sub { shift; shift }, 87 sources => sub { shift; shift }, 88 version => sub { shift; shift }, 89 trap => sub { shift; shift }, 90 ); 91 92 for my $method ( sort keys %VALIDATION_FOR ) { 93 no strict 'refs'; 94 if ( $method eq 'lib' || $method eq 'switches' ) { 95 *{$method} = sub { 96 my $self = shift; 97 unless (@_) { 98 $self->{$method} ||= []; 99 return wantarray 100 ? @{ $self->{$method} } 101 : $self->{$method}; 102 } 103 $self->_croak("Too many arguments to method '$method'") 104 if @_ > 1; 105 my $args = shift; 106 $args = [$args] unless ref $args; 107 $self->{$method} = $args; 108 return $self; 109 }; 110 } 111 else { 112 *{$method} = sub { 113 my $self = shift; 114 return $self->{$method} unless @_; 115 $self->{$method} = shift; 116 }; 117 } 118 } 119 120 for my $method (@FORMATTER_ARGS) { 121 no strict 'refs'; 122 *{$method} = sub { 123 my $self = shift; 124 return $self->formatter->$method(@_); 125 }; 126 } 127} 128 129############################################################################## 130 131=head1 METHODS 132 133=head2 Class Methods 134 135=head3 C<new> 136 137 my %args = ( 138 verbosity => 1, 139 lib => [ 'lib', 'blib/lib', 'blib/arch' ], 140 ) 141 my $harness = TAP::Harness->new( \%args ); 142 143The constructor returns a new C<TAP::Harness> object. It accepts an 144optional hashref whose allowed keys are: 145 146=over 4 147 148=item * C<verbosity> 149 150Set the verbosity level: 151 152 1 verbose Print individual test results to STDOUT. 153 0 normal 154 -1 quiet Suppress some test output (mostly failures 155 while tests are running). 156 -2 really quiet Suppress everything but the tests summary. 157 -3 silent Suppress everything. 158 159=item * C<timer> 160 161Append run time for each test to output. Uses L<Time::HiRes> if 162available. 163 164=item * C<failures> 165 166Show test failures (this is a no-op if C<verbose> is selected). 167 168=item * C<comments> 169 170Show test comments (this is a no-op if C<verbose> is selected). 171 172=item * C<show_count> 173 174Update the running test count during testing. 175 176=item * C<normalize> 177 178Set to a true value to normalize the TAP that is emitted in verbose modes. 179 180=item * C<lib> 181 182Accepts a scalar value or array ref of scalar values indicating which 183paths to allowed libraries should be included if Perl tests are 184executed. Naturally, this only makes sense in the context of tests 185written in Perl. 186 187=item * C<switches> 188 189Accepts a scalar value or array ref of scalar values indicating which 190switches should be included if Perl tests are executed. Naturally, this 191only makes sense in the context of tests written in Perl. 192 193=item * C<test_args> 194 195A reference to an C<@INC> style array of arguments to be passed to each 196test program. 197 198 test_args => ['foo', 'bar'], 199 200if you want to pass different arguments to each test then you should 201pass a hash of arrays, keyed by the alias for each test: 202 203 test_args => { 204 my_test => ['foo', 'bar'], 205 other_test => ['baz'], 206 } 207 208=item * C<color> 209 210Attempt to produce color output. 211 212=item * C<exec> 213 214Typically, Perl tests are run through this. However, anything which 215spits out TAP is fine. You can use this argument to specify the name of 216the program (and optional switches) to run your tests with: 217 218 exec => ['/usr/bin/ruby', '-w'] 219 220You can also pass a subroutine reference in order to determine and 221return the proper program to run based on a given test script. The 222subroutine reference should expect the TAP::Harness object itself as the 223first argument, and the file name as the second argument. It should 224return an array reference containing the command to be run and including 225the test file name. It can also simply return C<undef>, in which case 226TAP::Harness will fall back on executing the test script in Perl: 227 228 exec => sub { 229 my ( $harness, $test_file ) = @_; 230 231 # Let Perl tests run. 232 return undef if $test_file =~ /[.]t$/; 233 return [ qw( /usr/bin/ruby -w ), $test_file ] 234 if $test_file =~ /[.]rb$/; 235 } 236 237If the subroutine returns a scalar with a newline or a filehandle, it 238will be interpreted as raw TAP or as a TAP stream, respectively. 239 240=item * C<merge> 241 242If C<merge> is true the harness will create parsers that merge STDOUT 243and STDERR together for any processes they start. 244 245=item * C<sources> 246 247I<NEW to 3.18>. 248 249If set, C<sources> must be a hashref containing the names of the 250L<TAP::Parser::SourceHandler>s to load and/or configure. The values are a 251hash of configuration that will be accessible to to the source handlers via 252L<TAP::Parser::Source/config_for>. 253 254For example: 255 256 sources => { 257 Perl => { exec => '/path/to/custom/perl' }, 258 File => { extensions => [ '.tap', '.txt' ] }, 259 MyCustom => { some => 'config' }, 260 } 261 262The C<sources> parameter affects how C<source>, C<tap> and C<exec> parameters 263are handled. 264 265For more details, see the C<sources> parameter in L<TAP::Parser/new>, 266L<TAP::Parser::Source>, and L<TAP::Parser::IteratorFactory>. 267 268=item * C<aggregator_class> 269 270The name of the class to use to aggregate test results. The default is 271L<TAP::Parser::Aggregator>. 272 273=item * C<version> 274 275I<NEW to 3.22>. 276 277Assume this TAP version for L<TAP::Parser> instead of default TAP 278version 12. 279 280=item * C<formatter_class> 281 282The name of the class to use to format output. The default is 283L<TAP::Formatter::Console>, or L<TAP::Formatter::File> if the output 284isn't a TTY. 285 286=item * C<multiplexer_class> 287 288The name of the class to use to multiplex tests during parallel testing. 289The default is L<TAP::Parser::Multiplexer>. 290 291=item * C<parser_class> 292 293The name of the class to use to parse TAP. The default is 294L<TAP::Parser>. 295 296=item * C<scheduler_class> 297 298The name of the class to use to schedule test execution. The default is 299L<TAP::Parser::Scheduler>. 300 301=item * C<formatter> 302 303If set C<formatter> must be an object that is capable of formatting the 304TAP output. See L<TAP::Formatter::Console> for an example. 305 306=item * C<errors> 307 308If parse errors are found in the TAP output, a note of this will be 309made in the summary report. To see all of the parse errors, set this 310argument to true: 311 312 errors => 1 313 314=item * C<directives> 315 316If set to a true value, only test results with directives will be 317displayed. This overrides other settings such as C<verbose> or 318C<failures>. 319 320=item * C<ignore_exit> 321 322If set to a true value instruct C<TAP::Parser> to ignore exit and wait 323status from test scripts. 324 325=item * C<jobs> 326 327The maximum number of parallel tests to run at any time. Which tests 328can be run in parallel is controlled by C<rules>. The default is to 329run only one test at a time. 330 331=item * C<rules> 332 333A reference to a hash of rules that control which tests may be 334executed in parallel. This is an experimental feature and the 335interface may change. 336 337 $harness->rules( 338 { par => [ 339 { seq => '../ext/DB_File/t/*' }, 340 { seq => '../ext/IO_Compress_Zlib/t/*' }, 341 { seq => '../lib/CPANPLUS/*' }, 342 { seq => '../lib/ExtUtils/t/*' }, 343 '*' 344 ] 345 } 346 ); 347 348=item * C<stdout> 349 350A filehandle for catching standard output. 351 352=item * C<trap> 353 354Attempt to print summary information if run is interrupted by 355SIGINT (Ctrl-C). 356 357=back 358 359Any keys for which the value is C<undef> will be ignored. 360 361=cut 362 363# new supplied by TAP::Base 364 365{ 366 my @legal_callback = qw( 367 parser_args 368 made_parser 369 before_runtests 370 after_runtests 371 after_test 372 ); 373 374 my %default_class = ( 375 aggregator_class => 'TAP::Parser::Aggregator', 376 formatter_class => 'TAP::Formatter::Console', 377 multiplexer_class => 'TAP::Parser::Multiplexer', 378 parser_class => 'TAP::Parser', 379 scheduler_class => 'TAP::Parser::Scheduler', 380 ); 381 382 sub _initialize { 383 my ( $self, $arg_for ) = @_; 384 $arg_for ||= {}; 385 386 $self->SUPER::_initialize( $arg_for, \@legal_callback ); 387 my %arg_for = %$arg_for; # force a shallow copy 388 389 for my $name ( sort keys %VALIDATION_FOR ) { 390 my $property = delete $arg_for{$name}; 391 if ( defined $property ) { 392 my $validate = $VALIDATION_FOR{$name}; 393 394 my $value = $self->$validate($property); 395 if ( $self->_error ) { 396 $self->_croak; 397 } 398 $self->$name($value); 399 } 400 } 401 402 $self->jobs(1) unless defined $self->jobs; 403 404 local $default_class{formatter_class} = 'TAP::Formatter::File' 405 unless -t ( $arg_for{stdout} || \*STDOUT ) && !$ENV{HARNESS_NOTTY}; 406 407 while ( my ( $attr, $class ) = each %default_class ) { 408 $self->$attr( $self->$attr() || $class ); 409 } 410 411 unless ( $self->formatter ) { 412 413 # This is a little bodge to preserve legacy behaviour. It's 414 # pretty horrible that we know which args are destined for 415 # the formatter. 416 my %formatter_args = ( jobs => $self->jobs ); 417 for my $name (@FORMATTER_ARGS) { 418 if ( defined( my $property = delete $arg_for{$name} ) ) { 419 $formatter_args{$name} = $property; 420 } 421 } 422 423 $self->formatter( 424 $self->_construct( $self->formatter_class, \%formatter_args ) 425 ); 426 } 427 428 if ( my @props = sort keys %arg_for ) { 429 $self->_croak("Unknown arguments to TAP::Harness::new (@props)"); 430 } 431 432 return $self; 433 } 434} 435 436############################################################################## 437 438=head2 Instance Methods 439 440=head3 C<runtests> 441 442 $harness->runtests(@tests); 443 444Accepts an array of C<@tests> to be run. This should generally be the 445names of test files, but this is not required. Each element in C<@tests> 446will be passed to C<TAP::Parser::new()> as a C<source>. See 447L<TAP::Parser> for more information. 448 449It is possible to provide aliases that will be displayed in place of the 450test name by supplying the test as a reference to an array containing 451C<< [ $test, $alias ] >>: 452 453 $harness->runtests( [ 't/foo.t', 'Foo Once' ], 454 [ 't/foo.t', 'Foo Twice' ] ); 455 456Normally it is an error to attempt to run the same test twice. Aliases 457allow you to overcome this limitation by giving each run of the test a 458unique name. 459 460Tests will be run in the order found. 461 462If the environment variable C<PERL_TEST_HARNESS_DUMP_TAP> is defined it 463should name a directory into which a copy of the raw TAP for each test 464will be written. TAP is written to files named for each test. 465Subdirectories will be created as needed. 466 467Returns a L<TAP::Parser::Aggregator> containing the test results. 468 469=cut 470 471sub runtests { 472 my ( $self, @tests ) = @_; 473 474 my $aggregate = $self->_construct( $self->aggregator_class ); 475 476 $self->_make_callback( 'before_runtests', $aggregate ); 477 $aggregate->start; 478 my $finish = sub { 479 my $interrupted = shift; 480 $aggregate->stop; 481 $self->summary( $aggregate, $interrupted ); 482 $self->_make_callback( 'after_runtests', $aggregate ); 483 }; 484 my $run = sub { 485 $self->aggregate_tests( $aggregate, @tests ); 486 $finish->(); 487 }; 488 489 if ( $self->trap ) { 490 local $SIG{INT} = sub { 491 print "\n"; 492 $finish->(1); 493 exit; 494 }; 495 $run->(); 496 } 497 else { 498 $run->(); 499 } 500 501 return $aggregate; 502} 503 504=head3 C<summary> 505 506 $harness->summary( $aggregator ); 507 508Output the summary for a L<TAP::Parser::Aggregator>. 509 510=cut 511 512sub summary { 513 my ( $self, @args ) = @_; 514 $self->formatter->summary(@args); 515} 516 517sub _after_test { 518 my ( $self, $aggregate, $job, $parser ) = @_; 519 520 $self->_make_callback( 'after_test', $job->as_array_ref, $parser ); 521 $aggregate->add( $job->description, $parser ); 522} 523 524sub _bailout { 525 my ( $self, $result ) = @_; 526 my $explanation = $result->explanation; 527 die "FAILED--Further testing stopped" 528 . ( $explanation ? ": $explanation\n" : ".\n" ); 529} 530 531sub _aggregate_parallel { 532 my ( $self, $aggregate, $scheduler ) = @_; 533 534 my $jobs = $self->jobs; 535 my $mux = $self->_construct( $self->multiplexer_class ); 536 537 RESULT: { 538 539 # Keep multiplexer topped up 540 FILL: 541 while ( $mux->parsers < $jobs ) { 542 my $job = $scheduler->get_job; 543 544 # If we hit a spinner stop filling and start running. 545 last FILL if !defined $job || $job->is_spinner; 546 547 my ( $parser, $session ) = $self->make_parser($job); 548 $mux->add( $parser, [ $session, $job ] ); 549 } 550 551 if ( my ( $parser, $stash, $result ) = $mux->next ) { 552 my ( $session, $job ) = @$stash; 553 if ( defined $result ) { 554 $session->result($result); 555 $self->_bailout($result) if $result->is_bailout; 556 } 557 else { 558 559 # End of parser. Automatically removed from the mux. 560 $self->finish_parser( $parser, $session ); 561 $self->_after_test( $aggregate, $job, $parser ); 562 $job->finish; 563 } 564 redo RESULT; 565 } 566 } 567 568 return; 569} 570 571sub _aggregate_single { 572 my ( $self, $aggregate, $scheduler ) = @_; 573 574 JOB: 575 while ( my $job = $scheduler->get_job ) { 576 next JOB if $job->is_spinner; 577 578 my ( $parser, $session ) = $self->make_parser($job); 579 580 while ( defined( my $result = $parser->next ) ) { 581 $session->result($result); 582 if ( $result->is_bailout ) { 583 584 # Keep reading until input is exhausted in the hope 585 # of allowing any pending diagnostics to show up. 586 1 while $parser->next; 587 $self->_bailout($result); 588 } 589 } 590 591 $self->finish_parser( $parser, $session ); 592 $self->_after_test( $aggregate, $job, $parser ); 593 $job->finish; 594 } 595 596 return; 597} 598 599=head3 C<aggregate_tests> 600 601 $harness->aggregate_tests( $aggregate, @tests ); 602 603Run the named tests and display a summary of result. Tests will be run 604in the order found. 605 606Test results will be added to the supplied L<TAP::Parser::Aggregator>. 607C<aggregate_tests> may be called multiple times to run several sets of 608tests. Multiple C<Test::Harness> instances may be used to pass results 609to a single aggregator so that different parts of a complex test suite 610may be run using different C<TAP::Harness> settings. This is useful, for 611example, in the case where some tests should run in parallel but others 612are unsuitable for parallel execution. 613 614 my $formatter = TAP::Formatter::Console->new; 615 my $ser_harness = TAP::Harness->new( { formatter => $formatter } ); 616 my $par_harness = TAP::Harness->new( 617 { formatter => $formatter, 618 jobs => 9 619 } 620 ); 621 my $aggregator = TAP::Parser::Aggregator->new; 622 623 $aggregator->start(); 624 $ser_harness->aggregate_tests( $aggregator, @ser_tests ); 625 $par_harness->aggregate_tests( $aggregator, @par_tests ); 626 $aggregator->stop(); 627 $formatter->summary($aggregator); 628 629Note that for simpler testing requirements it will often be possible to 630replace the above code with a single call to C<runtests>. 631 632Each element of the C<@tests> array is either: 633 634=over 635 636=item * the source name of a test to run 637 638=item * a reference to a [ source name, display name ] array 639 640=back 641 642In the case of a perl test suite, typically I<source names> are simply the file 643names of the test scripts to run. 644 645When you supply a separate display name it becomes possible to run a 646test more than once; the display name is effectively the alias by which 647the test is known inside the harness. The harness doesn't care if it 648runs the same test more than once when each invocation uses a 649different name. 650 651=cut 652 653sub aggregate_tests { 654 my ( $self, $aggregate, @tests ) = @_; 655 656 my $jobs = $self->jobs; 657 my $scheduler = $self->make_scheduler(@tests); 658 659 # #12458 660 local $ENV{HARNESS_IS_VERBOSE} = 1 661 if $self->formatter->verbosity > 0; 662 663 # Formatter gets only names. 664 $self->formatter->prepare( map { $_->description } $scheduler->get_all ); 665 666 if ( $self->jobs > 1 ) { 667 $self->_aggregate_parallel( $aggregate, $scheduler ); 668 } 669 else { 670 $self->_aggregate_single( $aggregate, $scheduler ); 671 } 672 673 return; 674} 675 676sub _add_descriptions { 677 my $self = shift; 678 679 # Turn unwrapped scalars into anonymous arrays and copy the name as 680 # the description for tests that have only a name. 681 return map { @$_ == 1 ? [ $_->[0], $_->[0] ] : $_ } 682 map { 'ARRAY' eq ref $_ ? $_ : [$_] } @_; 683} 684 685=head3 C<make_scheduler> 686 687Called by the harness when it needs to create a 688L<TAP::Parser::Scheduler>. Override in a subclass to provide an 689alternative scheduler. C<make_scheduler> is passed the list of tests 690that was passed to C<aggregate_tests>. 691 692=cut 693 694sub make_scheduler { 695 my ( $self, @tests ) = @_; 696 return $self->_construct( 697 $self->scheduler_class, 698 tests => [ $self->_add_descriptions(@tests) ], 699 rules => $self->rules 700 ); 701} 702 703=head3 C<jobs> 704 705Gets or sets the number of concurrent test runs the harness is 706handling. By default, this value is 1 -- for parallel testing, this 707should be set higher. 708 709=cut 710 711############################################################################## 712 713sub _get_parser_args { 714 my ( $self, $job ) = @_; 715 my $test_prog = $job->filename; 716 my %args = (); 717 718 $args{sources} = $self->sources if $self->sources; 719 720 my @switches; 721 @switches = $self->lib if $self->lib; 722 push @switches => $self->switches if $self->switches; 723 $args{switches} = \@switches; 724 $args{spool} = $self->_open_spool($test_prog); 725 $args{merge} = $self->merge; 726 $args{ignore_exit} = $self->ignore_exit; 727 $args{version} = $self->version if $self->version; 728 729 if ( my $exec = $self->exec ) { 730 $args{exec} 731 = ref $exec eq 'CODE' 732 ? $exec->( $self, $test_prog ) 733 : [ @$exec, $test_prog ]; 734 if ( not defined $args{exec} ) { 735 $args{source} = $test_prog; 736 } 737 elsif ( ( ref( $args{exec} ) || "" ) ne "ARRAY" ) { 738 $args{source} = delete $args{exec}; 739 } 740 } 741 else { 742 $args{source} = $test_prog; 743 } 744 745 if ( defined( my $test_args = $self->test_args ) ) { 746 747 if ( ref($test_args) eq 'HASH' ) { 748 749 # different args for each test 750 if ( exists( $test_args->{ $job->description } ) ) { 751 $test_args = $test_args->{ $job->description }; 752 } 753 else { 754 $self->_croak( "TAP::Harness Can't find test_args for " 755 . $job->description ); 756 } 757 } 758 759 $args{test_args} = $test_args; 760 } 761 762 return \%args; 763} 764 765=head3 C<make_parser> 766 767Make a new parser and display formatter session. Typically used and/or 768overridden in subclasses. 769 770 my ( $parser, $session ) = $harness->make_parser; 771 772=cut 773 774sub make_parser { 775 my ( $self, $job ) = @_; 776 777 my $args = $self->_get_parser_args($job); 778 $self->_make_callback( 'parser_args', $args, $job->as_array_ref ); 779 my $parser = $self->_construct( $self->parser_class, $args ); 780 781 $self->_make_callback( 'made_parser', $parser, $job->as_array_ref ); 782 my $session = $self->formatter->open_test( $job->description, $parser ); 783 784 return ( $parser, $session ); 785} 786 787=head3 C<finish_parser> 788 789Terminate use of a parser. Typically used and/or overridden in 790subclasses. The parser isn't destroyed as a result of this. 791 792=cut 793 794sub finish_parser { 795 my ( $self, $parser, $session ) = @_; 796 797 $session->close_test; 798 $self->_close_spool($parser); 799 800 return $parser; 801} 802 803sub _open_spool { 804 my $self = shift; 805 my $test = shift; 806 807 if ( my $spool_dir = $ENV{PERL_TEST_HARNESS_DUMP_TAP} ) { 808 809 my $spool = File::Spec->catfile( $spool_dir, $test ); 810 811 # Make the directory 812 my ( $vol, $dir, undef ) = File::Spec->splitpath($spool); 813 my $path = File::Spec->catpath( $vol, $dir, '' ); 814 eval { mkpath($path) }; 815 $self->_croak($@) if $@; 816 817 my $spool_handle = IO::Handle->new; 818 open( $spool_handle, ">$spool" ) 819 or $self->_croak(" Can't write $spool ( $! ) "); 820 821 return $spool_handle; 822 } 823 824 return; 825} 826 827sub _close_spool { 828 my $self = shift; 829 my ($parser) = @_; 830 831 if ( my $spool_handle = $parser->delete_spool ) { 832 close($spool_handle) 833 or $self->_croak(" Error closing TAP spool file( $! ) \n "); 834 } 835 836 return; 837} 838 839sub _croak { 840 my ( $self, $message ) = @_; 841 unless ($message) { 842 $message = $self->_error; 843 } 844 $self->SUPER::_croak($message); 845 846 return; 847} 848 8491; 850 851__END__ 852 853############################################################################## 854 855=head1 CONFIGURING 856 857C<TAP::Harness> is designed to be easy to configure. 858 859=head2 Plugins 860 861C<TAP::Parser> plugins let you change the way TAP is I<input> to and I<output> 862from the parser. 863 864L<TAP::Parser::SourceHandler>s handle TAP I<input>. You can configure them 865and load custom handlers using the C<sources> parameter to L</new>. 866 867L<TAP::Formatter>s handle TAP I<output>. You can load custom formatters by 868using the C<formatter_class> parameter to L</new>. To configure a formatter, 869you currently need to instantiate it outside of L<TAP::Harness> and pass it in 870with the C<formatter> parameter to L</new>. This I<may> be addressed by adding 871a I<formatters> parameter to L</new> in the future. 872 873=head2 C<Module::Build> 874 875L<Module::Build> version C<0.30> supports C<TAP::Harness>. 876 877To load C<TAP::Harness> plugins, you'll need to use the C<tap_harness_args> 878parameter to C<new>, typically from your C<Build.PL>. For example: 879 880 Module::Build->new( 881 module_name => 'MyApp', 882 test_file_exts => [qw(.t .tap .txt)], 883 use_tap_harness => 1, 884 tap_harness_args => { 885 sources => { 886 MyCustom => {}, 887 File => { 888 extensions => ['.tap', '.txt'], 889 }, 890 }, 891 formatter_class => 'TAP::Formatter::HTML', 892 }, 893 build_requires => { 894 'Module::Build' => '0.30', 895 'TAP::Harness' => '3.18', 896 }, 897 )->create_build_script; 898 899See L</new> 900 901=head2 C<ExtUtils::MakeMaker> 902 903L<ExtUtils::MakeMaker> does not support L<TAP::Harness> out-of-the-box. 904 905=head2 C<prove> 906 907L<prove> supports C<TAP::Harness> plugins, and has a plugin system of its 908own. See L<prove/FORMATTERS>, L<prove/SOURCE HANDLERS> and L<App::Prove> 909for more details. 910 911=head1 WRITING PLUGINS 912 913If you can't configure C<TAP::Harness> to do what you want, and you can't find 914an existing plugin, consider writing one. 915 916The two primary use cases supported by L<TAP::Harness> for plugins are I<input> 917and I<output>: 918 919=over 2 920 921=item Customize how TAP gets into the parser 922 923To do this, you can either extend an existing L<TAP::Parser::SourceHandler>, 924or write your own. It's a pretty simple API, and they can be loaded and 925configured using the C<sources> parameter to L</new>. 926 927=item Customize how TAP results are output from the parser 928 929To do this, you can either extend an existing L<TAP::Formatter>, or write your 930own. Writing formatters are a bit more involved than writing a 931I<SourceHandler>, as you'll need to understand the L<TAP::Parser> API. A 932good place to start is by understanding how L</aggregate_tests> works. 933 934Custom formatters can be loaded configured using the C<formatter_class> 935parameter to L</new>. 936 937=back 938 939=head1 SUBCLASSING 940 941If you can't configure C<TAP::Harness> to do exactly what you want, and writing 942a plugin isn't an option, consider extending it. It is designed to be (mostly) 943easy to subclass, though the cases when sub-classing is necessary should be few 944and far between. 945 946=head2 Methods 947 948The following methods are ones you may wish to override if you want to 949subclass C<TAP::Harness>. 950 951=over 4 952 953=item L</new> 954 955=item L</runtests> 956 957=item L</summary> 958 959=back 960 961=cut 962 963=head1 REPLACING 964 965If you like the C<prove> utility and L<TAP::Parser> but you want your 966own harness, all you need to do is write one and provide C<new> and 967C<runtests> methods. Then you can use the C<prove> utility like so: 968 969 prove --harness My::Test::Harness 970 971Note that while C<prove> accepts a list of tests (or things to be 972tested), C<new> has a fairly rich set of arguments. You'll probably want 973to read over this code carefully to see how all of them are being used. 974 975=head1 SEE ALSO 976 977L<Test::Harness> 978 979=cut 980 981# vim:ts=4:sw=4:et:sta 982