B.1 测试函数

 
test name
test name quiet|normal|verbose
test ("name", "quiet|normal|verbose", fid)
test ("name", "quiet|normal|verbose", fname)
success = test (…)
[n, nmax, nxfail, nbug, nskip, nrtskip, nregression] = test (…)
[code, idx] = test ("name", "grabdemo")
test ([], "explain", fid)
test ([], "explain", fname)

从加载路径中匹配 name 的第一个文件执行内置自测试。

test 可以以命令形式或函数形式调用。测试的具体操作由模式(交互式或批处理)、报告级别("quiet""normal""verbose")以及是否使用日志文件或汇总输出变量的组合决定。

当从命令行调用 test 时,默认模式是交互式的。在此模式下,测试将一直运行,直到遇到第一个错误,或者所有测试都成功完成。在批处理模式下,无论是否有任何失败,所有测试都会运行,并收集结果以供报告。需要用户交互的测试(即演示块)永远不会在批处理模式下运行。

批处理模式可通过以下任一方式启用:1) 使用第三个参数 fnamefid 指定日志文件,或 2) 请求输出参数,例如 successn 等。

可选的第二个参数确定要生成的输出量以及要运行的测试类型。默认值为 "normal"。指定输出参数将禁止打印最终汇总消息和任何中间警告,除非启用了详细报告。

"quiet"

当所有测试通过时打印汇总消息,或者当发生失败时打印包含第一个失败测试结果的错误。不运行需要用户交互的测试。

"normal"

在测试执行期间显示关于跳过测试或失败 xtest 的警告消息。当所有测试通过时打印汇总消息,或者当发生失败时打印包含第一个失败测试结果的错误。不运行需要用户交互的测试。

"verbose"

执行前显示测试。打印所有警告消息。在交互模式下,运行所有测试,包括那些需要用户交互的测试。

可选的第三个输入参数指定应将测试结果写入的日志文件。日志文件可以是字符串(fname)或打开的文件描述符 ID(fid)。要启用批处理但仍将结果打印到屏幕,请使用 stdout 作为 fid

当仅使用单个输出参数 success 调用时,如果所有测试都成功,test 返回 true。如果使用多个输出参数调用,则返回成功测试的数量(n)、文件中的测试总数(nmax)、xtest 失败的数量(nxfail)、由于已知错误而失败的测试数(nbug)、由于缺少功能而跳过的测试数(nskip)、由于运行时条件而跳过的测试数(nrtskip)以及回归次数(nregression)。

示例

test sind
⇒ 
PASSES 5 out of 5 tests

[n, nmax] = test ("sind")
⇒ 
n =  5
nmax =  5

附加调用语法

如果第二个参数是字符串 "grabdemo",则会提取任何内置演示块的内容,但不执行。所有代码块的文本被连接起来,作为 code 返回,idx 是每个演示块结束位置构成的向量。有关从文件中提取演示块的更简单方法,请参见 example

如果第二个参数是 "explain",则忽略 name,并将关于 test 输出报告中使用的行标记的说明写入由 fnamefid 指定的文件。

另请参阅: assertfaildemoexampleerror

test 扫描指定的脚本文件,查找以标识符 ‘%!’ 开头的行。此前缀被剥离,该行的其余部分通过 Octave 解释器处理。如果代码生成错误,则该测试被视为失败。

由于 eval() 会在遇到第一个错误时停止,因此您必须将测试分成多个块,每个块中的内容单独求值。块通过在 ‘%!’ 之后紧跟有效的关键字(如 testfunctionassert)来引入。块的缩进规则与 Python 类似。以 ‘%!<空白字符>’ 开头的行属于前一个块的一部分。

例如:

%!test error ("this test fails!")
%!test "test doesn't fail.  it doesn't generate an error"

当测试失败时,您将看到类似以下内容:

  ***** test error ("this test fails!")
!!!!! test failed
this test fails!

通常,要测试某段代码是否工作正常,您需要断言它产生了正确的值。一个真实的测试可能如下所示:

%!test
%! a = [1, 2, 3; 4, 5, 6]; B = [1; 2];
%! expect = [ a ; 2*a ];
%! get = kron (b, a);
%! if (any (size (expect) != size (get)))
%!   error ("wrong size: expected %d,%d but got %d,%d",
%!          size (expect), size (get));
%! elseif (any (any (expect != get)))
%!   error ("didn't get what was expected.");
%! endif

为了简化该过程,请使用 assert 函数。例如,使用 assert 后,上述测试简化为:

%!test
%! a = [1, 2, 3; 4, 5, 6]; b = [1; 2];
%! assert (kron (b, a), [ a; 2*a ]);

assert 可以接受一个容差,以便您可以进行绝对或相对比较。例如,以下所有断言都成功:

%!test assert (1+eps, 1, 2*eps)           # 绝对误差
%!test assert (100+100*eps, 100, -2*eps)  # 相对误差

您也可以自行进行比较,但仍让 assert 生成错误:

%!test assert (isempty ([]))
%!test assert ([1, 2; 3, 4] > 0)

由于 assert 在测试块中如此频繁地单独使用,因此有一种简写形式:

%!assert (...)

它等价于:

%!test assert (...)

有时,一组测试将依赖于 Octave 中可选功能的支持。在测试此类块之前,必须检查所需功能是否可用。只有在 Octave 编译时具有 HAVE_XXX 功能时,%!testif HAVE_XXX 块才会运行。例如,稀疏奇异值分解 svds() 依赖于 ARPACK 库。所有 svds 的测试都以

%!testif HAVE_ARPACK

开头。查看 config.h__octave_config_info__ ("build_features") 可以了解一些可能的检查值。

有时在开发过程中,存在一个本应通过但已知会失败的测试。您仍然希望保留该测试,因为当最终代码准备就绪时该测试应该通过,但您可能无法立即修复它。为了避免对这些已知失败产生不必要的错误报告,请使用 xtest 而不是 test 来标记该块:

%!xtest assert (1==0)
%!xtest fail ("success=1", "error")

在这种情况下,测试将运行,任何失败都会被报告。但是,测试不会中止,后续测试块将正常处理。xtest 的另一个用途是用于统计测试,这些测试大多数时候应该通过,但偶尔已知会失败。

每个块在自己的函数环境中求值,这意味着一个块中定义的变量不会自动与其他块共享。如果您确实想共享变量,则必须在使用它们之前将其声明为 shared。例如,以下内容声明变量 a,赋予其初始值(默认为空),然后在后续的几个测试中使用它。

%!shared a
%! a = [1, 2, 3; 4, 5, 6];
%!assert (kron ([1; 2], a), [ a; 2*a ])
%!assert (kron ([1, 2], a), [ a, 2*a ])
%!assert (kron ([1,2; 3,4], a), [ a,2*a; 3*a,4*a ])

您可以同时共享多个变量:

%!shared a, b

对共享变量的修改仅在该测试成功时才会从一个测试持续到下一个测试。因此,如果一个测试修改了共享变量,后续测试无法知道该共享变量的预期值,因为先前测试的通过/失败状态是未知的。出于这个原因,不建议在测试中修改共享变量。

您也可以共享测试函数:

%!function a = fn (b)
%!  a = 2*b;
%!endfunction
%!assert (fn(2), 4)

请注意,当声明新的共享块时,所有先前的变量和值都会丢失。

请记住,%!function 开始一个新块,%!endfunction 结束该块。请注意,在开始一个新块之前,以 ‘%!<空格>’ 开头的行将被当作注释丢弃。以下内容与上面的示例几乎相同,但不做任何事情。

%!function a = fn (b)
%!  a = 2*b;
%!endfunction
%! assert (fn(2), 4)

因为 ‘%!’ 之后有一个空格,所以 assert 语句不会开始一个新块,这一行被视为注释。

错误块和警告块类似于测试块,但只有在代码生成错误时才成功。您可以使用可选的正则表达式 <pattern> 检查错误文本是否正确。例如:

%!error <passes!> error ("this test passes!")

如果代码没有生成错误,则测试失败。例如:

%!error "this is an error because it succeeds."

产生:

  ***** error "this is an error because it succeeds."
!!!!! test failed: no error

尽可能自动化测试非常重要,但有些测试需要用户交互。这些测试可以隔离到演示块中,如果您处于批处理模式,则只有在使用 demotestverbose 选项调用时才会运行。代码在执行前会显示。例如:

%!demo
%! t = [0:0.01:2*pi]; x = sin (t);
%! plot (t, x);
%! # you should now see a sine wave in your figure window

产生:

funcname example 1:
 t = [0:0.01:2*pi]; x = sin (t);
 plot (t, x);
 # you should now see a sine wave in your figure window

Press <enter> to continue:

请注意,演示块不能使用任何共享变量。这是为了确保它们可以独立执行,忽略所有其他测试。

如果您想暂时禁用一个测试块,可以将 # 放在块类型的位置。这将创建一个注释块,该块会在日志文件中回显但不会执行。例如:

%!#demo
%! t = [0:0.01:2*pi]; x = sin (t);
%! plot (t, x);
%! # you should now see a sine wave in your figure window

以下简单的代码片段提供了使用 fail、assert、error 和 xtest 的示例:

function output = must_be_zero (input)
  if (input != 0)
    error ("Nonzero input!")
  endif
  output = input;
endfunction

%!fail ("must_be_zero (1)")
%!assert (must_be_zero (0), 0)
%!error <Nonzero> must_be_zero (1)
%!xtest error ("This code generates an error")

当将其放入文件 must_be_zero.m 并运行测试时,我们看到:

test must_be_zero verbose

⇒ 
>>>>> /path/to/must_be_zero.m
***** fail ("must_be_zero (1)")
***** assert (must_be_zero (0), 0)
***** error <Nonzero> must_be_zero (1)
***** xtest error ("This code generates an error")
!!!!! known failure
This code generates an error
PASSES 3 out of 4 tests (1 expected failure)

块类型总结:

%!test
%!test <MESSAGE>

检查整个块是否正确。如果存在 <MESSAGE>,则该测试块被解释为 xtest

%!testif HAVE_XXX
%!testif HAVE_XXX, HAVE_YYY, …
%!testif …; RUNTIME_COND
%!testif … <MESSAGE>

仅当 Octave 编译时具有 HAVE_XXX 功能时才检查块。RUNTIME_COND 是一个可选表达式,用于在测试执行时评估是否满足某些条件。如果 RUNTIME_COND 为 false,则跳过测试。如果存在 <MESSAGE>,则该测试块被解释为 xtest

%!xtest
%!xtest <MESSAGE>

检查块,报告测试失败但不中止测试。如果存在 <MESSAGE>,则当测试失败时显示该消息文本,如下所示:

!!!!! known bug:  MESSAGE

如果消息是一个整数,它被解释为 Octave bug 跟踪器的 bug ID,并报告为:

!!!!! known bug: https://octave.org/testfailure/?BUG-ID

其中 BUG-ID 是整数形式的 bug 编号。其目的是允许更清晰地记录已知问题。

如果 MESSAGE 是一个以星号开头的整数(例如 *12345),则它被解释为已关闭的 bug 报告的 ID。这通常意味着该测试探查的问题已经解决。如果此类测试失败,它们会被 test 函数报告为回归:

!!!!! regression: https://octave.org/testfailure/?BUG-ID
%!error
%!error <MESSAGE>
%!warning
%!warning <MESSAGE>

检查正确的错误或警告消息。如果提供了 <MESSAGE>,则将其解释为预期匹配错误或警告消息的正则表达式模式。

%!demo

演示仅在交互模式下执行。

%!#

注释。忽略块内的所有内容。

%!shared x,y,z

声明用于多个测试的变量。

%!function

定义用于多个测试的函数。

%!endfunction

关闭函数定义。

%!assert (x, y, tol)
%!assert <MESSAGE> (x, y, tol)
%!fail (CODE, PATTERN)
%!fail <MESSAGE> (CODE, PATTERN)

%!test assert (x, y, tol)%!test fail (CODE, PATTERN) 的简写。如果存在 <MESSAGE>,则该测试块被解释为 xtest

在编写测试时,Octave 约定是以块类型开头的行末尾没有分号。然而,块内的任何代码都是普通的 Octave 代码,通常会有尾部分号。例如:

## 裸块实例化
%!assert (sin (0), 0)

但是:

## 包含普通 Octave 代码的测试块
%!test
%! assert (sin (0), 0);

您还可以为内置函数和您自己的 C++ 函数创建测试脚本。为此,将一个具有裸函数名称(无 .m 扩展名)的文件放在加载路径中的某个目录中,它将被 test 函数发现。或者,您可以直接在 C++ 代码中嵌入测试:

/*
%!test disp ("this is a test")
*/

或:

#if 0
%!test disp ("this is a test")
#endif

但是,在这种情况下,原始源代码需要位于加载路径上,并且用户必须记得输入 test ("funcname.cc")

 
assert (cond)
assert (cond, errmsg)
assert (cond, errmsg, …)
assert (cond, msg_id, errmsg, …)
assert (observed, expected)
assert (observed, expected, tol)

如果指定条件不满足,则产生一个错误。

assert 可以以三种不同的方式调用。

assert (cond)
assert (cond, errmsg)
assert (cond, errmsg, …)
assert (cond, msg_id, errmsg, …)

使用单个参数 cond 调用时,如果 cond 为 false(数值零),assert 会产生一个错误。

任何额外的参数都会传递给 error 函数进行处理。

assert (observed, expected)

如果 observed 与 expected 不相同,则产生一个错误。

请注意,observedexpected 可以是标量、向量、矩阵、字符串、元胞数组或结构体。

assert (observed, expected, tol)

如果 observed 与 expected 不相同则产生一个错误,但对于数值数据的相等性比较使用容差 tol

如果 tol 为正数,则将其用作绝对容差,如果 abs (observed - expected) > abs (tol),则产生错误。

如果 tol 为负数,则将其用作相对容差,如果 abs (observed - expected) > abs (tol * expected),则产生错误。

如果 expected 为零,则 tol 始终被解释为绝对容差。

如果 tol 不是标量,其维度必须与 observedexpected 的维度一致,并且按元素进行测试。

另请参阅: failtesterrorisequal

 
assert_equal (observed, expected)
assert_equal (observed, expected, tol)

如果 observed 与 expected 不相同,则产生一个错误。

assert_equal (observed, expected) 测试两个输入参数在类别、大小和值方面是否相等。所有 Octave 值都支持作为 observedexpected 的输入,并根据以下规则进行相等性比较。

  • 支持异常值(即 Inf、-Inf、NaN、NA)的数值输入按照额外假设它们相等的假设进行处理。还会测试它们的稀疏性和复杂性。
  • 逻辑输入还会额外测试稀疏性。
  • 字符数组使用 strcmp 函数进行比较。
  • 结构体还会额外检查是否具有相同的无序字段名。
  • 元胞数组以逐元素为基础进行递归测试。
  • 函数句柄使用 isequal 函数进行测试。
  • Classdef 对象仅在具有重载的 eq 方法时才被显式测试,并且除非 all (eq (observed, expected), "all") 为 true,否则会产生错误。缺失值的处理严格由重载的 eq 方法定义。

assert_equal (observed, expected, tol) 进一步指定了一个容差值,该值显式用于数值输入。将 tol 指定为 0 等同于仅使用两个输入参数调用 assert_equal。当 observedexpected 是非数值时,任何非零容差也会被忽略。逻辑数组和字符数组不被视为数值。

如果 tol 为正数,则将其用作绝对容差,如果 abs (observed - expected) > abs (tol),则产生错误。

如果 tol 为负数,则将其用作相对容差,如果 abs (observed - expected) > abs (tol * expected),则产生错误。

如果 expected 为零,则 tol 始终被解释为绝对容差。

如果 tol 不是标量,其维度必须与 observedexpected 的维度一致,并且按元素进行测试。

另请参阅: failtesterrorisequaleqstrcmpassert

 
status = fail (code)
status = fail (code, pattern)
status = fail (code, "warning")
status = fail (code, "warning", pattern)

如果 code 失败且错误消息与 pattern 匹配,则返回 true,否则产生一个错误。

code 必须是字符串形式,通过 evalin 函数传递给 Octave 解释器,即(带引号的)字符串常量或字符串变量。

请注意,如果 code 成功运行而不是失败,打印的错误是:

          expected error <.> but got none

如果使用两个参数调用,则仅当 code 失败且错误消息包含 pattern(区分大小写)时,返回值才为 true。如果代码失败但错误与 pattern 中指定的不同,则生成的消息是:

          expected <pattern>
           but got <text of actual error>

尖括号不是输出的一部分。

当使用 "warning" 选项调用时,如果执行代码没有产生警告,fail 将产生一个错误。

另请参阅: asserterror


版权所有 © 2024-2026 Octave中文网

ICP备案/许可证号:黑ICP备2024030411号-4