summaryrefslogtreecommitdiffstats
path: root/upstream/debian-bookworm/man3/TAP::Harness.3perl
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:43:11 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:43:11 +0000
commitfc22b3d6507c6745911b9dfcc68f1e665ae13dbc (patch)
treece1e3bce06471410239a6f41282e328770aa404a /upstream/debian-bookworm/man3/TAP::Harness.3perl
parentInitial commit. (diff)
downloadmanpages-l10n-fc22b3d6507c6745911b9dfcc68f1e665ae13dbc.tar.xz
manpages-l10n-fc22b3d6507c6745911b9dfcc68f1e665ae13dbc.zip
Adding upstream version 4.22.0.upstream/4.22.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'upstream/debian-bookworm/man3/TAP::Harness.3perl')
-rw-r--r--upstream/debian-bookworm/man3/TAP::Harness.3perl628
1 files changed, 628 insertions, 0 deletions
diff --git a/upstream/debian-bookworm/man3/TAP::Harness.3perl b/upstream/debian-bookworm/man3/TAP::Harness.3perl
new file mode 100644
index 00000000..492a071b
--- /dev/null
+++ b/upstream/debian-bookworm/man3/TAP::Harness.3perl
@@ -0,0 +1,628 @@
+.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.43)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. \*(C+ will
+.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
+.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
+.\" nothing in troff, for use with C<>.
+.tr \(*W-
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+. ds C`
+. ds C'
+'br\}
+.\"
+.\" Escape single quotes in literal strings from groff's Unicode transform.
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\"
+.\" If the F register is >0, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.\"
+.\" Avoid warning from groff about undefined register 'F'.
+.de IX
+..
+.nr rF 0
+.if \n(.g .if rF .nr rF 1
+.if (\n(rF:(\n(.g==0)) \{\
+. if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. if !\nF==2 \{\
+. nr % 0
+. nr F 2
+. \}
+. \}
+.\}
+.rr rF
+.\" ========================================================================
+.\"
+.IX Title "TAP::Harness 3perl"
+.TH TAP::Harness 3perl "2023-11-25" "perl v5.36.0" "Perl Programmers Reference Guide"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.if n .ad l
+.nh
+.SH "NAME"
+TAP::Harness \- Run test scripts with statistics
+.SH "VERSION"
+.IX Header "VERSION"
+Version 3.44
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+This is a simple test harness which allows tests to be run and results
+automatically aggregated and output to \s-1STDOUT.\s0
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+.Vb 3
+\& use TAP::Harness;
+\& my $harness = TAP::Harness\->new( \e%args );
+\& $harness\->runtests(@tests);
+.Ve
+.SH "METHODS"
+.IX Header "METHODS"
+.SS "Class Methods"
+.IX Subsection "Class Methods"
+\fI\f(CI\*(C`new\*(C'\fI\fR
+.IX Subsection "new"
+.PP
+.Vb 5
+\& my %args = (
+\& verbosity => 1,
+\& lib => [ \*(Aqlib\*(Aq, \*(Aqblib/lib\*(Aq, \*(Aqblib/arch\*(Aq ],
+\& )
+\& my $harness = TAP::Harness\->new( \e%args );
+.Ve
+.PP
+The constructor returns a new \f(CW\*(C`TAP::Harness\*(C'\fR object. It accepts an
+optional hashref whose allowed keys are:
+.IP "\(bu" 4
+\&\f(CW\*(C`verbosity\*(C'\fR
+.Sp
+Set the verbosity level:
+.Sp
+.Vb 6
+\& 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.
+.Ve
+.IP "\(bu" 4
+\&\f(CW\*(C`timer\*(C'\fR
+.Sp
+Append run time for each test to output. Uses Time::HiRes if
+available.
+.IP "\(bu" 4
+\&\f(CW\*(C`failures\*(C'\fR
+.Sp
+Show test failures (this is a no-op if \f(CW\*(C`verbose\*(C'\fR is selected).
+.IP "\(bu" 4
+\&\f(CW\*(C`comments\*(C'\fR
+.Sp
+Show test comments (this is a no-op if \f(CW\*(C`verbose\*(C'\fR is selected).
+.IP "\(bu" 4
+\&\f(CW\*(C`show_count\*(C'\fR
+.Sp
+Update the running test count during testing.
+.IP "\(bu" 4
+\&\f(CW\*(C`normalize\*(C'\fR
+.Sp
+Set to a true value to normalize the \s-1TAP\s0 that is emitted in verbose modes.
+.IP "\(bu" 4
+\&\f(CW\*(C`lib\*(C'\fR
+.Sp
+Accepts a scalar value or array ref of scalar values indicating which
+paths to allowed libraries should be included if Perl tests are
+executed. Naturally, this only makes sense in the context of tests
+written in Perl.
+.IP "\(bu" 4
+\&\f(CW\*(C`switches\*(C'\fR
+.Sp
+Accepts a scalar value or array ref of scalar values indicating which
+switches should be included if Perl tests are executed. Naturally, this
+only makes sense in the context of tests written in Perl.
+.IP "\(bu" 4
+\&\f(CW\*(C`test_args\*(C'\fR
+.Sp
+A reference to an \f(CW@INC\fR style array of arguments to be passed to each
+test program.
+.Sp
+.Vb 1
+\& test_args => [\*(Aqfoo\*(Aq, \*(Aqbar\*(Aq],
+.Ve
+.Sp
+if you want to pass different arguments to each test then you should
+pass a hash of arrays, keyed by the alias for each test:
+.Sp
+.Vb 4
+\& test_args => {
+\& my_test => [\*(Aqfoo\*(Aq, \*(Aqbar\*(Aq],
+\& other_test => [\*(Aqbaz\*(Aq],
+\& }
+.Ve
+.IP "\(bu" 4
+\&\f(CW\*(C`color\*(C'\fR
+.Sp
+Attempt to produce color output.
+.IP "\(bu" 4
+\&\f(CW\*(C`exec\*(C'\fR
+.Sp
+Typically, Perl tests are run through this. However, anything which
+spits out \s-1TAP\s0 is fine. You can use this argument to specify the name of
+the program (and optional switches) to run your tests with:
+.Sp
+.Vb 1
+\& exec => [\*(Aq/usr/bin/ruby\*(Aq, \*(Aq\-w\*(Aq]
+.Ve
+.Sp
+You can also pass a subroutine reference in order to determine and
+return the proper program to run based on a given test script. The
+subroutine reference should expect the TAP::Harness object itself as the
+first argument, and the file name as the second argument. It should
+return an array reference containing the command to be run and including
+the test file name. It can also simply return \f(CW\*(C`undef\*(C'\fR, in which case
+TAP::Harness will fall back on executing the test script in Perl:
+.Sp
+.Vb 2
+\& 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$/;
+\& }
+.Ve
+.Sp
+If the subroutine returns a scalar with a newline or a filehandle, it
+will be interpreted as raw \s-1TAP\s0 or as a \s-1TAP\s0 stream, respectively.
+.IP "\(bu" 4
+\&\f(CW\*(C`merge\*(C'\fR
+.Sp
+If \f(CW\*(C`merge\*(C'\fR is true the harness will create parsers that merge \s-1STDOUT\s0
+and \s-1STDERR\s0 together for any processes they start.
+.IP "\(bu" 4
+\&\f(CW\*(C`sources\*(C'\fR
+.Sp
+\&\fI\s-1NEW\s0 to 3.18\fR.
+.Sp
+If set, \f(CW\*(C`sources\*(C'\fR must be a hashref containing the names of the
+TAP::Parser::SourceHandlers to load and/or configure. The values are a
+hash of configuration that will be accessible to the source handlers via
+\&\*(L"config_for\*(R" in TAP::Parser::Source.
+.Sp
+For example:
+.Sp
+.Vb 5
+\& sources => {
+\& Perl => { exec => \*(Aq/path/to/custom/perl\*(Aq },
+\& File => { extensions => [ \*(Aq.tap\*(Aq, \*(Aq.txt\*(Aq ] },
+\& MyCustom => { some => \*(Aqconfig\*(Aq },
+\& }
+.Ve
+.Sp
+The \f(CW\*(C`sources\*(C'\fR parameter affects how \f(CW\*(C`source\*(C'\fR, \f(CW\*(C`tap\*(C'\fR and \f(CW\*(C`exec\*(C'\fR parameters
+are handled.
+.Sp
+For more details, see the \f(CW\*(C`sources\*(C'\fR parameter in \*(L"new\*(R" in TAP::Parser,
+TAP::Parser::Source, and TAP::Parser::IteratorFactory.
+.IP "\(bu" 4
+\&\f(CW\*(C`aggregator_class\*(C'\fR
+.Sp
+The name of the class to use to aggregate test results. The default is
+TAP::Parser::Aggregator.
+.IP "\(bu" 4
+\&\f(CW\*(C`version\*(C'\fR
+.Sp
+\&\fI\s-1NEW\s0 to 3.22\fR.
+.Sp
+Assume this \s-1TAP\s0 version for TAP::Parser instead of default \s-1TAP\s0
+version 12.
+.IP "\(bu" 4
+\&\f(CW\*(C`formatter_class\*(C'\fR
+.Sp
+The name of the class to use to format output. The default is
+TAP::Formatter::Console, or TAP::Formatter::File if the output
+isn't a \s-1TTY.\s0
+.IP "\(bu" 4
+\&\f(CW\*(C`multiplexer_class\*(C'\fR
+.Sp
+The name of the class to use to multiplex tests during parallel testing.
+The default is TAP::Parser::Multiplexer.
+.IP "\(bu" 4
+\&\f(CW\*(C`parser_class\*(C'\fR
+.Sp
+The name of the class to use to parse \s-1TAP.\s0 The default is
+TAP::Parser.
+.IP "\(bu" 4
+\&\f(CW\*(C`scheduler_class\*(C'\fR
+.Sp
+The name of the class to use to schedule test execution. The default is
+TAP::Parser::Scheduler.
+.IP "\(bu" 4
+\&\f(CW\*(C`formatter\*(C'\fR
+.Sp
+If set \f(CW\*(C`formatter\*(C'\fR must be an object that is capable of formatting the
+\&\s-1TAP\s0 output. See TAP::Formatter::Console for an example.
+.IP "\(bu" 4
+\&\f(CW\*(C`errors\*(C'\fR
+.Sp
+If parse errors are found in the \s-1TAP\s0 output, a note of this will be
+made in the summary report. To see all of the parse errors, set this
+argument to true:
+.Sp
+.Vb 1
+\& errors => 1
+.Ve
+.IP "\(bu" 4
+\&\f(CW\*(C`directives\*(C'\fR
+.Sp
+If set to a true value, only test results with directives will be
+displayed. This overrides other settings such as \f(CW\*(C`verbose\*(C'\fR or
+\&\f(CW\*(C`failures\*(C'\fR.
+.IP "\(bu" 4
+\&\f(CW\*(C`ignore_exit\*(C'\fR
+.Sp
+If set to a true value instruct \f(CW\*(C`TAP::Parser\*(C'\fR to ignore exit and wait
+status from test scripts.
+.IP "\(bu" 4
+\&\f(CW\*(C`jobs\*(C'\fR
+.Sp
+The maximum number of parallel tests to run at any time. Which tests
+can be run in parallel is controlled by \f(CW\*(C`rules\*(C'\fR. The default is to
+run only one test at a time.
+.IP "\(bu" 4
+\&\f(CW\*(C`rules\*(C'\fR
+.Sp
+A reference to a hash of rules that control which tests may be executed in
+parallel. If no rules are declared and CPAN::Meta::YAML is available,
+\&\f(CW\*(C`TAP::Harness\*(C'\fR attempts to load rules from a \s-1YAML\s0 file specified by the
+\&\f(CW\*(C`rulesfile\*(C'\fR parameter. If no rules file exists, the default is for all
+tests to be eligible to be run in parallel.
+.Sp
+Here some simple examples. For the full details of the data structure
+and the related glob-style pattern matching, see
+\&\*(L"Rules data structure\*(R" in TAP::Parser::Scheduler.
+.Sp
+.Vb 4
+\& # Run all tests in sequence, except those starting with "p"
+\& $harness\->rules({
+\& par => \*(Aqt/p*.t\*(Aq
+\& });
+\&
+\& # Equivalent YAML file
+\& \-\-\-
+\& par: t/p*.t
+\&
+\& # Run all tests in parallel, except those starting with "p"
+\& $harness\->rules({
+\& seq => [
+\& { seq => \*(Aqt/p*.t\*(Aq },
+\& { par => \*(Aq**\*(Aq },
+\& ],
+\& });
+\&
+\& # Equivalent YAML file
+\& \-\-\-
+\& seq:
+\& \- seq: t/p*.t
+\& \- par: **
+\&
+\& # Run some startup tests in sequence, then some parallel tests than some
+\& # teardown tests in sequence.
+\& $harness\->rules({
+\& seq => [
+\& { seq => \*(Aqt/startup/*.t\*(Aq },
+\& { par => [\*(Aqt/a/*.t\*(Aq,\*(Aqt/b/*.t\*(Aq,\*(Aqt/c/*.t\*(Aq], }
+\& { seq => \*(Aqt/shutdown/*.t\*(Aq },
+\& ],
+\&
+\& });
+\&
+\& # Equivalent YAML file
+\& \-\-\-
+\& seq:
+\& \- seq: t/startup/*.t
+\& \- par:
+\& \- t/a/*.t
+\& \- t/b/*.t
+\& \- t/c/*.t
+\& \- seq: t/shutdown/*.t
+.Ve
+.Sp
+This is an experimental feature and the interface may change.
+.IP "\(bu" 4
+\&\f(CW\*(C`rulesfiles\*(C'\fR
+.Sp
+This specifies where to find a \s-1YAML\s0 file of test scheduling rules. If not
+provided, it looks for a default file to use. It first checks for a file given
+in the \f(CW\*(C`HARNESS_RULESFILE\*(C'\fR environment variable, then it checks for
+\&\fItestrules.yml\fR and then \fIt/testrules.yml\fR.
+.IP "\(bu" 4
+\&\f(CW\*(C`stdout\*(C'\fR
+.Sp
+A filehandle for catching standard output.
+.IP "\(bu" 4
+\&\f(CW\*(C`trap\*(C'\fR
+.Sp
+Attempt to print summary information if run is interrupted by
+\&\s-1SIGINT\s0 (Ctrl-C).
+.PP
+Any keys for which the value is \f(CW\*(C`undef\*(C'\fR will be ignored.
+.SS "Instance Methods"
+.IX Subsection "Instance Methods"
+\fI\f(CI\*(C`runtests\*(C'\fI\fR
+.IX Subsection "runtests"
+.PP
+.Vb 1
+\& $harness\->runtests(@tests);
+.Ve
+.PP
+Accepts an array of \f(CW@tests\fR to be run. This should generally be the
+names of test files, but this is not required. Each element in \f(CW@tests\fR
+will be passed to \f(CW\*(C`TAP::Parser::new()\*(C'\fR as a \f(CW\*(C`source\*(C'\fR. See
+TAP::Parser for more information.
+.PP
+It is possible to provide aliases that will be displayed in place of the
+test name by supplying the test as a reference to an array containing
+\&\f(CW\*(C`[ $test, $alias ]\*(C'\fR:
+.PP
+.Vb 2
+\& $harness\->runtests( [ \*(Aqt/foo.t\*(Aq, \*(AqFoo Once\*(Aq ],
+\& [ \*(Aqt/foo.t\*(Aq, \*(AqFoo Twice\*(Aq ] );
+.Ve
+.PP
+Normally it is an error to attempt to run the same test twice. Aliases
+allow you to overcome this limitation by giving each run of the test a
+unique name.
+.PP
+Tests will be run in the order found.
+.PP
+If the environment variable \f(CW\*(C`PERL_TEST_HARNESS_DUMP_TAP\*(C'\fR is defined it
+should name a directory into which a copy of the raw \s-1TAP\s0 for each test
+will be written. \s-1TAP\s0 is written to files named for each test.
+Subdirectories will be created as needed.
+.PP
+Returns a TAP::Parser::Aggregator containing the test results.
+.PP
+\fI\f(CI\*(C`summary\*(C'\fI\fR
+.IX Subsection "summary"
+.PP
+.Vb 1
+\& $harness\->summary( $aggregator );
+.Ve
+.PP
+Output the summary for a TAP::Parser::Aggregator.
+.PP
+\fI\f(CI\*(C`aggregate_tests\*(C'\fI\fR
+.IX Subsection "aggregate_tests"
+.PP
+.Vb 1
+\& $harness\->aggregate_tests( $aggregate, @tests );
+.Ve
+.PP
+Run the named tests and display a summary of result. Tests will be run
+in the order found.
+.PP
+Test results will be added to the supplied TAP::Parser::Aggregator.
+\&\f(CW\*(C`aggregate_tests\*(C'\fR may be called multiple times to run several sets of
+tests. Multiple \f(CW\*(C`Test::Harness\*(C'\fR instances may be used to pass results
+to a single aggregator so that different parts of a complex test suite
+may be run using different \f(CW\*(C`TAP::Harness\*(C'\fR settings. This is useful, for
+example, in the case where some tests should run in parallel but others
+are unsuitable for parallel execution.
+.PP
+.Vb 8
+\& 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);
+.Ve
+.PP
+Note that for simpler testing requirements it will often be possible to
+replace the above code with a single call to \f(CW\*(C`runtests\*(C'\fR.
+.PP
+Each element of the \f(CW@tests\fR array is either:
+.IP "\(bu" 4
+the source name of a test to run
+.IP "\(bu" 4
+a reference to a [ source name, display name ] array
+.PP
+In the case of a perl test suite, typically \fIsource names\fR are simply the file
+names of the test scripts to run.
+.PP
+When you supply a separate display name it becomes possible to run a
+test more than once; the display name is effectively the alias by which
+the test is known inside the harness. The harness doesn't care if it
+runs the same test more than once when each invocation uses a
+different name.
+.PP
+\fI\f(CI\*(C`make_scheduler\*(C'\fI\fR
+.IX Subsection "make_scheduler"
+.PP
+Called by the harness when it needs to create a
+TAP::Parser::Scheduler. Override in a subclass to provide an
+alternative scheduler. \f(CW\*(C`make_scheduler\*(C'\fR is passed the list of tests
+that was passed to \f(CW\*(C`aggregate_tests\*(C'\fR.
+.PP
+\fI\f(CI\*(C`jobs\*(C'\fI\fR
+.IX Subsection "jobs"
+.PP
+Gets or sets the number of concurrent test runs the harness is
+handling. By default, this value is 1 \*(-- for parallel testing, this
+should be set higher.
+.PP
+\fI\f(CI\*(C`make_parser\*(C'\fI\fR
+.IX Subsection "make_parser"
+.PP
+Make a new parser and display formatter session. Typically used and/or
+overridden in subclasses.
+.PP
+.Vb 1
+\& my ( $parser, $session ) = $harness\->make_parser;
+.Ve
+.PP
+\fI\f(CI\*(C`finish_parser\*(C'\fI\fR
+.IX Subsection "finish_parser"
+.PP
+Terminate use of a parser. Typically used and/or overridden in
+subclasses. The parser isn't destroyed as a result of this.
+.SH "CONFIGURING"
+.IX Header "CONFIGURING"
+\&\f(CW\*(C`TAP::Harness\*(C'\fR is designed to be easy to configure.
+.SS "Plugins"
+.IX Subsection "Plugins"
+\&\f(CW\*(C`TAP::Parser\*(C'\fR plugins let you change the way \s-1TAP\s0 is \fIinput\fR to and \fIoutput\fR
+from the parser.
+.PP
+TAP::Parser::SourceHandlers handle \s-1TAP\s0 \fIinput\fR. You can configure them
+and load custom handlers using the \f(CW\*(C`sources\*(C'\fR parameter to \*(L"new\*(R".
+.PP
+TAP::Formatters handle \s-1TAP\s0 \fIoutput\fR. You can load custom formatters by
+using the \f(CW\*(C`formatter_class\*(C'\fR parameter to \*(L"new\*(R". To configure a formatter,
+you currently need to instantiate it outside of TAP::Harness and pass it in
+with the \f(CW\*(C`formatter\*(C'\fR parameter to \*(L"new\*(R". This \fImay\fR be addressed by adding
+a \fIformatters\fR parameter to \*(L"new\*(R" in the future.
+.ie n .SS """Module::Build"""
+.el .SS "\f(CWModule::Build\fP"
+.IX Subsection "Module::Build"
+Module::Build version \f(CW0.30\fR supports \f(CW\*(C`TAP::Harness\*(C'\fR.
+.PP
+To load \f(CW\*(C`TAP::Harness\*(C'\fR plugins, you'll need to use the \f(CW\*(C`tap_harness_args\*(C'\fR
+parameter to \f(CW\*(C`new\*(C'\fR, typically from your \f(CW\*(C`Build.PL\*(C'\fR. For example:
+.PP
+.Vb 10
+\& Module::Build\->new(
+\& module_name => \*(AqMyApp\*(Aq,
+\& test_file_exts => [qw(.t .tap .txt)],
+\& use_tap_harness => 1,
+\& tap_harness_args => {
+\& sources => {
+\& MyCustom => {},
+\& File => {
+\& extensions => [\*(Aq.tap\*(Aq, \*(Aq.txt\*(Aq],
+\& },
+\& },
+\& formatter_class => \*(AqTAP::Formatter::HTML\*(Aq,
+\& },
+\& build_requires => {
+\& \*(AqModule::Build\*(Aq => \*(Aq0.30\*(Aq,
+\& \*(AqTAP::Harness\*(Aq => \*(Aq3.18\*(Aq,
+\& },
+\& )\->create_build_script;
+.Ve
+.PP
+See \*(L"new\*(R"
+.ie n .SS """ExtUtils::MakeMaker"""
+.el .SS "\f(CWExtUtils::MakeMaker\fP"
+.IX Subsection "ExtUtils::MakeMaker"
+ExtUtils::MakeMaker does not support TAP::Harness out-of-the-box.
+.ie n .SS """prove"""
+.el .SS "\f(CWprove\fP"
+.IX Subsection "prove"
+prove supports \f(CW\*(C`TAP::Harness\*(C'\fR plugins, and has a plugin system of its
+own. See \*(L"\s-1FORMATTERS\*(R"\s0 in prove, \*(L"\s-1SOURCE HANDLERS\*(R"\s0 in prove and App::Prove
+for more details.
+.SH "WRITING PLUGINS"
+.IX Header "WRITING PLUGINS"
+If you can't configure \f(CW\*(C`TAP::Harness\*(C'\fR to do what you want, and you can't find
+an existing plugin, consider writing one.
+.PP
+The two primary use cases supported by TAP::Harness for plugins are \fIinput\fR
+and \fIoutput\fR:
+.IP "Customize how \s-1TAP\s0 gets into the parser" 2
+.IX Item "Customize how TAP gets into the parser"
+To do this, you can either extend an existing TAP::Parser::SourceHandler,
+or write your own. It's a pretty simple \s-1API,\s0 and they can be loaded and
+configured using the \f(CW\*(C`sources\*(C'\fR parameter to \*(L"new\*(R".
+.IP "Customize how \s-1TAP\s0 results are output from the parser" 2
+.IX Item "Customize how TAP results are output from the parser"
+To do this, you can either extend an existing TAP::Formatter, or write your
+own. Writing formatters are a bit more involved than writing a
+\&\fISourceHandler\fR, as you'll need to understand the TAP::Parser \s-1API.\s0 A
+good place to start is by understanding how \*(L"aggregate_tests\*(R" works.
+.Sp
+Custom formatters can be loaded configured using the \f(CW\*(C`formatter_class\*(C'\fR
+parameter to \*(L"new\*(R".
+.SH "SUBCLASSING"
+.IX Header "SUBCLASSING"
+If you can't configure \f(CW\*(C`TAP::Harness\*(C'\fR to do exactly what you want, and writing
+a plugin isn't an option, consider extending it. It is designed to be (mostly)
+easy to subclass, though the cases when sub-classing is necessary should be few
+and far between.
+.SS "Methods"
+.IX Subsection "Methods"
+The following methods are ones you may wish to override if you want to
+subclass \f(CW\*(C`TAP::Harness\*(C'\fR.
+.ie n .IP """new""" 4
+.el .IP "``new''" 4
+.IX Item "new"
+.PD 0
+.ie n .IP """runtests""" 4
+.el .IP "``runtests''" 4
+.IX Item "runtests"
+.ie n .IP """summary""" 4
+.el .IP "``summary''" 4
+.IX Item "summary"
+.PD
+.SH "REPLACING"
+.IX Header "REPLACING"
+If you like the \f(CW\*(C`prove\*(C'\fR utility and TAP::Parser but you want your
+own harness, all you need to do is write one and provide \f(CW\*(C`new\*(C'\fR and
+\&\f(CW\*(C`runtests\*(C'\fR methods. Then you can use the \f(CW\*(C`prove\*(C'\fR utility like so:
+.PP
+.Vb 1
+\& prove \-\-harness My::Test::Harness
+.Ve
+.PP
+Note that while \f(CW\*(C`prove\*(C'\fR accepts a list of tests (or things to be
+tested), \f(CW\*(C`new\*(C'\fR has a fairly rich set of arguments. You'll probably want
+to read over this code carefully to see how all of them are being used.
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+Test::Harness