TAP::Parser - 解析 TAP 输出
版本 3.44
use TAP::Parser;
my $parser = TAP::Parser->new( { source => $source } );
while ( my $result = $parser->next ) {
print $result->as_string;
}
TAP::Parser
旨在对 TAP 输出进行正确的解析。有关如何通过此模块运行测试的示例,请参见简单的测试程序 examples/
。
有一个专门用于测试任何协议的维基
它包含 TAP::Parser 食谱
http://testanything.org/testing-with-tap/perl/tap::parser-cookbook.html
new
my $parser = TAP::Parser->new(\%args);
返回一个新的 TAP::Parser
对象。
参数应为一个哈希引用,其中包含以下键之一
source
在 3.18 中更改
这是将输入传递给构造函数的首选方法。
source
用于创建一个 TAP::Parser::Source,该源传递给 "iterator_factory_class",该类反过来会找出如何处理源并为其创建一个 <TAP::Parser::Iterator>。迭代器由解析器用于读取 TAP 流。
要配置 IteratorFactory,请使用下面的 sources
参数。
请注意,source
、tap
和 exec
是互斥的。
tap
在 3.18 中更改
该值应为完整的 TAP 输出。
tap 用于创建一个 TAP::Parser::Source,该源传递给 "iterator_factory_class",该类反过来会找出如何处理源并为其创建一个 <TAP::Parser::Iterator>。迭代器由解析器用于读取 TAP 流。
要配置 IteratorFactory,请使用下面的 sources
参数。
请注意,source
、tap
和 exec
是互斥的。
exec
必须传递一个数组引用。
exec 数组引用用于创建一个 TAP::Parser::Source,该源传递给 "iterator_factory_class",该类反过来会找出如何处理源并为其创建一个 <TAP::Parser::Iterator>。迭代器由解析器用于读取 TAP 流。
默认情况下,TAP::Parser::SourceHandler::Executable 类将创建一个 TAP::Parser::Iterator::Process 对象来处理源代码。这将把数组引用字符串作为命令参数传递给 IPC::Open3::open3
exec => [ '/usr/bin/ruby', 't/my_test.rb' ]
如果提供了任何 test_args
,它们将被追加到命令参数列表的末尾。
要配置 IteratorFactory,请使用下面的 sources
参数。
请注意,source
、tap
和 exec
是互斥的。
以下键是可选的。
sources
3.18 版本新增.
如果设置,sources
必须是一个哈希引用,其中包含要加载和/或配置的 TAP::Parser::SourceHandler 的名称。这些值是一个哈希,其中包含配置信息,这些信息可以通过 "TAP::Parser::Source 中的 config_for" 被源代码处理程序访问。
例如
sources => {
Perl => { exec => '/path/to/custom/perl' },
File => { extensions => [ '.tap', '.txt' ] },
MyCustom => { some => 'config' },
}
这将导致 TAP::Parser
将自定义配置传递给两个内置的源代码处理程序 - TAP::Parser::SourceHandler::Perl,TAP::Parser::SourceHandler::File - 并尝试加载 MyCustom
类。有关更多详细信息,请参阅 "TAP::Parser::IteratorFactory 中的 load_handlers"。
sources
参数会影响 source
、tap
和 exec
参数的处理方式。
有关更多详细信息,请参阅 TAP::Parser::IteratorFactory、TAP::Parser::SourceHandler 及其子类。
callback
如果存在,每个与给定结果类型相对应的回调将在使用 run
方法时以结果作为参数被调用。
my %callbacks = (
test => \&test_callback,
plan => \&plan_callback,
comment => \&comment_callback,
bailout => \&bailout_callback,
unknown => \&unknown_callback,
);
my $aggregator = TAP::Parser::Aggregator->new;
for my $file ( @test_files ) {
my $parser = TAP::Parser->new(
{
source => $file,
callbacks => \%callbacks,
}
);
$parser->run;
$aggregator->add( $file, $parser );
}
switches
如果使用 Perl 文件作为源代码,则可以传递可选的开关,这些开关将在调用 Perl 可执行文件时使用。
my $parser = TAP::Parser->new( {
source => $test_file,
switches => [ '-Ilib' ],
} );
test_args
与 source
和 exec
选项结合使用,以提供对 @ARGV
样式数组的引用,该数组包含要传递给测试程序的参数。
spool
如果传递一个文件句柄,则会将所有解析的 TAP 的副本写入该句柄。
merge
如果为 false,则不会捕获 STDERR(尽管它会被“中继”以使其与 STDOUT 保持某种程度的同步)。
如果为 true,则 STDERR 和 STDOUT 是同一个文件句柄。如果 STDERR 包含任何类似 TAP 格式的内容,这可能会导致中断,但允许完全同步。
此行为的细微差别可能与平台相关,并且将来可能会发生变化。
grammar_class
此选项的引入是为了让您轻松自定义解析器应该使用的语法类。它默认为 TAP::Parser::Grammar。
另请参阅 "make_grammar"。
result_factory_class
此选项是为了让您轻松自定义解析器应使用的结果工厂类。它默认为 TAP::Parser::ResultFactory。
另请参阅 "make_result"。
iterator_factory_class
在 3.18 中更改
此选项是为了让您轻松自定义解析器应使用的迭代器工厂类。它默认为 TAP::Parser::IteratorFactory。
next
my $parser = TAP::Parser->new( { source => $file } );
while ( my $result = $parser->next ) {
print $result->as_string, "\n";
}
此方法一次返回一个解析结果。请注意,它是破坏性的。您无法回溯并检查以前的结果。
如果使用回调,它们将在此调用返回之前发出。
返回的每个结果都是 TAP::Parser::Result 的子类。有关如何使用它们的更多信息,请参阅该模块和相关类。
run
$parser->run;
此方法仅运行解析器并解析所有 TAP。
make_grammar
创建一个新的 TAP::Parser::Grammar 对象并返回它。传递给定的任何参数。
grammar_class
可以自定义,如 "new" 中所述。
make_result
使用解析器的 TAP::Parser::ResultFactory 创建一个新的 TAP::Parser::Result 对象,并返回它。传递给定的任何参数。
result_factory_class
可以自定义,如 "new" 中所述。
make_iterator_factory
3.18 版本新增.
创建一个新的 TAP::Parser::IteratorFactory 对象并返回它。传递给定的任何参数。
iterator_factory_class
可以自定义,如 "new" 中所述。
如果您已经阅读了文档中的这一部分,您已经看到了这一点
while ( my $result = $parser->next ) {
print $result->as_string;
}
返回的每个结果都是 TAP::Parser::Result 子类,称为结果类型。
基本上,您从 TAP 中获取单个结果。六种类型及其示例如下
版本
TAP version 12
计划
1..42
语义
pragma +strict
测试
ok 3 - We should start with some foobar!
注释
# Hope we don't use up the foobar.
中止
Bail out! We ran out of foobar!
未知
... yo, this ain't TAP! ...
每个获取的结果都是不同类型的结果对象。每个结果对象都有通用方法,不同类型可能具有特定于其类型的独特方法。有时,子类可能会覆盖类型方法,但其使用保证是相同的。
type
返回结果类型,例如 comment
或 test
。
as_string
打印令牌的字符串表示形式。但这可能不是确切的输出。测试将添加测试编号(如果不存在),TODO 和 SKIP 指令将大写,并且通常会进行清理。如果您需要令牌的原始文本,请参阅 raw
方法。
raw
返回解析的原始文本行。
is_plan
指示这是否是测试计划行。
is_test
指示这是否是测试行。
is_comment
指示这是否是注释。注释通常仅在将 STDERR 合并到 STDOUT 时才会出现在 TAP 流中。请参阅 merge
选项。
is_bailout
指示这是否是中止行。
is_yaml
指示当前项目是否是 YAML 块。
is_unknown
指示当前行是否可以解析。
is_ok
if ( $result->is_ok ) { ... }
报告给定结果是否已通过。任何不是测试结果的内容都将返回 true。这仅仅是作为一种方便的快捷方式提供,允许您执行以下操作
my $parser = TAP::Parser->new( { source => $source } );
while ( my $result = $parser->next ) {
# only print failing results
print $result->as_string unless $result->is_ok;
}
plan
方法if ( $result->is_plan ) { ... }
如果上述内容评估为 true,则以下方法将在 $result
对象上可用。
plan
if ( $result->is_plan ) {
print $result->plan;
}
这仅仅是 as_string
的同义词。
directive
my $directive = $result->directive;
如果计划中包含 SKIP 指令,则此方法将返回它。
1..0 # SKIP: why bother?
explanation
my $explanation = $result->explanation;
如果计划中包含 SKIP 指令,则此方法将返回解释(如果有)。
pragma
方法if ( $result->is_pragma ) { ... }
如果上述内容评估为 true,则以下方法将在 $result
对象上可用。
pragmas
返回一个 pragma 列表,每个 pragma 都是一个 + 或 - 后跟 pragma 名称。
comment
方法if ( $result->is_comment ) { ... }
如果上述内容评估为 true,则以下方法将在 $result
对象上可用。
comment
if ( $result->is_comment ) {
my $comment = $result->comment;
print "I have something to say: $comment";
}
bailout
方法if ( $result->is_bailout ) { ... }
如果上述内容评估为 true,则以下方法将在 $result
对象上可用。
explanation
if ( $result->is_bailout ) {
my $explanation = $result->explanation;
print "We bailed out because ($explanation)";
}
如果且仅当一个标记是 bailout 标记时,您可以通过此方法获取“解释”。解释是 tap 输出中神秘的“Bail out!”字词之后的文本。
unknown
方法if ( $result->is_unknown ) { ... }
未知结果没有独特的方法。
test
方法if ( $result->is_test ) { ... }
如果上述内容评估为 true,则以下方法将在 $result
对象上可用。
ok
my $ok = $result->ok;
返回 ok
或 not ok
状态的文字文本。
number
my $test_number = $result->number;
返回测试的编号,即使原始 TAP 输出没有提供该编号。
description
my $description = $result->description;
返回测试的描述(如果有)。这是测试编号之后但指令之前的部分。
directive
my $directive = $result->directive;
如果测试行中存在任何指令,则返回 TODO
或 SKIP
。
explanation
my $explanation = $result->explanation;
如果测试具有 TODO
或 SKIP
指令,则此方法将返回随附的解释(如果有)。
not ok 17 - 'Pigs can fly' # TODO not enough acid
对于上面的行,解释是 not enough acid。
is_ok
if ( $result->is_ok ) { ... }
返回一个布尔值,指示测试是否通过。请记住,对于 TODO 测试,测试始终通过。
注意:这以前是 passed
。后一种方法已弃用,并将发出警告。
is_actual_ok
if ( $result->is_actual_ok ) { ... }
返回一个布尔值,指示测试是否通过,无论其 TODO 状态如何。
注意:这以前是 actual_passed
。后一种方法已弃用,并将发出警告。
is_unplanned
if ( $test->is_unplanned ) { ... }
如果测试编号大于计划测试的编号,则此方法将返回 true。未计划的测试将始终为 is_ok
返回 false,无论测试是否 has_todo
(有关此的更多信息,请参阅 TAP::Parser::Result::Test)。
has_skip
if ( $result->has_skip ) { ... }
返回一个布尔值,指示此测试是否具有 SKIP 指令。
has_todo
if ( $result->has_todo ) { ... }
返回一个布尔值,指示此测试是否具有 TODO 指令。
请注意,TODO 测试始终通过。如果您需要知道它们是否真的通过,请检查 is_actual_ok
方法。
in_todo
if ( $parser->in_todo ) { ... }
当最近的结果为 TODO 时为真。在返回 TODO 结果之前变为真,并在返回下一个非 TODO 测试之前一直保持为真。
解析 TAP 后,您可以使用多种方法深入了解结果并确定对您有意义的内容。
这些结果指的是运行的单个测试。
passed
my @passed = $parser->passed; # the test numbers which passed
my $passed = $parser->passed; # the number of tests which passed
此方法让您知道哪些(或多少)测试通过。如果测试失败但有 TODO 指令,则将计为通过测试。
failed
my @failed = $parser->failed; # the test numbers which failed
my $failed = $parser->failed; # the number of tests which failed
此方法让您知道哪些(或多少)测试失败。如果测试通过但有 TODO 指令,则 **不会** 计为失败测试。
actual_passed
# the test numbers which actually passed
my @actual_passed = $parser->actual_passed;
# the number of tests which actually passed
my $actual_passed = $parser->actual_passed;
此方法让您知道哪些(或多少)测试实际通过,无论是否找到 TODO 指令。
actual_ok
此方法是 actual_passed
的同义词。
actual_failed
# the test numbers which actually failed
my @actual_failed = $parser->actual_failed;
# the number of tests which actually failed
my $actual_failed = $parser->actual_failed;
此方法让您知道哪些(或多少)测试实际失败,无论是否找到 TODO 指令。
todo
my @todo = $parser->todo; # the test numbers with todo directives
my $todo = $parser->todo; # the number of tests with todo directives
此方法让您知道哪些(或多少)测试有 TODO 指令。
todo_passed
# the test numbers which unexpectedly succeeded
my @todo_passed = $parser->todo_passed;
# the number of tests which unexpectedly succeeded
my $todo_passed = $parser->todo_passed;
此方法让您知道哪些(或多少)测试实际通过,但被声明为“TODO”测试。
todo_failed
# deprecated in favor of 'todo_passed'. This method was horribly misnamed.
这是一个命名错误的方法。它指示哪些 TODO 测试意外成功。现在将发出警告并调用 todo_passed
。
skipped
my @skipped = $parser->skipped; # the test numbers with SKIP directives
my $skipped = $parser->skipped; # the number of tests with SKIP directives
此方法让您知道哪些(或多少)测试有 SKIP 指令。
pragma
获取或设置 pragma。要获取 pragma 的状态
if ( $p->pragma('strict') ) {
# be strict
}
要设置 pragma 的状态
$p->pragma('strict', 1); # enable strict mode
pragmas
获取当前启用的所有 pragma 的列表
my @pragmas_enabled = $p->pragmas;
这些结果是关于单个测试程序总结果的“元”信息。
plan
my $plan = $parser->plan;
如果找到,则返回测试计划。
good_plan
已弃用。请使用 is_good_plan
代替。
is_good_plan
if ( $parser->is_good_plan ) { ... }
返回一个布尔值,指示计划的测试数量是否与实际运行的测试数量匹配。
注意: 此方法以前称为 good_plan
。 后者已弃用,将发出警告。
既然我们谈到这个话题...
tests_planned
print $parser->tests_planned;
返回根据计划计划的测试数量。 例如,计划为“1..17”意味着计划了 17 个测试。
tests_run
print $parser->tests_run;
返回实际运行的测试数量。 希望这将与 $parser->tests_planned
的数量匹配。
skip_all
如果所有测试都被跳过,则返回一个真值(实际上是跳过的原因)。
start_time
返回创建解析器时的挂钟时间。
end_time
返回看到 TAP 输入结束时的挂钟时间。
start_times
返回创建解析器时的 CPU 时间(如 "perlfunc 中的 times")。
end_times
返回看到 TAP 输入结束时的 CPU 时间(如 "perlfunc 中的 times")。
has_problems
if ( $parser->has_problems ) {
...
}
这是一个“万能”方法,如果任何测试当前失败,任何 TODO 测试意外成功或出现任何解析错误,则返回 true。
version
$parser->version;
解析器完成后,这将返回解析的 TAP 的版本号。 版本号是在 TAP 版本 13 中引入的,因此如果找不到版本号,则假定为版本 12。
exit
$parser->exit;
解析器完成后,这将返回退出状态。 如果解析器运行了可执行文件,则返回可执行文件的退出状态。
wait
$parser->wait;
解析器完成后,这将返回等待状态。 如果解析器运行了可执行文件,则返回可执行文件的等待状态。 否则,这仅返回 exit
状态。
ignore_exit
$parser->ignore_exit(1);
告诉解析器在确定测试是否通过时忽略测试的退出状态。通常,退出状态非零的测试被认为是失败的,即使所有单独的测试都通过了。在无法控制测试脚本的退出值的情况下,使用此选项来忽略它。
parse_errors
my @errors = $parser->parse_errors; # the parser errors
my $errors = $parser->parse_errors; # the number of parser_errors
幸运的是,所有 TAP 输出都是完美的。如果它不是,此方法将返回解析器错误。请注意,解析器无法识别的垃圾行不是
错误。这允许此解析器处理 TAP 的未来版本。以下是解析器报告的所有 TAP 错误
计划位置错误
计划(例如,“1..5”)只能出现在 TAP 输出的开头或结尾。
没有计划
必须有一个计划!
多个计划
1..3
ok 1 - input file opened
not ok 2 - first line of the input valid # todo some data
ok 3 read the rest of the file
1..3
好吧,很有趣。不要那样做。
测试编号顺序错误
1..3
ok 1 - input file opened
not ok 2 - first line of the input valid # todo some data
ok 2 read the rest of the file
上面的最后一行测试应该写“3”而不是“2”。
请注意,某些行有测试编号,而另一些行没有测试编号是完全可以接受的。但是,当找到测试编号时,它必须按顺序排列。以下也是错误的
1..3
ok 1 - input file opened
not ok - first line of the input valid # todo some data
ok 2 read the rest of the file
但这不是
1..3
ok - input file opened
not ok - first line of the input valid # todo some data
ok 3 read the rest of the file
get_select_handles
获取可以传递给select
的文件句柄列表,以确定此解析器的就绪状态。
delete_spool
删除并返回卷轴。
my $fh = $parser->delete_spool;
如前所述,可以在TAP::Parser
构造函数中添加一个“回调”键。如果存在,如果使用run
方法,则将调用与给定结果类型相对应的每个回调,并将结果作为参数。回调应为子例程引用(或匿名子例程),它以解析器结果作为参数调用。
my %callbacks = (
test => \&test_callback,
plan => \&plan_callback,
comment => \&comment_callback,
bailout => \&bailout_callback,
unknown => \&unknown_callback,
);
my $aggregator = TAP::Parser::Aggregator->new;
for my $file ( @test_files ) {
my $parser = TAP::Parser->new(
{
source => $file,
callbacks => \%callbacks,
}
);
$parser->run;
$aggregator->add( $file, $parser );
}
回调也可以这样添加
$parser->callback( test => \&test_callback );
$parser->callback( plan => \&plan_callback );
以下键允许回调。这些键区分大小写。
测试
如果$result->is_test
返回 true,则调用。
version
如果$result->is_version
返回 true,则调用。
plan
如果$result->is_plan
返回 true,则调用。
comment
如果$result->is_comment
返回 true,则调用。
bailout
如果 $result->is_unknown
返回 true,则调用。
yaml
如果 $result->is_yaml
返回 true,则调用。
unknown
如果 $result->is_unknown
返回 true,则调用。
ELSE
如果结果没有为其定义回调,则将调用此回调。因此,如果所有先前的结果类型都指定为回调,则此回调将永远不会被调用。
ALL
此回调将始终被调用,并且将在调用上述回调之一后的每个结果中发生。例如,如果加载了 Term::ANSIColor,则可以使用以下方法为测试输出着色
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";
},
);
EOF
当没有更多行要解析时调用。由于没有伴随的 TAP::Parser::Result 对象,因此传递 TAP::Parser
对象。
如果您正在寻找 EBNF 语法,请参阅 TAP::Parser::Grammar。
Perl-QA 列表试图确保与 Test::Harness 的向后兼容性。但是,有一些细微的差异。
TODO 计划
Test::Harness 的一个鲜为人知的功能是它支持计划中的 TODO 列表
1..2 todo 2
ok 1 - We have liftoff
not ok 2 - Anti-gravity device activated
在 Test::Harness 下,测试编号 2 将通过,因为它在计划行中被列为 TODO 测试。但是,我们不知道有人实际使用此功能,并且不鼓励硬编码测试编号,因为添加测试并破坏测试编号序列非常容易。这使得测试套件非常脆弱。相反,应该使用以下方法
1..2
ok 1 - We have liftoff
not ok 2 - Anti-gravity device activated # TODO
'缺少' 测试
这种情况很少发生,但有时一个 harness 可能会遇到“缺少测试”
ok 1
ok 2
ok 15
ok 16
ok 17
Test::Harness 会报告测试 3-14 失败。对于 TAP::Parser
,这些测试不被视为失败,因为它们从未运行过。它们被报告为解析失败(测试顺序错误)。
如果您发现需要提供自定义功能(就像使用 Test::Harness::Straps 一样),那么您很幸运:TAP::Parser
及其朋友旨在易于插入和/或子类化。
在您开始之前,了解一些事情很重要
所有 TAP::*
对象都继承自 TAP::Object。
许多 TAP::*
类都有一个子类化部分来指导您。
请注意,TAP::Parser
被设计为中央“制造商” - 也就是说:它负责创建 TAP::Parser::*
命名空间中的大多数新对象。
这使得您可以拥有一个配置应该使用哪些子类的单一位置,这意味着在许多情况下,您会发现您只需要对解析器的组件之一进行子类化。
此规则的例外是SourceHandlers和Iterators,但它们都是使用可自定义的IteratorFactory创建的。
通过子类化,您最终可能会覆盖未记录的方法。这本身并不是一件坏事,但请注意,未记录的方法可能会在不同版本之间发生更改而不会发出警告 - 我们无法保证向后兼容性。如果任何已记录的方法需要更改,它将首先被弃用,并在以后的版本中更改。
TAP 解析器从单个原始来源的 TAP 中获取输入,该来源可以来自任何地方(文件、可执行文件、数据库、IO 处理程序、URI 等)。该来源被捆绑在一个TAP::Parser::Source对象中,该对象收集有关它的元数据。然后,解析器使用TAP::Parser::IteratorFactory来确定使用哪个TAP::Parser::SourceHandler通过"迭代器"将原始来源转换为 TAP 流。
如果您只是想让TAP::Parser
处理新的 TAP 来源,您可能不需要对TAP::Parser
本身进行子类化。相反,您需要创建一个新的TAP::Parser::SourceHandler类,并将其插入解析器,使用sources参数传递给"new"。在您开始编写一个之前,请阅读TAP::Parser::IteratorFactory以了解该系统的工作原理。
如果您发现您确实需要使用自己的迭代器工厂,您仍然可以通过设置"iterator_factory_class"来做到这一点,而无需对TAP::Parser
进行子类化。
如果您只需要在创建时自定义对象,请对TAP::Parser进行子类化并覆盖"make_iterator_factory"。
请注意,make_source
和make_perl_source
已被弃用,现在已删除。
TAP 解析器使用迭代器来循环遍历从其给定的源读取的 TAP 流。默认情况下,有几种类型的迭代器可用,它们都是TAP::Parser::Iterator的子类。选择使用哪个迭代器是迭代器工厂的责任,尽管它只是委托给它使用的源处理程序。
如果您正在编写自己的TAP::Parser::SourceHandler,您可能也需要创建自己的迭代器。如果是这样,您需要子类化TAP::Parser::Iterator。
请注意,"make_iterator"已弃用,现已删除。
TAP 解析器在遍历输入流时创建TAP::Parser::Result。有相当多的结果类型可用;选择使用哪个类是结果工厂的责任。
要创建您自己的结果类型,您有两个选择
子类化TAP::Parser::Result,并将您的新结果类型/类注册到默认的TAP::Parser::ResultFactory。
子类化TAP::Parser::ResultFactory 本身,并实现您自己的TAP::Parser::Result 创建逻辑。然后,您需要通过设置result_factory_class
参数来定制解析器使用的类。有关更多详细信息,请参见"new"。
如果您需要在创建时定制对象,请子类化TAP::Parser 并覆盖"make_result"。
TAP::Parser::Grammar 是解析器的核心。它对 TAP 输入流进行标记化,并生成结果。如果您需要定制其行为,您可能应该首先熟悉源代码。讲座就到这里。
子类化TAP::Parser::Grammar,并通过设置grammar_class
参数来定制您的解析器。有关更多详细信息,请参见"new"。
如果您需要在创建时定制对象,请子类化TAP::Parser 并覆盖"make_grammar"
以下所有人员都提供了帮助。错误报告、补丁、(不)道德支持或仅仅是鼓励的话语都已出现。
Michael Schwern
Andy Lester
chromatic
GEOFFR
Shlomi Fish
Torsten Schoenfeld
Jerry Gay
亚里士多德
Adam Kennedy
Yves Orton
Adrian Howard
Sean & Lil
Andreas J. Koenig
Florian Ragwitz
Corion
Mark Stosberg
Matt Kraai
David Wheeler
Alex Vandiver
Cosimo Streppone
Ville Skyttä
Curtis "Ovid" Poe <[email protected]>
Andy Armstong <[email protected]>
Eric Wilhelm @ <ewilhelm at cpan dot org>
Michael Peters <mpeters at plusthree dot com>
Leif Eriksen <leif dot eriksen at bigpond dot com>
Steve Purkis <[email protected]>
Nicholas Clark <[email protected]>
Lee Johnson <notfadeaway at btinternet dot com>
Philippe Bruhat <[email protected]>
请将任何错误或功能请求报告给[email protected]
,或通过网页界面在http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-Harness报告。我们会收到通知,然后您会在我们进行更改时自动收到有关您错误的进度通知。
显然,包含补丁的错误是最好的。如果您愿意,您可以通过匿名签出最新版本来针对 bleed 进行修补
git clone git://github.com/Perl-Toolchain-Gang/Test-Harness.git
版权所有 2006-2008 Curtis "Ovid" Poe,保留所有权利。
本程序是自由软件;您可以在 Perl 本身相同的条款下重新发布和/或修改它。