内容

名称

Test2::API - 用于编写基于 Test2 的测试工具的主要接口。

***内部说明***

此包的内部结构随时可能更改! 提供的公共方法不会以向后不兼容的方式更改(一旦发布稳定版本),但底层实现细节可能会更改。不要破坏这里的封装!

目前,实现是创建一个 Test2::API::Instance 对象的单个实例。所有类方法都委托给单个实例。没有对单例的公共访问权限,这是故意的。此包提供的类方法提供了公开暴露的唯一功能。

这样做主要是为了避免 Test::Builder 通过暴露其单例而产生的问题。我们不希望任何人替换此单例、重新祝福它或直接修改其内部结构。如果您需要执行某些操作而无法执行,因为这里施加了限制,请将其报告为问题。如果可能,我们将创建一个方法供您实现您的功能,而不会暴露不应该暴露的内容。

描述

此包导出编写和/或验证测试工具所需的所有函数。使用这些构建块,您可以非常快地开始编写测试工具。您还将获得帮助您测试您编写的工具的工具。

概要

编写工具

context() 方法是您进入 Test2 框架的主要接口。

package My::Ok;
use Test2::API qw/context/;

our @EXPORT = qw/my_ok/;
use base 'Exporter';

# Just like ok() from Test::More
sub my_ok($;$) {
    my ($bool, $name) = @_;
    my $ctx = context(); # Get a context
    $ctx->ok($bool, $name);
    $ctx->release; # Release the context
    return $bool;
}

有关上下文对象上可用方法的列表,请参阅 Test2::API::Context

测试您的工具

intercept { ... } 工具允许您暂时拦截测试系统生成的所有事件

use Test2::API qw/intercept/;

use My::Ok qw/my_ok/;

my $events = intercept {
    # These events are not displayed
    my_ok(1, "pass");
    my_ok(0, "fail");
};

从 1.302178 版本开始,它现在返回一个数组引用,该数组引用也是 Test2::API::InterceptResult 的实例。有关如何最好地使用它的详细信息,请参阅 Test2::API::InterceptResult 文档。

其他 API 函数

use Test2::API qw{
    test2_init_done
    test2_stack
    test2_set_is_end
    test2_get_is_end
    test2_ipc
    test2_formatter_set
    test2_formatter
    test2_is_testing_done
};

my $init  = test2_init_done();
my $stack = test2_stack();
my $ipc   = test2_ipc();

test2_formatter_set($FORMATTER)
my $formatter = test2_formatter();

... And others ...

主要 API 导出

所有导出都是可选的。您必须指定子项才能导入。

use Test2::API qw/context intercept run_subtest/;

这是最常用的导出列表。如果您只是编写工具,那么这可能就是您需要的全部。如果您需要某些内容但在这里找不到,那么您也可以查看 "其他 API 导出"

这些导出缺少 'test2_' 前缀,因为它们非常重要/常用。在 "其他 API 导出" 部分中的导出具有 'test2_' 前缀,以确保它们脱颖而出。

context(...)

用法

$ctx = context()
$ctx = context(%params)

context() 函数始终返回当前上下文。如果已存在活动上下文,则将返回该上下文。如果没有活动上下文,则将生成一个上下文。当生成上下文时,它将默认使用当前运行的子项调用的文件和行号。

请参阅 "Test2::API::Context 中的关键细节",了解有关获取上下文后可以和不能对上下文执行的操作的重要规则。

注意 如果您忽略此函数返回的上下文对象,则此函数将抛出异常。

注意 在 perl 5.14+ 上,使用深度检查来确保没有上下文泄漏。由于 https://rt.perl.org/Public/Bug/Display.html?id=127774,这在较旧的 perl 上无法安全地完成。您可以通过设置 $ENV{T2_CHECK_DEPTH} = 1$Test2::API::DO_DEPTH_CHECK = 1 加载 Test2::API 之前来强制启用它。

可选参数

context 的所有参数都是可选的。

level => $int

如果您必须在比入口点更深的子项中获取上下文,您可以使用此选项来告诉它向后查看多少个额外的堆栈帧。如果未提供此选项,则使用默认值 0

sub third_party_tool {
    my $sub = shift;
    ... # Does not obtain a context
    $sub->();
    ...
}

third_party_tool(sub {
    my $ctx = context(level => 1);
    ...
    $ctx->release;
});
wrapped => $int

如果您需要编写自己的工具来包装对 context() 的调用,并希望它返回一个上下文对象,请使用此选项。

sub my_context {
    my %params = ( wrapped => 0, @_ );
    $params{wrapped}++;
    my $ctx = context(%params);
    ...
    return $ctx;
}

sub my_tool {
    my $ctx = my_context();
    ...
    $ctx->release;
}

如果您不这样做,那么您调用的也检查上下文的工具会注意到它们获取的上下文是在相同的堆栈深度创建的,这将触发保护措施,警告您并销毁现有的上下文。

stack => $stack

通常,context() 会查看全局 hub 堆栈。如果您正在维护自己的 Test2::API::Stack 实例,您可以将其传递进来,以代替全局实例使用。

hub => $hub

如果您想获取特定 hub 的上下文,而不是堆栈顶部的任何上下文,请使用此参数。

on_init => sub { ... }

这允许您提供一个回调子例程,该子例程仅在您的 context() 调用生成新上下文时才会被调用。如果 context() 返回现有上下文,则不会调用回调。传递给回调的唯一参数是上下文对象本身。

sub foo {
    my $ctx = context(on_init => sub { 'will run' });

    my $inner = sub {
        # This callback is not run since we are getting the existing
        # context from our parent sub.
        my $ctx = context(on_init => sub { 'will NOT run' });
        $ctx->release;
    }
    $inner->();

    $ctx->release;
}
on_release => sub { ... }

这允许您提供一个回调子例程,该子例程将在上下文实例被释放时被调用。即使返回现有上下文,此回调也会被添加到返回的上下文中。如果对 context 的多次调用添加了回调,那么当上下文最终被释放时,所有回调都将以相反的顺序被调用。

sub foo {
    my $ctx = context(on_release => sub { 'will run second' });

    my $inner = sub {
        my $ctx = context(on_release => sub { 'will run first' });

        # Neither callback runs on this release
        $ctx->release;
    }
    $inner->();

    # Both callbacks run here.
    $ctx->release;
}

release($;$)

用法

release $ctx;
release $ctx, ...;

这旨在作为一种快捷方式,让您在一个语句中释放上下文并返回一个值。此函数将获取您的上下文和一个可选的返回值。它将释放您的上下文,然后返回您的值。始终假设标量上下文。

sub tool {
    my $ctx = context();
    ...

    return release $ctx, 1;
}

当您想要返回从调用需要查看当前上下文的函数中获得的值时,此工具最有用。

my $ctx = context();
my $out = some_tool(...);
$ctx->release;
return $out;

我们可以将上面最后三行合并如下

my $ctx = context();
release $ctx, some_tool(...);

context_do(&;@)

用法

sub my_tool {
    context_do {
        my $ctx = shift;

        my (@args) = @_;

        $ctx->ok(1, "pass");

        ...

        # No need to call $ctx->release, done for you on scope exit.
    } @_;
}

在您的测试工具中使用它可以为您处理很多样板代码。它将确保获取上下文。它将捕获并重新抛出任何异常。它将确保在您完成时释放上下文。它保留子例程调用上下文(数组、标量、void)。

这是编写测试工具最安全的方式。这只有两个缺点:性能略有下降,以及源代码中多了一些缩进。如果缩进对你来说是个问题,你可以看看下一节。

no_context(&;$)

用法

no_context { ... };
no_context { ... } $hid;
sub my_tool(&) {
    my $code = shift;
    my $ctx = context();
    ...

    no_context {
        # Things in here will not see our current context, they get a new
        # one.

        $code->();
    };

    ...
    $ctx->release;
};

此工具将隐藏提供的代码块的上下文。这意味着在代码块内运行的任何工具,如果获取上下文,将获得一个全新的上下文。新上下文将被嵌套在获取上下文的工具之下的工具继承。

这通常会隐藏顶层集线器的当前上下文。如果你需要隐藏不同集线器的上下文,可以传入可选的 $hid 参数。

intercept(&)

用法

my $events = intercept {
    ok(1, "pass");
    ok(0, "fail");
    ...
};

此函数以代码块作为其唯一参数,并且它有一个原型。它将执行代码块,并在过程中拦截任何生成的事件。它将返回一个包含所有生成的事件对象的数组引用。所有事件都应该是 Test2::Event 的子类。

从 1.302178 版本开始,返回的事件数组被祝福为 Test2::API::InterceptResult 实例。 Test2::API::InterceptResult 提供了一个有用的接口,用于过滤和/或检查事件列表整体或列表中的单个事件。

这旨在帮助你测试你的测试代码。这并非针对仅仅编写测试的人员。

run_subtest(...)

用法

run_subtest($NAME, \&CODE, $BUFFERED, @ARGS)

# or

run_subtest($NAME, \&CODE, \%PARAMS, @ARGS)

这将使用 @args 中的参数运行提供的代码块。此代码块将作为子测试运行。子测试是一个隔离的测试状态,它被压缩成单个 Test2::Event::Subtest 事件,该事件包含子测试内部生成的全部事件。

ARGUMENTS

$NAME

子测试的名称。

\&CODE

在子测试内运行的代码。

$BUFFERED 或 \%PARAMS

如果这是一个简单的标量,它将被视为 'buffered' 设置的布尔值。如果这是一个哈希引用,它将用作参数哈希。参数哈希将用于集线器构建(使用指定的键删除)。

被删除并由 run_subtest 使用的键

'buffered' => $bool

切换缓冲状态。

'inherit_trace' => $bool

通常情况下,子测试集会被推入,并且子测试集被允许为集生成自己的根上下文。当此设置开启时,将为集创建一个根上下文,该上下文与当前上下文的跟踪相同。

如果您的工具在没有用户指定子测试的情况下生成子测试,请将此设置为 true。

'no_fork' => $bool

默认关闭。通常情况下,在子测试中进行分叉实际上会分叉子测试,导致 2 个最终的子测试事件。此参数将关闭此行为,只有原始进程/线程将返回最终的子测试事件。

@ARGS

您想要传递到子测试代码中的任何额外参数。

缓冲与非缓冲(或流式)

通常情况下,集会立即将子测试内部和外部的所有事件发送到格式化程序。有时,最好在子测试完成之前,不要发送子测试内的事件。这通常取决于所使用的格式化程序。

不受此标志影响的事项

在这两种情况下,都会生成事件并存储在数组中。此数组最终用于填充在子测试结束时生成的 Test2::Event::Subtest 事件的 subevents 属性。此标志对此部分没有影响,它始终发生。

在子测试结束时,最终的 Test2::Event::Subtest 事件将发送到格式化程序。

受此标志影响的事项

Test2::Event::Subtest 事件的 buffered 属性将设置为此标志的值。这意味着任何查看事件的格式化程序、监听器等都会知道它是否被缓冲。

依赖于格式化程序的事项

缓冲子测试中的事件可能会或可能不会在发生时发送到格式化程序。如果格式化程序未指定,则默认情况下不发送生成的事件,而是格式化程序可以从subevents属性中提取它们。

格式化程序可以通过实现hide_buffered()方法来指定。如果此方法返回 true,则在缓冲子测试中生成的事件将不会独立于最终子测试事件发送。

这是一个使用示例 Test2::Formatter::TAP 格式化程序。对于非缓冲子测试,事件在生成时呈现。在子测试结束时,将呈现最终的子测试事件,但会忽略subevents属性。对于缓冲子测试,则相反,事件在生成时不会呈现,而是使用subevents属性将它们全部一次性呈现。这在并行运行子测试时很有用,因为如果没有它,子测试的输出将交织在一起。

其他 API 导出

本节中的导出通常不需要。这些都带有“test2_”前缀,以帮助确保它们脱颖而出。您应该查看 "主要 API 导出" 部分,然后再查看这里。本节是“能力越大,责任越大”的地方。如果您不小心使用这些,可能会严重破坏事物。

所有导出都是可选的。您需要在导入时列出您想要的导出。

use Test2::API qw/test2_init_done .../;

状态和初始化状态

这些提供了对内部状态和对象实例的访问。

$bool = test2_init_done()

如果堆栈和 IPC 实例已初始化,则此方法将返回 true。如果它们尚未初始化,则将返回 false。初始化尽可能晚地进行。它在工具请求 IPC 实例、格式化程序或堆栈时立即发生。

$bool = test2_load_done()

这将简单地返回已加载标志的布尔值。如果 Test2 已完成加载,则为 true,否则为 false。首次工具请求上下文时,加载被认为已完成。

test2_set_is_end()
test2_set_is_end($bool)

这用于切换 Test2 对 END 阶段是否已开始的判断。如果没有参数,它将将其设置为 true。如果有参数,它将将其设置为第一个参数的值。

这用于防止在 END 块中使用caller(),这会导致段错误。这仅在某些可能具有多个 END 阶段的持久环境中才需要。

$bool = test2_get_is_end()

检查 Test2 是否认为它处于 END 阶段。

$stack = test2_stack()

这将返回全局 Test2::API::Stack 实例。如果它尚未初始化,它将立即被初始化。

$bool = test2_is_testing_done()

如果测试已完成且不应发送其他事件,则此函数将返回 true。这在警告处理程序等情况下很有用,您可能希望将警告转换为事件,但在测试完成后需要它们开始像普通警告一样工作。

$SIG{__WARN__} = sub {
    my ($warning) = @_;

    if (test2_is_testing_done()) {
        warn @_;
    }
    else {
        my $ctx = context();
        ...
        $ctx->release
    }
}
test2_ipc_disable

禁用 IPC。

$bool = test2_ipc_diabled

检查 IPC 是否已禁用。

test2_ipc_wait_enable()
test2_ipc_wait_disable()
$bool = test2_ipc_wait_enabled()

这些函数可用于打开和关闭 IPC 等待,或检查标志的当前值。

默认情况下,等待处于打开状态。等待将导致父进程/线程等待所有子进程和线程完成,然后再退出。您几乎永远不会想要关闭它。

$bool = test2_no_wait()
test2_no_wait($bool)

不建议:这是一个令人困惑的接口,最好使用 test2_ipc_wait_enable()test2_ipc_wait_disable()test2_ipc_wait_enabled()

此函数可用于获取/设置 no_wait 状态。默认情况下,等待处于打开状态。等待将导致父进程/线程等待所有子进程和线程完成,然后再退出。您几乎永远不会想要关闭它。

$fh = test2_stdout()
$fh = test2_stderr()

这些函数返回测试输出应该写入的文件句柄。它们主要在编写自定义格式化程序和将事件转换为实际输出(TAP 等)的代码时有用。它们将返回原始文件句柄的副本,格式化后的输出可以发送到这些句柄,而不管当前正在运行的测试可能将 STDOUT 和 STDERR 处于何种状态。

test2_reset_io()

重新复制由 test2_stdout()test2_stderr() 返回的内部文件句柄,这些句柄来自当前的 STDOUT 和 STDERR。除了在非常特殊的情况下(例如,您正在测试一个新的格式化程序,并且需要控制格式化程序将输出发送到哪里)之外,您不应该这样做。

行为钩子

这些是钩子,允许您向 Test2 及其上构建的工具采取的操作添加自定义行为。

test2_add_callback_exit(sub { ... })

这可以用来添加一个回调,该回调在所有测试完成后被调用。这对于添加额外的结果来说太晚了,此回调的主要用途是设置退出代码。

test2_add_callback_exit(
    sub {
        my ($context, $exit, \$new_exit) = @_;
        ...
    }
);

传入的 $context 将是 Test2::API::Context 的实例。$exit 参数将是任何内容修改它之前的原始退出代码。$$new_exit 是指向新退出代码的引用。您可以修改它来更改退出代码。请注意,$$new_exit 可能已经不同于 $exit

test2_add_callback_post_load(sub { ... })

添加一个回调,该回调将在 Test2 完成加载时被调用。这意味着回调将被运行一次,第一次获取上下文时。如果 Test2 已经完成加载,那么回调将立即运行。

test2_add_callback_testing_done(sub { ... })

这将您的代码引用作为 Test2 加载完成后对根集线器的后续操作添加。

这本质上是一个帮助程序,用于执行以下操作

test2_add_callback_post_load(sub {
    my $stack = test2_stack();
    $stack->top; # Insure we have a hub
    my ($hub) = Test2::API::test2_stack->all;

    $hub->set_active(1);

    $hub->follow_up(sub { ... }); # <-- Your coderef here
});
test2_add_callback_context_acquire(sub { ... })

添加一个回调,该回调将在每次有人尝试获取上下文时调用。这将在每次调用 context() 时调用。它接收一个参数,即对用于构造上下文的参数哈希的引用。这是您通过直接更改哈希来更改参数的机会。

test2_add_callback_context_acquire(sub {
    my $params = shift;
    $params->{level}++;
});

这是一个非常可怕的 API 函数。请勿在不需要的情况下使用它。这是为了 Test::Builder 和向后兼容性而存在的。出于性能原因,它让您直接操作哈希,而不是返回一个新的哈希。

test2_add_callback_context_init(sub { ... })

添加一个回调,该回调将在每次创建新上下文时调用。回调将接收新创建的上下文作为其唯一参数。

test2_add_callback_context_release(sub { ... })

添加一个回调,该回调将在每次释放上下文时调用。回调将接收已释放的上下文作为其唯一参数。

test2_add_callback_pre_subtest(sub { ... })

添加一个回调,该回调将在每次要运行子测试时调用。回调将接收子测试名称、代码引用和任何参数。

@list = test2_list_context_acquire_callbacks()

返回所有上下文获取回调引用。

@list = test2_list_context_init_callbacks()

返回所有上下文初始化回调引用。

@list = test2_list_context_release_callbacks()

返回所有上下文释放回调引用。

@list = test2_list_exit_callbacks()

返回所有退出回调引用。

@list = test2_list_post_load_callbacks()

返回所有加载后回调引用。

@list = test2_list_pre_subtest_callbacks()

返回所有子测试前回调引用。

test2_add_uuid_via(sub { ... })
$sub = test2_add_uuid_via()

这允许您提供一个 UUID 生成器。如果提供,UUID 将附加到所有事件、中心和上下文。这对于存储、跟踪和链接这些对象很有用。

您提供的子程序应始终返回一个唯一的标识符。大多数东西都期望一个正确的 UUID 字符串,但是 Test2::API 中没有任何东西强制执行这一点。

子程序将接收一个参数,即被标记的项目的类型“context”、“hub”或“event”。将来可能会标记其他项目,在这种情况下,将传入新的字符串。这些纯粹是信息性的,您可以(通常应该)忽略它们。

IPC 和并发

这些让您可以访问或指定 IPC 系统内部。

$bool = test2_has_ipc()

检查是否启用了 IPC。

$ipc = test2_ipc()

这将返回全局 Test2::IPC::Driver 实例。如果尚未初始化,它将立即初始化。

test2_ipc_add_driver($DRIVER)

将 IPC 驱动程序添加到列表中。这会将驱动程序添加到列表的开头。

@drivers = test2_ipc_drivers()

获取 IPC 驱动程序列表。

$bool = test2_ipc_polling()

检查是否启用了轮询。

test2_ipc_enable_polling()

打开轮询。这将在每次创建上下文时从其他进程和线程中剔除事件。

test2_ipc_disable_polling()

关闭 IPC 轮询。

test2_ipc_enable_shm()

遗留代码,目前这是一个返回 0 的空操作;

test2_ipc_set_pending($uniq_val)

告诉其他进程和事件,一个事件正在等待。$uniq_val 应该是一个其他线程/进程不会生成的唯一值。

注意:调用此函数后,test2_ipc_get_pending() 将返回 1。这是故意的,无法避免。

$pending = test2_ipc_get_pending()

如果无法检查(假设有),则返回 -1

如果(很可能)没有待处理的事件,则返回 0。

如果(很可能)有待处理的事件,则返回 1。返回后将重置,其他任何内容都无法看到有待处理的事件。

$timeout = test2_ipc_get_timeout()
test2_ipc_set_timeout($timeout)

获取/设置 IPC 系统的超时值。此超时是 IPC 系统在中止之前等待子进程和线程完成的时间。

默认值为 30 秒。

管理格式化程序

这些允许您访问或指定可以/应该使用的格式化程序。

$formatter = test2_formatter

这将返回全局格式化程序类。这不是一个实例。默认情况下,格式化程序设置为 Test2::Formatter::TAP

您可以使用 T2_FORMATTER 环境变量覆盖此默认值。

通常,环境变量中的值前面会加上 'Test2::Formatter::'

$ T2_FORMATTER='TAP' perl test.t     # Use the Test2::Formatter::TAP formatter
$ T2_FORMATTER='Foo' perl test.t     # Use the Test2::Formatter::Foo formatter

如果您想指定完整的模块名称,请使用 '+' 前缀

$ T2_FORMATTER='+Foo::Bar' perl test.t     # Use the Foo::Bar formatter
test2_formatter_set($class_or_instance)

设置全局格式化程序类。这只能设置一次。注意:这将覆盖 'T2_FORMATTER' 环境变量中指定的任何内容。

@formatters = test2_formatters()

获取所有已加载格式化程序的列表。

test2_formatter_add($class_or_instance)

将格式化程序添加到列表中。最后添加的格式化程序在初始化时使用。如果在初始化后调用此函数,将发出警告。

其他示例

请参阅此发行版中包含的 /Examples/ 目录。

另请参阅

Test2::API::Context - 上下文对象的详细文档。

Test2::IPC - 用于线程/fork 支持的 IPC 系统。

Test2::Formatter - 格式化程序(如 TAP)位于此处。

Test2::Event - 事件位于此命名空间中。

Test2::Hub - 所有事件最终都会通过集线器。自定义集线器是实现 intercept()run_subtest() 的方式。

魔法

此包具有一个 END 块。此 END 块负责根据测试结果设置退出代码。此 END 块还会调用可以添加到此包的回调。

源代码

Test2 的源代码存储库位于 http://github.com/Test-More/test-more/

维护者

Chad Granum <[email protected]>

作者

Chad Granum <[email protected]>

版权

版权所有 2020 Chad Granum <[email protected]>。

本程序是自由软件;您可以根据与 Perl 本身相同的条款重新发布和/或修改它。

请参阅 https://dev.perl5.cn/licenses/